diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-22 19:54:53 (GMT) |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-22 20:01:40 (GMT) |
commit | ae19ffbadc1b2100285a5b5b3d0a4e0a11390904 (patch) | |
tree | 3c2086ab67398a019089a47ca3f362a4bc6db74f /drivers/staging/dream/camera/msm_vfe7x.c | |
parent | 34e84f39a27d059a3e6ec6e8b94aafa702e6f220 (diff) | |
parent | 9173a8ef24a6b1b8031507b35b8ffe5f85a87692 (diff) | |
download | linux-fsl-qoriq-ae19ffbadc1b2100285a5b5b3d0a4e0a11390904.tar.xz |
Merge branch 'master' into for-linus
Diffstat (limited to 'drivers/staging/dream/camera/msm_vfe7x.c')
-rw-r--r-- | drivers/staging/dream/camera/msm_vfe7x.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/drivers/staging/dream/camera/msm_vfe7x.c b/drivers/staging/dream/camera/msm_vfe7x.c new file mode 100644 index 0000000..5de96c5 --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe7x.c @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include <linux/msm_adsp.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/android_pmem.h> +#include <mach/msm_adsp.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include "msm_vfe7x.h" + +#define QDSP_CMDQUEUE QDSP_vfeCommandQueue + +#define VFE_RESET_CMD 0 +#define VFE_START_CMD 1 +#define VFE_STOP_CMD 2 +#define VFE_FRAME_ACK 20 +#define STATS_AF_ACK 21 +#define STATS_WE_ACK 22 + +#define MSG_STOP_ACK 1 +#define MSG_SNAPSHOT 2 +#define MSG_OUTPUT1 6 +#define MSG_OUTPUT2 7 +#define MSG_STATS_AF 8 +#define MSG_STATS_WE 9 + +static struct msm_adsp_module *qcam_mod; +static struct msm_adsp_module *vfe_mod; +static struct msm_vfe_callback *resp; +static void *extdata; +static uint32_t extlen; + +struct mutex vfe_lock; +static void *vfe_syncdata; +static uint8_t vfestopped; + +static struct stop_event stopevent; + +static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo, + enum vfe_resp_msg type, + void *data, void **ext, int32_t *elen) +{ + switch (type) { + case VFE_MSG_OUTPUT1: + case VFE_MSG_OUTPUT2: { + pinfo->y_phy = ((struct vfe_endframe *)data)->y_address; + pinfo->cbcr_phy = + ((struct vfe_endframe *)data)->cbcr_address; + + CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n", + pinfo->y_phy, pinfo->cbcr_phy); + + ((struct vfe_frame_extra *)extdata)->bl_evencol = + ((struct vfe_endframe *)data)->blacklevelevencolumn; + + ((struct vfe_frame_extra *)extdata)->bl_oddcol = + ((struct vfe_endframe *)data)->blackleveloddcolumn; + + ((struct vfe_frame_extra *)extdata)->g_def_p_cnt = + ((struct vfe_endframe *)data)->greendefectpixelcount; + + ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt = + ((struct vfe_endframe *)data)->redbluedefectpixelcount; + + *ext = extdata; + *elen = extlen; + } + break; + + case VFE_MSG_STATS_AF: + case VFE_MSG_STATS_WE: + pinfo->sbuf_phy = *(uint32_t *)data; + break; + + default: + break; + } /* switch */ +} + +static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint32_t evt_buf[3]; + struct msm_vfe_resp *rp; + void *data; + + len = (id == (uint16_t)-1) ? 0 : len; + data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len, vfe_syncdata); + + if (!data) { + pr_err("rp: cannot allocate buffer\n"); + return; + } + rp = (struct msm_vfe_resp *)data; + rp->evt_msg.len = len; + + if (id == ((uint16_t)-1)) { + /* event */ + rp->type = VFE_EVENT; + rp->evt_msg.type = MSM_CAMERA_EVT; + getevent(evt_buf, sizeof(evt_buf)); + rp->evt_msg.msg_id = evt_buf[0]; + resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata); + } else { + /* messages */ + rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.msg_id = id; + rp->evt_msg.data = rp + 1; + getevent(rp->evt_msg.data, len); + + switch (rp->evt_msg.msg_id) { + case MSG_SNAPSHOT: + rp->type = VFE_MSG_SNAPSHOT; + break; + + case MSG_OUTPUT1: + rp->type = VFE_MSG_OUTPUT1; + vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT1, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case MSG_OUTPUT2: + rp->type = VFE_MSG_OUTPUT2; + vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT2, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case MSG_STATS_AF: + rp->type = VFE_MSG_STATS_AF; + vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF, + rp->evt_msg.data, NULL, NULL); + break; + + case MSG_STATS_WE: + rp->type = VFE_MSG_STATS_WE; + vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE, + rp->evt_msg.data, NULL, NULL); + + CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy); + break; + + case MSG_STOP_ACK: + rp->type = VFE_MSG_GENERAL; + stopevent.state = 1; + wake_up(&stopevent.wait); + break; + + + default: + rp->type = VFE_MSG_GENERAL; + break; + } + resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata); + } +} + +static struct msm_adsp_ops vfe_7x_sync = { + .event = vfe_7x_ops, +}; + +static int vfe_7x_enable(struct camera_enable_cmd *enable) +{ + int rc = -EFAULT; + + if (!strcmp(enable->name, "QCAMTASK")) + rc = msm_adsp_enable(qcam_mod); + else if (!strcmp(enable->name, "VFETASK")) + rc = msm_adsp_enable(vfe_mod); + + return rc; +} + +static int vfe_7x_disable(struct camera_enable_cmd *enable, + struct platform_device *dev __attribute__((unused))) +{ + int rc = -EFAULT; + + if (!strcmp(enable->name, "QCAMTASK")) + rc = msm_adsp_disable(qcam_mod); + else if (!strcmp(enable->name, "VFETASK")) + rc = msm_adsp_disable(vfe_mod); + + return rc; +} + +static int vfe_7x_stop(void) +{ + int rc = 0; + uint32_t stopcmd = VFE_STOP_CMD; + rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE, + &stopcmd, sizeof(uint32_t)); + if (rc < 0) { + CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc); + return rc; + } + + stopevent.state = 0; + rc = wait_event_timeout(stopevent.wait, + stopevent.state != 0, + msecs_to_jiffies(stopevent.timeout)); + + return rc; +} + +static void vfe_7x_release(struct platform_device *pdev) +{ + mutex_lock(&vfe_lock); + vfe_syncdata = NULL; + mutex_unlock(&vfe_lock); + + if (!vfestopped) { + CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__); + vfe_7x_stop(); + } else + vfestopped = 0; + + msm_adsp_disable(qcam_mod); + msm_adsp_disable(vfe_mod); + + msm_adsp_put(qcam_mod); + msm_adsp_put(vfe_mod); + + msm_camio_disable(pdev); + + kfree(extdata); + extlen = 0; +} + +static int vfe_7x_init(struct msm_vfe_callback *presp, + struct platform_device *dev) +{ + int rc = 0; + + init_waitqueue_head(&stopevent.wait); + stopevent.timeout = 200; + stopevent.state = 0; + + if (presp && presp->vfe_resp) + resp = presp; + else + return -EFAULT; + + /* Bring up all the required GPIOs and Clocks */ + rc = msm_camio_enable(dev); + if (rc < 0) + return rc; + + msm_camio_camif_pad_reg_reset(); + + extlen = sizeof(struct vfe_frame_extra); + + extdata = + kmalloc(sizeof(extlen), GFP_ATOMIC); + if (!extdata) { + rc = -ENOMEM; + goto init_fail; + } + + rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL); + if (rc) { + rc = -EBUSY; + goto get_qcam_fail; + } + + rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL); + if (rc) { + rc = -EBUSY; + goto get_vfe_fail; + } + + return 0; + +get_vfe_fail: + msm_adsp_put(qcam_mod); +get_qcam_fail: + kfree(extdata); +init_fail: + extlen = 0; + return rc; +} + +static int vfe_7x_config_axi(int mode, + struct axidata *ad, struct axiout *ao) +{ + struct msm_pmem_region *regptr; + unsigned long *bptr; + int cnt; + + int rc = 0; + + if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { + regptr = ad->region; + + CDBG("bufnum1 = %d\n", ad->bufnum1); + CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", + regptr->paddr, regptr->y_off, regptr->cbcr_off); + + bptr = &ao->output1buffer1_y_phy; + for (cnt = 0; cnt < ad->bufnum1; cnt++) { + *bptr = regptr->paddr + regptr->y_off; + bptr++; + *bptr = regptr->paddr + regptr->cbcr_off; + + bptr++; + regptr++; + } + + regptr--; + for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) { + *bptr = regptr->paddr + regptr->y_off; + bptr++; + *bptr = regptr->paddr + regptr->cbcr_off; + bptr++; + } + } /* if OUTPUT1 or Both */ + + if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { + regptr = &(ad->region[ad->bufnum1]); + + CDBG("bufnum2 = %d\n", ad->bufnum2); + CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", + regptr->paddr, regptr->y_off, regptr->cbcr_off); + + bptr = &ao->output2buffer1_y_phy; + for (cnt = 0; cnt < ad->bufnum2; cnt++) { + *bptr = regptr->paddr + regptr->y_off; + bptr++; + *bptr = regptr->paddr + regptr->cbcr_off; + + bptr++; + regptr++; + } + + regptr--; + for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) { + *bptr = regptr->paddr + regptr->y_off; + bptr++; + *bptr = regptr->paddr + regptr->cbcr_off; + bptr++; + } + } + + return rc; +} + +static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) +{ + struct msm_pmem_region *regptr; + unsigned char buf[256]; + + struct vfe_stats_ack sack; + struct axidata *axid; + uint32_t i; + + struct vfe_stats_we_cfg *scfg = NULL; + struct vfe_stats_af_cfg *sfcfg = NULL; + + struct axiout *axio = NULL; + void *cmd_data = NULL; + void *cmd_data_alloc = NULL; + long rc = 0; + struct msm_vfe_command_7k *vfecmd; + + vfecmd = + kmalloc(sizeof(struct msm_vfe_command_7k), + GFP_ATOMIC); + if (!vfecmd) { + pr_err("vfecmd alloc failed!\n"); + return -ENOMEM; + } + + if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { + if (copy_from_user(vfecmd, + (void __user *)(cmd->value), + sizeof(struct msm_vfe_command_7k))) { + rc = -EFAULT; + goto config_failure; + } + } + + switch (cmd->cmd_type) { + case CMD_STATS_ENABLE: + case CMD_STATS_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + scfg = + kmalloc(sizeof(struct vfe_stats_we_cfg), + GFP_ATOMIC); + if (!scfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(scfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, scfg->wb_expstatsenable); + + if (axid->bufnum1 > 0) { + regptr = axid->region; + + for (i = 0; i < axid->bufnum1; i++) { + + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); + + scfg->wb_expstatoutputbuffer[i] = + (void *)regptr->paddr; + regptr++; + } + + cmd_data = scfg; + + } else { + rc = -EINVAL; + goto config_done; + } + } + break; + + case CMD_STATS_AF_ENABLE: + case CMD_STATS_AF_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + sfcfg = + kmalloc(sizeof(struct vfe_stats_af_cfg), + GFP_ATOMIC); + + if (!sfcfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(sfcfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, sfcfg->af_enable); + + if (axid->bufnum1 > 0) { + regptr = axid->region; + + for (i = 0; i < axid->bufnum1; i++) { + + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); + + sfcfg->af_outbuf[i] = + (void *)regptr->paddr; + + regptr++; + } + + cmd_data = sfcfg; + + } else { + rc = -EINVAL; + goto config_done; + } + } + break; + + case CMD_FRAME_BUF_RELEASE: { + struct msm_frame *b; + unsigned long p; + struct vfe_outputack fack; + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; + + fack.header = VFE_FRAME_ACK; + + fack.output2newybufferaddress = + (void *)(p + b->y_off); + + fack.output2newcbcrbufferaddress = + (void *)(p + b->cbcr_off); + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_outputack); + cmd_data = &fack; + } + break; + + case CMD_SNAP_BUF_RELEASE: + break; + + case CMD_STATS_BUF_RELEASE: { + CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + sack.header = STATS_WE_ACK; + sack.bufaddr = (void *)*(uint32_t *)data; + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; + } + break; + + case CMD_STATS_AF_BUF_RELEASE: { + CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + sack.header = STATS_AF_ACK; + sack.bufaddr = (void *)*(uint32_t *)data; + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; + } + break; + + case CMD_GENERAL: + case CMD_STATS_DISABLE: { + if (vfecmd->length > 256) { + cmd_data_alloc = + cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC); + if (!cmd_data) { + rc = -ENOMEM; + goto config_failure; + } + } else + cmd_data = buf; + + if (copy_from_user(cmd_data, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + if (vfecmd->queue == QDSP_CMDQUEUE) { + switch (*(uint32_t *)cmd_data) { + case VFE_RESET_CMD: + msm_camio_vfe_blk_reset(); + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_START_CMD: + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_STOP_CMD: + vfestopped = 1; + goto config_send; + + default: + break; + } + } /* QDSP_CMDQUEUE */ + } + break; + + case CMD_AXI_CFG_OUT1: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_1, axid, axio); + + cmd_data = axio; + } + break; + + case CMD_AXI_CFG_OUT2: + case CMD_RAW_PICT_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_2, axid, axio); + cmd_data = axio; + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); + + cmd_data = axio; + } + break; + + default: + break; + } /* switch */ + + if (vfestopped) + goto config_done; + +config_send: + CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data); + rc = msm_adsp_write(vfe_mod, vfecmd->queue, + cmd_data, vfecmd->length); + +config_done: + if (cmd_data_alloc != NULL) + kfree(cmd_data_alloc); + +config_failure: + kfree(scfg); + kfree(axio); + kfree(vfecmd); + return rc; +} + +void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) +{ + mutex_init(&vfe_lock); + fptr->vfe_init = vfe_7x_init; + fptr->vfe_enable = vfe_7x_enable; + fptr->vfe_config = vfe_7x_config; + fptr->vfe_disable = vfe_7x_disable; + fptr->vfe_release = vfe_7x_release; + vfe_syncdata = data; +} |