diff options
Diffstat (limited to 'drivers/staging/dream/camera')
23 files changed, 17963 insertions, 0 deletions
diff --git a/drivers/staging/dream/camera/Kconfig b/drivers/staging/dream/camera/Kconfig new file mode 100644 index 0000000..0a3e903 --- /dev/null +++ b/drivers/staging/dream/camera/Kconfig @@ -0,0 +1,46 @@ +comment "Qualcomm MSM Camera And Video" + +menuconfig MSM_CAMERA + bool "Qualcomm MSM camera and video capture support" + depends on ARCH_MSM && VIDEO_V4L2_COMMON + help + Say Y here to enable selecting the video adapters for + Qualcomm msm camera and video encoding + +config MSM_CAMERA_DEBUG + bool "Qualcomm MSM camera debugging with printk" + depends on MSM_CAMERA + help + Enable printk() debug for msm camera + +config MSM_CAMERA_FLASH + bool "Qualcomm MSM camera flash support" + depends on MSM_CAMERA + ---help--- + Enable support for LED flash for msm camera + + +comment "Camera Sensor Selection" +config MT9T013 + bool "Sensor mt9t013 (BAYER 3M)" + depends on MSM_CAMERA + ---help--- + MICRON 3M Bayer Sensor with AutoFocus + +config MT9D112 + bool "Sensor mt9d112 (YUV 2M)" + depends on MSM_CAMERA + ---help--- + MICRON 2M YUV Sensor + +config MT9P012 + bool "Sensor mt9p012 (BAYER 5M)" + depends on MSM_CAMERA + ---help--- + MICRON 5M Bayer Sensor with Autofocus + +config S5K3E2FX + bool "Sensor s5k3e2fx (Samsung 5M)" + depends on MSM_CAMERA + ---help--- + Samsung 5M with Autofocus diff --git a/drivers/staging/dream/camera/Makefile b/drivers/staging/dream/camera/Makefile new file mode 100644 index 0000000..4429ae5 --- /dev/null +++ b/drivers/staging/dream/camera/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o +obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o +obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o +obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o +obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o +obj-$(CONFIG_ARCH_MSM) += msm_vfe7x.o msm_io7x.o +obj-$(CONFIG_ARCH_QSD) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o diff --git a/drivers/staging/dream/camera/msm_camera.c b/drivers/staging/dream/camera/msm_camera.c new file mode 100644 index 0000000..8816599 --- /dev/null +++ b/drivers/staging/dream/camera/msm_camera.c @@ -0,0 +1,2181 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +//FIXME: most allocations need not be GFP_ATOMIC +/* FIXME: management of mutexes */ +/* FIXME: msm_pmem_region_lookup return values */ +/* FIXME: way too many copy to/from user */ +/* FIXME: does region->active mean free */ +/* FIXME: check limits on command lenghts passed from userspace */ +/* FIXME: __msm_release: which queues should we flush when opencnt != 0 */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <mach/board.h> + +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/uaccess.h> +#include <linux/android_pmem.h> +#include <linux/poll.h> +#include <media/msm_camera.h> +#include <mach/camera.h> + +#define MSM_MAX_CAMERA_SENSORS 5 + +#define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \ + __func__, __LINE__, ((to) ? "to" : "from")) +#define ERR_COPY_FROM_USER() ERR_USER_COPY(0) +#define ERR_COPY_TO_USER() ERR_USER_COPY(1) + +static struct class *msm_class; +static dev_t msm_devno; +static LIST_HEAD(msm_sensors); + +#define __CONTAINS(r, v, l, field) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = __v >= __r->field && \ + __e <= __r->field + __r->len; \ + res; \ +}) + +#define CONTAINS(r1, r2, field) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->field, __r2->len, field); \ +}) + +#define IN_RANGE(r, v, field) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->field) && \ + (__vv < (__r->field + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2, field) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->field) __v = __r2->field; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v, field) || \ + IN_RANGE(__r1, __e, field)); \ + res; \ +}) + +#define MSM_DRAIN_QUEUE_NOSYNC(sync, name) do { \ + struct msm_queue_cmd *qcmd = NULL; \ + CDBG("%s: draining queue "#name"\n", __func__); \ + while (!list_empty(&(sync)->name)) { \ + qcmd = list_first_entry(&(sync)->name, \ + struct msm_queue_cmd, list); \ + list_del_init(&qcmd->list); \ + kfree(qcmd); \ + }; \ +} while(0) + +#define MSM_DRAIN_QUEUE(sync, name) do { \ + unsigned long flags; \ + spin_lock_irqsave(&(sync)->name##_lock, flags); \ + MSM_DRAIN_QUEUE_NOSYNC(sync, name); \ + spin_unlock_irqrestore(&(sync)->name##_lock, flags); \ +} while(0) + +static int check_overlap(struct hlist_head *ptype, + unsigned long paddr, + unsigned long len) +{ + struct msm_pmem_region *region; + struct msm_pmem_region t = { .paddr = paddr, .len = len }; + struct hlist_node *node; + + hlist_for_each_entry(region, node, ptype, list) { + if (CONTAINS(region, &t, paddr) || + CONTAINS(&t, region, paddr) || + OVERLAPS(region, &t, paddr)) { + printk(KERN_ERR + " region (PHYS %p len %ld)" + " clashes with registered region" + " (paddr %p len %ld)\n", + (void *)t.paddr, t.len, + (void *)region->paddr, region->len); + return -1; + } + } + + return 0; +} + +static int msm_pmem_table_add(struct hlist_head *ptype, + struct msm_pmem_info *info) +{ + struct file *file; + unsigned long paddr; + unsigned long vstart; + unsigned long len; + int rc; + struct msm_pmem_region *region; + + rc = get_pmem_file(info->fd, &paddr, &vstart, &len, &file); + if (rc < 0) { + pr_err("msm_pmem_table_add: get_pmem_file fd %d error %d\n", + info->fd, rc); + return rc; + } + + if (check_overlap(ptype, paddr, len) < 0) + return -EINVAL; + + CDBG("%s: type = %d, paddr = 0x%lx, vaddr = 0x%lx\n", + __func__, + info->type, paddr, (unsigned long)info->vaddr); + + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + + INIT_HLIST_NODE(®ion->list); + + region->type = info->type; + region->vaddr = info->vaddr; + region->paddr = paddr; + region->len = len; + region->file = file; + region->y_off = info->y_off; + region->cbcr_off = info->cbcr_off; + region->fd = info->fd; + region->active = info->active; + + hlist_add_head(&(region->list), ptype); + + return 0; +} + +/* return of 0 means failure */ +static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, + int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount) +{ + struct msm_pmem_region *region; + struct msm_pmem_region *regptr; + struct hlist_node *node, *n; + + uint8_t rc = 0; + + regptr = reg; + + hlist_for_each_entry_safe(region, node, n, ptype, list) { + if (region->type == pmem_type && region->active) { + *regptr = *region; + rc += 1; + if (rc >= maxcount) + break; + regptr++; + } + } + + return rc; +} + +static unsigned long msm_pmem_frame_ptov_lookup(struct msm_sync *sync, + unsigned long pyaddr, + unsigned long pcbcraddr, + uint32_t *yoff, uint32_t *cbcroff, int *fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->frame, list) { + if (pyaddr == (region->paddr + region->y_off) && + pcbcraddr == (region->paddr + + region->cbcr_off) && + region->active) { + /* offset since we could pass vaddr inside + * a registerd pmem buffer + */ + *yoff = region->y_off; + *cbcroff = region->cbcr_off; + *fd = region->fd; + region->active = 0; + return (unsigned long)(region->vaddr); + } + } + + return 0; +} + +static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync, + unsigned long addr, int *fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { + if (addr == region->paddr && region->active) { + /* offset since we could pass vaddr inside a + * registered pmem buffer */ + *fd = region->fd; + region->active = 0; + return (unsigned long)(region->vaddr); + } + } + + return 0; +} + +static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync, + unsigned long buffer, + uint32_t yoff, uint32_t cbcroff, int fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, + node, n, &sync->frame, list) { + if (((unsigned long)(region->vaddr) == buffer) && + (region->y_off == yoff) && + (region->cbcr_off == cbcroff) && + (region->fd == fd) && + (region->active == 0)) { + + region->active = 1; + return region->paddr; + } + } + + return 0; +} + +static unsigned long msm_pmem_stats_vtop_lookup( + struct msm_sync *sync, + unsigned long buffer, + int fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { + if (((unsigned long)(region->vaddr) == buffer) && + (region->fd == fd) && region->active == 0) { + region->active = 1; + return region->paddr; + } + } + + return 0; +} + +static int __msm_pmem_table_del(struct msm_sync *sync, + struct msm_pmem_info *pinfo) +{ + int rc = 0; + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + switch (pinfo->type) { + case MSM_PMEM_OUTPUT1: + case MSM_PMEM_OUTPUT2: + case MSM_PMEM_THUMBAIL: + case MSM_PMEM_MAINIMG: + case MSM_PMEM_RAW_MAINIMG: + hlist_for_each_entry_safe(region, node, n, + &sync->frame, list) { + + if (pinfo->type == region->type && + pinfo->vaddr == region->vaddr && + pinfo->fd == region->fd) { + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + } + break; + + case MSM_PMEM_AEC_AWB: + case MSM_PMEM_AF: + hlist_for_each_entry_safe(region, node, n, + &sync->stats, list) { + + if (pinfo->type == region->type && + pinfo->vaddr == region->vaddr && + pinfo->fd == region->fd) { + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + } + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_pmem_table_del(struct msm_sync *sync, void __user *arg) +{ + struct msm_pmem_info info; + + if (copy_from_user(&info, arg, sizeof(info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_pmem_table_del(sync, &info); +} + +static int __msm_get_frame(struct msm_sync *sync, + struct msm_frame *frame) +{ + unsigned long flags; + int rc = 0; + + struct msm_queue_cmd *qcmd = NULL; + struct msm_vfe_phy_info *pphy; + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + if (!list_empty(&sync->prev_frame_q)) { + qcmd = list_first_entry(&sync->prev_frame_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + } + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + + if (!qcmd) { + pr_err("%s: no preview frame.\n", __func__); + return -EAGAIN; + } + + pphy = (struct msm_vfe_phy_info *)(qcmd->command); + + frame->buffer = + msm_pmem_frame_ptov_lookup(sync, + pphy->y_phy, + pphy->cbcr_phy, &(frame->y_off), + &(frame->cbcr_off), &(frame->fd)); + if (!frame->buffer) { + pr_err("%s: cannot get frame, invalid lookup address " + "y=%x cbcr=%x offset=%d\n", + __FUNCTION__, + pphy->y_phy, + pphy->cbcr_phy, + frame->y_off); + rc = -EINVAL; + } + + CDBG("__msm_get_frame: y=0x%x, cbcr=0x%x, qcmd=0x%x, virt_addr=0x%x\n", + pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer); + + kfree(qcmd); + return rc; +} + +static int msm_get_frame(struct msm_sync *sync, void __user *arg) +{ + int rc = 0; + struct msm_frame frame; + + if (copy_from_user(&frame, + arg, + sizeof(struct msm_frame))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + rc = __msm_get_frame(sync, &frame); + if (rc < 0) + return rc; + + if (sync->croplen) { + if (frame.croplen > sync->croplen) { + pr_err("msm_get_frame: invalid frame croplen %d\n", + frame.croplen); + return -EINVAL; + } + + if (copy_to_user((void *)frame.cropinfo, + sync->cropinfo, + sync->croplen)) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + } + + if (copy_to_user((void *)arg, + &frame, sizeof(struct msm_frame))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + + CDBG("Got frame!!!\n"); + + return rc; +} + +static int msm_enable_vfe(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + struct camera_enable_cmd cfg; + + if (copy_from_user(&cfg, + arg, + sizeof(struct camera_enable_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (sync->vfefn.vfe_enable) + rc = sync->vfefn.vfe_enable(&cfg); + + CDBG("msm_enable_vfe: returned rc = %d\n", rc); + return rc; +} + +static int msm_disable_vfe(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + struct camera_enable_cmd cfg; + + if (copy_from_user(&cfg, + arg, + sizeof(struct camera_enable_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (sync->vfefn.vfe_disable) + rc = sync->vfefn.vfe_disable(&cfg, NULL); + + CDBG("msm_disable_vfe: returned rc = %d\n", rc); + return rc; +} + +static struct msm_queue_cmd* __msm_control(struct msm_sync *sync, + struct msm_control_device_queue *queue, + struct msm_queue_cmd *qcmd, + int timeout) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_add_tail(&qcmd->list, &sync->msg_event_q); + /* wake up config thread */ + wake_up(&sync->msg_event_wait); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + + if (!queue) + return NULL; + + /* wait for config status */ + rc = wait_event_interruptible_timeout( + queue->ctrl_status_wait, + !list_empty_careful(&queue->ctrl_status_q), + timeout); + if (list_empty_careful(&queue->ctrl_status_q)) { + if (!rc) + rc = -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_control: wait_event error %d\n", rc); +#if 0 + /* This is a bit scary. If we time out too early, we + * will free qcmd at the end of this function, and the + * dsp may do the same when it does respond, so we + * remove the message from the source queue. + */ + pr_err("%s: error waiting for ctrl_status_q: %d\n", + __func__, rc); + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); +#endif + return ERR_PTR(rc); + } + } + + /* control command status is ready */ + spin_lock_irqsave(&queue->ctrl_status_q_lock, flags); + BUG_ON(list_empty(&queue->ctrl_status_q)); + qcmd = list_first_entry(&queue->ctrl_status_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&queue->ctrl_status_q_lock, flags); + + return qcmd; +} + +static int msm_control(struct msm_control_device *ctrl_pmsm, + int block, + void __user *arg) +{ + int rc = 0; + + struct msm_sync *sync = ctrl_pmsm->pmsm->sync; + struct msm_ctrl_cmd udata, *ctrlcmd; + struct msm_queue_cmd *qcmd = NULL, *qcmd_temp; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd) + udata.length, + GFP_KERNEL); + if (!qcmd) { + pr_err("msm_control: cannot allocate buffer\n"); + rc = -ENOMEM; + goto end; + } + + qcmd->type = MSM_CAM_Q_CTRL; + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + *ctrlcmd = udata; + ctrlcmd->value = ctrlcmd + 1; + + if (udata.length) { + if (copy_from_user(ctrlcmd->value, + udata.value, udata.length)) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + } + + if (!block) { + /* qcmd will be set to NULL */ + qcmd = __msm_control(sync, NULL, qcmd, 0); + goto end; + } + + qcmd_temp = __msm_control(sync, + &ctrl_pmsm->ctrl_q, + qcmd, MAX_SCHEDULE_TIMEOUT); + + if (IS_ERR(qcmd_temp)) { + rc = PTR_ERR(qcmd_temp); + goto end; + } + qcmd = qcmd_temp; + + if (qcmd->command) { + void __user *to = udata.value; + udata = *(struct msm_ctrl_cmd *)qcmd->command; + if (udata.length > 0) { + if (copy_to_user(to, + udata.value, + udata.length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto end; + } + } + udata.value = to; + + if (copy_to_user((void *)arg, &udata, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto end; + } + } + +end: + /* Note: if we get here as a result of an error, we will free the + * qcmd that we kmalloc() in this function. When we come here as + * a result of a successful completion, we are freeing the qcmd that + * we dequeued from queue->ctrl_status_q. + */ + if (qcmd) + kfree(qcmd); + + CDBG("msm_control: end rc = %d\n", rc); + return rc; +} + +static int msm_get_stats(struct msm_sync *sync, void __user *arg) +{ + unsigned long flags; + int timeout; + int rc = 0; + + struct msm_stats_event_ctrl se; + + struct msm_queue_cmd *qcmd = NULL; + struct msm_ctrl_cmd *ctrl = NULL; + struct msm_vfe_resp *data = NULL; + struct msm_stats_buf stats; + + if (copy_from_user(&se, arg, + sizeof(struct msm_stats_event_ctrl))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + timeout = (int)se.timeout_ms; + + CDBG("msm_get_stats timeout %d\n", timeout); + rc = wait_event_interruptible_timeout( + sync->msg_event_wait, + !list_empty_careful(&sync->msg_event_q), + msecs_to_jiffies(timeout)); + if (list_empty_careful(&sync->msg_event_q)) { + if (rc == 0) + rc = -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_get_stats error %d\n", rc); + return rc; + } + } + CDBG("msm_get_stats returned from wait: %d\n", rc); + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + BUG_ON(list_empty(&sync->msg_event_q)); + qcmd = list_first_entry(&sync->msg_event_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + + CDBG("=== received from DSP === %d\n", qcmd->type); + + switch (qcmd->type) { + case MSM_CAM_Q_VFE_EVT: + case MSM_CAM_Q_VFE_MSG: + data = (struct msm_vfe_resp *)(qcmd->command); + + /* adsp event and message */ + se.resptype = MSM_CAM_RESP_STAT_EVT_MSG; + + /* 0 - msg from aDSP, 1 - event from mARM */ + se.stats_event.type = data->evt_msg.type; + se.stats_event.msg_id = data->evt_msg.msg_id; + se.stats_event.len = data->evt_msg.len; + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", se.stats_event.len); + CDBG("msg_id = %d\n", se.stats_event.msg_id); + + if ((data->type == VFE_MSG_STATS_AF) || + (data->type == VFE_MSG_STATS_WE)) { + + stats.buffer = + msm_pmem_stats_ptov_lookup(sync, + data->phy.sbuf_phy, + &(stats.fd)); + if (!stats.buffer) { + pr_err("%s: msm_pmem_stats_ptov_lookup error\n", + __FUNCTION__); + rc = -EINVAL; + goto failure; + } + + if (copy_to_user((void *)(se.stats_event.data), + &stats, + sizeof(struct msm_stats_buf))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } else if ((data->evt_msg.len > 0) && + (data->type == VFE_MSG_GENERAL)) { + if (copy_to_user((void *)(se.stats_event.data), + data->evt_msg.data, + data->evt_msg.len)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + } else if (data->type == VFE_MSG_OUTPUT1 || + data->type == VFE_MSG_OUTPUT2) { + if (copy_to_user((void *)(se.stats_event.data), + data->extdata, + data->extlen)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + } else if (data->type == VFE_MSG_SNAPSHOT && sync->pict_pp) { + struct msm_postproc buf; + struct msm_pmem_region region; + buf.fmnum = msm_pmem_region_lookup(&sync->frame, + MSM_PMEM_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.buffer = (unsigned long)region.vaddr; + buf.fmain.y_off = region.y_off; + buf.fmain.cbcr_off = region.cbcr_off; + buf.fmain.fd = region.fd; + } else { + buf.fmnum = msm_pmem_region_lookup(&sync->frame, + MSM_PMEM_RAW_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.path = MSM_FRAME_PREV_2; + buf.fmain.buffer = + (unsigned long)region.vaddr; + buf.fmain.fd = region.fd; + } + else { + pr_err("%s: pmem lookup failed\n", + __func__); + rc = -EINVAL; + } + } + + if (copy_to_user((void *)(se.stats_event.data), &buf, + sizeof(buf))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + CDBG("snapshot copy_to_user!\n"); + } + break; + + case MSM_CAM_Q_CTRL: + /* control command from control thread */ + ctrl = (struct msm_ctrl_cmd *)(qcmd->command); + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", ctrl->length); + + if (ctrl->length > 0) { + if (copy_to_user((void *)(se.ctrl_cmd.value), + ctrl->value, + ctrl->length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } + + se.resptype = MSM_CAM_RESP_CTRL; + + /* what to control */ + se.ctrl_cmd.type = ctrl->type; + se.ctrl_cmd.length = ctrl->length; + se.ctrl_cmd.resp_fd = ctrl->resp_fd; + break; + + case MSM_CAM_Q_V4L2_REQ: + /* control command from v4l2 client */ + ctrl = (struct msm_ctrl_cmd *)(qcmd->command); + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", ctrl->length); + + if (ctrl->length > 0) { + if (copy_to_user((void *)(se.ctrl_cmd.value), + ctrl->value, ctrl->length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } + + /* 2 tells config thread this is v4l2 request */ + se.resptype = MSM_CAM_RESP_V4L2; + + /* what to control */ + se.ctrl_cmd.type = ctrl->type; + se.ctrl_cmd.length = ctrl->length; + break; + + default: + rc = -EFAULT; + goto failure; + } /* switch qcmd->type */ + + if (copy_to_user((void *)arg, &se, sizeof(se))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + +failure: + if (qcmd) + kfree(qcmd); + + CDBG("msm_get_stats: %d\n", rc); + return rc; +} + +static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm, + void __user *arg) +{ + unsigned long flags; + int rc = 0; + + struct msm_ctrl_cmd udata, *ctrlcmd; + struct msm_queue_cmd *qcmd = NULL; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd) + udata.length, + GFP_KERNEL); + if (!qcmd) { + rc = -ENOMEM; + goto end; + } + + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + *ctrlcmd = udata; + if (udata.length > 0) { + ctrlcmd->value = ctrlcmd + 1; + if (copy_from_user(ctrlcmd->value, + (void *)udata.value, + udata.length)) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + kfree(qcmd); + goto end; + } + } + else ctrlcmd->value = NULL; + +end: + CDBG("msm_ctrl_cmd_done: end rc = %d\n", rc); + if (rc == 0) { + /* wake up control thread */ + spin_lock_irqsave(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, flags); + list_add_tail(&qcmd->list, &ctrl_pmsm->ctrl_q.ctrl_status_q); + wake_up(&ctrl_pmsm->ctrl_q.ctrl_status_wait); + spin_unlock_irqrestore(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, flags); + } + + return rc; +} + +static int msm_config_vfe(struct msm_sync *sync, void __user *arg) +{ + struct msm_vfe_cfg_cmd cfgcmd; + struct msm_pmem_region region[8]; + struct axidata axi_data; + void *data = NULL; + int rc = -EIO; + + memset(&axi_data, 0, sizeof(axi_data)); + + if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + switch(cfgcmd.cmd_type) { + case CMD_STATS_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, + MSM_PMEM_AEC_AWB, ®ion[0], + NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + data = &axi_data; + break; + case CMD_STATS_AF_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, + MSM_PMEM_AF, ®ion[0], + NUM_AF_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + data = &axi_data; + break; + case CMD_GENERAL: + case CMD_STATS_DISABLE: + break; + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd.cmd_type); + return -EINVAL; + } + + + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(&cfgcmd, data); + + return rc; +} + +static int msm_frame_axi_cfg(struct msm_sync *sync, + struct msm_vfe_cfg_cmd *cfgcmd) +{ + int rc = -EIO; + struct axidata axi_data; + void *data = &axi_data; + struct msm_pmem_region region[8]; + int pmem_type; + + memset(&axi_data, 0, sizeof(axi_data)); + + switch (cfgcmd->cmd_type) { + case CMD_AXI_CFG_OUT1: + pmem_type = MSM_PMEM_OUTPUT1; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_OUT2: + pmem_type = MSM_PMEM_OUTPUT2; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum2) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: + pmem_type = MSM_PMEM_THUMBAIL; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + + pmem_type = MSM_PMEM_MAINIMG; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[axi_data.bufnum1], 8); + if (!axi_data.bufnum2) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + break; + + case CMD_RAW_PICT_AXI_CFG: + pmem_type = MSM_PMEM_RAW_MAINIMG; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum2) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + break; + + case CMD_GENERAL: + data = NULL; + break; + + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd->cmd_type); + return -EINVAL; + } + + axi_data.region = ®ion[0]; + + /* send the AXI configuration command to driver */ + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(cfgcmd, data); + + return rc; +} + +static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg) +{ + int rc = 0; + struct msm_camsensor_info info; + struct msm_camera_sensor_info *sdata; + + if (copy_from_user(&info, + arg, + sizeof(struct msm_camsensor_info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + sdata = sync->pdev->dev.platform_data; + CDBG("sensor_name %s\n", sdata->sensor_name); + + memcpy(&info.name[0], + sdata->sensor_name, + MAX_SENSOR_NAME); + info.flash_enabled = sdata->flash_type != MSM_CAMERA_FLASH_NONE; + + /* copy back to user space */ + if (copy_to_user((void *)arg, + &info, + sizeof(struct msm_camsensor_info))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + + return rc; +} + +static int __msm_put_frame_buf(struct msm_sync *sync, + struct msm_frame *pb) +{ + unsigned long pphy; + struct msm_vfe_cfg_cmd cfgcmd; + + int rc = -EIO; + + pphy = msm_pmem_frame_vtop_lookup(sync, + pb->buffer, + pb->y_off, pb->cbcr_off, pb->fd); + + if (pphy != 0) { + CDBG("rel: vaddr = 0x%lx, paddr = 0x%lx\n", + pb->buffer, pphy); + cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE; + cfgcmd.value = (void *)pb; + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); + } else { + pr_err("%s: msm_pmem_frame_vtop_lookup failed\n", + __FUNCTION__); + rc = -EINVAL; + } + + return rc; +} + +static int msm_put_frame_buffer(struct msm_sync *sync, void __user *arg) +{ + struct msm_frame buf_t; + + if (copy_from_user(&buf_t, + arg, + sizeof(struct msm_frame))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_put_frame_buf(sync, &buf_t); +} + +static int __msm_register_pmem(struct msm_sync *sync, + struct msm_pmem_info *pinfo) +{ + int rc = 0; + + switch (pinfo->type) { + case MSM_PMEM_OUTPUT1: + case MSM_PMEM_OUTPUT2: + case MSM_PMEM_THUMBAIL: + case MSM_PMEM_MAINIMG: + case MSM_PMEM_RAW_MAINIMG: + rc = msm_pmem_table_add(&sync->frame, pinfo); + break; + + case MSM_PMEM_AEC_AWB: + case MSM_PMEM_AF: + rc = msm_pmem_table_add(&sync->stats, pinfo); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_register_pmem(struct msm_sync *sync, void __user *arg) +{ + struct msm_pmem_info info; + + if (copy_from_user(&info, arg, sizeof(info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_register_pmem(sync, &info); +} + +static int msm_stats_axi_cfg(struct msm_sync *sync, + struct msm_vfe_cfg_cmd *cfgcmd) +{ + int rc = -EIO; + struct axidata axi_data; + void *data = &axi_data; + + struct msm_pmem_region region[3]; + int pmem_type = MSM_PMEM_MAX; + + memset(&axi_data, 0, sizeof(axi_data)); + + switch (cfgcmd->cmd_type) { + case CMD_STATS_AXI_CFG: + pmem_type = MSM_PMEM_AEC_AWB; + break; + case CMD_STATS_AF_AXI_CFG: + pmem_type = MSM_PMEM_AF; + break; + case CMD_GENERAL: + data = NULL; + break; + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd->cmd_type); + return -EINVAL; + } + + if (cfgcmd->cmd_type != CMD_GENERAL) { + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, pmem_type, + ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s: pmem region lookup error\n", __FUNCTION__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + } + + /* send the AEC/AWB STATS configuration command to driver */ + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(cfgcmd, &axi_data); + + return rc; +} + +static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + + struct msm_stats_buf buf; + unsigned long pphy; + struct msm_vfe_cfg_cmd cfgcmd; + + if (copy_from_user(&buf, arg, + sizeof(struct msm_stats_buf))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + CDBG("msm_put_stats_buffer\n"); + pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd); + + if (pphy != 0) { + if (buf.type == STAT_AEAW) + cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE; + else if (buf.type == STAT_AF) + cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE; + else { + pr_err("%s: invalid buf type %d\n", + __FUNCTION__, + buf.type); + rc = -EINVAL; + goto put_done; + } + + cfgcmd.value = (void *)&buf; + + if (sync->vfefn.vfe_config) { + rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); + if (rc < 0) + pr_err("msm_put_stats_buffer: "\ + "vfe_config err %d\n", rc); + } else + pr_err("msm_put_stats_buffer: vfe_config is NULL\n"); + } else { + pr_err("msm_put_stats_buffer: NULL physical address\n"); + rc = -EINVAL; + } + +put_done: + return rc; +} + +static int msm_axi_config(struct msm_sync *sync, void __user *arg) +{ + struct msm_vfe_cfg_cmd cfgcmd; + + if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + switch (cfgcmd.cmd_type) { + case CMD_AXI_CFG_OUT1: + case CMD_AXI_CFG_OUT2: + case CMD_AXI_CFG_SNAP_O1_AND_O2: + case CMD_RAW_PICT_AXI_CFG: + return msm_frame_axi_cfg(sync, &cfgcmd); + + case CMD_STATS_AXI_CFG: + case CMD_STATS_AF_AXI_CFG: + return msm_stats_axi_cfg(sync, &cfgcmd); + + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, + cfgcmd.cmd_type); + return -EINVAL; + } + + return 0; +} + +static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) +{ + unsigned long flags; + int rc = 0; + int tm; + + struct msm_queue_cmd *qcmd = NULL; + + tm = (int)ctrl->timeout_ms; + + rc = wait_event_interruptible_timeout( + sync->pict_frame_wait, + !list_empty_careful(&sync->pict_frame_q), + msecs_to_jiffies(tm)); + if (list_empty_careful(&sync->pict_frame_q)) { + if (rc == 0) + return -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_camera_get_picture, rc = %d\n", rc); + return rc; + } + } + + spin_lock_irqsave(&sync->pict_frame_q_lock, flags); + BUG_ON(list_empty(&sync->pict_frame_q)); + qcmd = list_first_entry(&sync->pict_frame_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + + if (qcmd->command != NULL) { + struct msm_ctrl_cmd *q = + (struct msm_ctrl_cmd *)qcmd->command; + ctrl->type = q->type; + ctrl->status = q->status; + } else { + ctrl->type = -1; + ctrl->status = -1; + } + + kfree(qcmd); + return rc; +} + +static int msm_get_pic(struct msm_sync *sync, void __user *arg) +{ + struct msm_ctrl_cmd ctrlcmd_t; + int rc; + + if (copy_from_user(&ctrlcmd_t, + arg, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + rc = __msm_get_pic(sync, &ctrlcmd_t); + if (rc < 0) + return rc; + + if (sync->croplen) { + if (ctrlcmd_t.length < sync->croplen) { + pr_err("msm_get_pic: invalid len %d\n", + ctrlcmd_t.length); + return -EINVAL; + } + if (copy_to_user(ctrlcmd_t.value, + sync->cropinfo, + sync->croplen)) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + } + + if (copy_to_user((void *)arg, + &ctrlcmd_t, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + return 0; +} + +static int msm_set_crop(struct msm_sync *sync, void __user *arg) +{ + struct crop_info crop; + + if (copy_from_user(&crop, + arg, + sizeof(struct crop_info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (!sync->croplen) { + sync->cropinfo = kmalloc(crop.len, GFP_KERNEL); + if (!sync->cropinfo) + return -ENOMEM; + } else if (sync->croplen < crop.len) + return -EINVAL; + + if (copy_from_user(sync->cropinfo, + crop.info, + crop.len)) { + ERR_COPY_FROM_USER(); + kfree(sync->cropinfo); + return -EFAULT; + } + + sync->croplen = crop.len; + + return 0; +} + +static int msm_pict_pp_done(struct msm_sync *sync, void __user *arg) +{ + struct msm_ctrl_cmd udata; + struct msm_ctrl_cmd *ctrlcmd = NULL; + struct msm_queue_cmd *qcmd = NULL; + unsigned long flags; + int rc = 0; + + if (!sync->pict_pp) + return -EINVAL; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto pp_fail; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd), + GFP_KERNEL); + if (!qcmd) { + rc = -ENOMEM; + goto pp_fail; + } + + qcmd->type = MSM_CAM_Q_VFE_MSG; + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + memset(ctrlcmd, 0, sizeof(struct msm_ctrl_cmd)); + ctrlcmd->type = udata.type; + ctrlcmd->status = udata.status; + + spin_lock_irqsave(&sync->pict_frame_q_lock, flags); + list_add_tail(&qcmd->list, &sync->pict_frame_q); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + wake_up(&sync->pict_frame_wait); + +pp_fail: + return rc; +} + +static long msm_ioctl_common(struct msm_device *pmsm, + unsigned int cmd, + void __user *argp) +{ + CDBG("msm_ioctl_common\n"); + switch (cmd) { + case MSM_CAM_IOCTL_REGISTER_PMEM: + return msm_register_pmem(pmsm->sync, argp); + case MSM_CAM_IOCTL_UNREGISTER_PMEM: + return msm_pmem_table_del(pmsm->sync, argp); + default: + return -EINVAL; + } +} + +static long msm_ioctl_config(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_device *pmsm = filep->private_data; + + CDBG("msm_ioctl_config cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case MSM_CAM_IOCTL_GET_SENSOR_INFO: + rc = msm_get_sensor_info(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_CONFIG_VFE: + /* Coming from config thread for update */ + rc = msm_config_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_GET_STATS: + /* Coming from config thread wait + * for vfe statistics and control requests */ + rc = msm_get_stats(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_ENABLE_VFE: + /* This request comes from control thread: + * enable either QCAMTASK or VFETASK */ + rc = msm_enable_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_DISABLE_VFE: + /* This request comes from control thread: + * disable either QCAMTASK or VFETASK */ + rc = msm_disable_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_VFE_APPS_RESET: + msm_camio_vfe_blk_reset(); + rc = 0; + break; + + case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER: + rc = msm_put_stats_buffer(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_AXI_CONFIG: + rc = msm_axi_config(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_SET_CROP: + rc = msm_set_crop(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_PICT_PP: { + uint8_t enable; + if (copy_from_user(&enable, argp, sizeof(enable))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + } else { + pmsm->sync->pict_pp = enable; + rc = 0; + } + break; + } + + case MSM_CAM_IOCTL_PICT_PP_DONE: + rc = msm_pict_pp_done(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_SENSOR_IO_CFG: + rc = pmsm->sync->sctrl.s_config(argp); + break; + + case MSM_CAM_IOCTL_FLASH_LED_CFG: { + uint32_t led_state; + if (copy_from_user(&led_state, argp, sizeof(led_state))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + } else + rc = msm_camera_flash_set_led_state(led_state); + break; + } + + default: + rc = msm_ioctl_common(pmsm, cmd, argp); + break; + } + + CDBG("msm_ioctl_config cmd = %d DONE\n", _IOC_NR(cmd)); + return rc; +} + +static int msm_unblock_poll_frame(struct msm_sync *); + +static long msm_ioctl_frame(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_device *pmsm = filep->private_data; + + + switch (cmd) { + case MSM_CAM_IOCTL_GETFRAME: + /* Coming from frame thread to get frame + * after SELECT is done */ + rc = msm_get_frame(pmsm->sync, argp); + break; + case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER: + rc = msm_put_frame_buffer(pmsm->sync, argp); + break; + case MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME: + rc = msm_unblock_poll_frame(pmsm->sync); + break; + default: + break; + } + + return rc; +} + + +static long msm_ioctl_control(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_control_device *ctrl_pmsm = filep->private_data; + struct msm_device *pmsm = ctrl_pmsm->pmsm; + + switch (cmd) { + case MSM_CAM_IOCTL_CTRL_COMMAND: + /* Coming from control thread, may need to wait for + * command status */ + rc = msm_control(ctrl_pmsm, 1, argp); + break; + case MSM_CAM_IOCTL_CTRL_COMMAND_2: + /* Sends a message, returns immediately */ + rc = msm_control(ctrl_pmsm, 0, argp); + break; + case MSM_CAM_IOCTL_CTRL_CMD_DONE: + /* Config thread calls the control thread to notify it + * of the result of a MSM_CAM_IOCTL_CTRL_COMMAND. + */ + rc = msm_ctrl_cmd_done(ctrl_pmsm, argp); + break; + case MSM_CAM_IOCTL_GET_PICTURE: + rc = msm_get_pic(pmsm->sync, argp); + break; + default: + rc = msm_ioctl_common(pmsm, cmd, argp); + break; + } + + return rc; +} + +static int __msm_release(struct msm_sync *sync) +{ + struct msm_pmem_region *region; + struct hlist_node *hnode; + struct hlist_node *n; + + mutex_lock(&sync->lock); + if (sync->opencnt) + sync->opencnt--; + + if (!sync->opencnt) { + /* need to clean up system resource */ + if (sync->vfefn.vfe_release) + sync->vfefn.vfe_release(sync->pdev); + + if (sync->cropinfo) { + kfree(sync->cropinfo); + sync->cropinfo = NULL; + sync->croplen = 0; + } + + hlist_for_each_entry_safe(region, hnode, n, + &sync->frame, list) { + hlist_del(hnode); + put_pmem_file(region->file); + kfree(region); + } + + hlist_for_each_entry_safe(region, hnode, n, + &sync->stats, list) { + hlist_del(hnode); + put_pmem_file(region->file); + kfree(region); + } + + MSM_DRAIN_QUEUE(sync, msg_event_q); + MSM_DRAIN_QUEUE(sync, prev_frame_q); + MSM_DRAIN_QUEUE(sync, pict_frame_q); + + sync->sctrl.s_release(); + wake_unlock(&sync->wake_lock); + + sync->apps_id = NULL; + CDBG("msm_release completed!\n"); + } + mutex_unlock(&sync->lock); + + return 0; +} + +static int msm_release_config(struct inode *node, struct file *filep) +{ + int rc; + struct msm_device *pmsm = filep->private_data; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + atomic_set(&pmsm->opened, 0); + return rc; +} + +static int msm_release_control(struct inode *node, struct file *filep) +{ + int rc; + struct msm_control_device *ctrl_pmsm = filep->private_data; + struct msm_device *pmsm = ctrl_pmsm->pmsm; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + if (!rc) { + MSM_DRAIN_QUEUE(&ctrl_pmsm->ctrl_q, ctrl_status_q); + MSM_DRAIN_QUEUE(pmsm->sync, pict_frame_q); + } + kfree(ctrl_pmsm); + return rc; +} + +static int msm_release_frame(struct inode *node, struct file *filep) +{ + int rc; + struct msm_device *pmsm = filep->private_data; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + if (!rc) { + MSM_DRAIN_QUEUE(pmsm->sync, prev_frame_q); + atomic_set(&pmsm->opened, 0); + } + return rc; +} + +static int msm_unblock_poll_frame(struct msm_sync *sync) +{ + unsigned long flags; + CDBG("msm_unblock_poll_frame\n"); + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + sync->unblock_poll_frame = 1; + wake_up(&sync->prev_frame_wait); + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + return 0; +} + +static unsigned int __msm_poll_frame(struct msm_sync *sync, + struct file *filep, + struct poll_table_struct *pll_table) +{ + int rc = 0; + unsigned long flags; + + poll_wait(filep, &sync->prev_frame_wait, pll_table); + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + if (!list_empty_careful(&sync->prev_frame_q)) + /* frame ready */ + rc = POLLIN | POLLRDNORM; + if (sync->unblock_poll_frame) { + CDBG("%s: sync->unblock_poll_frame is true\n", __func__); + rc |= POLLPRI; + sync->unblock_poll_frame = 0; + } + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + + return rc; +} + +static unsigned int msm_poll_frame(struct file *filep, + struct poll_table_struct *pll_table) +{ + struct msm_device *pmsm = filep->private_data; + return __msm_poll_frame(pmsm->sync, filep, pll_table); +} + +/* + * This function executes in interrupt context. + */ + +static void *msm_vfe_sync_alloc(int size, + void *syncdata __attribute__((unused))) +{ + struct msm_queue_cmd *qcmd = + kmalloc(sizeof(struct msm_queue_cmd) + size, GFP_ATOMIC); + return qcmd ? qcmd + 1 : NULL; +} + +/* + * This function executes in interrupt context. + */ + +static void msm_vfe_sync(struct msm_vfe_resp *vdata, + enum msm_queue qtype, void *syncdata) +{ + struct msm_queue_cmd *qcmd = NULL; + struct msm_queue_cmd *qcmd_frame = NULL; + struct msm_vfe_phy_info *fphy; + + unsigned long flags; + struct msm_sync *sync = (struct msm_sync *)syncdata; + if (!sync) { + pr_err("msm_camera: no context in dsp callback.\n"); + return; + } + + qcmd = ((struct msm_queue_cmd *)vdata) - 1; + qcmd->type = qtype; + + if (qtype == MSM_CAM_Q_VFE_MSG) { + switch(vdata->type) { + case VFE_MSG_OUTPUT1: + case VFE_MSG_OUTPUT2: + qcmd_frame = + kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_vfe_phy_info), + GFP_ATOMIC); + if (!qcmd_frame) + goto mem_fail; + fphy = (struct msm_vfe_phy_info *)(qcmd_frame + 1); + *fphy = vdata->phy; + + qcmd_frame->type = MSM_CAM_Q_VFE_MSG; + qcmd_frame->command = fphy; + + CDBG("qcmd_frame= 0x%x phy_y= 0x%x, phy_cbcr= 0x%x\n", + (int) qcmd_frame, fphy->y_phy, fphy->cbcr_phy); + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + list_add_tail(&qcmd_frame->list, &sync->prev_frame_q); + wake_up(&sync->prev_frame_wait); + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + CDBG("woke up frame thread\n"); + break; + case VFE_MSG_SNAPSHOT: + if (sync->pict_pp) + break; + + CDBG("snapshot pp = %d\n", sync->pict_pp); + qcmd_frame = + kmalloc(sizeof(struct msm_queue_cmd), + GFP_ATOMIC); + if (!qcmd_frame) + goto mem_fail; + qcmd_frame->type = MSM_CAM_Q_VFE_MSG; + qcmd_frame->command = NULL; + spin_lock_irqsave(&sync->pict_frame_q_lock, + flags); + list_add_tail(&qcmd_frame->list, &sync->pict_frame_q); + wake_up(&sync->pict_frame_wait); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + CDBG("woke up picture thread\n"); + break; + default: + CDBG("%s: qtype = %d not handled\n", + __func__, vdata->type); + break; + } + } + + qcmd->command = (void *)vdata; + CDBG("vdata->type = %d\n", vdata->type); + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_add_tail(&qcmd->list, &sync->msg_event_q); + wake_up(&sync->msg_event_wait); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + CDBG("woke up config thread\n"); + return; + +mem_fail: + kfree(qcmd); +} + +static struct msm_vfe_callback msm_vfe_s = { + .vfe_resp = msm_vfe_sync, + .vfe_alloc = msm_vfe_sync_alloc, +}; + +static int __msm_open(struct msm_sync *sync, const char *const apps_id) +{ + int rc = 0; + + mutex_lock(&sync->lock); + if (sync->apps_id && strcmp(sync->apps_id, apps_id)) { + pr_err("msm_camera(%s): sensor %s is already opened for %s\n", + apps_id, + sync->sdata->sensor_name, + sync->apps_id); + rc = -EBUSY; + goto msm_open_done; + } + + sync->apps_id = apps_id; + + if (!sync->opencnt) { + wake_lock(&sync->wake_lock); + + msm_camvfe_fn_init(&sync->vfefn, sync); + if (sync->vfefn.vfe_init) { + rc = sync->vfefn.vfe_init(&msm_vfe_s, + sync->pdev); + if (rc < 0) { + pr_err("vfe_init failed at %d\n", rc); + goto msm_open_done; + } + rc = sync->sctrl.s_init(sync->sdata); + if (rc < 0) { + pr_err("sensor init failed: %d\n", rc); + goto msm_open_done; + } + } else { + pr_err("no sensor init func\n"); + rc = -ENODEV; + goto msm_open_done; + } + + if (rc >= 0) { + INIT_HLIST_HEAD(&sync->frame); + INIT_HLIST_HEAD(&sync->stats); + sync->unblock_poll_frame = 0; + } + } + sync->opencnt++; + +msm_open_done: + mutex_unlock(&sync->lock); + return rc; +} + +static int msm_open_common(struct inode *inode, struct file *filep, + int once) +{ + int rc; + struct msm_device *pmsm = + container_of(inode->i_cdev, struct msm_device, cdev); + + CDBG("msm_camera: open %s\n", filep->f_path.dentry->d_name.name); + + if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) { + pr_err("msm_camera: %s is already opened.\n", + filep->f_path.dentry->d_name.name); + return -EBUSY; + } + + rc = nonseekable_open(inode, filep); + if (rc < 0) { + pr_err("msm_open: nonseekable_open error %d\n", rc); + return rc; + } + + rc = __msm_open(pmsm->sync, MSM_APPS_ID_PROP); + if (rc < 0) + return rc; + + filep->private_data = pmsm; + + CDBG("msm_open() open: rc = %d\n", rc); + return rc; +} + +static int msm_open(struct inode *inode, struct file *filep) +{ + return msm_open_common(inode, filep, 1); +} + +static int msm_open_control(struct inode *inode, struct file *filep) +{ + int rc; + + struct msm_control_device *ctrl_pmsm = + kmalloc(sizeof(struct msm_control_device), GFP_KERNEL); + if (!ctrl_pmsm) + return -ENOMEM; + + rc = msm_open_common(inode, filep, 0); + if (rc < 0) + return rc; + + ctrl_pmsm->pmsm = filep->private_data; + filep->private_data = ctrl_pmsm; + spin_lock_init(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock); + INIT_LIST_HEAD(&ctrl_pmsm->ctrl_q.ctrl_status_q); + init_waitqueue_head(&ctrl_pmsm->ctrl_q.ctrl_status_wait); + + CDBG("msm_open() open: rc = %d\n", rc); + return rc; +} + +static int __msm_v4l2_control(struct msm_sync *sync, + struct msm_ctrl_cmd *out) +{ + int rc = 0; + + struct msm_queue_cmd *qcmd = NULL, *rcmd = NULL; + struct msm_ctrl_cmd *ctrl; + struct msm_control_device_queue FIXME; + + /* wake up config thread, 4 is for V4L2 application */ + qcmd = kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL); + if (!qcmd) { + pr_err("msm_control: cannot allocate buffer\n"); + rc = -ENOMEM; + goto end; + } + qcmd->type = MSM_CAM_Q_V4L2_REQ; + qcmd->command = out; + + rcmd = __msm_control(sync, &FIXME, qcmd, out->timeout_ms); + if (IS_ERR(rcmd)) { + rc = PTR_ERR(rcmd); + goto end; + } + + ctrl = (struct msm_ctrl_cmd *)(rcmd->command); + /* FIXME: we should just set out->length = ctrl->length; */ + BUG_ON(out->length < ctrl->length); + memcpy(out->value, ctrl->value, ctrl->length); + +end: + if (rcmd) kfree(rcmd); + CDBG("__msm_v4l2_control: end rc = %d\n", rc); + return rc; +} + +static const struct file_operations msm_fops_config = { + .owner = THIS_MODULE, + .open = msm_open, + .unlocked_ioctl = msm_ioctl_config, + .release = msm_release_config, +}; + +static const struct file_operations msm_fops_control = { + .owner = THIS_MODULE, + .open = msm_open_control, + .unlocked_ioctl = msm_ioctl_control, + .release = msm_release_control, +}; + +static const struct file_operations msm_fops_frame = { + .owner = THIS_MODULE, + .open = msm_open, + .unlocked_ioctl = msm_ioctl_frame, + .release = msm_release_frame, + .poll = msm_poll_frame, +}; + +static int msm_setup_cdev(struct msm_device *msm, + int node, + dev_t devno, + const char *suffix, + const struct file_operations *fops) +{ + int rc = -ENODEV; + + struct device *device = + device_create(msm_class, NULL, + devno, NULL, + "%s%d", suffix, node); + + if (IS_ERR(device)) { + rc = PTR_ERR(device); + pr_err("msm_camera: error creating device: %d\n", rc); + return rc; + } + + cdev_init(&msm->cdev, fops); + msm->cdev.owner = THIS_MODULE; + + rc = cdev_add(&msm->cdev, devno, 1); + if (rc < 0) { + pr_err("msm_camera: error adding cdev: %d\n", rc); + device_destroy(msm_class, devno); + return rc; + } + + return rc; +} + +static int msm_tear_down_cdev(struct msm_device *msm, dev_t devno) +{ + cdev_del(&msm->cdev); + device_destroy(msm_class, devno); + return 0; +} + +int msm_v4l2_register(struct msm_v4l2_driver *drv) +{ + /* FIXME: support multiple sensors */ + if (list_empty(&msm_sensors)) + return -ENODEV; + + drv->sync = list_first_entry(&msm_sensors, struct msm_sync, list); + drv->open = __msm_open; + drv->release = __msm_release; + drv->ctrl = __msm_v4l2_control; + drv->reg_pmem = __msm_register_pmem; + drv->get_frame = __msm_get_frame; + drv->put_frame = __msm_put_frame_buf; + drv->get_pict = __msm_get_pic; + drv->drv_poll = __msm_poll_frame; + + return 0; +} +EXPORT_SYMBOL(msm_v4l2_register); + +int msm_v4l2_unregister(struct msm_v4l2_driver *drv) +{ + drv->sync = NULL; + return 0; +} +EXPORT_SYMBOL(msm_v4l2_unregister); + +static int msm_sync_init(struct msm_sync *sync, + struct platform_device *pdev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)) +{ + int rc = 0; + struct msm_sensor_ctrl sctrl; + sync->sdata = pdev->dev.platform_data; + + spin_lock_init(&sync->msg_event_q_lock); + INIT_LIST_HEAD(&sync->msg_event_q); + init_waitqueue_head(&sync->msg_event_wait); + + spin_lock_init(&sync->prev_frame_q_lock); + INIT_LIST_HEAD(&sync->prev_frame_q); + init_waitqueue_head(&sync->prev_frame_wait); + + spin_lock_init(&sync->pict_frame_q_lock); + INIT_LIST_HEAD(&sync->pict_frame_q); + init_waitqueue_head(&sync->pict_frame_wait); + + wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera"); + + rc = msm_camio_probe_on(pdev); + if (rc < 0) + return rc; + rc = sensor_probe(sync->sdata, &sctrl); + if (rc >= 0) { + sync->pdev = pdev; + sync->sctrl = sctrl; + } + msm_camio_probe_off(pdev); + if (rc < 0) { + pr_err("msm_camera: failed to initialize %s\n", + sync->sdata->sensor_name); + wake_lock_destroy(&sync->wake_lock); + return rc; + } + + sync->opencnt = 0; + mutex_init(&sync->lock); + CDBG("initialized %s\n", sync->sdata->sensor_name); + return rc; +} + +static int msm_sync_destroy(struct msm_sync *sync) +{ + wake_lock_destroy(&sync->wake_lock); + return 0; +} + +static int msm_device_init(struct msm_device *pmsm, + struct msm_sync *sync, + int node) +{ + int dev_num = 3 * node; + int rc = msm_setup_cdev(pmsm, node, + MKDEV(MAJOR(msm_devno), dev_num), + "control", &msm_fops_control); + if (rc < 0) { + pr_err("error creating control node: %d\n", rc); + return rc; + } + + rc = msm_setup_cdev(pmsm + 1, node, + MKDEV(MAJOR(msm_devno), dev_num + 1), + "config", &msm_fops_config); + if (rc < 0) { + pr_err("error creating config node: %d\n", rc); + msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno), + dev_num)); + return rc; + } + + rc = msm_setup_cdev(pmsm + 2, node, + MKDEV(MAJOR(msm_devno), dev_num + 2), + "frame", &msm_fops_frame); + if (rc < 0) { + pr_err("error creating frame node: %d\n", rc); + msm_tear_down_cdev(pmsm, + MKDEV(MAJOR(msm_devno), dev_num)); + msm_tear_down_cdev(pmsm + 1, + MKDEV(MAJOR(msm_devno), dev_num + 1)); + return rc; + } + + atomic_set(&pmsm[0].opened, 0); + atomic_set(&pmsm[1].opened, 0); + atomic_set(&pmsm[2].opened, 0); + + pmsm[0].sync = sync; + pmsm[1].sync = sync; + pmsm[2].sync = sync; + + return rc; +} + +int msm_camera_drv_start(struct platform_device *dev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)) +{ + struct msm_device *pmsm = NULL; + struct msm_sync *sync; + int rc = -ENODEV; + static int camera_node; + + if (camera_node >= MSM_MAX_CAMERA_SENSORS) { + pr_err("msm_camera: too many camera sensors\n"); + return rc; + } + + if (!msm_class) { + /* There are three device nodes per sensor */ + rc = alloc_chrdev_region(&msm_devno, 0, + 3 * MSM_MAX_CAMERA_SENSORS, + "msm_camera"); + if (rc < 0) { + pr_err("msm_camera: failed to allocate chrdev: %d\n", + rc); + return rc; + } + + msm_class = class_create(THIS_MODULE, "msm_camera"); + if (IS_ERR(msm_class)) { + rc = PTR_ERR(msm_class); + pr_err("msm_camera: create device class failed: %d\n", + rc); + return rc; + } + } + + pmsm = kzalloc(sizeof(struct msm_device) * 3 + + sizeof(struct msm_sync), GFP_ATOMIC); + if (!pmsm) + return -ENOMEM; + sync = (struct msm_sync *)(pmsm + 3); + + rc = msm_sync_init(sync, dev, sensor_probe); + if (rc < 0) { + kfree(pmsm); + return rc; + } + + CDBG("setting camera node %d\n", camera_node); + rc = msm_device_init(pmsm, sync, camera_node); + if (rc < 0) { + msm_sync_destroy(sync); + kfree(pmsm); + return rc; + } + + camera_node++; + list_add(&sync->list, &msm_sensors); + return rc; +} +EXPORT_SYMBOL(msm_camera_drv_start); diff --git a/drivers/staging/dream/camera/msm_io7x.c b/drivers/staging/dream/camera/msm_io7x.c new file mode 100644 index 0000000..55c020b --- /dev/null +++ b/drivers/staging/dream/camera/msm_io7x.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2008-2009 QUALCOMM Incorporated + */ + +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <mach/gpio.h> +#include <mach/board.h> +#include <mach/camera.h> + +#define CAMIF_CFG_RMSK 0x1fffff +#define CAM_SEL_BMSK 0x2 +#define CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define CAM_PCLK_INVERT_BMSK 0x80000 +#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 +#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 +#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 + +#define CAM_SEL_SHFT 0x1 +#define CAM_PCLK_SRC_SEL_SHFT 0x11 +#define CAM_PCLK_INVERT_SHFT 0x13 +#define CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 +#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF +#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 +#define APPS_RESET_OFFSET 0x00000210 + +static struct clk *camio_vfe_mdc_clk; +static struct clk *camio_mdc_clk; +static struct clk *camio_vfe_clk; + +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +extern int clk_set_flags(struct clk *clk, unsigned long flags); + +int msm_camio_clk_enable(enum msm_camio_clk_type clktype) +{ + int rc = -1; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk = clk_get(NULL, "vfe_mdc_clk"); + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk = clk_get(NULL, "mdc_clk"); + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk = clk_get(NULL, "vfe_clk"); + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_enable(clk); + rc = 0; + } + + return rc; +} + +int msm_camio_clk_disable(enum msm_camio_clk_type clktype) +{ + int rc = -1; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk; + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk; + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk; + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_disable(clk); + clk_put(clk); + rc = 0; + } + + return rc; +} + +void msm_camio_clk_rate_set(int rate) +{ + struct clk *clk = camio_vfe_clk; + + if (clk != ERR_PTR(-ENOENT)) + clk_set_rate(clk, rate); +} + +int msm_camio_enable(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camio_ext = camdev->ioext; + + appio = request_mem_region(camio_ext.appphy, + camio_ext.appsz, pdev->name); + if (!appio) { + rc = -EBUSY; + goto enable_fail; + } + + appbase = ioremap(camio_ext.appphy, + camio_ext.appsz); + if (!appbase) { + rc = -ENOMEM; + goto apps_no_mem; + } + + mdcio = request_mem_region(camio_ext.mdcphy, + camio_ext.mdcsz, pdev->name); + if (!mdcio) { + rc = -EBUSY; + goto mdc_busy; + } + + mdcbase = ioremap(camio_ext.mdcphy, + camio_ext.mdcsz); + if (!mdcbase) { + rc = -ENOMEM; + goto mdc_no_mem; + } + + camdev->camera_gpio_on(); + + msm_camio_clk_enable(CAMIO_VFE_CLK); + msm_camio_clk_enable(CAMIO_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + return 0; + +mdc_no_mem: + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); +mdc_busy: + iounmap(appbase); +apps_no_mem: + release_mem_region(camio_ext.appphy, camio_ext.appsz); +enable_fail: + return rc; +} + +void msm_camio_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + iounmap(mdcbase); + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); + iounmap(appbase); + release_mem_region(camio_ext.appphy, camio_ext.appsz); + + camdev->camera_gpio_off(); + + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_MDC_CLK); + msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); +} + +void msm_camio_camif_pad_reg_reset(void) +{ + uint32_t reg; + uint32_t mask, value; + + /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */ + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + + mask = CAM_SEL_BMSK | + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK; + + value = 1 << CAM_SEL_SHFT | + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT; + + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL); + mdelay(10); +} + +void msm_camio_vfe_blk_reset(void) +{ + uint32_t val; + + val = readl(appbase + 0x00000210); + val |= 0x1; + writel(val, appbase + 0x00000210); + mdelay(10); + + val = readl(appbase + 0x00000210); + val &= ~0x1; + writel(val, appbase + 0x00000210); + mdelay(10); +} + +void msm_camio_camif_pad_reg_reset_2(void) +{ + uint32_t reg; + uint32_t mask, value; + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); +} + +void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) +{ + struct clk *clk = NULL; + + clk = camio_vfe_clk; + + if (clk != NULL && clk != ERR_PTR(-ENOENT)) { + switch (srctype) { + case MSM_CAMIO_CLK_SRC_INTERNAL: + clk_set_flags(clk, 0x00000100 << 1); + break; + + case MSM_CAMIO_CLK_SRC_EXTERNAL: + clk_set_flags(clk, 0x00000100); + break; + + default: + break; + } + } +} + +int msm_camio_probe_on(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_on(); + return msm_camio_clk_enable(CAMIO_VFE_CLK); +} + +int msm_camio_probe_off(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_off(); + return msm_camio_clk_disable(CAMIO_VFE_CLK); +} diff --git a/drivers/staging/dream/camera/msm_io8x.c b/drivers/staging/dream/camera/msm_io8x.c new file mode 100644 index 0000000..895161a --- /dev/null +++ b/drivers/staging/dream/camera/msm_io8x.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2008-2009 QUALCOMM Incorporated + */ + +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <mach/gpio.h> +#include <mach/board.h> +#include <mach/camera.h> + +#define CAMIF_CFG_RMSK 0x1fffff +#define CAM_SEL_BMSK 0x2 +#define CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define CAM_PCLK_INVERT_BMSK 0x80000 +#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 +#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 +#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 + +#define CAM_SEL_SHFT 0x1 +#define CAM_PCLK_SRC_SEL_SHFT 0x11 +#define CAM_PCLK_INVERT_SHFT 0x13 +#define CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 +#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF +#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 +#define APPS_RESET_OFFSET 0x00000210 + +static struct clk *camio_vfe_mdc_clk; +static struct clk *camio_mdc_clk; +static struct clk *camio_vfe_clk; +static struct clk *camio_vfe_axi_clk; +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +extern int clk_set_flags(struct clk *clk, unsigned long flags); + +int msm_camio_clk_enable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + camio_vfe_mdc_clk = + clk = clk_get(NULL, "vfe_mdc_clk"); + break; + + case CAMIO_MDC_CLK: + camio_mdc_clk = + clk = clk_get(NULL, "mdc_clk"); + break; + + case CAMIO_VFE_CLK: + camio_vfe_clk = + clk = clk_get(NULL, "vfe_clk"); + break; + + case CAMIO_VFE_AXI_CLK: + camio_vfe_axi_clk = + clk = clk_get(NULL, "vfe_axi_clk"); + break; + + default: + break; + } + + if (!IS_ERR(clk)) + clk_enable(clk); + else + rc = -1; + + return rc; +} + +int msm_camio_clk_disable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk; + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk; + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk; + break; + + case CAMIO_VFE_AXI_CLK: + clk = camio_vfe_axi_clk; + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_disable(clk); + clk_put(clk); + } else + rc = -1; + + return rc; +} + +void msm_camio_clk_rate_set(int rate) +{ + struct clk *clk = camio_vfe_mdc_clk; + + /* TODO: check return */ + clk_set_rate(clk, rate); +} + +int msm_camio_enable(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camio_ext = camdev->ioext; + + appio = request_mem_region(camio_ext.appphy, + camio_ext.appsz, pdev->name); + if (!appio) { + rc = -EBUSY; + goto enable_fail; + } + + appbase = ioremap(camio_ext.appphy, + camio_ext.appsz); + if (!appbase) { + rc = -ENOMEM; + goto apps_no_mem; + } + + mdcio = request_mem_region(camio_ext.mdcphy, + camio_ext.mdcsz, pdev->name); + if (!mdcio) { + rc = -EBUSY; + goto mdc_busy; + } + + mdcbase = ioremap(camio_ext.mdcphy, + camio_ext.mdcsz); + if (!mdcbase) { + rc = -ENOMEM; + goto mdc_no_mem; + } + + camdev->camera_gpio_on(); + + msm_camio_clk_enable(CAMIO_VFE_CLK); + msm_camio_clk_enable(CAMIO_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_AXI_CLK); + return 0; + +mdc_no_mem: + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); +mdc_busy: + iounmap(appbase); +apps_no_mem: + release_mem_region(camio_ext.appphy, camio_ext.appsz); +enable_fail: + return rc; +} + +void msm_camio_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + iounmap(mdcbase); + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); + iounmap(appbase); + release_mem_region(camio_ext.appphy, camio_ext.appsz); + + camdev->camera_gpio_off(); + + msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_disable(CAMIO_MDC_CLK); + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_VFE_AXI_CLK); +} + +void msm_camio_camif_pad_reg_reset(void) +{ + uint32_t reg; + uint32_t mask, value; + + /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */ + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + + mask = CAM_SEL_BMSK | + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK | + EXT_CAM_HSYNC_POL_SEL_BMSK | + EXT_CAM_VSYNC_POL_SEL_BMSK | + MDDI_CLK_CHICKEN_BIT_BMSK; + + value = 1 << CAM_SEL_SHFT | + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT | + 0 << EXT_CAM_HSYNC_POL_SEL_SHFT | + 0 << EXT_CAM_VSYNC_POL_SEL_SHFT | + 0 << MDDI_CLK_CHICKEN_BIT_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL); + + mdelay(10); + + /* todo: check return */ + if (camio_vfe_clk) + clk_set_rate(camio_vfe_clk, 96000000); +} + +void msm_camio_vfe_blk_reset(void) +{ + uint32_t val; + + val = readl(appbase + 0x00000210); + val |= 0x1; + writel(val, appbase + 0x00000210); + mdelay(10); + + val = readl(appbase + 0x00000210); + val &= ~0x1; + writel(val, appbase + 0x00000210); + mdelay(10); +} + +void msm_camio_camif_pad_reg_reset_2(void) +{ + uint32_t reg; + uint32_t mask, value; + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); +} + +void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) +{ + struct clk *clk = NULL; + + clk = camio_vfe_clk; + + if (clk != NULL) { + switch (srctype) { + case MSM_CAMIO_CLK_SRC_INTERNAL: + clk_set_flags(clk, 0x00000100 << 1); + break; + + case MSM_CAMIO_CLK_SRC_EXTERNAL: + clk_set_flags(clk, 0x00000100); + break; + + default: + break; + } + } +} + +void msm_camio_clk_axi_rate_set(int rate) +{ + struct clk *clk = camio_vfe_axi_clk; + /* todo: check return */ + clk_set_rate(clk, rate); +} + +int msm_camio_probe_on(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camdev->camera_gpio_on(); + return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); +} + +int msm_camio_probe_off(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camdev->camera_gpio_off(); + return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); +} diff --git a/drivers/staging/dream/camera/msm_v4l2.c b/drivers/staging/dream/camera/msm_v4l2.c new file mode 100644 index 0000000..6a7d46c --- /dev/null +++ b/drivers/staging/dream/camera/msm_v4l2.c @@ -0,0 +1,797 @@ +/* + * + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + * + */ + +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/ioctl.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> +#include <linux/proc_fs.h> +#include <media/v4l2-dev.h> +#include <media/msm_camera.h> +#include <mach/camera.h> +#include <media/v4l2-ioctl.h> +/*#include <linux/platform_device.h>*/ + +#define MSM_V4L2_START_SNAPSHOT _IOWR('V', BASE_VIDIOC_PRIVATE+1, \ + struct v4l2_buffer) + +#define MSM_V4L2_GET_PICTURE _IOWR('V', BASE_VIDIOC_PRIVATE+2, \ + struct v4l2_buffer) + +#define MSM_V4L2_DEVICE_NAME "msm_v4l2" + +#define MSM_V4L2_PROC_NAME "msm_v4l2" + +#define MSM_V4L2_DEVNUM_MPEG2 0 +#define MSM_V4L2_DEVNUM_YUV 20 + +/* HVGA-P (portrait) and HVGA-L (landscape) */ +#define MSM_V4L2_WIDTH 480 +#define MSM_V4L2_HEIGHT 320 + +#if 1 +#define D(fmt, args...) printk(KERN_INFO "msm_v4l2: " fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +#define PREVIEW_FRAMES_NUM 4 + +struct msm_v4l2_device { + struct list_head read_queue; + struct v4l2_format current_cap_format; + struct v4l2_format current_pix_format; + struct video_device *pvdev; + struct msm_v4l2_driver *drv; + uint8_t opencnt; + + spinlock_t read_queue_lock; +}; + +static struct msm_v4l2_device *g_pmsm_v4l2_dev; + + +static DEFINE_MUTEX(msm_v4l2_opencnt_lock); + +static int msm_v4l2_open(struct file *f) +{ + int rc = 0; + D("%s\n", __func__); + mutex_lock(&msm_v4l2_opencnt_lock); + if (!g_pmsm_v4l2_dev->opencnt) { + rc = g_pmsm_v4l2_dev->drv->open( + g_pmsm_v4l2_dev->drv->sync, + MSM_APPS_ID_V4L2); + } + g_pmsm_v4l2_dev->opencnt++; + mutex_unlock(&msm_v4l2_opencnt_lock); + return rc; +} + +static int msm_v4l2_release(struct file *f) +{ + int rc = 0; + D("%s\n", __func__); + mutex_lock(&msm_v4l2_opencnt_lock); + if (!g_pmsm_v4l2_dev->opencnt) { + g_pmsm_v4l2_dev->opencnt--; + if (!g_pmsm_v4l2_dev->opencnt) { + rc = g_pmsm_v4l2_dev->drv->release( + g_pmsm_v4l2_dev->drv->sync); + } + } + mutex_unlock(&msm_v4l2_opencnt_lock); + return rc; +} + +static unsigned int msm_v4l2_poll(struct file *f, struct poll_table_struct *w) +{ + return g_pmsm_v4l2_dev->drv->drv_poll(g_pmsm_v4l2_dev->drv->sync, f, w); +} + +static long msm_v4l2_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct msm_ctrl_cmd *ctrlcmd; + + D("msm_v4l2_ioctl, cmd = %d, %d\n", cmd, __LINE__); + + switch (cmd) { + case MSM_V4L2_START_SNAPSHOT: + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_ioctl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + ctrlcmd->timeout_ms = 10000; + + D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n", + cmd); + ctrlcmd->type = MSM_V4L2_SNAPSHOT; + return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + case MSM_V4L2_GET_PICTURE: + D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd); + ctrlcmd = (struct msm_ctrl_cmd *)arg; + return g_pmsm_v4l2_dev->drv->get_pict( + g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + + default: + D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd); + return video_ioctl2(filep, cmd, arg); + } +} + +static void msm_v4l2_release_dev(struct video_device *d) +{ + D("%s\n", __func__); +} + +static int msm_v4l2_querycap(struct file *f, + void *pctx, struct v4l2_capability *pcaps) +{ + D("%s\n", __func__); + strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver)); + strncpy(pcaps->card, + MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card)); + pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id *pnorm) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_queryctrl(struct file *f, + void *pctx, struct v4l2_queryctrl *pqctrl) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_queryctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_QUERY_CTRL; + ctrlcmd->length = sizeof(struct v4l2_queryctrl); + ctrlcmd->value = pqctrl; + ctrlcmd->timeout_ms = 10000; + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_g_ctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_GET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; + ctrlcmd->timeout_ms = 10000; + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_ctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_SET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; + ctrlcmd->timeout_ms = 10000; + + D("%s\n", __func__); + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_reqbufs(struct file *f, + void *pctx, struct v4l2_requestbuffers *b) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + struct msm_pmem_info pmem_buf; +#if 0 + __u32 width = 0; + __u32 height = 0; + __u32 y_size = 0; + __u32 y_pad = 0; + + /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.width; */ + width = 640; + /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.height; */ + height = 480; + + D("%s: width = %d, height = %d\n", __func__, width, height); + + y_size = width * height; + y_pad = y_size % 4; +#endif + + __u32 y_pad = pb->bytesused % 4; + + /* V4L2 videodev will do the copy_from_user. */ + + memset(&pmem_buf, 0, sizeof(struct msm_pmem_info)); + pmem_buf.type = MSM_PMEM_OUTPUT2; + pmem_buf.vaddr = (void *)pb->m.userptr; + pmem_buf.y_off = 0; + pmem_buf.fd = (int)pb->reserved; + /* pmem_buf.cbcr_off = (y_size + y_pad); */ + pmem_buf.cbcr_off = (pb->bytesused + y_pad); + + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf); + + return 0; +} + +static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + /* + __u32 y_size = 0; + __u32 y_pad = 0; + __u32 width = 0; + __u32 height = 0; + */ + + __u32 y_pad = 0; + + struct msm_pmem_info meminfo; + struct msm_frame frame; + static int cnt; + + if ((pb->flags >> 16) & 0x0001) { + /* this is for previwe */ +#if 0 + width = 640; + height = 480; + + /* V4L2 videodev will do the copy_from_user. */ + D("%s: width = %d, height = %d\n", __func__, width, height); + y_size = width * height; + y_pad = y_size % 4; +#endif + + y_pad = pb->bytesused % 4; + + if (pb->type == V4L2_BUF_TYPE_PRIVATE) { + /* this qbuf is actually for releasing */ + + frame.buffer = pb->m.userptr; + frame.y_off = 0; + /* frame.cbcr_off = (y_size + y_pad); */ + frame.cbcr_off = (pb->bytesused + y_pad); + frame.fd = pb->reserved; + + D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n", + pb->bytesused); + + g_pmsm_v4l2_dev->drv->put_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + + return 0; + } + + D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n", + pb->bytesused); + + meminfo.type = MSM_PMEM_OUTPUT2; + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; + /* meminfo.cbcr_off = (y_size + y_pad); */ + meminfo.cbcr_off = (pb->bytesused + y_pad); + if (cnt == PREVIEW_FRAMES_NUM - 1) + meminfo.active = 0; + else + meminfo.active = 1; + cnt++; + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, + &meminfo); + } else if ((pb->flags) & 0x0001) { + /* this is for snapshot */ + + __u32 y_size = 0; + + if ((pb->flags >> 8) & 0x01) { + + y_size = pb->bytesused; + + meminfo.type = MSM_PMEM_THUMBAIL; + } else if ((pb->flags >> 9) & 0x01) { + + y_size = pb->bytesused; + + meminfo.type = MSM_PMEM_MAINIMG; + } + + y_pad = y_size % 4; + + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; + /* meminfo.cbcr_off = (y_size + y_pad); */ + meminfo.cbcr_off = (y_size + y_pad); + meminfo.active = 1; + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, + &meminfo); + } + + return 0; +} + +static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + struct msm_frame frame; + D("%s\n", __func__); + + /* V4L2 videodev will do the copy_to_user. */ + if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + + D("%s, %d\n", __func__, __LINE__); + + g_pmsm_v4l2_dev->drv->get_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + + pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */ + pb->reserved = (int)frame.fd; + /* pb->length = (int)frame.cbcr_off; */ + + pb->bytesused = frame.cbcr_off; + + } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) { + __u32 y_pad = pb->bytesused % 4; + + frame.buffer = pb->m.userptr; + frame.y_off = 0; + /* frame.cbcr_off = (y_size + y_pad); */ + frame.cbcr_off = (pb->bytesused + y_pad); + frame.fd = pb->reserved; + + g_pmsm_v4l2_dev->drv->put_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + } + + return 0; +} + +static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i) +{ + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_STREAM_ON; + ctrlcmd->timeout_ms = 10000; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + + D("%s\n", __func__); + + g_pmsm_v4l2_dev->drv->ctrl( + g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + D("%s after drv->ctrl \n", __func__); + + return 0; +} + +static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i) +{ + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_STREAM_OFF; + ctrlcmd->timeout_ms = 10000; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + + + D("%s\n", __func__); + + g_pmsm_v4l2_dev->drv->ctrl( + g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + return 0; +} + +static int msm_v4l2_enum_fmt_overlay(struct file *f, + void *pctx, struct v4l2_fmtdesc *pfmtdesc) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_enum_fmt_cap(struct file *f, + void *pctx, struct v4l2_fmtdesc *pfmtdesc) +{ + D("%s\n", __func__); + + switch (pfmtdesc->index) { + case 0: + pfmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pfmtdesc->flags = 0; + strncpy(pfmtdesc->description, "YUV 4:2:0", + sizeof(pfmtdesc->description)); + pfmtdesc->pixelformat = V4L2_PIX_FMT_YVU420; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int msm_v4l2_g_fmt_cap(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + pfmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pfmt->fmt.pix.width = MSM_V4L2_WIDTH; + pfmt->fmt.pix.height = MSM_V4L2_HEIGHT; + pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; + pfmt->fmt.pix.field = V4L2_FIELD_ANY; + pfmt->fmt.pix.bytesperline = 0; + pfmt->fmt.pix.sizeimage = 0; + pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + pfmt->fmt.pix.priv = 0; + return 0; +} + +static int msm_v4l2_s_fmt_cap(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE; + ctrlcmd->length = sizeof(struct v4l2_format); + ctrlcmd->value = pfmt; + ctrlcmd->timeout_ms = 10000; + + if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + kfree(ctrlcmd); + return -1; + } + +#if 0 + /* FIXEME */ + if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420) { + kfree(ctrlcmd); + return -EINVAL; + } +#endif + + /* Ok, but check other params, too. */ + +#if 0 + memcpy(&g_pmsm_v4l2_dev->current_pix_format.fmt.pix, pfmt, + sizeof(struct v4l2_format)); +#endif + + g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + + return 0; +} + +static int msm_v4l2_g_fmt_overlay(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + pfmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + pfmt->fmt.pix.width = MSM_V4L2_WIDTH; + pfmt->fmt.pix.height = MSM_V4L2_HEIGHT; + pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; + pfmt->fmt.pix.field = V4L2_FIELD_ANY; + pfmt->fmt.pix.bytesperline = 0; + pfmt->fmt.pix.sizeimage = 0; + pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + pfmt->fmt.pix.priv = 0; + return 0; +} + +static int msm_v4l2_s_fmt_overlay(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_overlay(struct file *f, void *pctx, unsigned int i) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_g_jpegcomp(struct file *f, + void *pctx, struct v4l2_jpegcompression *pcomp) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_s_jpegcomp(struct file *f, + void *pctx, struct v4l2_jpegcompression *pcomp) +{ + D("%s\n", __func__); + return 0; +} + +#ifdef CONFIG_PROC_FS +int msm_v4l2_read_proc(char *pbuf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len = 0; + len += snprintf(pbuf, strlen("stats\n") + 1, "stats\n"); + + if (g_pmsm_v4l2_dev) { + len += snprintf(pbuf, strlen("mode: ") + 1, "mode: "); + + if (g_pmsm_v4l2_dev->current_cap_format.type + == V4L2_BUF_TYPE_VIDEO_CAPTURE) + len += snprintf(pbuf, strlen("capture\n") + 1, + "capture\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + + len += snprintf(pbuf, 21, "resolution: %dx%d\n", + g_pmsm_v4l2_dev->current_cap_format.fmt.pix. + width, + g_pmsm_v4l2_dev->current_cap_format.fmt.pix. + height); + + len += snprintf(pbuf, + strlen("pixel format: ") + 1, "pixel format: "); + if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.pixelformat + == V4L2_PIX_FMT_YVU420) + len += snprintf(pbuf, strlen("yvu420\n") + 1, + "yvu420\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + + len += snprintf(pbuf, strlen("colorspace: ") + 1, + "colorspace: "); + if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.colorspace + == V4L2_COLORSPACE_JPEG) + len += snprintf(pbuf, strlen("jpeg\n") + 1, "jpeg\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + } + + *eof = 1; + return len; +} +#endif + +static const struct v4l2_file_operations msm_v4l2_fops = { + .owner = THIS_MODULE, + .open = msm_v4l2_open, + .poll = msm_v4l2_poll, + .release = msm_v4l2_release, + .ioctl = msm_v4l2_ioctl, +}; + +static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev) +{ + pmsm_v4l2_dev->read_queue_lock = + __SPIN_LOCK_UNLOCKED(pmsm_v4l2_dev->read_queue_lock); + INIT_LIST_HEAD(&pmsm_v4l2_dev->read_queue); +} + +static int msm_v4l2_try_fmt_cap(struct file *file, + void *fh, struct v4l2_format *f) +{ + /* FIXME */ + return 0; +} + +static int mm_v4l2_try_fmt_type_private(struct file *file, + void *fh, struct v4l2_format *f) +{ + /* FIXME */ + return 0; +} + +/* + * should the following structure be used instead of the code in the function? + * static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { + * .vidioc_querycap = .... + * } + */ +static const struct v4l2_ioctl_ops msm_ioctl_ops = { + .vidioc_querycap = msm_v4l2_querycap, + .vidioc_s_std = msm_v4l2_s_std, + + .vidioc_queryctrl = msm_v4l2_queryctrl, + .vidioc_g_ctrl = msm_v4l2_g_ctrl, + .vidioc_s_ctrl = msm_v4l2_s_ctrl, + + .vidioc_reqbufs = msm_v4l2_reqbufs, + .vidioc_querybuf = msm_v4l2_querybuf, + .vidioc_qbuf = msm_v4l2_qbuf, + .vidioc_dqbuf = msm_v4l2_dqbuf, + + .vidioc_streamon = msm_v4l2_streamon, + .vidioc_streamoff = msm_v4l2_streamoff, + + .vidioc_enum_fmt_vid_overlay = msm_v4l2_enum_fmt_overlay, + .vidioc_enum_fmt_vid_cap = msm_v4l2_enum_fmt_cap, + + .vidioc_try_fmt_vid_cap = msm_v4l2_try_fmt_cap, + .vidioc_try_fmt_type_private = mm_v4l2_try_fmt_type_private, + + .vidioc_g_fmt_vid_cap = msm_v4l2_g_fmt_cap, + .vidioc_s_fmt_vid_cap = msm_v4l2_s_fmt_cap, + .vidioc_g_fmt_vid_overlay = msm_v4l2_g_fmt_overlay, + .vidioc_s_fmt_vid_overlay = msm_v4l2_s_fmt_overlay, + .vidioc_overlay = msm_v4l2_overlay, + + .vidioc_g_jpegcomp = msm_v4l2_g_jpegcomp, + .vidioc_s_jpegcomp = msm_v4l2_s_jpegcomp, +}; + +static int msm_v4l2_video_dev_init(struct video_device *pvd) +{ + strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name)); + pvd->vfl_type = 1; + pvd->fops = &msm_v4l2_fops; + pvd->release = msm_v4l2_release_dev; + pvd->minor = -1; + pvd->ioctl_ops = &msm_ioctl_ops; + return msm_v4l2_register(g_pmsm_v4l2_dev->drv); +} + +static int __init msm_v4l2_init(void) +{ + int rc = -ENOMEM; + struct video_device *pvdev = NULL; + struct msm_v4l2_device *pmsm_v4l2_dev = NULL; + D("%s\n", __func__); + + pvdev = video_device_alloc(); + if (pvdev == NULL) + return rc; + + pmsm_v4l2_dev = + kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL); + if (pmsm_v4l2_dev == NULL) { + video_device_release(pvdev); + return rc; + } + + msm_v4l2_dev_init(pmsm_v4l2_dev); + + g_pmsm_v4l2_dev = pmsm_v4l2_dev; + g_pmsm_v4l2_dev->pvdev = pvdev; + + g_pmsm_v4l2_dev->drv = + kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL); + if (!g_pmsm_v4l2_dev->drv) { + video_device_release(pvdev); + kfree(pmsm_v4l2_dev); + return rc; + } + + rc = msm_v4l2_video_dev_init(pvdev); + if (rc < 0) { + video_device_release(pvdev); + kfree(g_pmsm_v4l2_dev->drv); + kfree(pmsm_v4l2_dev); + return rc; + } + + if (video_register_device(pvdev, VFL_TYPE_GRABBER, + MSM_V4L2_DEVNUM_YUV)) { + D("failed to register device\n"); + video_device_release(pvdev); + kfree(g_pmsm_v4l2_dev); + g_pmsm_v4l2_dev = NULL; + return -ENOENT; + } +#ifdef CONFIG_PROC_FS + create_proc_read_entry(MSM_V4L2_PROC_NAME, + 0, NULL, msm_v4l2_read_proc, NULL); +#endif + + return 0; +} + +static void __exit msm_v4l2_exit(void) +{ + struct video_device *pvdev = g_pmsm_v4l2_dev->pvdev; + D("%s\n", __func__); +#ifdef CONFIG_PROC_FS + remove_proc_entry(MSM_V4L2_PROC_NAME, NULL); +#endif + video_unregister_device(pvdev); + video_device_release(pvdev); + + msm_v4l2_unregister(g_pmsm_v4l2_dev->drv); + + kfree(g_pmsm_v4l2_dev->drv); + g_pmsm_v4l2_dev->drv = NULL; + + kfree(g_pmsm_v4l2_dev); + g_pmsm_v4l2_dev = NULL; +} + +module_init(msm_v4l2_init); +module_exit(msm_v4l2_exit); + +MODULE_DESCRIPTION("MSM V4L2 driver"); +MODULE_LICENSE("GPL v2"); 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; +} diff --git a/drivers/staging/dream/camera/msm_vfe7x.h b/drivers/staging/dream/camera/msm_vfe7x.h new file mode 100644 index 0000000..be3e9ad --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe7x.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __MSM_VFE7X_H__ +#define __MSM_VFE7X_H__ +#include <media/msm_camera.h> +#include <mach/camera.h> + +struct vfe_frame_extra { + uint32_t bl_evencol; + uint32_t bl_oddcol; + uint16_t g_def_p_cnt; + uint16_t r_b_def_p_cnt; +}; + +struct vfe_endframe { + uint32_t y_address; + uint32_t cbcr_address; + + unsigned int blacklevelevencolumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddcolumn:23; + uint16_t reserved2:9; + + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__((packed, aligned(4))); + +struct vfe_outputack { + uint32_t header; + void *output2newybufferaddress; + void *output2newcbcrbufferaddress; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_ack { + uint32_t header; + /* MUST BE 64 bit ALIGNED */ + void *bufaddr; +} __attribute__((packed, aligned(4))); + +/* AXI Output Config Command sent to DSP */ +struct axiout { + uint32_t cmdheader:32; + int outputmode:3; + uint8_t format:2; + uint32_t /* reserved */ : 27; + + /* AXI Output 1 Y Configuration, Part 1 */ + uint32_t out1yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 Y Configuration, Part 2 */ + uint8_t out1yburstlen:2; + uint32_t out1ynumrows:12; + uint32_t out1yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 CbCr Configuration, Part 1 */ + uint32_t out1cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1cbcrimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 CbCr Configuration, Part 2 */ + uint8_t out1cbcrburstlen:2; + uint32_t out1cbcrnumrows:12; + uint32_t out1cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 Y Configuration, Part 1 */ + uint32_t out2yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 Y Configuration, Part 2 */ + uint8_t out2yburstlen:2; + uint32_t out2ynumrows:12; + uint32_t out2yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 CbCr Configuration, Part 1 */ + uint32_t out2cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2cbcrimagewidtein64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 CbCr Configuration, Part 2 */ + uint8_t out2cbcrburstlen:2; + uint32_t out2cbcrnumrows:12; + uint32_t out2cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* Address configuration: + * output1 phisycal address */ + unsigned long output1buffer1_y_phy; + unsigned long output1buffer1_cbcr_phy; + unsigned long output1buffer2_y_phy; + unsigned long output1buffer2_cbcr_phy; + unsigned long output1buffer3_y_phy; + unsigned long output1buffer3_cbcr_phy; + unsigned long output1buffer4_y_phy; + unsigned long output1buffer4_cbcr_phy; + unsigned long output1buffer5_y_phy; + unsigned long output1buffer5_cbcr_phy; + unsigned long output1buffer6_y_phy; + unsigned long output1buffer6_cbcr_phy; + unsigned long output1buffer7_y_phy; + unsigned long output1buffer7_cbcr_phy; + unsigned long output1buffer8_y_phy; + unsigned long output1buffer8_cbcr_phy; + + /* output2 phisycal address */ + unsigned long output2buffer1_y_phy; + unsigned long output2buffer1_cbcr_phy; + unsigned long output2buffer2_y_phy; + unsigned long output2buffer2_cbcr_phy; + unsigned long output2buffer3_y_phy; + unsigned long output2buffer3_cbcr_phy; + unsigned long output2buffer4_y_phy; + unsigned long output2buffer4_cbcr_phy; + unsigned long output2buffer5_y_phy; + unsigned long output2buffer5_cbcr_phy; + unsigned long output2buffer6_y_phy; + unsigned long output2buffer6_cbcr_phy; + unsigned long output2buffer7_y_phy; + unsigned long output2buffer7_cbcr_phy; + unsigned long output2buffer8_y_phy; + unsigned long output2buffer8_cbcr_phy; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_we_cfg { + uint32_t header; + + /* White Balance/Exposure Statistic Selection */ + uint8_t wb_expstatsenable:1; + uint8_t wb_expstatbuspriorityselection:1; + unsigned int wb_expstatbuspriorityvalue:4; + unsigned int /* reserved */ : 26; + + /* White Balance/Exposure Statistic Configuration, Part 1 */ + uint8_t exposurestatregions:1; + uint8_t exposurestatsubregions:1; + unsigned int /* reserved */ : 14; + + unsigned int whitebalanceminimumy:8; + unsigned int whitebalancemaximumy:8; + + /* White Balance/Exposure Statistic Configuration, Part 2 */ + uint8_t wb_expstatslopeofneutralregionline[ + NUM_WB_EXP_NEUTRAL_REGION_LINES]; + + /* White Balance/Exposure Statistic Configuration, Part 3 */ + unsigned int wb_expstatcrinterceptofneutralregionline2:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralreginnline1:12; + unsigned int /* reserved */ : 4; + + /* White Balance/Exposure Statistic Configuration, Part 4 */ + unsigned int wb_expstatcrinterceptofneutralregionline4:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralregionline3:12; + unsigned int /* reserved */ : 4; + + /* White Balance/Exposure Statistic Output Buffer Header */ + unsigned int wb_expmetricheaderpattern:8; + unsigned int /* reserved */ : 24; + + /* White Balance/Exposure Statistic Output Buffers-MUST + * BE 64 bit ALIGNED */ + void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS]; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_af_cfg { + uint32_t header; + + /* Autofocus Statistic Selection */ + uint8_t af_enable:1; + uint8_t af_busprioritysel:1; + unsigned int af_buspriorityval:4; + unsigned int /* reserved */ : 26; + + /* Autofocus Statistic Configuration, Part 1 */ + unsigned int af_singlewinvoffset:12; + unsigned int /* reserved */ : 4; + unsigned int af_singlewinhoffset:12; + unsigned int /* reserved */ : 3; + uint8_t af_winmode:1; + + /* Autofocus Statistic Configuration, Part 2 */ + unsigned int af_singglewinvh:11; + unsigned int /* reserved */ : 5; + unsigned int af_singlewinhw:11; + unsigned int /* reserved */ : 5; + + /* Autofocus Statistic Configuration, Parts 3-6 */ + uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS]; + + /* Autofocus Statistic Configuration, Part 7 */ + signed int af_metrichpfcoefa00:5; + signed int af_metrichpfcoefa04:5; + unsigned int af_metricmaxval:11; + uint8_t af_metricsel:1; + unsigned int /* reserved */ : 10; + + /* Autofocus Statistic Configuration, Part 8 */ + signed int af_metrichpfcoefa20:5; + signed int af_metrichpfcoefa21:5; + signed int af_metrichpfcoefa22:5; + signed int af_metrichpfcoefa23:5; + signed int af_metrichpfcoefa24:5; + unsigned int /* reserved */ : 7; + + /* Autofocus Statistic Output Buffer Header */ + unsigned int af_metrichp:8; + unsigned int /* reserved */ : 24; + + /* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */ + void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS]; +} __attribute__((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */ + +struct msm_camera_frame_msg { + unsigned long output_y_address; + unsigned long output_cbcr_address; + + unsigned int blacklevelevenColumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddColumn:23; + uint16_t reserved2:9; + + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__((packed, aligned(4))); + +/* New one for 7k */ +struct msm_vfe_command_7k { + uint16_t queue; + uint16_t length; + void *value; +}; + +struct stop_event { + wait_queue_head_t wait; + int state; + int timeout; +}; + + +#endif /* __MSM_VFE7X_H__ */ diff --git a/drivers/staging/dream/camera/msm_vfe8x.c b/drivers/staging/dream/camera/msm_vfe8x.c new file mode 100644 index 0000000..03de6ec --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe8x.c @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <mach/irqs.h> +#include "msm_vfe8x_proc.h" + +#define ON 1 +#define OFF 0 + +struct mutex vfe_lock; +static void *vfe_syncdata; + +static int vfe_enable(struct camera_enable_cmd *enable) +{ + int rc = 0; + return rc; +} + +static int vfe_disable(struct camera_enable_cmd *enable, + struct platform_device *dev) +{ + int rc = 0; + + vfe_stop(); + + msm_camio_disable(dev); + return rc; +} + +static void vfe_release(struct platform_device *dev) +{ + msm_camio_disable(dev); + vfe_cmd_release(dev); + + mutex_lock(&vfe_lock); + vfe_syncdata = NULL; + mutex_unlock(&vfe_lock); +} + +static void vfe_config_axi(int mode, + struct axidata *ad, struct vfe_cmd_axi_output_config *ao) +{ + struct msm_pmem_region *regptr; + int i, j; + uint32_t *p1, *p2; + + if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { + regptr = ad->region; + for (i = 0; + i < ad->bufnum1; i++) { + + p1 = &(ao->output1.outputY.outFragments[i][0]); + p2 = &(ao->output1.outputCbcr.outFragments[i][0]); + + for (j = 0; + j < ao->output1.fragmentCount; j++) { + + *p1 = regptr->paddr + regptr->y_off; + p1++; + + *p2 = regptr->paddr + regptr->cbcr_off; + p2++; + } + regptr++; + } + } /* if OUTPUT1 or Both */ + + if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { + + regptr = &(ad->region[ad->bufnum1]); + CDBG("bufnum2 = %d\n", ad->bufnum2); + + for (i = 0; + i < ad->bufnum2; i++) { + + p1 = &(ao->output2.outputY.outFragments[i][0]); + p2 = &(ao->output2.outputCbcr.outFragments[i][0]); + + CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, cbcr_off = %d\n", + regptr->paddr, regptr->y_off, regptr->cbcr_off); + + for (j = 0; + j < ao->output2.fragmentCount; j++) { + + *p1 = regptr->paddr + regptr->y_off; + CDBG("vfe_config_axi: p1 = 0x%x\n", *p1); + p1++; + + *p2 = regptr->paddr + regptr->cbcr_off; + CDBG("vfe_config_axi: p2 = 0x%x\n", *p2); + p2++; + } + regptr++; + } + } +} + +static int vfe_proc_general(struct msm_vfe_command_8k *cmd) +{ + int rc = 0; + + CDBG("vfe_proc_general: cmdID = %d\n", cmd->id); + + switch (cmd->id) { + case VFE_CMD_ID_RESET: + msm_camio_vfe_blk_reset(); + msm_camio_camif_pad_reg_reset_2(); + vfe_reset(); + break; + + case VFE_CMD_ID_START: { + struct vfe_cmd_start start; + if (copy_from_user(&start, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + /* msm_camio_camif_pad_reg_reset_2(); */ + msm_camio_camif_pad_reg_reset(); + vfe_start(&start); + } + break; + + case VFE_CMD_ID_CAMIF_CONFIG: { + struct vfe_cmd_camif_config camif; + if (copy_from_user(&camif, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_camif_config(&camif); + } + break; + + case VFE_CMD_ID_BLACK_LEVEL_CONFIG: { + struct vfe_cmd_black_level_config bl; + if (copy_from_user(&bl, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_black_level_config(&bl); + } + break; + + case VFE_CMD_ID_ROLL_OFF_CONFIG: { + struct vfe_cmd_roll_off_config rolloff; + if (copy_from_user(&rolloff, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_roll_off_config(&rolloff); + } + break; + + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: { + struct vfe_cmd_demux_channel_gain_config demuxc; + if (copy_from_user(&demuxc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + /* demux is always enabled. */ + vfe_demux_channel_gain_config(&demuxc); + } + break; + + case VFE_CMD_ID_DEMOSAIC_CONFIG: { + struct vfe_cmd_demosaic_config demosaic; + if (copy_from_user(&demosaic, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_config(&demosaic); + } + break; + + case VFE_CMD_ID_FOV_CROP_CONFIG: + case VFE_CMD_ID_FOV_CROP_UPDATE: { + struct vfe_cmd_fov_crop_config fov; + if (copy_from_user(&fov, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_fov_crop_config(&fov); + } + break; + + case VFE_CMD_ID_MAIN_SCALER_CONFIG: + case VFE_CMD_ID_MAIN_SCALER_UPDATE: { + struct vfe_cmd_main_scaler_config mainds; + if (copy_from_user(&mainds, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_main_scaler_config(&mainds); + } + break; + + case VFE_CMD_ID_WHITE_BALANCE_CONFIG: + case VFE_CMD_ID_WHITE_BALANCE_UPDATE: { + struct vfe_cmd_white_balance_config wb; + if (copy_from_user(&wb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_white_balance_config(&wb); + } + break; + + case VFE_CMD_ID_COLOR_CORRECTION_CONFIG: + case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: { + struct vfe_cmd_color_correction_config cc; + if (copy_from_user(&cc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_color_correction_config(&cc); + } + break; + + case VFE_CMD_ID_LA_CONFIG: { + struct vfe_cmd_la_config la; + if (copy_from_user(&la, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_la_config(&la); + } + break; + + case VFE_CMD_ID_RGB_GAMMA_CONFIG: { + struct vfe_cmd_rgb_gamma_config rgb; + if (copy_from_user(&rgb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + rc = vfe_rgb_gamma_config(&rgb); + } + break; + + case VFE_CMD_ID_CHROMA_ENHAN_CONFIG: + case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: { + struct vfe_cmd_chroma_enhan_config chrom; + if (copy_from_user(&chrom, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_enhan_config(&chrom); + } + break; + + case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG: + case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: { + struct vfe_cmd_chroma_suppression_config chromsup; + if (copy_from_user(&chromsup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_sup_config(&chromsup); + } + break; + + case VFE_CMD_ID_ASF_CONFIG: { + struct vfe_cmd_asf_config asf; + if (copy_from_user(&asf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_asf_config(&asf); + } + break; + + case VFE_CMD_ID_SCALER2Y_CONFIG: + case VFE_CMD_ID_SCALER2Y_UPDATE: { + struct vfe_cmd_scaler2_config ds2y; + if (copy_from_user(&ds2y, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_scaler2y_config(&ds2y); + } + break; + + case VFE_CMD_ID_SCALER2CbCr_CONFIG: + case VFE_CMD_ID_SCALER2CbCr_UPDATE: { + struct vfe_cmd_scaler2_config ds2cbcr; + if (copy_from_user(&ds2cbcr, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_scaler2cbcr_config(&ds2cbcr); + } + break; + + case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: { + struct vfe_cmd_chroma_subsample_config sub; + if (copy_from_user(&sub, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_subsample_config(&sub); + } + break; + + case VFE_CMD_ID_FRAME_SKIP_CONFIG: { + struct vfe_cmd_frame_skip_config fskip; + if (copy_from_user(&fskip, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_frame_skip_config(&fskip); + } + break; + + case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: { + struct vfe_cmd_output_clamp_config clamp; + if (copy_from_user(&clamp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_output_clamp_config(&clamp); + } + break; + + /* module update commands */ + case VFE_CMD_ID_BLACK_LEVEL_UPDATE: { + struct vfe_cmd_black_level_config blk; + if (copy_from_user(&blk, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_black_level_update(&blk); + } + break; + + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: { + struct vfe_cmd_demux_channel_gain_config dmu; + if (copy_from_user(&dmu, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demux_channel_gain_update(&dmu); + } + break; + + case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: { + struct vfe_cmd_demosaic_bpc_update demo_bpc; + if (copy_from_user(&demo_bpc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_bpc_update(&demo_bpc); + } + break; + + case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: { + struct vfe_cmd_demosaic_abf_update demo_abf; + if (copy_from_user(&demo_abf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_abf_update(&demo_abf); + } + break; + + case VFE_CMD_ID_LA_UPDATE: { + struct vfe_cmd_la_config la; + if (copy_from_user(&la, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_la_update(&la); + } + break; + + case VFE_CMD_ID_RGB_GAMMA_UPDATE: { + struct vfe_cmd_rgb_gamma_config rgb; + if (copy_from_user(&rgb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + rc = vfe_rgb_gamma_update(&rgb); + } + break; + + case VFE_CMD_ID_ASF_UPDATE: { + struct vfe_cmd_asf_update asf; + if (copy_from_user(&asf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_asf_update(&asf); + } + break; + + case VFE_CMD_ID_FRAME_SKIP_UPDATE: { + struct vfe_cmd_frame_skip_update fskip; + if (copy_from_user(&fskip, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_frame_skip_update(&fskip); + } + break; + + case VFE_CMD_ID_CAMIF_FRAME_UPDATE: { + struct vfe_cmds_camif_frame fup; + if (copy_from_user(&fup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_camif_frame_update(&fup); + } + break; + + /* stats update commands */ + case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: { + struct vfe_cmd_stats_af_update afup; + if (copy_from_user(&afup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_update_af(&afup); + } + break; + + case VFE_CMD_ID_STATS_WB_EXP_UPDATE: { + struct vfe_cmd_stats_wb_exp_update wbexp; + if (copy_from_user(&wbexp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_update_wb_exp(&wbexp); + } + break; + + /* control of start, stop, update, etc... */ + case VFE_CMD_ID_STOP: + vfe_stop(); + break; + + case VFE_CMD_ID_GET_HW_VERSION: + break; + + /* stats */ + case VFE_CMD_ID_STATS_SETTING: { + struct vfe_cmd_stats_setting stats; + if (copy_from_user(&stats, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_setting(&stats); + } + break; + + case VFE_CMD_ID_STATS_AUTOFOCUS_START: { + struct vfe_cmd_stats_af_start af; + if (copy_from_user(&af, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_start_af(&af); + } + break; + + case VFE_CMD_ID_STATS_AUTOFOCUS_STOP: + vfe_stats_af_stop(); + break; + + case VFE_CMD_ID_STATS_WB_EXP_START: { + struct vfe_cmd_stats_wb_exp_start awexp; + if (copy_from_user(&awexp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_start_wb_exp(&awexp); + } + break; + + case VFE_CMD_ID_STATS_WB_EXP_STOP: + vfe_stats_wb_exp_stop(); + break; + + case VFE_CMD_ID_ASYNC_TIMER_SETTING: + break; + + case VFE_CMD_ID_UPDATE: + vfe_update(); + break; + + /* test gen */ + case VFE_CMD_ID_TEST_GEN_START: + break; + +/* + acknowledge from upper layer + these are not in general command. + + case VFE_CMD_ID_OUTPUT1_ACK: + break; + case VFE_CMD_ID_OUTPUT2_ACK: + break; + case VFE_CMD_ID_EPOCH1_ACK: + break; + case VFE_CMD_ID_EPOCH2_ACK: + break; + case VFE_CMD_ID_STATS_AUTOFOCUS_ACK: + break; + case VFE_CMD_ID_STATS_WB_EXP_ACK: + break; +*/ + + default: + break; + } /* switch */ + + return rc; +} + +static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) +{ + struct msm_pmem_region *regptr; + struct msm_vfe_command_8k vfecmd; + + uint32_t i; + + void *cmd_data = NULL; + long rc = 0; + + struct vfe_cmd_axi_output_config *axio = NULL; + struct vfe_cmd_stats_setting *scfg = NULL; + + if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_BUF_RELEASE) { + + if (copy_from_user(&vfecmd, + (void __user *)(cmd->value), + sizeof(struct msm_vfe_command_8k))) + return -EFAULT; + } + + CDBG("vfe_config: cmdType = %d\n", cmd->cmd_type); + + switch (cmd->cmd_type) { + case CMD_GENERAL: + rc = vfe_proc_general(&vfecmd); + break; + + case CMD_STATS_ENABLE: + case CMD_STATS_AXI_CFG: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + scfg = + kmalloc(sizeof(struct vfe_cmd_stats_setting), + GFP_ATOMIC); + if (!scfg) + return -ENOMEM; + + if (copy_from_user(scfg, + (void __user *)(vfecmd.value), + vfecmd.length)) { + + kfree(scfg); + return -EFAULT; + } + + regptr = axid->region; + if (axid->bufnum1 > 0) { + for (i = 0; i < axid->bufnum1; i++) { + scfg->awbBuffer[i] = + (uint32_t)(regptr->paddr); + regptr++; + } + } + + if (axid->bufnum2 > 0) { + for (i = 0; i < axid->bufnum2; i++) { + scfg->afBuffer[i] = + (uint32_t)(regptr->paddr); + regptr++; + } + } + + vfe_stats_config(scfg); + } + break; + + case CMD_STATS_AF_AXI_CFG: { + } + break; + + case CMD_FRAME_BUF_RELEASE: { + /* preview buffer release */ + struct msm_frame *b; + unsigned long p; + struct vfe_cmd_output_ack fack; + + if (!data) + return -EFAULT; + + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; + + b->path = MSM_FRAME_ENC; + + fack.ybufaddr[0] = + (uint32_t)(p + b->y_off); + + fack.chromabufaddr[0] = + (uint32_t)(p + b->cbcr_off); + + if (b->path == MSM_FRAME_PREV_1) + vfe_output1_ack(&fack); + + if (b->path == MSM_FRAME_ENC || + b->path == MSM_FRAME_PREV_2) + vfe_output2_ack(&fack); + } + break; + + case CMD_SNAP_BUF_RELEASE: { + } + break; + + case CMD_STATS_BUF_RELEASE: { + struct vfe_cmd_stats_wb_exp_ack sack; + + if (!data) + return -EFAULT; + + sack.nextWbExpOutputBufferAddr = *(uint32_t *)data; + vfe_stats_wb_exp_ack(&sack); + } + break; + + case CMD_AXI_CFG_OUT1: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_1, axid, axio); + vfe_axi_output_config(axio); + } + break; + + case CMD_AXI_CFG_OUT2: + case CMD_RAW_PICT_AXI_CFG: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_2, axid, axio); + + axio->outputDataSize = 0; + vfe_axi_output_config(axio); + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: { + struct axidata *axid; + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_1_AND_2, + axid, axio); + vfe_axi_output_config(axio); + cmd_data = axio; + } + break; + + default: + break; + } /* switch */ + + kfree(scfg); + + kfree(axio); + +/* + if (cmd->length > 256 && + cmd_data && + (cmd->cmd_type == CMD_GENERAL || + cmd->cmd_type == CMD_STATS_DISABLE)) { + kfree(cmd_data); + } +*/ + return rc; +} + +static int vfe_init(struct msm_vfe_callback *presp, + struct platform_device *dev) +{ + int rc = 0; + + rc = vfe_cmd_init(presp, dev, vfe_syncdata); + if (rc < 0) + return rc; + + /* Bring up all the required GPIOs and Clocks */ + return msm_camio_enable(dev); +} + +void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) +{ + mutex_init(&vfe_lock); + fptr->vfe_init = vfe_init; + fptr->vfe_enable = vfe_enable; + fptr->vfe_config = vfe_config; + fptr->vfe_disable = vfe_disable; + fptr->vfe_release = vfe_release; + vfe_syncdata = data; +} diff --git a/drivers/staging/dream/camera/msm_vfe8x.h b/drivers/staging/dream/camera/msm_vfe8x.h new file mode 100644 index 0000000..28a70a9 --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe8x.h @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __MSM_VFE8X_H__ +#define __MSM_VFE8X_H__ + +#define TRUE 1 +#define FALSE 0 +#define boolean uint8_t + +enum VFE_STATE { + VFE_STATE_IDLE, + VFE_STATE_ACTIVE +}; + +enum vfe_cmd_id { + /* + *Important! Command_ID are arranged in order. + *Don't change!*/ + VFE_CMD_ID_START, + VFE_CMD_ID_RESET, + + /* bus and camif config */ + VFE_CMD_ID_AXI_INPUT_CONFIG, + VFE_CMD_ID_CAMIF_CONFIG, + VFE_CMD_ID_AXI_OUTPUT_CONFIG, + + /* module config */ + VFE_CMD_ID_BLACK_LEVEL_CONFIG, + VFE_CMD_ID_ROLL_OFF_CONFIG, + VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG, + VFE_CMD_ID_DEMOSAIC_CONFIG, + VFE_CMD_ID_FOV_CROP_CONFIG, + VFE_CMD_ID_MAIN_SCALER_CONFIG, + VFE_CMD_ID_WHITE_BALANCE_CONFIG, + VFE_CMD_ID_COLOR_CORRECTION_CONFIG, + VFE_CMD_ID_LA_CONFIG, + VFE_CMD_ID_RGB_GAMMA_CONFIG, + VFE_CMD_ID_CHROMA_ENHAN_CONFIG, + VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG, + VFE_CMD_ID_ASF_CONFIG, + VFE_CMD_ID_SCALER2Y_CONFIG, + VFE_CMD_ID_SCALER2CbCr_CONFIG, + VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG, + VFE_CMD_ID_FRAME_SKIP_CONFIG, + VFE_CMD_ID_OUTPUT_CLAMP_CONFIG, + + /* test gen */ + VFE_CMD_ID_TEST_GEN_START, + + VFE_CMD_ID_UPDATE, + + /* ackownledge from upper layer */ + VFE_CMD_ID_OUTPUT1_ACK, + VFE_CMD_ID_OUTPUT2_ACK, + VFE_CMD_ID_EPOCH1_ACK, + VFE_CMD_ID_EPOCH2_ACK, + VFE_CMD_ID_STATS_AUTOFOCUS_ACK, + VFE_CMD_ID_STATS_WB_EXP_ACK, + + /* module update commands */ + VFE_CMD_ID_BLACK_LEVEL_UPDATE, + VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE, + VFE_CMD_ID_DEMOSAIC_BPC_UPDATE, + VFE_CMD_ID_DEMOSAIC_ABF_UPDATE, + VFE_CMD_ID_FOV_CROP_UPDATE, + VFE_CMD_ID_WHITE_BALANCE_UPDATE, + VFE_CMD_ID_COLOR_CORRECTION_UPDATE, + VFE_CMD_ID_LA_UPDATE, + VFE_CMD_ID_RGB_GAMMA_UPDATE, + VFE_CMD_ID_CHROMA_ENHAN_UPDATE, + VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE, + VFE_CMD_ID_MAIN_SCALER_UPDATE, + VFE_CMD_ID_SCALER2CbCr_UPDATE, + VFE_CMD_ID_SCALER2Y_UPDATE, + VFE_CMD_ID_ASF_UPDATE, + VFE_CMD_ID_FRAME_SKIP_UPDATE, + VFE_CMD_ID_CAMIF_FRAME_UPDATE, + + /* stats update commands */ + VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE, + VFE_CMD_ID_STATS_WB_EXP_UPDATE, + + /* control of start, stop, update, etc... */ + VFE_CMD_ID_STOP, + VFE_CMD_ID_GET_HW_VERSION, + + /* stats */ + VFE_CMD_ID_STATS_SETTING, + VFE_CMD_ID_STATS_AUTOFOCUS_START, + VFE_CMD_ID_STATS_AUTOFOCUS_STOP, + VFE_CMD_ID_STATS_WB_EXP_START, + VFE_CMD_ID_STATS_WB_EXP_STOP, + + VFE_CMD_ID_ASYNC_TIMER_SETTING, + + /* max id */ + VFE_CMD_ID_MAX +}; + +struct vfe_cmd_hw_version { + uint32_t minorVersion; + uint32_t majorVersion; + uint32_t coreVersion; +}; + +enum VFE_CAMIF_SYNC_EDGE { + VFE_CAMIF_SYNC_EDGE_ActiveHigh, + VFE_CAMIF_SYNC_EDGE_ActiveLow +}; + +enum VFE_CAMIF_SYNC_MODE { + VFE_CAMIF_SYNC_MODE_APS, + VFE_CAMIF_SYNC_MODE_EFS, + VFE_CAMIF_SYNC_MODE_ELS, + VFE_CAMIF_SYNC_MODE_ILLEGAL +}; + +struct vfe_cmds_camif_efs { + uint8_t efsendofline; + uint8_t efsstartofline; + uint8_t efsendofframe; + uint8_t efsstartofframe; +}; + +struct vfe_cmds_camif_frame { + uint16_t pixelsPerLine; + uint16_t linesPerFrame; +}; + +struct vfe_cmds_camif_window { + uint16_t firstpixel; + uint16_t lastpixel; + uint16_t firstline; + uint16_t lastline; +}; + +enum CAMIF_SUBSAMPLE_FRAME_SKIP { + CAMIF_SUBSAMPLE_FRAME_SKIP_0, + CAMIF_SUBSAMPLE_FRAME_SKIP_AllFrames, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_2Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_3Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_4Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_5Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_6Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_7Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_8Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_9Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_10Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_11Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_12Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_13Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_14Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_15Frame +}; + +struct vfe_cmds_camif_subsample { + uint16_t pixelskipmask; + uint16_t lineskipmask; + enum CAMIF_SUBSAMPLE_FRAME_SKIP frameskip; + uint8_t frameskipmode; + uint8_t pixelskipwrap; +}; + +struct vfe_cmds_camif_epoch { + uint8_t enable; + uint16_t lineindex; +}; + +struct vfe_cmds_camif_cfg { + enum VFE_CAMIF_SYNC_EDGE vSyncEdge; + enum VFE_CAMIF_SYNC_EDGE hSyncEdge; + enum VFE_CAMIF_SYNC_MODE syncMode; + uint8_t vfeSubSampleEnable; + uint8_t busSubSampleEnable; + uint8_t irqSubSampleEnable; + uint8_t binningEnable; + uint8_t misrEnable; +}; + +struct vfe_cmd_camif_config { + struct vfe_cmds_camif_cfg camifConfig; + struct vfe_cmds_camif_efs EFS; + struct vfe_cmds_camif_frame frame; + struct vfe_cmds_camif_window window; + struct vfe_cmds_camif_subsample subsample; + struct vfe_cmds_camif_epoch epoch1; + struct vfe_cmds_camif_epoch epoch2; +}; + +enum VFE_AXI_OUTPUT_MODE { + VFE_AXI_OUTPUT_MODE_Output1, + VFE_AXI_OUTPUT_MODE_Output2, + VFE_AXI_OUTPUT_MODE_Output1AndOutput2, + VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2, + VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1, + VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2, + VFE_AXI_LAST_OUTPUT_MODE_ENUM +}; + +enum VFE_RAW_WR_PATH_SEL { + VFE_RAW_OUTPUT_DISABLED, + VFE_RAW_OUTPUT_ENC_CBCR_PATH, + VFE_RAW_OUTPUT_VIEW_CBCR_PATH, + VFE_RAW_OUTPUT_PATH_INVALID +}; + +enum VFE_RAW_PIXEL_DATA_SIZE { + VFE_RAW_PIXEL_DATA_SIZE_8BIT, + VFE_RAW_PIXEL_DATA_SIZE_10BIT, + VFE_RAW_PIXEL_DATA_SIZE_12BIT, +}; + +#define VFE_AXI_OUTPUT_BURST_LENGTH 4 +#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4 +#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3 + +struct vfe_cmds_axi_out_per_component { + uint16_t imageWidth; + uint16_t imageHeight; + uint16_t outRowCount; + uint16_t outRowIncrement; + uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT] + [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; +}; + +struct vfe_cmds_axi_per_output_path { + uint8_t fragmentCount; + struct vfe_cmds_axi_out_per_component outputY; + struct vfe_cmds_axi_out_per_component outputCbcr; +}; + +enum VFE_AXI_BURST_LENGTH { + VFE_AXI_BURST_LENGTH_IS_2 = 2, + VFE_AXI_BURST_LENGTH_IS_4 = 4, + VFE_AXI_BURST_LENGTH_IS_8 = 8, + VFE_AXI_BURST_LENGTH_IS_16 = 16 +}; + +struct vfe_cmd_axi_output_config { + enum VFE_AXI_BURST_LENGTH burstLength; + enum VFE_AXI_OUTPUT_MODE outputMode; + enum VFE_RAW_PIXEL_DATA_SIZE outputDataSize; + struct vfe_cmds_axi_per_output_path output1; + struct vfe_cmds_axi_per_output_path output2; +}; + +struct vfe_cmd_fov_crop_config { + uint8_t enable; + uint16_t firstPixel; + uint16_t lastPixel; + uint16_t firstLine; + uint16_t lastLine; +}; + +struct vfe_cmds_main_scaler_stripe_init { + uint16_t MNCounterInit; + uint16_t phaseInit; +}; + +struct vfe_cmds_scaler_one_dimension { + uint8_t enable; + uint16_t inputSize; + uint16_t outputSize; + uint32_t phaseMultiplicationFactor; + uint8_t interpolationResolution; +}; + +struct vfe_cmd_main_scaler_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; + struct vfe_cmds_main_scaler_stripe_init MNInitH; + struct vfe_cmds_main_scaler_stripe_init MNInitV; +}; + +struct vfe_cmd_scaler2_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; +}; + +struct vfe_cmd_frame_skip_config { + uint8_t output1Period; + uint32_t output1Pattern; + uint8_t output2Period; + uint32_t output2Pattern; +}; + +struct vfe_cmd_frame_skip_update { + uint32_t output1Pattern; + uint32_t output2Pattern; +}; + +struct vfe_cmd_output_clamp_config { + uint8_t minCh0; + uint8_t minCh1; + uint8_t minCh2; + uint8_t maxCh0; + uint8_t maxCh1; + uint8_t maxCh2; +}; + +struct vfe_cmd_chroma_subsample_config { + uint8_t enable; + uint8_t cropEnable; + uint8_t vsubSampleEnable; + uint8_t hsubSampleEnable; + uint8_t vCosited; + uint8_t hCosited; + uint8_t vCositedPhase; + uint8_t hCositedPhase; + uint16_t cropWidthFirstPixel; + uint16_t cropWidthLastPixel; + uint16_t cropHeightFirstLine; + uint16_t cropHeightLastLine; +}; + +enum VFE_START_INPUT_SOURCE { + VFE_START_INPUT_SOURCE_CAMIF, + VFE_START_INPUT_SOURCE_TESTGEN, + VFE_START_INPUT_SOURCE_AXI, + VFE_START_INPUT_SOURCE_INVALID +}; + +enum VFE_START_OPERATION_MODE { + VFE_START_OPERATION_MODE_CONTINUOUS, + VFE_START_OPERATION_MODE_SNAPSHOT +}; + +enum VFE_START_PIXEL_PATTERN { + VFE_BAYER_RGRGRG, + VFE_BAYER_GRGRGR, + VFE_BAYER_BGBGBG, + VFE_BAYER_GBGBGB, + VFE_YUV_YCbYCr, + VFE_YUV_YCrYCb, + VFE_YUV_CbYCrY, + VFE_YUV_CrYCbY +}; + +enum VFE_BUS_RD_INPUT_PIXEL_PATTERN { + VFE_BAYER_RAW, + VFE_YUV_INTERLEAVED, + VFE_YUV_PSEUDO_PLANAR_Y, + VFE_YUV_PSEUDO_PLANAR_CBCR +}; + +enum VFE_YUV_INPUT_COSITING_MODE { + VFE_YUV_COSITED, + VFE_YUV_INTERPOLATED +}; + +struct vfe_cmd_start { + enum VFE_START_INPUT_SOURCE inputSource; + enum VFE_START_OPERATION_MODE operationMode; + uint8_t snapshotCount; + enum VFE_START_PIXEL_PATTERN pixel; + enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode; +}; + +struct vfe_cmd_output_ack { + uint32_t ybufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; + uint32_t chromabufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; +}; + +#define VFE_STATS_BUFFER_COUNT 3 + +struct vfe_cmd_stats_setting { + uint16_t frameHDimension; + uint16_t frameVDimension; + uint8_t afBusPrioritySelection; + uint8_t afBusPriority; + uint8_t awbBusPrioritySelection; + uint8_t awbBusPriority; + uint8_t histBusPrioritySelection; + uint8_t histBusPriority; + uint32_t afBuffer[VFE_STATS_BUFFER_COUNT]; + uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT]; + uint32_t histBuffer[VFE_STATS_BUFFER_COUNT]; +}; + +struct vfe_cmd_stats_af_start { + uint8_t enable; + uint8_t windowMode; + uint16_t windowHOffset; + uint16_t windowVOffset; + uint16_t windowWidth; + uint16_t windowHeight; + uint8_t gridForMultiWindows[16]; + uint8_t metricSelection; + int16_t metricMax; + int8_t highPassCoef[7]; + int8_t bufferHeader; +}; + +struct vfe_cmd_stats_af_update { + uint8_t windowMode; + uint16_t windowHOffset; + uint16_t windowVOffset; + uint16_t windowWidth; + uint16_t windowHeight; +}; + +struct vfe_cmd_stats_wb_exp_start { + uint8_t enable; + uint8_t wbExpRegions; + uint8_t wbExpSubRegion; + uint8_t awbYMin; + uint8_t awbYMax; + int8_t awbMCFG[4]; + int16_t awbCCFG[4]; + int8_t axwHeader; +}; + +struct vfe_cmd_stats_wb_exp_update { + uint8_t wbExpRegions; + uint8_t wbExpSubRegion; + int8_t awbYMin; + int8_t awbYMax; + int8_t awbMCFG[4]; + int16_t awbCCFG[4]; +}; + +struct vfe_cmd_stats_af_ack { + uint32_t nextAFOutputBufferAddr; +}; + +struct vfe_cmd_stats_wb_exp_ack { + uint32_t nextWbExpOutputBufferAddr; +}; + +struct vfe_cmd_black_level_config { + uint8_t enable; + uint16_t evenEvenAdjustment; + uint16_t evenOddAdjustment; + uint16_t oddEvenAdjustment; + uint16_t oddOddAdjustment; +}; + +/* 13*1 */ +#define VFE_ROLL_OFF_INIT_TABLE_SIZE 13 +/* 13*16 */ +#define VFE_ROLL_OFF_DELTA_TABLE_SIZE 208 + +struct vfe_cmd_roll_off_config { + uint8_t enable; + uint16_t gridWidth; + uint16_t gridHeight; + uint16_t yDelta; + uint8_t gridXIndex; + uint8_t gridYIndex; + uint16_t gridPixelXIndex; + uint16_t gridPixelYIndex; + uint16_t yDeltaAccum; + uint16_t initTableR[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; +}; + +struct vfe_cmd_demux_channel_gain_config { + uint16_t ch0EvenGain; + uint16_t ch0OddGain; + uint16_t ch1Gain; + uint16_t ch2Gain; +}; + +struct vfe_cmds_demosaic_abf { + uint8_t enable; + uint8_t forceOn; + uint8_t shift; + uint16_t lpThreshold; + uint16_t max; + uint16_t min; + uint8_t ratio; +}; + +struct vfe_cmds_demosaic_bpc { + uint8_t enable; + uint16_t fmaxThreshold; + uint16_t fminThreshold; + uint16_t redDiffThreshold; + uint16_t blueDiffThreshold; + uint16_t greenDiffThreshold; +}; + +struct vfe_cmd_demosaic_config { + uint8_t enable; + uint8_t slopeShift; + struct vfe_cmds_demosaic_abf abfConfig; + struct vfe_cmds_demosaic_bpc bpcConfig; +}; + +struct vfe_cmd_demosaic_bpc_update { + struct vfe_cmds_demosaic_bpc bpcUpdate; +}; + +struct vfe_cmd_demosaic_abf_update { + struct vfe_cmds_demosaic_abf abfUpdate; +}; + +struct vfe_cmd_white_balance_config { + uint8_t enable; + uint16_t ch2Gain; + uint16_t ch1Gain; + uint16_t ch0Gain; +}; + +enum VFE_COLOR_CORRECTION_COEF_QFACTOR { + COEF_IS_Q7_SIGNED, + COEF_IS_Q8_SIGNED, + COEF_IS_Q9_SIGNED, + COEF_IS_Q10_SIGNED +}; + +struct vfe_cmd_color_correction_config { + uint8_t enable; + enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor; + int16_t C0; + int16_t C1; + int16_t C2; + int16_t C3; + int16_t C4; + int16_t C5; + int16_t C6; + int16_t C7; + int16_t C8; + int16_t K0; + int16_t K1; + int16_t K2; +}; + +#define VFE_LA_TABLE_LENGTH 256 +struct vfe_cmd_la_config { + uint8_t enable; + int16_t table[VFE_LA_TABLE_LENGTH]; +}; + +#define VFE_GAMMA_TABLE_LENGTH 256 +enum VFE_RGB_GAMMA_TABLE_SELECT { + RGB_GAMMA_CH0_SELECTED, + RGB_GAMMA_CH1_SELECTED, + RGB_GAMMA_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_SELECTED, + RGB_GAMMA_CH0_CH2_SELECTED, + RGB_GAMMA_CH1_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_CH2_SELECTED +}; + +struct vfe_cmd_rgb_gamma_config { + uint8_t enable; + enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect; + int16_t table[VFE_GAMMA_TABLE_LENGTH]; +}; + +struct vfe_cmd_chroma_enhan_config { + uint8_t enable; + int16_t am; + int16_t ap; + int16_t bm; + int16_t bp; + int16_t cm; + int16_t cp; + int16_t dm; + int16_t dp; + int16_t kcr; + int16_t kcb; + int16_t RGBtoYConversionV0; + int16_t RGBtoYConversionV1; + int16_t RGBtoYConversionV2; + uint8_t RGBtoYConversionOffset; +}; + +struct vfe_cmd_chroma_suppression_config { + uint8_t enable; + uint8_t m1; + uint8_t m3; + uint8_t n1; + uint8_t n3; + uint8_t nn1; + uint8_t mm1; +}; + +struct vfe_cmd_asf_config { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; + uint16_t cropFirstPixel; + uint16_t cropLastPixel; + uint16_t cropFirstLine; + uint16_t cropLastLine; +}; + +struct vfe_cmd_asf_update { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; +}; + +enum VFE_TEST_GEN_SYNC_EDGE { + VFE_TEST_GEN_SYNC_EDGE_ActiveHigh, + VFE_TEST_GEN_SYNC_EDGE_ActiveLow +}; + +struct vfe_cmd_test_gen_start { + uint8_t pixelDataSelect; + uint8_t systematicDataSelect; + enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge; + enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge; + uint16_t numFrame; + enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize; + uint16_t imageWidth; + uint16_t imageHeight; + uint32_t startOfFrameOffset; + uint32_t endOfFrameNOffset; + uint16_t startOfLineOffset; + uint16_t endOfLineNOffset; + uint16_t hbi; + uint8_t vblEnable; + uint16_t vbl; + uint8_t startOfFrameDummyLine; + uint8_t endOfFrameDummyLine; + uint8_t unicolorBarEnable; + uint8_t colorBarsSplitEnable; + uint8_t unicolorBarSelect; + enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern; + uint8_t colorBarsRotatePeriod; + uint16_t testGenRandomSeed; +}; + +struct vfe_cmd_bus_pm_start { + uint8_t output2YWrPmEnable; + uint8_t output2CbcrWrPmEnable; + uint8_t output1YWrPmEnable; + uint8_t output1CbcrWrPmEnable; +}; + +struct vfe_cmd_camif_frame_update { + struct vfe_cmds_camif_frame camifFrame; +}; + +struct vfe_cmd_sync_timer_setting { + uint8_t whichSyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t hsyncCount; + uint32_t pclkCount; + uint32_t outputDuration; +}; + +struct vfe_cmd_async_timer_setting { + uint8_t whichAsyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t inactiveCount; + uint32_t activeCount; +}; + +struct vfe_frame_skip_counts { + uint32_t totalFrameCount; + uint32_t output1Count; + uint32_t output2Count; +}; + +enum VFE_AXI_RD_UNPACK_HBI_SEL { + VFE_AXI_RD_HBI_32_CLOCK_CYCLES, + VFE_AXI_RD_HBI_64_CLOCK_CYCLES, + VFE_AXI_RD_HBI_128_CLOCK_CYCLES, + VFE_AXI_RD_HBI_256_CLOCK_CYCLES, + VFE_AXI_RD_HBI_512_CLOCK_CYCLES, + VFE_AXI_RD_HBI_1024_CLOCK_CYCLES, + VFE_AXI_RD_HBI_2048_CLOCK_CYCLES, + VFE_AXI_RD_HBI_4096_CLOCK_CYCLES +}; + +struct vfe_cmd_axi_input_config { + uint32_t fragAddr[4]; + uint8_t totalFragmentCount; + uint16_t ySize; + uint16_t xOffset; + uint16_t xSize; + uint16_t rowIncrement; + uint16_t numOfRows; + enum VFE_AXI_BURST_LENGTH burstLength; + uint8_t unpackPhase; + enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi; + enum VFE_RAW_PIXEL_DATA_SIZE pixelSize; + uint8_t padRepeatCountLeft; + uint8_t padRepeatCountRight; + uint8_t padRepeatCountTop; + uint8_t padRepeatCountBottom; + uint8_t padLeftComponentSelectCycle0; + uint8_t padLeftComponentSelectCycle1; + uint8_t padLeftComponentSelectCycle2; + uint8_t padLeftComponentSelectCycle3; + uint8_t padLeftStopCycle0; + uint8_t padLeftStopCycle1; + uint8_t padLeftStopCycle2; + uint8_t padLeftStopCycle3; + uint8_t padRightComponentSelectCycle0; + uint8_t padRightComponentSelectCycle1; + uint8_t padRightComponentSelectCycle2; + uint8_t padRightComponentSelectCycle3; + uint8_t padRightStopCycle0; + uint8_t padRightStopCycle1; + uint8_t padRightStopCycle2; + uint8_t padRightStopCycle3; + uint8_t padTopLineCount; + uint8_t padBottomLineCount; +}; + +struct vfe_interrupt_status { + uint8_t camifErrorIrq; + uint8_t camifSofIrq; + uint8_t camifEolIrq; + uint8_t camifEofIrq; + uint8_t camifEpoch1Irq; + uint8_t camifEpoch2Irq; + uint8_t camifOverflowIrq; + uint8_t ceIrq; + uint8_t regUpdateIrq; + uint8_t resetAckIrq; + uint8_t encYPingpongIrq; + uint8_t encCbcrPingpongIrq; + uint8_t viewYPingpongIrq; + uint8_t viewCbcrPingpongIrq; + uint8_t rdPingpongIrq; + uint8_t afPingpongIrq; + uint8_t awbPingpongIrq; + uint8_t histPingpongIrq; + uint8_t encIrq; + uint8_t viewIrq; + uint8_t busOverflowIrq; + uint8_t afOverflowIrq; + uint8_t awbOverflowIrq; + uint8_t syncTimer0Irq; + uint8_t syncTimer1Irq; + uint8_t syncTimer2Irq; + uint8_t asyncTimer0Irq; + uint8_t asyncTimer1Irq; + uint8_t asyncTimer2Irq; + uint8_t asyncTimer3Irq; + uint8_t axiErrorIrq; + uint8_t violationIrq; + uint8_t anyErrorIrqs; + uint8_t anyOutput1PathIrqs; + uint8_t anyOutput2PathIrqs; + uint8_t anyOutputPathIrqs; + uint8_t anyAsyncTimerIrqs; + uint8_t anySyncTimerIrqs; + uint8_t anyIrqForActiveStatesOnly; +}; + +enum VFE_MESSAGE_ID { + VFE_MSG_ID_RESET_ACK, + VFE_MSG_ID_START_ACK, + VFE_MSG_ID_STOP_ACK, + VFE_MSG_ID_UPDATE_ACK, + VFE_MSG_ID_OUTPUT1, + VFE_MSG_ID_OUTPUT2, + VFE_MSG_ID_SNAPSHOT_DONE, + VFE_MSG_ID_STATS_AUTOFOCUS, + VFE_MSG_ID_STATS_WB_EXP, + VFE_MSG_ID_EPOCH1, + VFE_MSG_ID_EPOCH2, + VFE_MSG_ID_SYNC_TIMER0_DONE, + VFE_MSG_ID_SYNC_TIMER1_DONE, + VFE_MSG_ID_SYNC_TIMER2_DONE, + VFE_MSG_ID_ASYNC_TIMER0_DONE, + VFE_MSG_ID_ASYNC_TIMER1_DONE, + VFE_MSG_ID_ASYNC_TIMER2_DONE, + VFE_MSG_ID_ASYNC_TIMER3_DONE, + VFE_MSG_ID_AF_OVERFLOW, + VFE_MSG_ID_AWB_OVERFLOW, + VFE_MSG_ID_AXI_ERROR, + VFE_MSG_ID_CAMIF_OVERFLOW, + VFE_MSG_ID_VIOLATION, + VFE_MSG_ID_CAMIF_ERROR, + VFE_MSG_ID_BUS_OVERFLOW, +}; + +struct vfe_msg_stats_autofocus { + uint32_t afBuffer; + uint32_t frameCounter; +}; + +struct vfe_msg_stats_wb_exp { + uint32_t awbBuffer; + uint32_t frameCounter; +}; + +struct vfe_frame_bpc_info { + uint32_t greenDefectPixelCount; + uint32_t redBlueDefectPixelCount; +}; + +struct vfe_frame_asf_info { + uint32_t asfMaxEdge; + uint32_t asfHbiCount; +}; + +struct vfe_msg_camif_status { + uint8_t camifState; + uint32_t pixelCount; + uint32_t lineCount; +}; + +struct vfe_bus_pm_per_path { + uint32_t yWrPmStats0; + uint32_t yWrPmStats1; + uint32_t cbcrWrPmStats0; + uint32_t cbcrWrPmStats1; +}; + +struct vfe_bus_performance_monitor { + struct vfe_bus_pm_per_path encPathPmInfo; + struct vfe_bus_pm_per_path viewPathPmInfo; +}; + +struct vfe_irq_thread_msg { + uint32_t vfeIrqStatus; + uint32_t camifStatus; + uint32_t demosaicStatus; + uint32_t asfMaxEdge; + struct vfe_bus_performance_monitor pmInfo; +}; + +struct vfe_msg_output { + uint32_t yBuffer; + uint32_t cbcrBuffer; + struct vfe_frame_bpc_info bpcInfo; + struct vfe_frame_asf_info asfInfo; + uint32_t frameCounter; + struct vfe_bus_pm_per_path pmData; +}; + +struct vfe_message { + enum VFE_MESSAGE_ID _d; + union { + struct vfe_msg_output msgOutput1; + struct vfe_msg_output msgOutput2; + struct vfe_msg_stats_autofocus msgStatsAf; + struct vfe_msg_stats_wb_exp msgStatsWbExp; + struct vfe_msg_camif_status msgCamifError; + struct vfe_bus_performance_monitor msgBusOverflow; + } _u; +}; + +/* New one for 8k */ +struct msm_vfe_command_8k { + int32_t id; + uint16_t length; + void *value; +}; + +struct vfe_frame_extra { + struct vfe_frame_bpc_info bpcInfo; + struct vfe_frame_asf_info asfInfo; + uint32_t frameCounter; + struct vfe_bus_pm_per_path pmData; +}; +#endif /* __MSM_VFE8X_H__ */ diff --git a/drivers/staging/dream/camera/msm_vfe8x_proc.c b/drivers/staging/dream/camera/msm_vfe8x_proc.c new file mode 100644 index 0000000..10aef0e --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe8x_proc.c @@ -0,0 +1,4003 @@ +/* +* Copyright (C) 2008-2009 QUALCOMM Incorporated. +*/ +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include "msm_vfe8x_proc.h" +#include <media/msm_camera.h> + +struct msm_vfe8x_ctrl { + /* bit 1:0 ENC_IRQ_MASK = 0x11: + * generate IRQ when both y and cbcr frame is ready. */ + + /* bit 1:0 VIEW_IRQ_MASK= 0x11: + * generate IRQ when both y and cbcr frame is ready. */ + struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal; + struct vfe_module_enable vfeModuleEnableLocal; + struct vfe_camif_cfg_data vfeCamifConfigLocal; + struct vfe_interrupt_mask vfeImaskLocal; + struct vfe_stats_cmd_data vfeStatsCmdLocal; + struct vfe_bus_cfg_data vfeBusConfigLocal; + struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal; + struct vfe_bus_cmd_data vfeBusCmdLocal; + enum vfe_interrupt_name vfeInterruptNameLocal; + uint32_t vfeLaBankSel; + struct vfe_gamma_lut_sel vfeGammaLutSel; + + boolean vfeStartAckPendingFlag; + boolean vfeStopAckPending; + boolean vfeResetAckPending; + boolean vfeUpdateAckPending; + + enum VFE_AXI_OUTPUT_MODE axiOutputMode; + enum VFE_START_OPERATION_MODE vfeOperationMode; + + uint32_t vfeSnapShotCount; + uint32_t vfeRequestedSnapShotCount; + boolean vfeStatsPingPongReloadFlag; + uint32_t vfeFrameId; + + struct vfe_cmd_frame_skip_config vfeFrameSkip; + uint32_t vfeFrameSkipPattern; + uint8_t vfeFrameSkipCount; + uint8_t vfeFrameSkipPeriod; + + boolean vfeTestGenStartFlag; + uint32_t vfeImaskPacked; + uint32_t vfeImaskCompositePacked; + enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize; + struct vfe_irq_thread_msg vfeIrqThreadMsgLocal; + + struct vfe_output_path_combo viewPath; + struct vfe_output_path_combo encPath; + struct vfe_frame_skip_counts vfeDroppedFrameCounts; + struct vfe_stats_control afStatsControl; + struct vfe_stats_control awbStatsControl; + + enum VFE_STATE vstate; + + spinlock_t ack_lock; + spinlock_t state_lock; + spinlock_t io_lock; + + struct msm_vfe_callback *resp; + uint32_t extlen; + void *extdata; + + spinlock_t tasklet_lock; + struct list_head tasklet_q; + + int vfeirq; + void __iomem *vfebase; + + void *syncdata; +}; +static struct msm_vfe8x_ctrl *ctrl; +static irqreturn_t vfe_parse_irq(int irq_num, void *data); + +struct isr_queue_cmd { + struct list_head list; + struct vfe_interrupt_status vfeInterruptStatus; + struct vfe_frame_asf_info vfeAsfFrameInfo; + struct vfe_frame_bpc_info vfeBpcFrameInfo; + struct vfe_msg_camif_status vfeCamifStatusLocal; + struct vfe_bus_performance_monitor vfePmData; +}; + +static void vfe_prog_hw(uint8_t *hwreg, + uint32_t *inptr, uint32_t regcnt) +{ + /* unsigned long flags; */ + uint32_t i; + uint32_t *p; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + + p = (uint32_t *)(hwreg); + for (i = 0; i < (regcnt >> 2); i++) + writel(*inptr++, p++); + /* *p++ = *inptr++; */ + + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ +} + +static void vfe_read_reg_values(uint8_t *hwreg, + uint32_t *dest, uint32_t count) +{ + /* unsigned long flags; */ + uint32_t *temp; + uint32_t i; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + + temp = (uint32_t *)(hwreg); + for (i = 0; i < count; i++) + *dest++ = *temp++; + + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ +} + +static struct vfe_irqenable vfe_read_irq_mask(void) +{ + /* unsigned long flags; */ + uint32_t *temp; + struct vfe_irqenable rc; + + memset(&rc, 0, sizeof(rc)); + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_MASK); + + rc = *((struct vfe_irqenable *)temp); + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ + + return rc; +} + +static void +vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath, + struct vfe_output_path_combo *epath) +{ + vpath->yPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR); + vpath->yPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR); + vpath->cbcrPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR); + vpath->cbcrPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR); + + epath->yPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR); + epath->yPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR); + epath->cbcrPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR); + epath->cbcrPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR); +} + +static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, + struct vfe_output_path_combo *out1, + struct vfe_output_path_combo *out2, uint16_t out) +{ + struct vfe_axi_out_cfg cmd; + + uint16_t temp; + uint32_t burstLength; + + /* force it to burst length 4, hardware does not support it. */ + burstLength = 1; + + /* AXI Output 2 Y Configuration*/ + /* VFE_BUS_ENC_Y_WR_PING_ADDR */ + cmd.out2YPingAddr = out2->yPath.addressBuffer[0]; + + /* VFE_BUS_ENC_Y_WR_PONG_ADDR */ + cmd.out2YPongAddr = out2->yPath.addressBuffer[1]; + + /* VFE_BUS_ENC_Y_WR_IMAGE_SIZE */ + cmd.out2YImageHeight = in->output2.outputY.imageHeight; + /* convert the image width and row increment to be in + * unit of 64bit (8 bytes) */ + temp = (in->output2.outputY.imageWidth + (out - 1)) / + out; + cmd.out2YImageWidthin64bit = temp; + + /* VFE_BUS_ENC_Y_WR_BUFFER_CFG */ + cmd.out2YBurstLength = burstLength; + cmd.out2YNumRows = in->output2.outputY.outRowCount; + temp = (in->output2.outputY.outRowIncrement + (out - 1)) / + out; + cmd.out2YRowIncrementIn64bit = temp; + + /* AXI Output 2 Cbcr Configuration*/ + /* VFE_BUS_ENC_Cbcr_WR_PING_ADDR */ + cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0]; + + /* VFE_BUS_ENC_Cbcr_WR_PONG_ADDR */ + cmd.out2CbcrPongAddr = out2->cbcrPath.addressBuffer[1]; + + /* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */ + cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight; + temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / + out; + cmd.out2CbcrImageWidthIn64bit = temp; + + /* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */ + cmd.out2CbcrBurstLength = burstLength; + cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount; + temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / + out; + cmd.out2CbcrRowIncrementIn64bit = temp; + + /* AXI Output 1 Y Configuration */ + /* VFE_BUS_VIEW_Y_WR_PING_ADDR */ + cmd.out1YPingAddr = out1->yPath.addressBuffer[0]; + + /* VFE_BUS_VIEW_Y_WR_PONG_ADDR */ + cmd.out1YPongAddr = out1->yPath.addressBuffer[1]; + + /* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */ + cmd.out1YImageHeight = in->output1.outputY.imageHeight; + temp = (in->output1.outputY.imageWidth + (out - 1)) / + out; + cmd.out1YImageWidthin64bit = temp; + + /* VFE_BUS_VIEW_Y_WR_BUFFER_CFG */ + cmd.out1YBurstLength = burstLength; + cmd.out1YNumRows = in->output1.outputY.outRowCount; + + temp = + (in->output1.outputY.outRowIncrement + + (out - 1)) / out; + cmd.out1YRowIncrementIn64bit = temp; + + /* AXI Output 1 Cbcr Configuration*/ + cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0]; + + /* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR */ + cmd.out1CbcrPongAddr = + out1->cbcrPath.addressBuffer[1]; + + /* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */ + cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight; + temp = (in->output1.outputCbcr.imageWidth + + (out - 1)) / out; + cmd.out1CbcrImageWidthIn64bit = temp; + + cmd.out1CbcrBurstLength = burstLength; + cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount; + temp = + (in->output1.outputCbcr.outRowIncrement + + (out - 1)) / out; + + cmd.out1CbcrRowIncrementIn64bit = temp; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in) +{ + struct vfe_axi_bus_cfg cmd; + + cmd.stripeRdPathEn = in->stripeRdPathEn; + cmd.encYWrPathEn = in->encYWrPathEn; + cmd.encCbcrWrPathEn = in->encCbcrWrPathEn; + cmd.viewYWrPathEn = in->viewYWrPathEn; + cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn; + cmd.rawPixelDataSize = (uint32_t)in->rawPixelDataSize; + cmd.rawWritePathSelect = (uint32_t)in->rawWritePathSelect; + + /* program vfe_bus_cfg */ + writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG); +} + +static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in) +{ + struct VFE_CAMIFConfigType cfg; + + memset(&cfg, 0, sizeof(cfg)); + + cfg.VSyncEdge = + in->camifCfgFromCmd.vSyncEdge; + + cfg.HSyncEdge = + in->camifCfgFromCmd.hSyncEdge; + + cfg.syncMode = + in->camifCfgFromCmd.syncMode; + + cfg.vfeSubsampleEnable = + in->camifCfgFromCmd.vfeSubSampleEnable; + + cfg.busSubsampleEnable = + in->camifCfgFromCmd.busSubSampleEnable; + + cfg.camif2vfeEnable = + in->camif2OutputEnable; + + cfg.camif2busEnable = + in->camif2BusEnable; + + cfg.irqSubsampleEnable = + in->camifCfgFromCmd.irqSubSampleEnable; + + cfg.binningEnable = + in->camifCfgFromCmd.binningEnable; + + cfg.misrEnable = + in->camifCfgFromCmd.misrEnable; + + /* program camif_config */ + writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG); +} + +static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in) +{ + struct vfe_buscmd cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.stripeReload = in->stripeReload; + cmd.busPingpongReload = in->busPingpongReload; + cmd.statsPingpongReload = in->statsPingpongReload; + + writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD); + + CDBG("bus command = 0x%x\n", (*((uint32_t *)&cmd))); + + /* this is needed, as the control bits are pulse based. + * Don't want to reload bus pingpong again. */ + in->busPingpongReload = 0; + in->statsPingpongReload = 0; + in->stripeReload = 0; +} + +static void vfe_reg_module_cfg(struct vfe_module_enable *in) +{ + struct vfe_mod_enable ena; + + memset(&ena, 0, sizeof(ena)); + + ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable; + ena.lensRollOffEnable = in->lensRollOffEnable; + ena.demuxEnable = in->demuxEnable; + ena.chromaUpsampleEnable = in->chromaUpsampleEnable; + ena.demosaicEnable = in->demosaicEnable; + ena.statsEnable = in->statsEnable; + ena.cropEnable = in->cropEnable; + ena.mainScalerEnable = in->mainScalerEnable; + ena.whiteBalanceEnable = in->whiteBalanceEnable; + ena.colorCorrectionEnable = in->colorCorrectionEnable; + ena.yHistEnable = in->yHistEnable; + ena.skinToneEnable = in->skinToneEnable; + ena.lumaAdaptationEnable = in->lumaAdaptationEnable; + ena.rgbLUTEnable = in->rgbLUTEnable; + ena.chromaEnhanEnable = in->chromaEnhanEnable; + ena.asfEnable = in->asfEnable; + ena.chromaSuppressionEnable = in->chromaSuppressionEnable; + ena.chromaSubsampleEnable = in->chromaSubsampleEnable; + ena.scaler2YEnable = in->scaler2YEnable; + ena.scaler2CbcrEnable = in->scaler2CbcrEnable; + + writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG); +} + +static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel) +{ + /* set bit 8 for auto increment. */ + uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT; + + value += (uint32_t)bankSel; + /* CDBG("dmi cfg input bank is 0x%x\n", bankSel); */ + + writel(value, ctrl->vfebase + VFE_DMI_CFG); + writel(0, ctrl->vfebase + VFE_DMI_ADDR); +} + +static void vfe_write_lens_roll_off_table( + struct vfe_cmd_roll_off_config *in) +{ + uint16_t i; + uint32_t data; + + uint16_t *initGr = in->initTableGr; + uint16_t *initGb = in->initTableGb; + uint16_t *initB = in->initTableB; + uint16_t *initR = in->initTableR; + + int16_t *pDeltaGr = in->deltaTableGr; + int16_t *pDeltaGb = in->deltaTableGb; + int16_t *pDeltaB = in->deltaTableB; + int16_t *pDeltaR = in->deltaTableR; + + vfe_program_dmi_cfg(ROLLOFF_RAM); + + /* first pack and write init table */ + for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) { + data = (((uint32_t)(*initR)) & 0x0000FFFF) | + (((uint32_t)(*initGr)) << 16); + initR++; + initGr++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + + data = (((uint32_t)(*initB)) & 0x0000FFFF) | + (((uint32_t)(*initGr))<<16); + initB++; + initGb++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + } + + /* there are gaps between the init table and delta table, + * set the offset for delta table. */ + writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, + ctrl->vfebase + VFE_DMI_ADDR); + + /* pack and write delta table */ + for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) { + data = (((int32_t)(*pDeltaR)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGr))<<16); + pDeltaR++; + pDeltaGr++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + + data = (((int32_t)(*pDeltaB)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGb))<<16); + pDeltaB++; + pDeltaGb++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + } + + /* After DMI transfer, to make it safe, need to set the + * DMI_CFG to unselect any SRAM + */ + /* unselect the SRAM Bank. */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); +} + +static void vfe_set_default_reg_values(void) +{ + writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_0); + writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_1); + writel(0xFFFFF, ctrl->vfebase + VFE_CGC_OVERRIDE); + + /* default frame drop period and pattern */ + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_CFG); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_CFG); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN); + writel(0, ctrl->vfebase + VFE_CLAMP_MIN_CFG); + writel(0xFFFFFF, ctrl->vfebase + VFE_CLAMP_MAX_CFG); +} + +static void vfe_config_demux(uint32_t period, uint32_t even, uint32_t odd) +{ + writel(period, ctrl->vfebase + VFE_DEMUX_CFG); + writel(even, ctrl->vfebase + VFE_DEMUX_EVEN_CFG); + writel(odd, ctrl->vfebase + VFE_DEMUX_ODD_CFG); +} + +static void vfe_pm_stop(void) +{ + writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD); +} + +static void vfe_program_bus_rd_irq_en(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); +} + +static void vfe_camif_go(void) +{ + writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); +} + +static void vfe_camif_stop_immediately(void) +{ + writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND); + writel(0, ctrl->vfebase + VFE_CGC_OVERRIDE); +} + +static void vfe_program_reg_update_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD); +} + +static void vfe_program_bus_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_BUS_CMD); +} + +static void vfe_program_global_reset_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD); +} + +static void vfe_program_axi_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_AXI_CMD); +} + +static void vfe_program_irq_composite_mask(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); +} + +static inline void vfe_program_irq_mask(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_IRQ_MASK); +} + +static void vfe_program_chroma_upsample_cfg(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); +} + +static uint32_t vfe_read_axi_status(void) +{ + return readl(ctrl->vfebase + VFE_AXI_STATUS); +} + +static uint32_t vfe_read_pm_status_in_raw_capture(void) +{ + return readl(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); +} + +static void +vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl, + struct vfe_stats_control *awbControl) +{ + afControl->hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + afControl->hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); + + awbControl->hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + awbControl->hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static uint32_t vfe_read_camif_status(void) +{ + return readl(ctrl->vfebase + CAMIF_STATUS); +} + +static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in) +{ + struct VFE_GammaLutSelect_ConfigCmdType cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0BankSelect = in->ch0BankSelect; + cmd.ch1BankSelect = in->ch1BankSelect; + cmd.ch2BankSelect = in->ch2BankSelect; + CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd)); + vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in) +{ + struct VFE_StatsCmdType stats; + memset(&stats, 0, sizeof(stats)); + + stats.autoFocusEnable = in->autoFocusEnable; + stats.axwEnable = in->axwEnable; + stats.histEnable = in->histEnable; + stats.clearHistEnable = in->clearHistEnable; + stats.histAutoClearEnable = in->histAutoClearEnable; + stats.colorConversionEnable = in->colorConversionEnable; + + writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD); +} + +static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in) +{ + struct VFE_Bus_Pm_ConfigCmdType cmd; + memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType)); + + cmd.output2YWrPmEnable = in->output2YWrPmEnable; + cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable; + cmd.output1YWrPmEnable = in->output1YWrPmEnable; + cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in) +{ + in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn; + in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn; + in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn; + in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn; + + if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable) + ctrl->viewPath.pmEnabled = TRUE; + + if (in->output2CbcrWrPmEnable || in->output2YWrPmEnable) + ctrl->encPath.pmEnabled = TRUE; + + vfe_pm_start(in); + + writel(VFE_PERFORMANCE_MONITOR_GO, ctrl->vfebase + VFE_BUS_PM_CMD); +} + +static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data) +{ + struct vfe_irqenable packedData; + + memset(&packedData, 0, sizeof(packedData)); + + packedData.camifErrorIrq = data.camifErrorIrq; + packedData.camifSofIrq = data.camifSofIrq; + packedData.camifEolIrq = data.camifEolIrq; + packedData.camifEofIrq = data.camifEofIrq; + packedData.camifEpoch1Irq = data.camifEpoch1Irq; + packedData.camifEpoch2Irq = data.camifEpoch2Irq; + packedData.camifOverflowIrq = data.camifOverflowIrq; + packedData.ceIrq = data.ceIrq; + packedData.regUpdateIrq = data.regUpdateIrq; + packedData.resetAckIrq = data.resetAckIrq; + packedData.encYPingpongIrq = data.encYPingpongIrq; + packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq; + packedData.viewYPingpongIrq = data.viewYPingpongIrq; + packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq; + packedData.rdPingpongIrq = data.rdPingpongIrq; + packedData.afPingpongIrq = data.afPingpongIrq; + packedData.awbPingpongIrq = data.awbPingpongIrq; + packedData.histPingpongIrq = data.histPingpongIrq; + packedData.encIrq = data.encIrq; + packedData.viewIrq = data.viewIrq; + packedData.busOverflowIrq = data.busOverflowIrq; + packedData.afOverflowIrq = data.afOverflowIrq; + packedData.awbOverflowIrq = data.awbOverflowIrq; + packedData.syncTimer0Irq = data.syncTimer0Irq; + packedData.syncTimer1Irq = data.syncTimer1Irq; + packedData.syncTimer2Irq = data.syncTimer2Irq; + packedData.asyncTimer0Irq = data.asyncTimer0Irq; + packedData.asyncTimer1Irq = data.asyncTimer1Irq; + packedData.asyncTimer2Irq = data.asyncTimer2Irq; + packedData.asyncTimer3Irq = data.asyncTimer3Irq; + packedData.axiErrorIrq = data.axiErrorIrq; + packedData.violationIrq = data.violationIrq; + + return *((uint32_t *)&packedData); +} + +static uint32_t +vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data) +{ + struct VFE_Irq_Composite_MaskType packedData; + + memset(&packedData, 0, sizeof(packedData)); + + packedData.encIrqComMaskBits = data.encIrqComMask; + packedData.viewIrqComMaskBits = data.viewIrqComMask; + packedData.ceDoneSelBits = data.ceDoneSel; + + return *((uint32_t *)&packedData); +} + +static void vfe_addr_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: { + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput1.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput1.cbcrBuffer; + + ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = + ((struct vfe_message *)data)->_u.msgOutput1.asfInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = + ((struct vfe_message *)data)->_u.msgOutput1.frameCounter; + + ((struct vfe_frame_extra *)ctrl->extdata)->pmData = + ((struct vfe_message *)data)->_u.msgOutput1.pmData; + + *ext = ctrl->extdata; + *elen = ctrl->extlen; + } + break; + + case VFE_MSG_OUTPUT2: { + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput2.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput2.cbcrBuffer; + + CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", pinfo->y_phy); + CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n", + pinfo->cbcr_phy); + + ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = + ((struct vfe_message *)data)->_u.msgOutput2.asfInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = + ((struct vfe_message *)data)->_u.msgOutput2.frameCounter; + + ((struct vfe_frame_extra *)ctrl->extdata)->pmData = + ((struct vfe_message *)data)->_u.msgOutput2.pmData; + + *ext = ctrl->extdata; + *elen = ctrl->extlen; + } + break; + + case VFE_MSG_STATS_AF: + pinfo->sbuf_phy = + ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer; + break; + + case VFE_MSG_STATS_WE: + pinfo->sbuf_phy = + ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer; + break; + + default: + break; + } /* switch */ +} + +static void +vfe_proc_ops(enum VFE_MESSAGE_ID id, void *msg, size_t len) +{ + struct msm_vfe_resp *rp; + + /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before + * SNAPSHOT_DONE. We don't send such messages to user */ + + CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n", + ctrl->vfeOperationMode, id); + + if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && + (id == VFE_MSG_ID_OUTPUT1 || id == VFE_MSG_ID_OUTPUT2)) { + return; + } + + rp = ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), ctrl->syncdata); + if (!rp) { + CDBG("rp: cannot allocate buffer\n"); + return; + } + + CDBG("vfe_proc_ops, msgId = %d\n", id); + + rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.msg_id = id; + rp->evt_msg.len = len; + rp->evt_msg.data = msg; + + switch (rp->evt_msg.msg_id) { + case VFE_MSG_ID_SNAPSHOT_DONE: + rp->type = VFE_MSG_SNAPSHOT; + break; + + case VFE_MSG_ID_OUTPUT1: + rp->type = VFE_MSG_OUTPUT1; + vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT1, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case VFE_MSG_ID_OUTPUT2: + rp->type = VFE_MSG_OUTPUT2; + vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT2, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case VFE_MSG_ID_STATS_AUTOFOCUS: + rp->type = VFE_MSG_STATS_AF; + vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AF, + rp->evt_msg.data, NULL, NULL); + break; + + case VFE_MSG_ID_STATS_WB_EXP: + rp->type = VFE_MSG_STATS_WE; + vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_WE, + rp->evt_msg.data, NULL, NULL); + break; + + default: + rp->type = VFE_MSG_GENERAL; + break; + } + + ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata); +} + +static void vfe_send_msg_no_payload(enum VFE_MESSAGE_ID id) +{ + struct vfe_message *msg; + + msg = kzalloc(sizeof(*msg), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = id; + vfe_proc_ops(id, msg, 0); +} + +static void vfe_send_bus_overflow_msg(void) +{ + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = VFE_MSG_ID_BUS_OVERFLOW; +#if 0 + memcpy(&(msg->_u.msgBusOverflow), + &ctrl->vfePmData, sizeof(ctrl->vfePmData)); +#endif + + vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, + msg, sizeof(struct vfe_message)); +} + +static void vfe_send_camif_error_msg(void) +{ +#if 0 + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = VFE_MSG_ID_CAMIF_ERROR; + memcpy(&(msg->_u.msgCamifError), + &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal)); + + vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, + msg, sizeof(struct vfe_message)); +#endif +} + +static void vfe_process_error_irq( + struct vfe_interrupt_status *irqstatus) +{ + /* all possible error irq. Note error irqs are not enabled, it is + * checked only when other interrupts are present. */ + if (irqstatus->afOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AF_OVERFLOW); + + if (irqstatus->awbOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AWB_OVERFLOW); + + if (irqstatus->axiErrorIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AXI_ERROR); + + if (irqstatus->busOverflowIrq) + vfe_send_bus_overflow_msg(); + + if (irqstatus->camifErrorIrq) + vfe_send_camif_error_msg(); + + if (irqstatus->camifOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_CAMIF_OVERFLOW); + + if (irqstatus->violationIrq) + ; +} + +static void vfe_process_camif_sof_irq(void) +{ + /* increment the frame id number. */ + ctrl->vfeFrameId++; + + CDBG("camif_sof_irq, frameId = %d\n", + ctrl->vfeFrameId); + + /* In snapshot mode, if frame skip is programmed, + * need to check it accordingly to stop camif at + * correct frame boundary. For the dropped frames, + * there won't be any output path irqs, but there is + * still SOF irq, which can help us determine when + * to stop the camif. + */ + if (ctrl->vfeOperationMode) { + if ((1 << ctrl->vfeFrameSkipCount) & + ctrl->vfeFrameSkipPattern) { + + ctrl->vfeSnapShotCount--; + if (ctrl->vfeSnapShotCount == 0) + /* terminate vfe pipeline at frame boundary. */ + writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, + ctrl->vfebase + CAMIF_COMMAND); + } + + /* update frame skip counter for bit checking. */ + ctrl->vfeFrameSkipCount++; + if (ctrl->vfeFrameSkipCount == + (ctrl->vfeFrameSkipPeriod + 1)) + ctrl->vfeFrameSkipCount = 0; + } +} + +static int vfe_get_af_pingpong_status(void) +{ + uint32_t busPingPongStatus; + int rc = 0; + + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + if ((busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT) == 0) + return -EFAULT; + + return rc; +} + +static uint32_t vfe_read_af_buf_addr(boolean pipo) +{ + if (pipo == FALSE) + return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + else + return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); +} + +static void +vfe_update_af_buf_addr(boolean pipo, uint32_t addr) +{ + if (pipo == FALSE) + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + else + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); +} + +static void +vfe_send_af_stats_msg(uint32_t afBufAddress) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) { + kfree(msg); + goto af_stats_done; + } + + msg->_d = VFE_MSG_ID_STATS_AUTOFOCUS; + msg->_u.msgStatsAf.afBuffer = afBufAddress; + msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId; + + vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS, + msg, sizeof(struct vfe_message)); + + ctrl->afStatsControl.ackPending = TRUE; + +af_stats_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_process_stats_af_irq(void) +{ + boolean bufferAvailable; + + if (!(ctrl->afStatsControl.ackPending)) { + + /* read hardware status. */ + ctrl->afStatsControl.pingPongStatus = + vfe_get_af_pingpong_status(); + + bufferAvailable = + (ctrl->afStatsControl.pingPongStatus) ^ 1; + + ctrl->afStatsControl.bufToRender = + vfe_read_af_buf_addr(bufferAvailable); + + /* update the same buffer address (ping or pong) */ + vfe_update_af_buf_addr(bufferAvailable, + ctrl->afStatsControl.nextFrameAddrBuf); + + vfe_send_af_stats_msg(ctrl->afStatsControl.bufToRender); + } else + ctrl->afStatsControl.droppedStatsFrameCount++; +} + +static boolean vfe_get_awb_pingpong_status(void) +{ + uint32_t busPingPongStatus; + + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + if ((busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT) == 0) + return FALSE; + + return TRUE; +} + +static uint32_t +vfe_read_awb_buf_addr(boolean pingpong) +{ + if (pingpong == FALSE) + return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + else + return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static void vfe_update_awb_buf_addr( + boolean pingpong, uint32_t addr) +{ + if (pingpong == FALSE) + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + else + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static void vfe_send_awb_stats_msg(uint32_t awbBufAddress) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) { + kfree(msg); + goto awb_stats_done; + } + + msg->_d = VFE_MSG_ID_STATS_WB_EXP; + msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress; + msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId; + + vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP, + msg, sizeof(struct vfe_message)); + + ctrl->awbStatsControl.ackPending = TRUE; + +awb_stats_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_process_stats_awb_irq(void) +{ + boolean bufferAvailable; + + if (!(ctrl->awbStatsControl.ackPending)) { + + ctrl->awbStatsControl.pingPongStatus = + vfe_get_awb_pingpong_status(); + + bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1; + + ctrl->awbStatsControl.bufToRender = + vfe_read_awb_buf_addr(bufferAvailable); + + vfe_update_awb_buf_addr(bufferAvailable, + ctrl->awbStatsControl.nextFrameAddrBuf); + + vfe_send_awb_stats_msg(ctrl->awbStatsControl.bufToRender); + + } else + ctrl->awbStatsControl.droppedStatsFrameCount++; +} + +static void vfe_process_sync_timer_irq( + struct vfe_interrupt_status *irqstatus) +{ + if (irqstatus->syncTimer0Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER0_DONE); + + if (irqstatus->syncTimer1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER1_DONE); + + if (irqstatus->syncTimer2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER2_DONE); +} + +static void vfe_process_async_timer_irq( + struct vfe_interrupt_status *irqstatus) +{ + + if (irqstatus->asyncTimer0Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); + + if (irqstatus->asyncTimer1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER1_DONE); + + if (irqstatus->asyncTimer2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER2_DONE); + + if (irqstatus->asyncTimer3Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER3_DONE); +} + +static void vfe_send_violation_msg(void) +{ + vfe_send_msg_no_payload(VFE_MSG_ID_VIOLATION); +} + +static void vfe_send_async_timer_msg(void) +{ + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); +} + +static void vfe_write_gamma_table(uint8_t channel, + boolean bank, int16_t *pTable) +{ + uint16_t i; + + enum VFE_DMI_RAM_SEL dmiRamSel = NO_MEM_SELECTED; + + switch (channel) { + case 0: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH0_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH0_BANK1; + break; + + case 1: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH1_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH1_BANK1; + break; + + case 2: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH2_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH2_BANK1; + break; + + default: + break; + } + + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, need to set the DMI_CFG to unselect any SRAM + unselect the SRAM Bank. */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); +} + +static void vfe_prog_hw_testgen_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_HW_TESTGEN_CMD); +} + +static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out) +{ + uint32_t *temp; + + memset(out, 0, sizeof(struct vfe_irq_thread_msg)); + + temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_STATUS); + out->vfeIrqStatus = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + CAMIF_STATUS); + out->camifStatus = readl(temp); + writel(0x7, ctrl->vfebase + CAMIF_COMMAND); + writel(0x3, ctrl->vfebase + CAMIF_COMMAND); + CDBG("camifStatus = 0x%x\n", out->camifStatus); + +/* + temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS); + out->demosaicStatus = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + VFE_ASF_MAX_EDGE); + out->asfMaxEdge = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PM_STATS_0); +*/ + +#if 0 + out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp); +#endif /* if 0 Jeff */ +} + +static struct vfe_interrupt_status +vfe_parse_interrupt_status(uint32_t irqStatusIn) +{ + struct vfe_irqenable hwstat; + struct vfe_interrupt_status ret; + boolean temp; + + memset(&hwstat, 0, sizeof(hwstat)); + memset(&ret, 0, sizeof(ret)); + + hwstat = *((struct vfe_irqenable *)(&irqStatusIn)); + + ret.camifErrorIrq = hwstat.camifErrorIrq; + ret.camifSofIrq = hwstat.camifSofIrq; + ret.camifEolIrq = hwstat.camifEolIrq; + ret.camifEofIrq = hwstat.camifEofIrq; + ret.camifEpoch1Irq = hwstat.camifEpoch1Irq; + ret.camifEpoch2Irq = hwstat.camifEpoch2Irq; + ret.camifOverflowIrq = hwstat.camifOverflowIrq; + ret.ceIrq = hwstat.ceIrq; + ret.regUpdateIrq = hwstat.regUpdateIrq; + ret.resetAckIrq = hwstat.resetAckIrq; + ret.encYPingpongIrq = hwstat.encYPingpongIrq; + ret.encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq; + ret.viewYPingpongIrq = hwstat.viewYPingpongIrq; + ret.viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq; + ret.rdPingpongIrq = hwstat.rdPingpongIrq; + ret.afPingpongIrq = hwstat.afPingpongIrq; + ret.awbPingpongIrq = hwstat.awbPingpongIrq; + ret.histPingpongIrq = hwstat.histPingpongIrq; + ret.encIrq = hwstat.encIrq; + ret.viewIrq = hwstat.viewIrq; + ret.busOverflowIrq = hwstat.busOverflowIrq; + ret.afOverflowIrq = hwstat.afOverflowIrq; + ret.awbOverflowIrq = hwstat.awbOverflowIrq; + ret.syncTimer0Irq = hwstat.syncTimer0Irq; + ret.syncTimer1Irq = hwstat.syncTimer1Irq; + ret.syncTimer2Irq = hwstat.syncTimer2Irq; + ret.asyncTimer0Irq = hwstat.asyncTimer0Irq; + ret.asyncTimer1Irq = hwstat.asyncTimer1Irq; + ret.asyncTimer2Irq = hwstat.asyncTimer2Irq; + ret.asyncTimer3Irq = hwstat.asyncTimer3Irq; + ret.axiErrorIrq = hwstat.axiErrorIrq; + ret.violationIrq = hwstat.violationIrq; + + /* logic OR of any error bits + * although each irq corresponds to a bit, the data type here is a + * boolean already. hence use logic operation. + */ + temp = + ret.camifErrorIrq || + ret.camifOverflowIrq || + ret.afOverflowIrq || + ret.awbPingpongIrq || + ret.busOverflowIrq || + ret.axiErrorIrq || + ret.violationIrq; + + ret.anyErrorIrqs = temp; + + /* logic OR of any output path bits*/ + temp = + ret.encYPingpongIrq || + ret.encCbcrPingpongIrq || + ret.encIrq; + + ret.anyOutput2PathIrqs = temp; + + temp = + ret.viewYPingpongIrq || + ret.viewCbcrPingpongIrq || + ret.viewIrq; + + ret.anyOutput1PathIrqs = temp; + + ret.anyOutputPathIrqs = + ret.anyOutput1PathIrqs || + ret.anyOutput2PathIrqs; + + /* logic OR of any sync timer bits*/ + temp = + ret.syncTimer0Irq || + ret.syncTimer1Irq || + ret.syncTimer2Irq; + + ret.anySyncTimerIrqs = temp; + + /* logic OR of any async timer bits*/ + temp = + ret.asyncTimer0Irq || + ret.asyncTimer1Irq || + ret.asyncTimer2Irq || + ret.asyncTimer3Irq; + + ret.anyAsyncTimerIrqs = temp; + + /* bool for all interrupts that are not allowed in idle state */ + temp = + ret.anyErrorIrqs || + ret.anyOutputPathIrqs || + ret.anySyncTimerIrqs || + ret.regUpdateIrq || + ret.awbPingpongIrq || + ret.afPingpongIrq || + ret.camifSofIrq || + ret.camifEpoch2Irq || + ret.camifEpoch1Irq; + + ret.anyIrqForActiveStatesOnly = + temp; + + return ret; +} + +static struct vfe_frame_asf_info +vfe_get_asf_frame_info(struct vfe_irq_thread_msg *in) +{ + struct vfe_asf_info asfInfoTemp; + struct vfe_frame_asf_info rc; + + memset(&rc, 0, sizeof(rc)); + memset(&asfInfoTemp, 0, sizeof(asfInfoTemp)); + + asfInfoTemp = + *((struct vfe_asf_info *)(&(in->asfMaxEdge))); + + rc.asfHbiCount = asfInfoTemp.HBICount; + rc.asfMaxEdge = asfInfoTemp.maxEdge; + + return rc; +} + +static struct vfe_frame_bpc_info +vfe_get_demosaic_frame_info(struct vfe_irq_thread_msg *in) +{ + struct vfe_bps_info bpcInfoTemp; + struct vfe_frame_bpc_info rc; + + memset(&rc, 0, sizeof(rc)); + memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp)); + + bpcInfoTemp = + *((struct vfe_bps_info *)(&(in->demosaicStatus))); + + rc.greenDefectPixelCount = + bpcInfoTemp.greenBadPixelCount; + + rc.redBlueDefectPixelCount = + bpcInfoTemp.RedBlueBadPixelCount; + + return rc; +} + +static struct vfe_msg_camif_status +vfe_get_camif_status(struct vfe_irq_thread_msg *in) +{ + struct vfe_camif_stats camifStatusTemp; + struct vfe_msg_camif_status rc; + + memset(&rc, 0, sizeof(rc)); + memset(&camifStatusTemp, 0, sizeof(camifStatusTemp)); + + camifStatusTemp = + *((struct vfe_camif_stats *)(&(in->camifStatus))); + + rc.camifState = (boolean)camifStatusTemp.camifHalt; + rc.lineCount = camifStatusTemp.lineCount; + rc.pixelCount = camifStatusTemp.pixelCount; + + return rc; +} + +static struct vfe_bus_performance_monitor +vfe_get_performance_monitor_data(struct vfe_irq_thread_msg *in) +{ + struct vfe_bus_performance_monitor rc; + memset(&rc, 0, sizeof(rc)); + + rc.encPathPmInfo.yWrPmStats0 = + in->pmInfo.encPathPmInfo.yWrPmStats0; + rc.encPathPmInfo.yWrPmStats1 = + in->pmInfo.encPathPmInfo.yWrPmStats1; + rc.encPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats0; + rc.encPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats1; + rc.viewPathPmInfo.yWrPmStats0 = + in->pmInfo.viewPathPmInfo.yWrPmStats0; + rc.viewPathPmInfo.yWrPmStats1 = + in->pmInfo.viewPathPmInfo.yWrPmStats1; + rc.viewPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats0; + rc.viewPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats1; + + return rc; +} + +static void vfe_process_reg_update_irq(void) +{ + CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n", + ctrl->vfeStartAckPendingFlag); + if (ctrl->vfeStartAckPendingFlag == TRUE) { + vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + ctrl->vfeStartAckPendingFlag = FALSE; + } else + vfe_send_msg_no_payload(VFE_MSG_ID_UPDATE_ACK); +} + +static void vfe_process_reset_irq(void) +{ + /* unsigned long flags; */ + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + ctrl->vstate = VFE_STATE_IDLE; + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + + if (ctrl->vfeStopAckPending == TRUE) { + ctrl->vfeStopAckPending = FALSE; + vfe_send_msg_no_payload(VFE_MSG_ID_STOP_ACK); + } else { + vfe_set_default_reg_values(); + vfe_send_msg_no_payload(VFE_MSG_ID_RESET_ACK); + } +} + +static void vfe_process_pingpong_irq(struct vfe_output_path *in, + uint8_t fragmentCount) +{ + uint16_t circularIndex; + uint32_t nextFragmentAddr; + + /* get next fragment address from circular buffer */ + circularIndex = (in->fragIndex) % (2 * fragmentCount); + nextFragmentAddr = in->addressBuffer[circularIndex]; + + in->fragIndex = circularIndex + 1; + + /* use next fragment to program hardware ping/pong address. */ + if (in->hwCurrentFlag == ping) { + writel(nextFragmentAddr, in->hwRegPingAddress); + in->hwCurrentFlag = pong; + + } else { + writel(nextFragmentAddr, in->hwRegPongAddress); + in->hwCurrentFlag = ping; + } +} + +static void vfe_send_output2_msg( + struct vfe_msg_output *pPayload) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) { + kfree(msg); + goto output2_msg_done; + } + + msg->_d = VFE_MSG_ID_OUTPUT2; + + memcpy(&(msg->_u.msgOutput2), + (void *)pPayload, sizeof(struct vfe_msg_output)); + + vfe_proc_ops(VFE_MSG_ID_OUTPUT2, + msg, sizeof(struct vfe_message)); + + ctrl->encPath.ackPending = TRUE; + + if (!(ctrl->vfeRequestedSnapShotCount <= 3) && + (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT)) + ctrl->encPath.ackPending = TRUE; + +output2_msg_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_send_output1_msg( + struct vfe_msg_output *pPayload) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) { + kfree(msg); + goto output1_msg_done; + } + + msg->_d = VFE_MSG_ID_OUTPUT1; + memmove(&(msg->_u), + (void *)pPayload, sizeof(struct vfe_msg_output)); + + vfe_proc_ops(VFE_MSG_ID_OUTPUT1, + msg, sizeof(struct vfe_message)); + + ctrl->viewPath.ackPending = TRUE; + + if (!(ctrl->vfeRequestedSnapShotCount <= 3) && + (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT)) + ctrl->viewPath.ackPending = TRUE; + +output1_msg_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_send_output_msg(boolean whichOutputPath, + uint32_t yPathAddr, uint32_t cbcrPathAddr) +{ + struct vfe_msg_output msgPayload; + + msgPayload.yBuffer = yPathAddr; + msgPayload.cbcrBuffer = cbcrPathAddr; + + /* asf info is common for both output1 and output2 */ +#if 0 + msgPayload.asfInfo.asfHbiCount = ctrl->vfeAsfFrameInfo.asfHbiCount; + msgPayload.asfInfo.asfMaxEdge = ctrl->vfeAsfFrameInfo.asfMaxEdge; + + /* demosaic info is common for both output1 and output2 */ + msgPayload.bpcInfo.greenDefectPixelCount = + ctrl->vfeBpcFrameInfo.greenDefectPixelCount; + msgPayload.bpcInfo.redBlueDefectPixelCount = + ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount; +#endif /* if 0 */ + + /* frame ID is common for both paths. */ + msgPayload.frameCounter = ctrl->vfeFrameId; + + if (whichOutputPath) { + /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */ + vfe_send_output2_msg(&msgPayload); + } else { + /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */ + vfe_send_output1_msg(&msgPayload); + } +} + +static void vfe_process_frame_done_irq_multi_frag( + struct vfe_output_path_combo *in) +{ + uint32_t yAddress, cbcrAddress; + uint16_t idx; + uint32_t *ptrY; + uint32_t *ptrCbcr; + const uint32_t *ptrSrc; + uint8_t i; + + if (!in->ackPending) { + + idx = (in->currentFrame) * (in->fragCount); + + /* Send output message. */ + yAddress = in->yPath.addressBuffer[idx]; + cbcrAddress = in->cbcrPath.addressBuffer[idx]; + + /* copy next frame to current frame. */ + ptrSrc = in->nextFrameAddrBuf; + ptrY = (uint32_t *)&(in->yPath.addressBuffer[idx]); + ptrCbcr = (uint32_t *)&(in->cbcrPath.addressBuffer[idx]); + + /* Copy Y address */ + for (i = 0; i < in->fragCount; i++) + *ptrY++ = *ptrSrc++; + + /* Copy Cbcr address */ + for (i = 0; i < in->fragCount; i++) + *ptrCbcr++ = *ptrSrc++; + + vfe_send_output_msg(in->whichOutputPath, yAddress, cbcrAddress); + + } else { + if (in->whichOutputPath == 0) + ctrl->vfeDroppedFrameCounts.output1Count++; + + if (in->whichOutputPath == 1) + ctrl->vfeDroppedFrameCounts.output2Count++; + } + + /* toggle current frame. */ + in->currentFrame = in->currentFrame^1; + + if (ctrl->vfeOperationMode) + in->snapshotPendingCount--; +} + +static void vfe_process_frame_done_irq_no_frag_io( + struct vfe_output_path_combo *in, uint32_t *pNextAddr, + uint32_t *pdestRenderAddr) +{ + uint32_t busPingPongStatus; + uint32_t tempAddress; + + /* 1. read hw status register. */ + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + CDBG("hardware status is 0x%x\n", busPingPongStatus); + + /* 2. determine ping or pong */ + /* use cbcr status */ + busPingPongStatus = busPingPongStatus & (1<<(in->cbcrStatusBit)); + + /* 3. read out address and update address */ + if (busPingPongStatus == 0) { + /* hw is working on ping, render pong buffer */ + /* a. read out pong address */ + /* read out y address. */ + tempAddress = readl(in->yPath.hwRegPongAddress); + + CDBG("pong 1 addr = 0x%x\n", tempAddress); + *pdestRenderAddr++ = tempAddress; + /* read out cbcr address. */ + tempAddress = readl(in->cbcrPath.hwRegPongAddress); + + CDBG("pong 2 addr = 0x%x\n", tempAddress); + *pdestRenderAddr = tempAddress; + + /* b. update pong address */ + writel(*pNextAddr++, in->yPath.hwRegPongAddress); + writel(*pNextAddr, in->cbcrPath.hwRegPongAddress); + } else { + /* hw is working on pong, render ping buffer */ + + /* a. read out ping address */ + tempAddress = readl(in->yPath.hwRegPingAddress); + CDBG("ping 1 addr = 0x%x\n", tempAddress); + *pdestRenderAddr++ = tempAddress; + tempAddress = readl(in->cbcrPath.hwRegPingAddress); + + CDBG("ping 2 addr = 0x%x\n", tempAddress); + *pdestRenderAddr = tempAddress; + + /* b. update ping address */ + writel(*pNextAddr++, in->yPath.hwRegPingAddress); + CDBG("NextAddress = 0x%x\n", *pNextAddr); + writel(*pNextAddr, in->cbcrPath.hwRegPingAddress); + } +} + +static void vfe_process_frame_done_irq_no_frag( + struct vfe_output_path_combo *in) +{ + uint32_t addressToRender[2]; + static uint32_t fcnt; + + if (fcnt++ < 3) + return; + + if (!in->ackPending) { + vfe_process_frame_done_irq_no_frag_io(in, + in->nextFrameAddrBuf, addressToRender); + + /* use addressToRender to send out message. */ + vfe_send_output_msg(in->whichOutputPath, + addressToRender[0], addressToRender[1]); + + } else { + /* ackPending is still there, accumulate dropped frame count. + * These count can be read through ioctrl command. */ + CDBG("waiting frame ACK\n"); + + if (in->whichOutputPath == 0) + ctrl->vfeDroppedFrameCounts.output1Count++; + + if (in->whichOutputPath == 1) + ctrl->vfeDroppedFrameCounts.output2Count++; + } + + /* in case of multishot when upper layer did not ack, there will still + * be a snapshot done msg sent out, even though the number of frames + * sent out may be less than the desired number of frames. snapshot + * done msg would be helpful to indicate that vfe pipeline has stop, + * and in good known state. + */ + if (ctrl->vfeOperationMode) + in->snapshotPendingCount--; +} + +static void vfe_process_output_path_irq( + struct vfe_interrupt_status *irqstatus) +{ + /* unsigned long flags; */ + + /* process the view path interrupts */ + if (irqstatus->anyOutput1PathIrqs) { + if (ctrl->viewPath.multiFrag) { + + if (irqstatus->viewCbcrPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->viewPath.cbcrPath), + ctrl->viewPath.fragCount); + + if (irqstatus->viewYPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->viewPath.yPath), + ctrl->viewPath.fragCount); + + if (irqstatus->viewIrq) + vfe_process_frame_done_irq_multi_frag( + &ctrl->viewPath); + + } else { + /* typical case for no fragment, + only frame done irq is enabled. */ + if (irqstatus->viewIrq) + vfe_process_frame_done_irq_no_frag( + &ctrl->viewPath); + } + } + + /* process the encoder path interrupts */ + if (irqstatus->anyOutput2PathIrqs) { + if (ctrl->encPath.multiFrag) { + if (irqstatus->encCbcrPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->encPath.cbcrPath), + ctrl->encPath.fragCount); + + if (irqstatus->encYPingpongIrq) + vfe_process_pingpong_irq(&(ctrl->encPath.yPath), + ctrl->encPath.fragCount); + + if (irqstatus->encIrq) + vfe_process_frame_done_irq_multi_frag( + &ctrl->encPath); + + } else { + if (irqstatus->encIrq) + vfe_process_frame_done_irq_no_frag( + &ctrl->encPath); + } + } + + if (ctrl->vfeOperationMode) { + if ((ctrl->encPath.snapshotPendingCount == 0) && + (ctrl->viewPath.snapshotPendingCount == 0)) { + + /* @todo This is causing issues, further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + ctrl->vstate = VFE_STATE_IDLE; + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + + vfe_send_msg_no_payload(VFE_MSG_ID_SNAPSHOT_DONE); + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); + vfe_pm_stop(); + } + } +} + +static void vfe_do_tasklet(unsigned long data) +{ + unsigned long flags; + + struct isr_queue_cmd *qcmd = NULL; + + CDBG("=== vfe_do_tasklet start === \n"); + + spin_lock_irqsave(&ctrl->tasklet_lock, flags); + qcmd = list_first_entry(&ctrl->tasklet_q, + struct isr_queue_cmd, list); + + if (!qcmd) { + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + return; + } + + list_del(&qcmd->list); + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + + if (qcmd->vfeInterruptStatus.regUpdateIrq) { + CDBG("irq regUpdateIrq\n"); + vfe_process_reg_update_irq(); + } + + if (qcmd->vfeInterruptStatus.resetAckIrq) { + CDBG("irq resetAckIrq\n"); + vfe_process_reset_irq(); + } + + spin_lock_irqsave(&ctrl->state_lock, flags); + if (ctrl->vstate != VFE_STATE_ACTIVE) { + spin_unlock_irqrestore(&ctrl->state_lock, flags); + return; + } + spin_unlock_irqrestore(&ctrl->state_lock, flags); + +#if 0 + if (qcmd->vfeInterruptStatus.camifEpoch1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH1); + + if (qcmd->vfeInterruptStatus.camifEpoch2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH2); +#endif /* Jeff */ + + /* next, check output path related interrupts. */ + if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) { + CDBG("irq anyOutputPathIrqs\n"); + vfe_process_output_path_irq(&qcmd->vfeInterruptStatus); + } + + if (qcmd->vfeInterruptStatus.afPingpongIrq) + vfe_process_stats_af_irq(); + + if (qcmd->vfeInterruptStatus.awbPingpongIrq) + vfe_process_stats_awb_irq(); + + /* any error irqs*/ + if (qcmd->vfeInterruptStatus.anyErrorIrqs) + vfe_process_error_irq(&qcmd->vfeInterruptStatus); + +#if 0 + if (qcmd->vfeInterruptStatus.anySyncTimerIrqs) + vfe_process_sync_timer_irq(); + + if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs) + vfe_process_async_timer_irq(); +#endif /* Jeff */ + + if (qcmd->vfeInterruptStatus.camifSofIrq) { + CDBG("irq camifSofIrq\n"); + vfe_process_camif_sof_irq(); + } + + kfree(qcmd); + CDBG("=== vfe_do_tasklet end === \n"); +} + +DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0); + +static irqreturn_t vfe_parse_irq(int irq_num, void *data) +{ + unsigned long flags; + uint32_t irqStatusLocal; + struct vfe_irq_thread_msg irq; + struct isr_queue_cmd *qcmd; + + CDBG("vfe_parse_irq\n"); + + vfe_read_irq_status(&irq); + + if (irq.vfeIrqStatus == 0) { + CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n"); + return IRQ_HANDLED; + } + + qcmd = kzalloc(sizeof(struct isr_queue_cmd), + GFP_ATOMIC); + if (!qcmd) { + CDBG("vfe_parse_irq: qcmd malloc failed!\n"); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&ctrl->ack_lock, flags); + + if (ctrl->vfeStopAckPending) + irqStatusLocal = + (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus); + else + irqStatusLocal = + ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) & + irq.vfeIrqStatus); + + spin_unlock_irqrestore(&ctrl->ack_lock, flags); + + /* first parse the interrupt status to local data structures. */ + qcmd->vfeInterruptStatus = vfe_parse_interrupt_status(irqStatusLocal); + qcmd->vfeAsfFrameInfo = vfe_get_asf_frame_info(&irq); + qcmd->vfeBpcFrameInfo = vfe_get_demosaic_frame_info(&irq); + qcmd->vfeCamifStatusLocal = vfe_get_camif_status(&irq); + qcmd->vfePmData = vfe_get_performance_monitor_data(&irq); + + spin_lock_irqsave(&ctrl->tasklet_lock, flags); + list_add_tail(&qcmd->list, &ctrl->tasklet_q); + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + tasklet_schedule(&vfe_tasklet); + + /* clear the pending interrupt of the same kind.*/ + writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR); + + return IRQ_HANDLED; +} + +int vfe_cmd_init(struct msm_vfe_callback *presp, + struct platform_device *pdev, void *sdata) +{ + struct resource *vfemem, *vfeirq, *vfeio; + int rc; + + vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!vfemem) { + CDBG("no mem resource?\n"); + return -ENODEV; + } + + vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!vfeirq) { + CDBG("no irq resource?\n"); + return -ENODEV; + } + + vfeio = request_mem_region(vfemem->start, + resource_size(vfemem), pdev->name); + if (!vfeio) { + CDBG("VFE region already claimed\n"); + return -EBUSY; + } + + ctrl = + kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL); + if (!ctrl) { + rc = -ENOMEM; + goto cmd_init_failed1; + } + + ctrl->vfeirq = vfeirq->start; + + ctrl->vfebase = + ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); + if (!ctrl->vfebase) { + rc = -ENOMEM; + goto cmd_init_failed2; + } + + rc = request_irq(ctrl->vfeirq, vfe_parse_irq, + IRQF_TRIGGER_RISING, "vfe", 0); + if (rc < 0) + goto cmd_init_failed2; + + if (presp && presp->vfe_resp) + ctrl->resp = presp; + else { + rc = -EINVAL; + goto cmd_init_failed3; + } + + ctrl->extdata = + kmalloc(sizeof(struct vfe_frame_extra), GFP_KERNEL); + if (!ctrl->extdata) { + rc = -ENOMEM; + goto cmd_init_failed3; + } + + spin_lock_init(&ctrl->ack_lock); + spin_lock_init(&ctrl->state_lock); + spin_lock_init(&ctrl->io_lock); + + ctrl->extlen = sizeof(struct vfe_frame_extra); + + spin_lock_init(&ctrl->tasklet_lock); + INIT_LIST_HEAD(&ctrl->tasklet_q); + + ctrl->syncdata = sdata; + return 0; + +cmd_init_failed3: + disable_irq(ctrl->vfeirq); + free_irq(ctrl->vfeirq, 0); + iounmap(ctrl->vfebase); +cmd_init_failed2: + kfree(ctrl); +cmd_init_failed1: + release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); + return rc; +} + +void vfe_cmd_release(struct platform_device *dev) +{ + struct resource *mem; + + disable_irq(ctrl->vfeirq); + free_irq(ctrl->vfeirq, 0); + + iounmap(ctrl->vfebase); + mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + ctrl->extlen = 0; + + kfree(ctrl->extdata); + kfree(ctrl); +} + +void vfe_stats_af_stop(void) +{ + ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE; + ctrl->vfeImaskLocal.afPingpongIrq = FALSE; +} + +void vfe_stop(void) +{ + boolean vfeAxiBusy; + uint32_t vfeAxiStauts; + + /* for reset hw modules, and send msg when reset_irq comes.*/ + ctrl->vfeStopAckPending = TRUE; + + ctrl->vfeStatsPingPongReloadFlag = FALSE; + vfe_pm_stop(); + + /* disable all interrupts. */ + vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS); + + /* in either continuous or snapshot mode, stop command can be issued + * at any time. + */ + vfe_camif_stop_immediately(); + vfe_program_axi_cmd(AXI_HALT); + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); + + vfeAxiBusy = TRUE; + + while (vfeAxiBusy) { + vfeAxiStauts = vfe_read_axi_status(); + if ((vfeAxiStauts & AXI_STATUS_BUSY_MASK) != 0) + vfeAxiBusy = FALSE; + } + + vfe_program_axi_cmd(AXI_HALT_CLEAR); + + /* clear all pending interrupts */ + writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR); + + /* enable reset_ack and async timer interrupt only while stopping + * the pipeline. + */ + vfe_program_irq_mask(VFE_IMASK_WHILE_STOPPING); + + vfe_program_global_reset_cmd(VFE_RESET_UPON_STOP_CMD); +} + +void vfe_update(void) +{ + ctrl->vfeModuleEnableLocal.statsEnable = + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; + + vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); + + vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal); + + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); + + if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) && + (ctrl->vfeStatsPingPongReloadFlag == FALSE)) { + ctrl->vfeStatsPingPongReloadFlag = TRUE; + + ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE; + vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal); + } + + vfe_program_reg_update_cmd(VFE_REG_UPDATE_TRIGGER); +} + +int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in) +{ + int rc = 0; + + ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable; + + switch (in->channelSelect) { + case RGB_GAMMA_CH0_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + vfe_write_gamma_table(0, + ctrl->vfeGammaLutSel.ch0BankSelect, in->table); + break; + + case RGB_GAMMA_CH1_SELECTED: + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + vfe_write_gamma_table(1, + ctrl->vfeGammaLutSel.ch1BankSelect, in->table); + break; + + case RGB_GAMMA_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(2, + ctrl->vfeGammaLutSel.ch2BankSelect, in->table); + break; + + case RGB_GAMMA_CH0_CH1_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + break; + + case RGB_GAMMA_CH0_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + case RGB_GAMMA_CH1_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + case RGB_GAMMA_CH0_CH1_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + default: + return -EINVAL; + } /* switch */ + + /* update the gammaLutSel register. */ + vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel); + + return rc; +} + +int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in) +{ + int rc = 0; + + ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable; + + switch (in->channelSelect) { + case RGB_GAMMA_CH0_SELECTED: +vfe_write_gamma_table(0, 0, in->table); +break; + + case RGB_GAMMA_CH1_SELECTED: + vfe_write_gamma_table(1, 0, in->table); + break; + + case RGB_GAMMA_CH2_SELECTED: + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH1_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(1, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH2_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH1_CH2_SELECTED: + vfe_write_gamma_table(1, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH1_CH2_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(1, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + default: + rc = -EINVAL; + break; + } /* switch */ + + return rc; +} + +void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *in) +{ + ctrl->afStatsControl.nextFrameAddrBuf = in->nextAFOutputBufferAddr; + ctrl->afStatsControl.ackPending = FALSE; +} + +void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in) +{ + ctrl->awbStatsControl.nextFrameAddrBuf = in->nextWbExpOutputBufferAddr; + ctrl->awbStatsControl.ackPending = FALSE; +} + +void vfe_output2_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + pdest = ctrl->encPath.nextFrameAddrBuf; + + CDBG("output2_ack: ack addr = 0x%x\n", in->ybufaddr[0]); + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->encPath.ackPending = FALSE; +} + +void vfe_output1_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + pdest = ctrl->viewPath.nextFrameAddrBuf; + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->viewPath.ackPending = FALSE; +} + +void vfe_start(struct vfe_cmd_start *in) +{ + unsigned long flags; + uint32_t pmstatus = 0; + boolean rawmode; + uint32_t demperiod = 0; + uint32_t demeven = 0; + uint32_t demodd = 0; + + /* derived from other commands. (camif config, axi output config, + * etc) + */ + struct vfe_cfg hwcfg; + struct vfe_upsample_cfg chromupcfg; + + CDBG("vfe_start operationMode = %d\n", in->operationMode); + + memset(&hwcfg, 0, sizeof(hwcfg)); + memset(&chromupcfg, 0, sizeof(chromupcfg)); + + switch (in->pixel) { + case VFE_BAYER_RGRGRG: + demperiod = 1; + demeven = 0xC9; + demodd = 0xAC; + break; + + case VFE_BAYER_GRGRGR: + demperiod = 1; + demeven = 0x9C; + demodd = 0xCA; + break; + + case VFE_BAYER_BGBGBG: + demperiod = 1; + demeven = 0xCA; + demodd = 0x9C; + break; + + case VFE_BAYER_GBGBGB: + demperiod = 1; + demeven = 0xAC; + demodd = 0xC9; + break; + + case VFE_YUV_YCbYCr: + demperiod = 3; + demeven = 0x9CAC; + demodd = 0x9CAC; + break; + + case VFE_YUV_YCrYCb: + demperiod = 3; + demeven = 0xAC9C; + demodd = 0xAC9C; + break; + + case VFE_YUV_CbYCrY: + demperiod = 3; + demeven = 0xC9CA; + demodd = 0xC9CA; + break; + + case VFE_YUV_CrYCbY: + demperiod = 3; + demeven = 0xCAC9; + demodd = 0xCAC9; + break; + + default: + return; + } + + vfe_config_demux(demperiod, demeven, demodd); + + vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel); + + /* save variables to local. */ + ctrl->vfeOperationMode = in->operationMode; + if (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT) { + /* in snapshot mode, initialize snapshot count*/ + ctrl->vfeSnapShotCount = in->snapshotCount; + + /* save the requested count, this is temporarily done, to + help with HJR / multishot. */ + ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount; + + CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount); + + /* Assumption is to have the same pattern and period for both + paths, if both paths are used. */ + if (ctrl->viewPath.pathEnabled) { + ctrl->viewPath.snapshotPendingCount = + in->snapshotCount; + + ctrl->vfeFrameSkipPattern = + ctrl->vfeFrameSkip.output1Pattern; + ctrl->vfeFrameSkipPeriod = + ctrl->vfeFrameSkip.output1Period; + } + + if (ctrl->encPath.pathEnabled) { + ctrl->encPath.snapshotPendingCount = + in->snapshotCount; + + ctrl->vfeFrameSkipPattern = + ctrl->vfeFrameSkip.output2Pattern; + ctrl->vfeFrameSkipPeriod = + ctrl->vfeFrameSkip.output2Period; + } + } + + /* enable color conversion for bayer sensor + if stats enabled, need to do color conversion. */ + if (in->pixel <= VFE_BAYER_GBGBGB) + ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE; + + vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal); + + if (in->pixel >= VFE_YUV_YCbYCr) + ctrl->vfeModuleEnableLocal.chromaUpsampleEnable = TRUE; + + ctrl->vfeModuleEnableLocal.demuxEnable = TRUE; + + /* if any stats module is enabled, the main bit is enabled. */ + ctrl->vfeModuleEnableLocal.statsEnable = + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; + + vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); + + /* in case of offline processing, do not need to config camif. Having + * bus output enabled in camif_config register might confuse the + * hardware? + */ + if (in->inputSource != VFE_START_INPUT_SOURCE_AXI) { + vfe_reg_camif_config(&ctrl->vfeCamifConfigLocal); + } else { + /* offline processing, enable axi read */ + ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE; + ctrl->vfeBusCmdLocal.stripeReload = TRUE; + ctrl->vfeBusConfigLocal.rawPixelDataSize = + ctrl->axiInputDataSize; + } + + vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal); + + /* directly from start command */ + hwcfg.pixelPattern = in->pixel; + hwcfg.inputSource = in->inputSource; + writel(*(uint32_t *)&hwcfg, ctrl->vfebase + VFE_CFG); + + /* regardless module enabled or not, it does not hurt + * to program the cositing mode. */ + chromupcfg.chromaCositingForYCbCrInputs = + in->yuvInputCositingMode; + + writel(*(uint32_t *)&(chromupcfg), + ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); + + /* MISR to monitor the axi read. */ + writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0); + + /* clear all pending interrupts. */ + writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR); + + /* define how composite interrupt work. */ + ctrl->vfeImaskCompositePacked = + vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal); + + vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked); + + /* enable all necessary interrupts. */ + ctrl->vfeImaskLocal.camifSofIrq = TRUE; + ctrl->vfeImaskLocal.regUpdateIrq = TRUE; + ctrl->vfeImaskLocal.resetAckIrq = TRUE; + + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); + + /* enable bus performance monitor */ + vfe_8k_pm_start(&ctrl->vfeBusPmConfigLocal); + + /* trigger vfe reg update */ + ctrl->vfeStartAckPendingFlag = TRUE; + + /* write bus command to trigger reload of ping pong buffer. */ + ctrl->vfeBusCmdLocal.busPingpongReload = TRUE; + + if (ctrl->vfeModuleEnableLocal.statsEnable == TRUE) { + ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE; + ctrl->vfeStatsPingPongReloadFlag = TRUE; + } + + writel(VFE_REG_UPDATE_TRIGGER, + ctrl->vfebase + VFE_REG_UPDATE_CMD); + + /* program later than the reg update. */ + vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal); + + if ((in->inputSource == + VFE_START_INPUT_SOURCE_CAMIF) || + (in->inputSource == + VFE_START_INPUT_SOURCE_TESTGEN)) + writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); + + /* start test gen if it is enabled */ + if (ctrl->vfeTestGenStartFlag == TRUE) { + ctrl->vfeTestGenStartFlag = FALSE; + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_GO); + } + + CDBG("ctrl->axiOutputMode = %d\n", ctrl->axiOutputMode); + if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2) { + /* raw dump mode */ + rawmode = TRUE; + + while (rawmode) { + pmstatus = + readl(ctrl->vfebase + + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); + + if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0) + rawmode = FALSE; + } + + vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + ctrl->vfeStartAckPendingFlag = FALSE; + } + + spin_lock_irqsave(&ctrl->state_lock, flags); + ctrl->vstate = VFE_STATE_ACTIVE; + spin_unlock_irqrestore(&ctrl->state_lock, flags); +} + +void vfe_la_update(struct vfe_cmd_la_config *in) +{ + int16_t *pTable; + enum VFE_DMI_RAM_SEL dmiRamSel; + int i; + + pTable = in->table; + ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable; + + /* toggle the bank to be used. */ + ctrl->vfeLaBankSel ^= 1; + + if (ctrl->vfeLaBankSel == 0) + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0; + else + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1; + + /* configure the DMI_CFG to select right sram */ + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, to make it safe, need to set + * the DMI_CFG to unselect any SRAM */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); + writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG); +} + +void vfe_la_config(struct vfe_cmd_la_config *in) +{ + uint16_t i; + int16_t *pTable; + enum VFE_DMI_RAM_SEL dmiRamSel; + + pTable = in->table; + ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable; + + if (ctrl->vfeLaBankSel == 0) + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0; + else + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1; + + /* configure the DMI_CFG to select right sram */ + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, to make it safe, need to set the + * DMI_CFG to unselect any SRAM */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); + + /* can only be bank 0 or bank 1 for now. */ + writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG); + CDBG("VFE Luma adaptation bank selection is 0x%x\n", + *(uint32_t *)&ctrl->vfeLaBankSel); +} + +void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in) +{ + struct VFE_TestGen_ConfigCmdType cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.numFrame = in->numFrame; + cmd.pixelDataSelect = in->pixelDataSelect; + cmd.systematicDataSelect = in->systematicDataSelect; + cmd.pixelDataSize = (uint32_t)in->pixelDataSize; + cmd.hsyncEdge = (uint32_t)in->hsyncEdge; + cmd.vsyncEdge = (uint32_t)in->vsyncEdge; + cmd.imageWidth = in->imageWidth; + cmd.imageHeight = in->imageHeight; + cmd.sofOffset = in->startOfFrameOffset; + cmd.eofNOffset = in->endOfFrameNOffset; + cmd.solOffset = in->startOfLineOffset; + cmd.eolNOffset = in->endOfLineNOffset; + cmd.hBlankInterval = in->hbi; + cmd.vBlankInterval = in->vbl; + cmd.vBlankIntervalEnable = in->vblEnable; + cmd.sofDummy = in->startOfFrameDummyLine; + cmd.eofDummy = in->endOfFrameDummyLine; + cmd.unicolorBarSelect = in->unicolorBarSelect; + cmd.unicolorBarEnable = in->unicolorBarEnable; + cmd.splitEnable = in->colorBarsSplitEnable; + cmd.pixelPattern = (uint32_t)in->colorBarsPixelPattern; + cmd.rotatePeriod = in->colorBarsRotatePeriod; + cmd.randomSeed = in->testGenRandomSeed; + + vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG, + (uint32_t *) &cmd, sizeof(cmd)); +} + +void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in) +{ + struct VFE_FRAME_SKIP_UpdateCmdType cmd; + + cmd.yPattern = in->output1Pattern; + cmd.cbcrPattern = in->output1Pattern; + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd.yPattern = in->output2Pattern; + cmd.cbcrPattern = in->output2Pattern; + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in) +{ + struct vfe_frame_skip_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeFrameSkip = *in; + + cmd.output2YPeriod = in->output2Period; + cmd.output2CbCrPeriod = in->output2Period; + cmd.output2YPattern = in->output2Pattern; + cmd.output2CbCrPattern = in->output2Pattern; + cmd.output1YPeriod = in->output1Period; + cmd.output1CbCrPeriod = in->output1Period; + cmd.output1YPattern = in->output1Pattern; + cmd.output1CbCrPattern = in->output1Pattern; + + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in) +{ + struct vfe_output_clamp_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.yChanMax = in->maxCh0; + cmd.cbChanMax = in->maxCh1; + cmd.crChanMax = in->maxCh2; + + cmd.yChanMin = in->minCh0; + cmd.cbChanMin = in->minCh1; + cmd.crChanMin = in->minCh2; + + vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd, + sizeof(cmd)); +} + +void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in) +{ + struct vfe_camifframe_update cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.pixelsPerLine = in->pixelsPerLine; + cmd.linesPerFrame = in->linesPerFrame; + + vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd, + sizeof(cmd)); +} + +void vfe_color_correction_config( + struct vfe_cmd_color_correction_config *in) +{ + struct vfe_color_correction_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + ctrl->vfeModuleEnableLocal.colorCorrectionEnable = in->enable; + + cmd.c0 = in->C0; + cmd.c1 = in->C1; + cmd.c2 = in->C2; + cmd.c3 = in->C3; + cmd.c4 = in->C4; + cmd.c5 = in->C5; + cmd.c6 = in->C6; + cmd.c7 = in->C7; + cmd.c8 = in->C8; + + cmd.k0 = in->K0; + cmd.k1 = in->K1; + cmd.k2 = in->K2; + + cmd.coefQFactor = in->coefQFactor; + + vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in) +{ +struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_abf_cfg cmdabf; + uint32_t temp; + + memset(&cmd, 0, sizeof(cmd)); + temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG); + + cmd = *((struct vfe_demosaic_cfg *)(&temp)); + cmd.abfEnable = in->abfUpdate.enable; + cmd.forceAbfOn = in->abfUpdate.forceOn; + cmd.abfShift = in->abfUpdate.shift; + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmdabf.lpThreshold = in->abfUpdate.lpThreshold; + cmdabf.ratio = in->abfUpdate.ratio; + cmdabf.minValue = in->abfUpdate.min; + cmdabf.maxValue = in->abfUpdate.max; + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, + (uint32_t *)&cmdabf, sizeof(cmdabf)); +} + +void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in) +{ + struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_bpc_cfg cmdbpc; + uint32_t temp; + + memset(&cmd, 0, sizeof(cmd)); + + temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG); + + cmd = *((struct vfe_demosaic_cfg *)(&temp)); + cmd.badPixelCorrEnable = in->bpcUpdate.enable; + cmd.fminThreshold = in->bpcUpdate.fminThreshold; + cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold; + cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold; + cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, + (uint32_t *)&cmdbpc, sizeof(cmdbpc)); +} + +void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in) +{ + struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_bpc_cfg cmd_bpc; + struct vfe_demosaic_abf_cfg cmd_abf; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd_bpc, 0, sizeof(cmd_bpc)); + memset(&cmd_abf, 0, sizeof(cmd_abf)); + + ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable; + + cmd.abfEnable = in->abfConfig.enable; + cmd.badPixelCorrEnable = in->bpcConfig.enable; + cmd.forceAbfOn = in->abfConfig.forceOn; + cmd.abfShift = in->abfConfig.shift; + cmd.fminThreshold = in->bpcConfig.fminThreshold; + cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold; + cmd.slopeShift = in->slopeShift; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd_abf.lpThreshold = in->abfConfig.lpThreshold; + cmd_abf.ratio = in->abfConfig.ratio; + cmd_abf.minValue = in->abfConfig.min; + cmd_abf.maxValue = in->abfConfig.max; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, + (uint32_t *)&cmd_abf, sizeof(cmd_abf)); + + cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold; + cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold; + cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, + (uint32_t *)&cmd_bpc, sizeof(cmd_bpc)); +} + +void vfe_demux_channel_gain_update( + struct vfe_cmd_demux_channel_gain_config *in) +{ + struct vfe_demux_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0EvenGain = in->ch0EvenGain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_demux_channel_gain_config( + struct vfe_cmd_demux_channel_gain_config *in) +{ + struct vfe_demux_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0EvenGain = in->ch0EvenGain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_black_level_update(struct vfe_cmd_black_level_config *in) +{ + struct vfe_blacklevel_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; + + cmd.evenEvenAdjustment = in->evenEvenAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; + + vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_black_level_config(struct vfe_cmd_black_level_config *in) +{ + struct vfe_blacklevel_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; + + cmd.evenEvenAdjustment = in->evenEvenAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; + + vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_asf_update(struct vfe_cmd_asf_update *in) +{ + struct vfe_asf_update cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.asfEnable = in->enable; + + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff1 = in->smoothCoefCenter; + cmd.smoothCoeff0 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; + cmd.sharpThresholdE1 = in->sharpThreshE1; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpThresholdE2 = in->sharpThreshE2; + cmd.sharpThresholdE3 = in->sharpThreshE3; + cmd.sharpThresholdE4 = in->sharpThreshE4; + cmd.sharpThresholdE5 = in->sharpThreshE5; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_asf_config(struct vfe_cmd_asf_config *in) +{ + struct vfe_asf_update cmd; + struct vfe_asfcrop_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->vfeModuleEnableLocal.asfEnable = in->enable; + + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff0 = in->smoothCoefCenter; + cmd.smoothCoeff1 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; + cmd.sharpThresholdE1 = in->sharpThreshE1; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpThresholdE2 = in->sharpThreshE2; + cmd.sharpThresholdE3 = in->sharpThreshE3; + cmd.sharpThresholdE4 = in->sharpThreshE4; + cmd.sharpThresholdE5 = in->sharpThreshE5; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.firstLine = in->cropFirstLine; + cmd2.lastLine = in->cropLastLine; + cmd2.firstPixel = in->cropFirstPixel; + cmd2.lastPixel = in->cropLastPixel; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in) +{ + struct vfe_wb_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.whiteBalanceEnable = + in->enable; + + cmd.ch0Gain = in->ch0Gain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in) +{ + struct vfe_chroma_suppress_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable; + + cmd.m1 = in->m1; + cmd.m3 = in->m3; + cmd.n1 = in->n1; + cmd.n3 = in->n3; + cmd.mm1 = in->mm1; + cmd.nn1 = in->nn1; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in) +{ + struct vfe_rolloff_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable; + + cmd.gridWidth = in->gridWidth; + cmd.gridHeight = in->gridHeight; + cmd.yDelta = in->yDelta; + cmd.gridX = in->gridXIndex; + cmd.gridY = in->gridYIndex; + cmd.pixelX = in->gridPixelXIndex; + cmd.pixelY = in->gridPixelYIndex; + cmd.yDeltaAccum = in->yDeltaAccum; + + vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0, + (uint32_t *)&cmd, sizeof(cmd)); + + vfe_write_lens_roll_off_table(in); +} + +void vfe_chroma_subsample_config( + struct vfe_cmd_chroma_subsample_config *in) +{ + struct vfe_chromasubsample_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable; + + cmd.hCositedPhase = in->hCositedPhase; + cmd.vCositedPhase = in->vCositedPhase; + cmd.hCosited = in->hCosited; + cmd.vCosited = in->vCosited; + cmd.hsubSampleEnable = in->hsubSampleEnable; + cmd.vsubSampleEnable = in->vsubSampleEnable; + cmd.cropEnable = in->cropEnable; + cmd.cropWidthLastPixel = in->cropWidthLastPixel; + cmd.cropWidthFirstPixel = in->cropWidthFirstPixel; + cmd.cropHeightLastLine = in->cropHeightLastLine; + cmd.cropHeightFirstLine = in->cropHeightFirstLine; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in) +{ + struct vfe_chroma_enhance_cfg cmd; + struct vfe_color_convert_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable; + + cmd.ap = in->ap; + cmd.am = in->am; + cmd.bp = in->bp; + cmd.bm = in->bm; + cmd.cp = in->cp; + cmd.cm = in->cm; + cmd.dp = in->dp; + cmd.dm = in->dm; + cmd.kcb = in->kcb; + cmd.kcr = in->kcr; + + cmd2.v0 = in->RGBtoYConversionV0; + cmd2.v1 = in->RGBtoYConversionV1; + cmd2.v2 = in->RGBtoYConversionV2; + cmd2.ConvertOffset = in->RGBtoYConversionOffset; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A, + (uint32_t *)&cmd, sizeof(cmd)); + + vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in) +{ + struct vfe_scaler2_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in) +{ + struct vfe_scaler2_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in) +{ + struct vfe_main_scaler_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.horizMNInit = in->MNInitH.MNCounterInit; + cmd.horizPhaseInit = in->MNInitH.phaseInit; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + cmd.vertMNInit = in->MNInitV.MNCounterInit; + cmd.vertPhaseInit = in->MNInitV.phaseInit; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_stats_wb_exp_stop(void) +{ + ctrl->vfeStatsCmdLocal.axwEnable = FALSE; + ctrl->vfeImaskLocal.awbPingpongIrq = FALSE; +} + +void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in) +{ + struct vfe_statsawb_update cmd; + struct vfe_statsawbae_update cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeSubregionCfg = in->wbExpSubRegion; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in) +{ + struct vfe_statsaf_update cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.windowVOffset = in->windowVOffset; + cmd.windowHOffset = in->windowHOffset; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in) +{ + struct vfe_statsawb_update cmd; + struct vfe_statsawbae_update cmd2; + struct vfe_statsaxw_hdr_cfg cmd3; + + ctrl->vfeStatsCmdLocal.axwEnable = in->enable; + ctrl->vfeImaskLocal.awbPingpongIrq = TRUE; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + memset(&cmd3, 0, sizeof(cmd3)); + + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeSubregionCfg = in->wbExpSubRegion; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); + + cmd3.axwHeader = in->axwHeader; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER, + (uint32_t *)&cmd3, sizeof(cmd3)); +} + +void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in) +{ + struct vfe_statsaf_update cmd; + struct vfe_statsaf_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + +ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable; +ctrl->vfeImaskLocal.afPingpongIrq = TRUE; + + cmd.windowVOffset = in->windowVOffset; + cmd.windowHOffset = in->windowHOffset; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.a00 = in->highPassCoef[0]; + cmd2.a04 = in->highPassCoef[1]; + cmd2.a20 = in->highPassCoef[2]; + cmd2.a21 = in->highPassCoef[3]; + cmd2.a22 = in->highPassCoef[4]; + cmd2.a23 = in->highPassCoef[5]; + cmd2.a24 = in->highPassCoef[6]; + cmd2.fvMax = in->metricMax; + cmd2.fvMetric = in->metricSelection; + cmd2.afHeader = in->bufferHeader; + cmd2.entry00 = in->gridForMultiWindows[0]; + cmd2.entry01 = in->gridForMultiWindows[1]; + cmd2.entry02 = in->gridForMultiWindows[2]; + cmd2.entry03 = in->gridForMultiWindows[3]; + cmd2.entry10 = in->gridForMultiWindows[4]; + cmd2.entry11 = in->gridForMultiWindows[5]; + cmd2.entry12 = in->gridForMultiWindows[6]; + cmd2.entry13 = in->gridForMultiWindows[7]; + cmd2.entry20 = in->gridForMultiWindows[8]; + cmd2.entry21 = in->gridForMultiWindows[9]; + cmd2.entry22 = in->gridForMultiWindows[10]; + cmd2.entry23 = in->gridForMultiWindows[11]; + cmd2.entry30 = in->gridForMultiWindows[12]; + cmd2.entry31 = in->gridForMultiWindows[13]; + cmd2.entry32 = in->gridForMultiWindows[14]; + cmd2.entry33 = in->gridForMultiWindows[15]; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_stats_setting(struct vfe_cmd_stats_setting *in) +{ + struct vfe_statsframe cmd1; + struct vfe_busstats_wrprio cmd2; + + memset(&cmd1, 0, sizeof(cmd1)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0]; + ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1]; + ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2]; + + ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0]; + ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1]; + ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; + + cmd1.lastPixel = in->frameHDimension; + cmd1.lastLine = in->frameVDimension; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE, + (uint32_t *)&cmd1, sizeof(cmd1)); + + cmd2.afBusPriority = in->afBusPriority; + cmd2.awbBusPriority = in->awbBusPriority; + cmd2.histBusPriority = in->histBusPriority; + cmd2.afBusPriorityEn = in->afBusPrioritySelection; + cmd2.awbBusPriorityEn = in->awbBusPrioritySelection; + cmd2.histBusPriorityEn = in->histBusPrioritySelection; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY, + (uint32_t *)&cmd2, sizeof(cmd2)); + + /* Program the bus ping pong address for statistics modules. */ + writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); + writel(in->awbBuffer[0], + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + writel(in->awbBuffer[1], + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); + writel(in->histBuffer[0], + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR); + writel(in->histBuffer[1], + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR); +} + +void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in) +{ + struct VFE_AxiInputCmdType cmd; + uint32_t xSizeWord, axiRdUnpackPattern; + uint8_t axiInputPpw; + uint32_t busPingpongRdIrqEnable; + + ctrl->vfeImaskLocal.rdPingpongIrq = TRUE; + + switch (in->pixelSize) { + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_10BIT; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_12BIT; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + default: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_8BIT; + break; + } + + memset(&cmd, 0, sizeof(cmd)); + + switch (in->pixelSize) { + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + axiInputPpw = 6; + axiRdUnpackPattern = 0xD43210; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + axiInputPpw = 5; + axiRdUnpackPattern = 0xC3210; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + default: + axiInputPpw = 8; + axiRdUnpackPattern = 0xF6543210; + break; + } + + xSizeWord = + ((((in->xOffset % axiInputPpw) + in->xSize) + + (axiInputPpw-1)) / axiInputPpw) - 1; + + cmd.stripeStartAddr0 = in->fragAddr[0]; + cmd.stripeStartAddr1 = in->fragAddr[1]; + cmd.stripeStartAddr2 = in->fragAddr[2]; + cmd.stripeStartAddr3 = in->fragAddr[3]; + cmd.ySize = in->ySize; + cmd.yOffsetDelta = 0; + cmd.xSizeWord = xSizeWord; + cmd.burstLength = 1; + cmd.NumOfRows = in->numOfRows; + cmd.RowIncrement = + (in->rowIncrement + (axiInputPpw-1))/axiInputPpw; + cmd.mainUnpackHeight = in->ySize; + cmd.mainUnpackWidth = in->xSize - 1; + cmd.mainUnpackHbiSel = (uint32_t)in->unpackHbi; + cmd.mainUnpackPhase = in->unpackPhase; + cmd.unpackPattern = axiRdUnpackPattern; + cmd.padLeft = in->padRepeatCountLeft; + cmd.padRight = in->padRepeatCountRight; + cmd.padTop = in->padRepeatCountTop; + cmd.padBottom = in->padRepeatCountBottom; + cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0; + cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1; + cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2; + cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3; + cmd.leftUnpackStop0 = in->padLeftStopCycle0; + cmd.leftUnpackStop1 = in->padLeftStopCycle1; + cmd.leftUnpackStop2 = in->padLeftStopCycle2; + cmd.leftUnpackStop3 = in->padLeftStopCycle3; + cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0; + cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1; + cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2; + cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3; + cmd.rightUnpackStop0 = in->padRightStopCycle0; + cmd.rightUnpackStop1 = in->padRightStopCycle1; + cmd.rightUnpackStop2 = in->padRightStopCycle2; + cmd.rightUnpackStop3 = in->padRightStopCycle3; + cmd.topUnapckPattern = in->padTopLineCount; + cmd.bottomUnapckPattern = in->padBottomLineCount; + + /* program vfe_bus_cfg */ + vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0, + (uint32_t *)&cmd, sizeof(cmd)); + + /* hacking code, put it to default value */ + busPingpongRdIrqEnable = 0xf; + + writel(busPingpongRdIrqEnable, + ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); +} + +void vfe_stats_config(struct vfe_cmd_stats_setting *in) +{ + ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0]; + ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1]; + ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2]; + + ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0]; + ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1]; + ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; + + vfe_stats_setting(in); +} + +void vfe_axi_output_config( + struct vfe_cmd_axi_output_config *in) +{ + /* local variable */ + uint32_t *pcircle; + uint32_t *pdest; + uint32_t *psrc; + uint8_t i; + uint8_t fcnt; + uint16_t axioutpw = 8; + + /* parameters check, condition and usage mode check */ + ctrl->encPath.fragCount = in->output2.fragmentCount; + if (ctrl->encPath.fragCount > 1) + ctrl->encPath.multiFrag = TRUE; + + ctrl->viewPath.fragCount = in->output1.fragmentCount; + if (ctrl->viewPath.fragCount > 1) + ctrl->viewPath.multiFrag = TRUE; + + /* VFE_BUS_CFG. raw data size */ + ctrl->vfeBusConfigLocal.rawPixelDataSize = in->outputDataSize; + + switch (in->outputDataSize) { + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + axioutpw = 8; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + axioutpw = 6; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + axioutpw = 5; + break; + } + + ctrl->axiOutputMode = in->outputMode; + + CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode); + + switch (ctrl->axiOutputMode) { + case VFE_AXI_OUTPUT_MODE_Output1: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.encIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output1AndOutput2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */ + break; + + case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2: { + /* For raw snapshot, we need both ping and pong buffer + * initialized to the same address. Otherwise, if we + * leave the pong buffer to NULL, there will be axi_error. + * Note that ideally we should deal with this at upper layer, + * which is in msm_vfe8x.c */ + if (!in->output2.outputCbcr.outFragments[1][0]) { + in->output2.outputCbcr.outFragments[1][0] = + in->output2.outputCbcr.outFragments[0][0]; + } + + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_VIEW_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */ + break; + + case VFE_AXI_LAST_OUTPUT_MODE_ENUM: + break; + } /* switch */ + + /* Save the addresses for each path. */ + /* output2 path */ + fcnt = ctrl->encPath.fragCount; + + pcircle = ctrl->encPath.yPath.addressBuffer; + pdest = ctrl->encPath.nextFrameAddrBuf; + + psrc = &(in->output2.outputY.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputY.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputY.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + pcircle = ctrl->encPath.cbcrPath.addressBuffer; + + psrc = &(in->output2.outputCbcr.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputCbcr.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputCbcr.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + vfe_set_bus_pipo_addr(&ctrl->viewPath, &ctrl->encPath); + + ctrl->encPath.ackPending = FALSE; + ctrl->encPath.currentFrame = ping; + ctrl->encPath.whichOutputPath = 1; + ctrl->encPath.yPath.fragIndex = 2; + ctrl->encPath.cbcrPath.fragIndex = 2; + ctrl->encPath.yPath.hwCurrentFlag = ping; + ctrl->encPath.cbcrPath.hwCurrentFlag = ping; + + /* output1 path */ + pcircle = ctrl->viewPath.yPath.addressBuffer; + pdest = ctrl->viewPath.nextFrameAddrBuf; + fcnt = ctrl->viewPath.fragCount; + + psrc = &(in->output1.outputY.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputY.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputY.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + pcircle = ctrl->viewPath.cbcrPath.addressBuffer; + + psrc = &(in->output1.outputCbcr.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputCbcr.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputCbcr.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + ctrl->viewPath.ackPending = FALSE; + ctrl->viewPath.currentFrame = ping; + ctrl->viewPath.whichOutputPath = 0; + ctrl->viewPath.yPath.fragIndex = 2; + ctrl->viewPath.cbcrPath.fragIndex = 2; + ctrl->viewPath.yPath.hwCurrentFlag = ping; + ctrl->viewPath.cbcrPath.hwCurrentFlag = ping; + + /* call to program the registers. */ + vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw); +} + +void vfe_camif_config(struct vfe_cmd_camif_config *in) +{ + struct vfe_camifcfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine); + CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame); + CDBG("camif.window firstpixel = %d\n", in->window.firstpixel); + CDBG("camif.window lastpixel = %d\n", in->window.lastpixel); + CDBG("camif.window firstline = %d\n", in->window.firstline); + CDBG("camif.window lastline = %d\n", in->window.lastline); + + /* determine if epoch interrupt needs to be enabled. */ + if ((in->epoch1.enable == TRUE) && + (in->epoch1.lineindex <= + in->frame.linesPerFrame)) + ctrl->vfeImaskLocal.camifEpoch1Irq = 1; + + if ((in->epoch2.enable == TRUE) && + (in->epoch2.lineindex <= + in->frame.linesPerFrame)) { + ctrl->vfeImaskLocal.camifEpoch2Irq = 1; + } + + /* save the content to program CAMIF_CONFIG seperately. */ + ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig; + + /* EFS_Config */ + cmd.efsEndOfLine = in->EFS.efsendofline; + cmd.efsStartOfLine = in->EFS.efsstartofline; + cmd.efsEndOfFrame = in->EFS.efsendofframe; + cmd.efsStartOfFrame = in->EFS.efsstartofframe; + + /* Frame Config */ + cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine; + cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame; + + /* Window Width Config */ + cmd.windowWidthCfgLastPixel = in->window.lastpixel; + cmd.windowWidthCfgFirstPixel = in->window.firstpixel; + + /* Window Height Config */ + cmd.windowHeightCfglastLine = in->window.lastline; + cmd.windowHeightCfgfirstLine = in->window.firstline; + + /* Subsample 1 Config */ + cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask; + cmd.subsample1CfgLineSkip = in->subsample.lineskipmask; + + /* Subsample 2 Config */ + cmd.subsample2CfgFrameSkip = in->subsample.frameskip; + cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode; + cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap; + + /* Epoch Interrupt */ + cmd.epoch1Line = in->epoch1.lineindex; + cmd.epoch2Line = in->epoch2.lineindex; + + vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in) +{ + struct vfe_fov_crop_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.cropEnable = in->enable; + + /* FOV Corp, Part 1 */ + cmd.lastPixel = in->lastPixel; + cmd.firstPixel = in->firstPixel; + + /* FOV Corp, Part 2 */ + cmd.lastLine = in->lastLine; + cmd.firstLine = in->firstLine; + + vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_get_hw_version(struct vfe_cmd_hw_version *out) +{ + uint32_t vfeHwVersionPacked; + struct vfe_hw_ver ver; + + vfeHwVersionPacked = readl(ctrl->vfebase + VFE_HW_VERSION); + + ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked); + + out->coreVersion = ver.coreVersion; + out->minorVersion = ver.minorVersion; + out->majorVersion = ver.majorVersion; +} + +static void vfe_reset_internal_variables(void) +{ + unsigned long flags; + + /* local variables to program the hardware. */ + ctrl->vfeImaskPacked = 0; + ctrl->vfeImaskCompositePacked = 0; + + /* FALSE = disable, 1 = enable. */ + memset(&ctrl->vfeModuleEnableLocal, 0, + sizeof(ctrl->vfeModuleEnableLocal)); + + /* 0 = disable, 1 = enable */ + memset(&ctrl->vfeCamifConfigLocal, 0, + sizeof(ctrl->vfeCamifConfigLocal)); + /* 0 = disable, 1 = enable */ + memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal)); + memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal)); + memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal)); + memset(&ctrl->vfeBusPmConfigLocal, 0, + sizeof(ctrl->vfeBusPmConfigLocal)); + memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal)); + memset(&ctrl->vfeInterruptNameLocal, 0, + sizeof(ctrl->vfeInterruptNameLocal)); + memset(&ctrl->vfeDroppedFrameCounts, 0, + sizeof(ctrl->vfeDroppedFrameCounts)); + memset(&ctrl->vfeIrqThreadMsgLocal, 0, + sizeof(ctrl->vfeIrqThreadMsgLocal)); + + /* state control variables */ + ctrl->vfeStartAckPendingFlag = FALSE; + ctrl->vfeStopAckPending = FALSE; + ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + spin_lock_irqsave(&ctrl->state_lock, flags); + ctrl->vstate = VFE_STATE_IDLE; + spin_unlock_irqrestore(&ctrl->state_lock, flags); + + ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM; + /* 0 for continuous mode, 1 for snapshot mode */ + ctrl->vfeOperationMode = VFE_START_OPERATION_MODE_CONTINUOUS; + ctrl->vfeSnapShotCount = 0; + ctrl->vfeStatsPingPongReloadFlag = FALSE; + /* this is unsigned 32 bit integer. */ + ctrl->vfeFrameId = 0; + ctrl->vfeFrameSkip.output1Pattern = 0xffffffff; + ctrl->vfeFrameSkip.output1Period = 31; + ctrl->vfeFrameSkip.output2Pattern = 0xffffffff; + ctrl->vfeFrameSkip.output2Period = 31; + ctrl->vfeFrameSkipPattern = 0xffffffff; + ctrl->vfeFrameSkipCount = 0; + ctrl->vfeFrameSkipPeriod = 31; + + memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath)); + memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath)); + + ctrl->encPath.whichOutputPath = 1; + ctrl->encPath.cbcrStatusBit = 5; + ctrl->viewPath.whichOutputPath = 0; + ctrl->viewPath.cbcrStatusBit = 7; + + ctrl->vfeTestGenStartFlag = FALSE; + + /* default to bank 0. */ + ctrl->vfeLaBankSel = 0; + + /* default to bank 0 for all channels. */ + memset(&ctrl->vfeGammaLutSel, 0, sizeof(ctrl->vfeGammaLutSel)); + + /* Stats control variables. */ + memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl)); + memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl)); + vfe_set_stats_pingpong_address(&ctrl->afStatsControl, + &ctrl->awbStatsControl); +} + +void vfe_reset(void) +{ + vfe_reset_internal_variables(); + + ctrl->vfeImaskLocal.resetAckIrq = TRUE; + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + + /* disable all interrupts. */ + writel(VFE_DISABLE_ALL_IRQS, + ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); + + /* clear all pending interrupts*/ + writel(VFE_CLEAR_ALL_IRQS, + ctrl->vfebase + VFE_IRQ_CLEAR); + + /* enable reset_ack interrupt. */ + writel(ctrl->vfeImaskPacked, + ctrl->vfebase + VFE_IRQ_MASK); + + writel(VFE_RESET_UPON_RESET_CMD, + ctrl->vfebase + VFE_GLOBAL_RESET_CMD); +} diff --git a/drivers/staging/dream/camera/msm_vfe8x_proc.h b/drivers/staging/dream/camera/msm_vfe8x_proc.h new file mode 100644 index 0000000..9182856 --- /dev/null +++ b/drivers/staging/dream/camera/msm_vfe8x_proc.h @@ -0,0 +1,1549 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef __MSM_VFE8X_REG_H__ +#define __MSM_VFE8X_REG_H__ + +#include <mach/msm_iomap.h> +#include <mach/camera.h> +#include "msm_vfe8x.h" + +/* at start of camif, bit 1:0 = 0x01:enable + * image data capture at frame boundary. */ +#define CAMIF_COMMAND_START 0x00000005 + +/* bit 2= 0x1:clear the CAMIF_STATUS register + * value. */ +#define CAMIF_COMMAND_CLEAR 0x00000004 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x10: + * disable image data capture immediately. */ +#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x00: + * disable image data capture at frame boundary */ +#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000 + +/* to halt axi bridge */ +#define AXI_HALT 0x00000001 + +/* clear the halt bit. */ +#define AXI_HALT_CLEAR 0x00000000 + +/* reset the pipeline when stop command is issued. + * (without reset the register.) bit 26-31 = 0, + * domain reset, bit 0-9 = 1 for module reset, except + * register module. */ +#define VFE_RESET_UPON_STOP_CMD 0x000003ef + +/* reset the pipeline when reset command. + * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */ +#define VFE_RESET_UPON_RESET_CMD 0x000003ff + +/* bit 5 is for axi status idle or busy. + * 1 = halted, 0 = busy */ +#define AXI_STATUS_BUSY_MASK 0x00000020 + +/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present + * for frame done interrupt */ +#define VFE_COMP_IRQ_BOTH_Y_CBCR 3 + +/* bit 1 = 1, only cbcr irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_CBCR_ONLY 2 + +/* bit 0 = 1, only y irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_Y_ONLY 1 + +/* bit 0 = 1, PM go; bit1 = 1, PM stop */ +#define VFE_PERFORMANCE_MONITOR_GO 0x00000001 +#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002 + +/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */ +#define VFE_TEST_GEN_GO 0x00000001 +#define VFE_TEST_GEN_STOP 0x00000002 + +/* the chroma is assumed to be interpolated between + * the luma samples. JPEG 4:2:2 */ +#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0 + +/* constants for irq registers */ +#define VFE_DISABLE_ALL_IRQS 0 +/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */ +#define VFE_CLEAR_ALL_IRQS 0xffffffff +/* imask for while waiting for stop ack, driver has already + * requested stop, waiting for reset irq, + * bit 29,28,27,26 for async timer, bit 9 for reset */ +#define VFE_IMASK_WHILE_STOPPING 0x3c000200 + +/* when normal case, don't want to block error status. + * bit 0,6,20,21,22,30,31 */ +#define VFE_IMASK_ERROR_ONLY 0xC0700041 +#define VFE_REG_UPDATE_TRIGGER 1 +#define VFE_PM_BUF_MAX_CNT_MASK 0xFF +#define VFE_DMI_CFG_DEFAULT 0x00000100 +#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32 +#define VFE_AF_PINGPONG_STATUS_BIT 0x100 +#define VFE_AWB_PINGPONG_STATUS_BIT 0x200 + +/* VFE I/O registers */ +enum { + VFE_HW_VERSION = 0x00000000, + VFE_GLOBAL_RESET_CMD = 0x00000004, + VFE_MODULE_RESET = 0x00000008, + VFE_CGC_OVERRIDE = 0x0000000C, + VFE_MODULE_CFG = 0x00000010, + VFE_CFG = 0x00000014, + VFE_IRQ_MASK = 0x00000018, + VFE_IRQ_CLEAR = 0x0000001C, +VFE_IRQ_STATUS = 0x00000020, +VFE_IRQ_COMPOSITE_MASK = 0x00000024, +VFE_BUS_CMD = 0x00000028, +VFE_BUS_CFG = 0x0000002C, +VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030, +VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034, +VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038, +VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C, +VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040, +VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044, +VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048, +VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C, +VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050, +VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054, +VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058, +VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C, +VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060, +VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064, +VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068, +VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C, +VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070, +VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074, +VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078, +VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C, +VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080, +VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084, +VFE_BUS_STATS_WR_PRIORITY = 0x00000088, +VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C, +VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090, +VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094, +VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098, +VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C, +VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0, +VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4, +VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8, +VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC, +VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0, +VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4, +VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8, +VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC, +VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0, +VFE_BUS_PINGPONG_STATUS = 0x000000C4, +VFE_BUS_PM_CMD = 0x000000C8, +VFE_BUS_PM_CFG = 0x000000CC, +VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0, +VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4, +VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8, +VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC, +VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0, +VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4, +VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8, +VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC, +VFE_BUS_MISR_CFG = 0x000000F4, +VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8, +VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC, +VFE_BUS_MISR_RD_VAL = 0x00000100, +VFE_AXI_CMD = 0x00000104, +VFE_AXI_CFG = 0x00000108, +VFE_AXI_STATUS = 0x0000010C, +CAMIF_COMMAND = 0x00000110, +CAMIF_CONFIG = 0x00000114, +CAMIF_EFS_CONFIG = 0x00000118, +CAMIF_FRAME_CONFIG = 0x0000011C, +CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120, +CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124, +CAMIF_SUBSAMPLE1_CONFIG = 0x00000128, +CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C, +CAMIF_EPOCH_IRQ = 0x00000130, +CAMIF_STATUS = 0x00000134, +CAMIF_MISR = 0x00000138, +VFE_SYNC_TIMER_CMD = 0x0000013C, +VFE_SYNC_TIMER0_LINE_START = 0x00000140, +VFE_SYNC_TIMER0_PIXEL_START = 0x00000144, +VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148, +VFE_SYNC_TIMER1_LINE_START = 0x0000014C, +VFE_SYNC_TIMER1_PIXEL_START = 0x00000150, +VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154, +VFE_SYNC_TIMER2_LINE_START = 0x00000158, +VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C, +VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160, +VFE_SYNC_TIMER_POLARITY = 0x00000164, +VFE_ASYNC_TIMER_CMD = 0x00000168, +VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C, +VFE_ASYNC_TIMER0_CFG_1 = 0x00000170, +VFE_ASYNC_TIMER1_CFG_0 = 0x00000174, +VFE_ASYNC_TIMER1_CFG_1 = 0x00000178, +VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C, +VFE_ASYNC_TIMER2_CFG_1 = 0x00000180, +VFE_ASYNC_TIMER3_CFG_0 = 0x00000184, +VFE_ASYNC_TIMER3_CFG_1 = 0x00000188, +VFE_TIMER_SEL = 0x0000018C, +VFE_REG_UPDATE_CMD = 0x00000190, +VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194, +VFE_BLACK_EVEN_ODD_VALUE = 0x00000198, +VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C, +VFE_BLACK_ODD_ODD_VALUE = 0x000001A0, +VFE_ROLLOFF_CFG_0 = 0x000001A4, +VFE_ROLLOFF_CFG_1 = 0x000001A8, +VFE_ROLLOFF_CFG_2 = 0x000001AC, +VFE_DEMUX_CFG = 0x000001B0, +VFE_DEMUX_GAIN_0 = 0x000001B4, +VFE_DEMUX_GAIN_1 = 0x000001B8, +VFE_DEMUX_EVEN_CFG = 0x000001BC, +VFE_DEMUX_ODD_CFG = 0x000001C0, +VFE_DEMOSAIC_CFG = 0x000001C4, +VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8, +VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC, +VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0, +VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4, +VFE_DEMOSAIC_STATUS = 0x000001D8, +VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC, +VFE_CROP_WIDTH_CFG = 0x000001E0, +VFE_CROP_HEIGHT_CFG = 0x000001E4, +VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8, +VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC, +VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0, +VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4, +VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8, +VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC, +VFE_COLOR_CORRECT_COEFF_6 = 0x00000200, +VFE_COLOR_CORRECT_COEFF_7 = 0x00000204, +VFE_COLOR_CORRECT_COEFF_8 = 0x00000208, +VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C, +VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210, +VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214, +VFE_COLOR_CORRECT_COEFF_Q = 0x00000218, +VFE_LA_CFG = 0x0000021C, +VFE_LUT_BANK_SEL = 0x00000220, +VFE_CHROMA_ENHAN_A = 0x00000224, +VFE_CHROMA_ENHAN_B = 0x00000228, +VFE_CHROMA_ENHAN_C = 0x0000022C, +VFE_CHROMA_ENHAN_D = 0x00000230, +VFE_CHROMA_ENHAN_K = 0x00000234, +VFE_COLOR_CONVERT_COEFF_0 = 0x00000238, +VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C, +VFE_COLOR_CONVERT_COEFF_2 = 0x00000240, +VFE_COLOR_CONVERT_OFFSET = 0x00000244, +VFE_ASF_CFG = 0x00000248, +VFE_ASF_SHARP_CFG_0 = 0x0000024C, +VFE_ASF_SHARP_CFG_1 = 0x00000250, +VFE_ASF_SHARP_COEFF_0 = 0x00000254, +VFE_ASF_SHARP_COEFF_1 = 0x00000258, +VFE_ASF_SHARP_COEFF_2 = 0x0000025C, +VFE_ASF_SHARP_COEFF_3 = 0x00000260, +VFE_ASF_MAX_EDGE = 0x00000264, +VFE_ASF_CROP_WIDTH_CFG = 0x00000268, +VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C, +VFE_SCALE_CFG = 0x00000270, +VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274, +VFE_SCALE_H_PHASE_CFG = 0x00000278, +VFE_SCALE_H_STRIPE_CFG = 0x0000027C, +VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280, +VFE_SCALE_V_PHASE_CFG = 0x00000284, +VFE_SCALE_V_STRIPE_CFG = 0x00000288, +VFE_SCALE_Y_CFG = 0x0000028C, +VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290, +VFE_SCALE_Y_H_PHASE_CFG = 0x00000294, +VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298, +VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C, +VFE_SCALE_CBCR_CFG = 0x000002A0, +VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4, +VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8, +VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC, +VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0, +VFE_WB_CFG = 0x000002B4, +VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8, +VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC, +VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0, +VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4, +VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8, +VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC, +VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0, +VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4, +VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8, +VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC, +VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0, +VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4, +VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8, +VFE_CLAMP_MAX_CFG = 0x000002EC, +VFE_CLAMP_MIN_CFG = 0x000002F0, +VFE_STATS_CMD = 0x000002F4, +VFE_STATS_AF_CFG = 0x000002F8, +VFE_STATS_AF_DIM = 0x000002FC, +VFE_STATS_AF_GRID_0 = 0x00000300, +VFE_STATS_AF_GRID_1 = 0x00000304, +VFE_STATS_AF_GRID_2 = 0x00000308, +VFE_STATS_AF_GRID_3 = 0x0000030C, +VFE_STATS_AF_HEADER = 0x00000310, +VFE_STATS_AF_COEF0 = 0x00000314, +VFE_STATS_AF_COEF1 = 0x00000318, +VFE_STATS_AWBAE_CFG = 0x0000031C, +VFE_STATS_AXW_HEADER = 0x00000320, +VFE_STATS_AWB_MCFG = 0x00000324, +VFE_STATS_AWB_CCFG1 = 0x00000328, +VFE_STATS_AWB_CCFG2 = 0x0000032C, +VFE_STATS_HIST_HEADER = 0x00000330, +VFE_STATS_HIST_INNER_OFFSET = 0x00000334, +VFE_STATS_HIST_INNER_DIM = 0x00000338, +VFE_STATS_FRAME_SIZE = 0x0000033C, +VFE_DMI_CFG = 0x00000340, +VFE_DMI_ADDR = 0x00000344, +VFE_DMI_DATA_HI = 0x00000348, +VFE_DMI_DATA_LO = 0x0000034C, +VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350, +VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354, +VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358, +VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C, +VFE_TESTBUS_SEL = 0x00000360, +VFE_TESTGEN_CFG = 0x00000364, +VFE_SW_TESTGEN_CMD = 0x00000368, +VFE_HW_TESTGEN_CMD = 0x0000036C, +VFE_HW_TESTGEN_CFG = 0x00000370, +VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374, +VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378, +VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C, +VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380, +VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384, +VFE_HW_TESTGEN_HBI_CFG = 0x00000388, +VFE_HW_TESTGEN_VBL_CFG = 0x0000038C, +VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390, +VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394, +VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398, +VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C, +VFE_SPARE = 0x000003A0, +}; + +#define ping 0x0 +#define pong 0x1 + +struct vfe_bus_cfg_data { + boolean stripeRdPathEn; + boolean encYWrPathEn; + boolean encCbcrWrPathEn; + boolean viewYWrPathEn; + boolean viewCbcrWrPathEn; + enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize; + enum VFE_RAW_WR_PATH_SEL rawWritePathSelect; +}; + +struct vfe_camif_cfg_data { + boolean camif2OutputEnable; + boolean camif2BusEnable; + struct vfe_cmds_camif_cfg camifCfgFromCmd; +}; + +struct vfe_irq_composite_mask_config { + uint8_t encIrqComMask; + uint8_t viewIrqComMask; + uint8_t ceDoneSel; +}; + +/* define a structure for each output path.*/ +struct vfe_output_path { + uint32_t addressBuffer[8]; + uint16_t fragIndex; + boolean hwCurrentFlag; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; +}; + +struct vfe_output_path_combo { + boolean whichOutputPath; + boolean pathEnabled; + boolean multiFrag; + uint8_t fragCount; + boolean ackPending; + uint8_t currentFrame; + uint32_t nextFrameAddrBuf[8]; + struct vfe_output_path yPath; + struct vfe_output_path cbcrPath; + uint8_t snapshotPendingCount; + boolean pmEnabled; + uint8_t cbcrStatusBit; +}; + +struct vfe_stats_control { + boolean ackPending; + uint32_t addressBuffer[2]; + uint32_t nextFrameAddrBuf; + boolean pingPongStatus; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; + uint32_t droppedStatsFrameCount; + uint32_t bufToRender; +}; + +struct vfe_gamma_lut_sel { + boolean ch0BankSelect; + boolean ch1BankSelect; + boolean ch2BankSelect; +}; + +struct vfe_interrupt_mask { + boolean camifErrorIrq; + boolean camifSofIrq; + boolean camifEolIrq; + boolean camifEofIrq; + boolean camifEpoch1Irq; + boolean camifEpoch2Irq; + boolean camifOverflowIrq; + boolean ceIrq; + boolean regUpdateIrq; + boolean resetAckIrq; + boolean encYPingpongIrq; + boolean encCbcrPingpongIrq; + boolean viewYPingpongIrq; + boolean viewCbcrPingpongIrq; + boolean rdPingpongIrq; + boolean afPingpongIrq; + boolean awbPingpongIrq; + boolean histPingpongIrq; + boolean encIrq; + boolean viewIrq; + boolean busOverflowIrq; + boolean afOverflowIrq; + boolean awbOverflowIrq; + boolean syncTimer0Irq; + boolean syncTimer1Irq; + boolean syncTimer2Irq; + boolean asyncTimer0Irq; + boolean asyncTimer1Irq; + boolean asyncTimer2Irq; + boolean asyncTimer3Irq; + boolean axiErrorIrq; + boolean violationIrq; +}; + +enum vfe_interrupt_name { + CAMIF_ERROR_IRQ, + CAMIF_SOF_IRQ, + CAMIF_EOL_IRQ, + CAMIF_EOF_IRQ, + CAMIF_EPOCH1_IRQ, + CAMIF_EPOCH2_IRQ, + CAMIF_OVERFLOW_IRQ, + CE_IRQ, + REG_UPDATE_IRQ, + RESET_ACK_IRQ, + ENC_Y_PINGPONG_IRQ, + ENC_CBCR_PINGPONG_IRQ, + VIEW_Y_PINGPONG_IRQ, + VIEW_CBCR_PINGPONG_IRQ, + RD_PINGPONG_IRQ, + AF_PINGPONG_IRQ, + AWB_PINGPONG_IRQ, + HIST_PINGPONG_IRQ, + ENC_IRQ, + VIEW_IRQ, + BUS_OVERFLOW_IRQ, + AF_OVERFLOW_IRQ, + AWB_OVERFLOW_IRQ, + SYNC_TIMER0_IRQ, + SYNC_TIMER1_IRQ, + SYNC_TIMER2_IRQ, + ASYNC_TIMER0_IRQ, + ASYNC_TIMER1_IRQ, + ASYNC_TIMER2_IRQ, + ASYNC_TIMER3_IRQ, + AXI_ERROR_IRQ, + VIOLATION_IRQ +}; + +enum VFE_DMI_RAM_SEL { + NO_MEM_SELECTED = 0, + ROLLOFF_RAM = 0x1, + RGBLUT_RAM_CH0_BANK0 = 0x2, + RGBLUT_RAM_CH0_BANK1 = 0x3, + RGBLUT_RAM_CH1_BANK0 = 0x4, + RGBLUT_RAM_CH1_BANK1 = 0x5, + RGBLUT_RAM_CH2_BANK0 = 0x6, + RGBLUT_RAM_CH2_BANK1 = 0x7, + STATS_HIST_CB_EVEN_RAM = 0x8, + STATS_HIST_CB_ODD_RAM = 0x9, + STATS_HIST_CR_EVEN_RAM = 0xa, + STATS_HIST_CR_ODD_RAM = 0xb, + RGBLUT_CHX_BANK0 = 0xc, + RGBLUT_CHX_BANK1 = 0xd, + LUMA_ADAPT_LUT_RAM_BANK0 = 0xe, + LUMA_ADAPT_LUT_RAM_BANK1 = 0xf +}; + +struct vfe_module_enable { + boolean blackLevelCorrectionEnable; + boolean lensRollOffEnable; + boolean demuxEnable; + boolean chromaUpsampleEnable; + boolean demosaicEnable; + boolean statsEnable; + boolean cropEnable; + boolean mainScalerEnable; + boolean whiteBalanceEnable; + boolean colorCorrectionEnable; + boolean yHistEnable; + boolean skinToneEnable; + boolean lumaAdaptationEnable; + boolean rgbLUTEnable; + boolean chromaEnhanEnable; + boolean asfEnable; + boolean chromaSuppressionEnable; + boolean chromaSubsampleEnable; + boolean scaler2YEnable; + boolean scaler2CbcrEnable; +}; + +struct vfe_bus_cmd_data { + boolean stripeReload; + boolean busPingpongReload; + boolean statsPingpongReload; +}; + +struct vfe_stats_cmd_data { + boolean autoFocusEnable; + boolean axwEnable; + boolean histEnable; + boolean clearHistEnable; + boolean histAutoClearEnable; + boolean colorConversionEnable; +}; + +struct vfe_hw_ver { + uint32_t minorVersion:8; + uint32_t majorVersion:8; + uint32_t coreVersion:4; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct vfe_cfg { + uint32_t pixelPattern:3; + uint32_t /* reserved */ : 13; + uint32_t inputSource:2; + uint32_t /* reserved */ : 14; +} __attribute__((packed, aligned(4))); + +struct vfe_buscmd { + uint32_t stripeReload:1; + uint32_t /* reserved */ : 3; + uint32_t busPingpongReload:1; + uint32_t statsPingpongReload:1; + uint32_t /* reserved */ : 26; +} __attribute__((packed, aligned(4))); + +struct VFE_Irq_Composite_MaskType { + uint32_t encIrqComMaskBits:2; + uint32_t viewIrqComMaskBits:2; + uint32_t ceDoneSelBits:5; + uint32_t /* reserved */ : 23; +} __attribute__((packed, aligned(4))); + +struct vfe_mod_enable { + uint32_t blackLevelCorrectionEnable:1; + uint32_t lensRollOffEnable:1; + uint32_t demuxEnable:1; + uint32_t chromaUpsampleEnable:1; + uint32_t demosaicEnable:1; + uint32_t statsEnable:1; + uint32_t cropEnable:1; + uint32_t mainScalerEnable:1; + uint32_t whiteBalanceEnable:1; + uint32_t colorCorrectionEnable:1; + uint32_t yHistEnable:1; + uint32_t skinToneEnable:1; + uint32_t lumaAdaptationEnable:1; + uint32_t rgbLUTEnable:1; + uint32_t chromaEnhanEnable:1; + uint32_t asfEnable:1; + uint32_t chromaSuppressionEnable:1; + uint32_t chromaSubsampleEnable:1; + uint32_t scaler2YEnable:1; + uint32_t scaler2CbcrEnable:1; + uint32_t /* reserved */ : 14; +} __attribute__((packed, aligned(4))); + +struct vfe_irqenable { + uint32_t camifErrorIrq:1; + uint32_t camifSofIrq:1; + uint32_t camifEolIrq:1; + uint32_t camifEofIrq:1; + uint32_t camifEpoch1Irq:1; + uint32_t camifEpoch2Irq:1; + uint32_t camifOverflowIrq:1; + uint32_t ceIrq:1; + uint32_t regUpdateIrq:1; + uint32_t resetAckIrq:1; + uint32_t encYPingpongIrq:1; + uint32_t encCbcrPingpongIrq:1; + uint32_t viewYPingpongIrq:1; + uint32_t viewCbcrPingpongIrq:1; + uint32_t rdPingpongIrq:1; + uint32_t afPingpongIrq:1; + uint32_t awbPingpongIrq:1; + uint32_t histPingpongIrq:1; + uint32_t encIrq:1; + uint32_t viewIrq:1; + uint32_t busOverflowIrq:1; + uint32_t afOverflowIrq:1; + uint32_t awbOverflowIrq:1; + uint32_t syncTimer0Irq:1; + uint32_t syncTimer1Irq:1; + uint32_t syncTimer2Irq:1; + uint32_t asyncTimer0Irq:1; + uint32_t asyncTimer1Irq:1; + uint32_t asyncTimer2Irq:1; + uint32_t asyncTimer3Irq:1; + uint32_t axiErrorIrq:1; + uint32_t violationIrq:1; +} __attribute__((packed, aligned(4))); + +struct vfe_upsample_cfg { + uint32_t chromaCositingForYCbCrInputs:1; + uint32_t /* reserved */ : 31; +} __attribute__((packed, aligned(4))); + +struct VFE_CAMIFConfigType { + /* CAMIF Config */ + uint32_t /* reserved */ : 1; + uint32_t VSyncEdge:1; + uint32_t HSyncEdge:1; + uint32_t syncMode:2; + uint32_t vfeSubsampleEnable:1; + uint32_t /* reserved */ : 1; + uint32_t busSubsampleEnable:1; + uint32_t camif2vfeEnable:1; + uint32_t /* reserved */ : 1; + uint32_t camif2busEnable:1; + uint32_t irqSubsampleEnable:1; + uint32_t binningEnable:1; + uint32_t /* reserved */ : 18; + uint32_t misrEnable:1; +} __attribute__((packed, aligned(4))); + +struct vfe_camifcfg { + /* EFS_Config */ + uint32_t efsEndOfLine:8; + uint32_t efsStartOfLine:8; + uint32_t efsEndOfFrame:8; + uint32_t efsStartOfFrame:8; + /* Frame Config */ + uint32_t frameConfigPixelsPerLine:14; + uint32_t /* reserved */ : 2; + uint32_t frameConfigLinesPerFrame:14; + uint32_t /* reserved */ : 2; + /* Window Width Config */ + uint32_t windowWidthCfgLastPixel:14; + uint32_t /* reserved */ : 2; + uint32_t windowWidthCfgFirstPixel:14; + uint32_t /* reserved */ : 2; + /* Window Height Config */ + uint32_t windowHeightCfglastLine:14; + uint32_t /* reserved */ : 2; + uint32_t windowHeightCfgfirstLine:14; + uint32_t /* reserved */ : 2; + /* Subsample 1 Config */ + uint32_t subsample1CfgPixelSkip:16; + uint32_t subsample1CfgLineSkip:16; + /* Subsample 2 Config */ + uint32_t subsample2CfgFrameSkip:4; + uint32_t subsample2CfgFrameSkipMode:1; + uint32_t subsample2CfgPixelSkipWrap:1; + uint32_t /* reserved */ : 26; + /* Epoch Interrupt */ + uint32_t epoch1Line:14; + uint32_t /* reserved */ : 2; + uint32_t epoch2Line:14; + uint32_t /* reserved */ : 2; +} __attribute__((packed, aligned(4))); + +struct vfe_camifframe_update { + uint32_t pixelsPerLine:14; + uint32_t /* reserved */ : 2; + uint32_t linesPerFrame:14; + uint32_t /* reserved */ : 2; +} __attribute__((packed, aligned(4))); + +struct vfe_axi_bus_cfg { + uint32_t stripeRdPathEn:1; + uint32_t /* reserved */ : 3; + uint32_t encYWrPathEn:1; + uint32_t encCbcrWrPathEn:1; + uint32_t viewYWrPathEn:1; + uint32_t viewCbcrWrPathEn:1; + uint32_t rawPixelDataSize:2; + uint32_t rawWritePathSelect:2; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_axi_out_cfg { + uint32_t out2YPingAddr:32; + uint32_t out2YPongAddr:32; + uint32_t out2YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2YNumRows:12; + uint32_t out2YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrPingAddr:32; + uint32_t out2CbcrPongAddr:32; + uint32_t out2CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2CbcrNumRows:12; + uint32_t out2CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1YPingAddr:32; + uint32_t out1YPongAddr:32; + uint32_t out1YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1YNumRows:12; + uint32_t out1YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrPingAddr:32; + uint32_t out1CbcrPongAddr:32; + uint32_t out1CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1CbcrNumRows:12; + uint32_t out1CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_output_clamp_cfg { + /* Output Clamp Maximums */ + uint32_t yChanMax:8; + uint32_t cbChanMax:8; + uint32_t crChanMax:8; + uint32_t /* reserved */ : 8; + /* Output Clamp Minimums */ + uint32_t yChanMin:8; + uint32_t cbChanMin:8; + uint32_t crChanMin:8; + uint32_t /* reserved */ : 8; +} __attribute__((packed, aligned(4))); + +struct vfe_fov_crop_cfg { + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t firstPixel:12; + uint32_t /* reserved */ : 4; + + /* FOV Corp, Part 2 */ + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; + uint32_t firstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_FRAME_SKIP_UpdateCmdType { + uint32_t yPattern:32; + uint32_t cbcrPattern:32; +} __attribute__((packed, aligned(4))); + +struct vfe_frame_skip_cfg { + /* Frame Drop Enc (output2) */ + uint32_t output2YPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output2CbCrPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output2YPattern:32; + uint32_t output2CbCrPattern:32; + /* Frame Drop View (output1) */ + uint32_t output1YPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output1CbCrPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output1YPattern:32; + uint32_t output1CbCrPattern:32; +} __attribute__((packed, aligned(4))); + +struct vfe_main_scaler_cfg { + /* Scaler Enable Config */ + uint32_t hEnable:1; + uint32_t vEnable:1; + uint32_t /* reserved */ : 30; + /* Scale H Image Size Config */ + uint32_t inWidth:12; + uint32_t /* reserved */ : 4; + uint32_t outWidth:12; + uint32_t /* reserved */ : 4; + /* Scale H Phase Config */ + uint32_t horizPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t horizInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scale H Stripe Config */ + uint32_t horizMNInit:12; + uint32_t /* reserved */ : 4; + uint32_t horizPhaseInit:15; + uint32_t /* reserved */ : 1; + /* Scale V Image Size Config */ + uint32_t inHeight:12; + uint32_t /* reserved */ : 4; + uint32_t outHeight:12; + uint32_t /* reserved */ : 4; + /* Scale V Phase Config */ + uint32_t vertPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t vertInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scale V Stripe Config */ + uint32_t vertMNInit:12; + uint32_t /* reserved */ : 4; + uint32_t vertPhaseInit:15; + uint32_t /* reserved */ : 1; +} __attribute__((packed, aligned(4))); + +struct vfe_scaler2_cfg { + /* Scaler Enable Config */ + uint32_t hEnable:1; + uint32_t vEnable:1; + uint32_t /* reserved */ : 30; + /* Scaler H Image Size Config */ + uint32_t inWidth:12; + uint32_t /* reserved */ : 4; + uint32_t outWidth:12; + uint32_t /* reserved */ : 4; + /* Scaler H Phase Config */ + uint32_t horizPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t horizInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scaler V Image Size Config */ + uint32_t inHeight:12; + uint32_t /* reserved */ : 4; + uint32_t outHeight:12; + uint32_t /* reserved */ : 4; + /* Scaler V Phase Config */ + uint32_t vertPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t vertInterResolution:2; + uint32_t /* reserved */ : 10; +} __attribute__((packed, aligned(4))); + +struct vfe_rolloff_cfg { + /* Rolloff 0 Config */ + uint32_t gridWidth:9; + uint32_t gridHeight:9; + uint32_t yDelta:9; + uint32_t /* reserved */ : 5; + /* Rolloff 1 Config*/ + uint32_t gridX:4; + uint32_t gridY:4; + uint32_t pixelX:9; + uint32_t /* reserved */ : 3; + uint32_t pixelY:9; + uint32_t /* reserved */ : 3; + /* Rolloff 2 Config */ + uint32_t yDeltaAccum:12; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_asf_update { + /* ASF Config Command */ + uint32_t smoothEnable:1; + uint32_t sharpMode:2; + uint32_t /* reserved */ : 1; + uint32_t smoothCoeff1:4; + uint32_t smoothCoeff0:8; + uint32_t pipeFlushCount:12; + uint32_t pipeFlushOvd:1; + uint32_t flushHaltOvd:1; + uint32_t cropEnable:1; + uint32_t /* reserved */ : 1; + /* Sharpening Config 0 */ + uint32_t sharpThresholdE1:7; + uint32_t /* reserved */ : 1; + uint32_t sharpDegreeK1:5; + uint32_t /* reserved */ : 3; + uint32_t sharpDegreeK2:5; + uint32_t /* reserved */ : 3; + uint32_t normalizeFactor:7; + uint32_t /* reserved */ : 1; + /* Sharpening Config 1 */ + uint32_t sharpThresholdE2:8; + uint32_t sharpThresholdE3:8; + uint32_t sharpThresholdE4:8; + uint32_t sharpThresholdE5:8; + /* Sharpening Coefficients 0 */ + uint32_t F1Coeff0:6; + uint32_t F1Coeff1:6; + uint32_t F1Coeff2:6; + uint32_t F1Coeff3:6; + uint32_t F1Coeff4:6; + uint32_t /* reserved */ : 2; + /* Sharpening Coefficients 1 */ + uint32_t F1Coeff5:6; + uint32_t F1Coeff6:6; + uint32_t F1Coeff7:6; + uint32_t F1Coeff8:7; + uint32_t /* reserved */ : 7; + /* Sharpening Coefficients 2 */ + uint32_t F2Coeff0:6; + uint32_t F2Coeff1:6; + uint32_t F2Coeff2:6; + uint32_t F2Coeff3:6; + uint32_t F2Coeff4:6; + uint32_t /* reserved */ : 2; + /* Sharpening Coefficients 3 */ + uint32_t F2Coeff5:6; + uint32_t F2Coeff6:6; + uint32_t F2Coeff7:6; + uint32_t F2Coeff8:7; + uint32_t /* reserved */ : 7; +} __attribute__((packed, aligned(4))); + +struct vfe_asfcrop_cfg { + /* ASF Crop Width Config */ + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t firstPixel:12; + uint32_t /* reserved */ : 4; + /* ASP Crop Height Config */ + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; + uint32_t firstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_chroma_suppress_cfg { + /* Chroma Suppress 0 Config */ + uint32_t m1:8; + uint32_t m3:8; + uint32_t n1:3; + uint32_t /* reserved */ : 1; + uint32_t n3:3; + uint32_t /* reserved */ : 9; + /* Chroma Suppress 1 Config */ + uint32_t mm1:8; + uint32_t nn1:3; + uint32_t /* reserved */ : 21; +} __attribute__((packed, aligned(4))); + +struct vfe_chromasubsample_cfg { + /* Chroma Subsample Selection */ + uint32_t hCositedPhase:1; + uint32_t vCositedPhase:1; + uint32_t hCosited:1; + uint32_t vCosited:1; + uint32_t hsubSampleEnable:1; + uint32_t vsubSampleEnable:1; + uint32_t cropEnable:1; + uint32_t /* reserved */ : 25; + uint32_t cropWidthLastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropWidthFirstPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightLastLine:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightFirstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_blacklevel_cfg { + /* Black Even-Even Value Config */ + uint32_t evenEvenAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Even-Odd Value Config */ + uint32_t evenOddAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Odd-Even Value Config */ + uint32_t oddEvenAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Odd-Odd Value Config */ + uint32_t oddOddAdjustment:9; + uint32_t /* reserved */ : 23; +} __attribute__((packed, aligned(4))); + +struct vfe_demux_cfg { + /* Demux Gain 0 Config */ + uint32_t ch0EvenGain:10; + uint32_t /* reserved */ : 6; + uint32_t ch0OddGain:10; + uint32_t /* reserved */ : 6; + /* Demux Gain 1 Config */ + uint32_t ch1Gain:10; + uint32_t /* reserved */ : 6; + uint32_t ch2Gain:10; + uint32_t /* reserved */ : 6; +} __attribute__((packed, aligned(4))); + +struct vfe_bps_info { + uint32_t greenBadPixelCount:8; + uint32_t /* reserved */ : 8; + uint32_t RedBlueBadPixelCount:8; + uint32_t /* reserved */ : 8; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_cfg { + /* Demosaic Config */ + uint32_t abfEnable:1; + uint32_t badPixelCorrEnable:1; + uint32_t forceAbfOn:1; + uint32_t /* reserved */ : 1; + uint32_t abfShift:4; + uint32_t fminThreshold:7; + uint32_t /* reserved */ : 1; + uint32_t fmaxThreshold:7; + uint32_t /* reserved */ : 5; + uint32_t slopeShift:3; + uint32_t /* reserved */ : 1; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_bpc_cfg { + /* Demosaic BPC Config 0 */ + uint32_t blueDiffThreshold:12; + uint32_t redDiffThreshold:12; + uint32_t /* reserved */ : 8; + /* Demosaic BPC Config 1 */ + uint32_t greenDiffThreshold:12; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_abf_cfg { + /* Demosaic ABF Config 0 */ + uint32_t lpThreshold:10; + uint32_t /* reserved */ : 22; + /* Demosaic ABF Config 1 */ + uint32_t ratio:4; + uint32_t minValue:10; + uint32_t /* reserved */ : 2; + uint32_t maxValue:10; + uint32_t /* reserved */ : 6; +} __attribute__((packed, aligned(4))); + +struct vfe_color_correction_cfg { + /* Color Corr. Coefficient 0 Config */ + uint32_t c0:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 1 Config */ + uint32_t c1:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 2 Config */ + uint32_t c2:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 3 Config */ + uint32_t c3:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 4 Config */ + uint32_t c4:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 5 Config */ + uint32_t c5:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 6 Config */ + uint32_t c6:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 7 Config */ + uint32_t c7:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 8 Config */ + uint32_t c8:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Offset 0 Config */ + uint32_t k0:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Offset 1 Config */ + uint32_t k1:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Offset 2 Config */ + uint32_t k2:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Coefficient Q Config */ + uint32_t coefQFactor:2; + uint32_t /* reserved */ : 30; +} __attribute__((packed, aligned(4))); + +struct VFE_LumaAdaptation_ConfigCmdType { + /* LA Config */ + uint32_t lutBankSelect:1; + uint32_t /* reserved */ : 31; +} __attribute__((packed, aligned(4))); + +struct vfe_wb_cfg { + /* WB Config */ + uint32_t ch0Gain:9; + uint32_t ch1Gain:9; + uint32_t ch2Gain:9; + uint32_t /* reserved */ : 5; +} __attribute__((packed, aligned(4))); + +struct VFE_GammaLutSelect_ConfigCmdType { + /* LUT Bank Select Config */ + uint32_t ch0BankSelect:1; + uint32_t ch1BankSelect:1; + uint32_t ch2BankSelect:1; + uint32_t /* reserved */ : 29; +} __attribute__((packed, aligned(4))); + +struct vfe_chroma_enhance_cfg { + /* Chroma Enhance A Config */ + uint32_t ap:11; + uint32_t /* reserved */ : 5; + uint32_t am:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance B Config */ + uint32_t bp:11; + uint32_t /* reserved */ : 5; + uint32_t bm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance C Config */ + uint32_t cp:11; + uint32_t /* reserved */ : 5; + uint32_t cm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance D Config */ + uint32_t dp:11; + uint32_t /* reserved */ : 5; + uint32_t dm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance K Config */ + uint32_t kcb:11; + uint32_t /* reserved */ : 5; + uint32_t kcr:11; + uint32_t /* reserved */ : 5; +} __attribute__((packed, aligned(4))); + +struct vfe_color_convert_cfg { + /* Conversion Coefficient 0 */ + uint32_t v0:12; + uint32_t /* reserved */ : 20; + /* Conversion Coefficient 1 */ + uint32_t v1:12; + uint32_t /* reserved */ : 20; + /* Conversion Coefficient 2 */ + uint32_t v2:12; + uint32_t /* reserved */ : 20; + /* Conversion Offset */ + uint32_t ConvertOffset:8; + uint32_t /* reserved */ : 24; +} __attribute__((packed, aligned(4))); + +struct VFE_SyncTimer_ConfigCmdType { + /* Timer Line Start Config */ + uint32_t timerLineStart:12; + uint32_t /* reserved */ : 20; + /* Timer Pixel Start Config */ + uint32_t timerPixelStart:18; + uint32_t /* reserved */ : 14; + /* Timer Pixel Duration Config */ + uint32_t timerPixelDuration:28; + uint32_t /* reserved */ : 4; + /* Sync Timer Polarity Config */ + uint32_t timer0Polarity:1; + uint32_t timer1Polarity:1; + uint32_t timer2Polarity:1; + uint32_t /* reserved */ : 29; +} __attribute__((packed, aligned(4))); + +struct VFE_AsyncTimer_ConfigCmdType { + /* Async Timer Config 0 */ + uint32_t inactiveLength:20; + uint32_t numRepetition:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; + /* Async Timer Config 1 */ + uint32_t activeLength:20; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AWBAEStatistics_ConfigCmdType { + /* AWB autoexposure Config */ + uint32_t aeRegionConfig:1; + uint32_t aeSubregionConfig:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; + /* AXW Header */ + uint32_t axwHeader:8; + uint32_t /* reserved */ : 24; + /* AWB Mconfig */ + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; + /* AWB Cconfig */ + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; + /* AWB Cconfig 2 */ + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_TestGen_ConfigCmdType { + /* HW Test Gen Config */ + uint32_t numFrame:10; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSelect:1; + uint32_t systematicDataSelect:1; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSize:2; + uint32_t hsyncEdge:1; + uint32_t vsyncEdge:1; + uint32_t /* reserved */ : 12; + /* HW Test Gen Image Config */ + uint32_t imageWidth:14; + uint32_t /* reserved */ : 2; + uint32_t imageHeight:14; + uint32_t /* reserved */ : 2; + /* SOF Offset Config */ + uint32_t sofOffset:24; + uint32_t /* reserved */ : 8; + /* EOF NOffset Config */ + uint32_t eofNOffset:24; + uint32_t /* reserved */ : 8; + /* SOL Offset Config */ + uint32_t solOffset:9; + uint32_t /* reserved */ : 23; + /* EOL NOffset Config */ + uint32_t eolNOffset:9; + uint32_t /* reserved */ : 23; + /* HBI Config */ + uint32_t hBlankInterval:14; + uint32_t /* reserved */ : 18; + /* VBL Config */ + uint32_t vBlankInterval:14; + uint32_t /* reserved */ : 2; + uint32_t vBlankIntervalEnable:1; + uint32_t /* reserved */ : 15; + /* SOF Dummy Line Config */ + uint32_t sofDummy:8; + uint32_t /* reserved */ : 24; + /* EOF Dummy Line Config */ + uint32_t eofDummy:8; + uint32_t /* reserved */ : 24; + /* Color Bars Config */ + uint32_t unicolorBarSelect:3; + uint32_t /* reserved */ : 1; + uint32_t unicolorBarEnable:1; + uint32_t splitEnable:1; + uint32_t pixelPattern:2; + uint32_t rotatePeriod:6; + uint32_t /* reserved */ : 18; + /* Random Config */ + uint32_t randomSeed:16; + uint32_t /* reserved */ : 16; +} __attribute__((packed, aligned(4))); + +struct VFE_Bus_Pm_ConfigCmdType { + /* VFE Bus Performance Monitor Config */ + uint32_t output2YWrPmEnable:1; + uint32_t output2CbcrWrPmEnable:1; + uint32_t output1YWrPmEnable:1; + uint32_t output1CbcrWrPmEnable:1; + uint32_t /* reserved */ : 28; +} __attribute__((packed, aligned(4))); + +struct vfe_asf_info { + /* asf max edge */ + uint32_t maxEdge:13; + uint32_t /* reserved */ : 3; + /* HBi count */ + uint32_t HBICount:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_camif_stats { + uint32_t pixelCount:14; + uint32_t /* reserved */ : 2; + uint32_t lineCount:14; + uint32_t /* reserved */ : 1; + uint32_t camifHalt:1; +} __attribute__((packed, aligned(4))); + +struct VFE_StatsCmdType { + uint32_t autoFocusEnable:1; + uint32_t axwEnable:1; + uint32_t histEnable:1; + uint32_t clearHistEnable:1; + uint32_t histAutoClearEnable:1; + uint32_t colorConversionEnable:1; + uint32_t /* reserved */ : 26; +} __attribute__((packed, aligned(4))); + + +struct vfe_statsframe { + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_busstats_wrprio { + uint32_t afBusPriority:4; + uint32_t awbBusPriority:4; + uint32_t histBusPriority:4; + uint32_t afBusPriorityEn:1; + uint32_t awbBusPriorityEn:1; + uint32_t histBusPriorityEn:1; + uint32_t /* reserved */ : 17; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaf_update { + /* VFE_STATS_AF_CFG */ + uint32_t windowVOffset:12; + uint32_t /* reserved */ : 4; + uint32_t windowHOffset:12; + uint32_t /* reserved */ : 3; + uint32_t windowMode:1; + + /* VFE_STATS_AF_DIM */ + uint32_t windowHeight:12; + uint32_t /* reserved */ : 4; + uint32_t windowWidth:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaf_cfg { + /* VFE_STATS_AF_GRID_0 */ + uint32_t entry00:8; + uint32_t entry01:8; + uint32_t entry02:8; + uint32_t entry03:8; + + /* VFE_STATS_AF_GRID_1 */ + uint32_t entry10:8; + uint32_t entry11:8; + uint32_t entry12:8; + uint32_t entry13:8; + + /* VFE_STATS_AF_GRID_2 */ + uint32_t entry20:8; + uint32_t entry21:8; + uint32_t entry22:8; + uint32_t entry23:8; + + /* VFE_STATS_AF_GRID_3 */ + uint32_t entry30:8; + uint32_t entry31:8; + uint32_t entry32:8; + uint32_t entry33:8; + + /* VFE_STATS_AF_HEADER */ + uint32_t afHeader:8; + uint32_t /* reserved */ : 24; + /* VFE_STATS_AF_COEF0 */ + uint32_t a00:5; + uint32_t a04:5; + uint32_t fvMax:11; + uint32_t fvMetric:1; + uint32_t /* reserved */ : 10; + + /* VFE_STATS_AF_COEF1 */ + uint32_t a20:5; + uint32_t a21:5; + uint32_t a22:5; + uint32_t a23:5; + uint32_t a24:5; + uint32_t /* reserved */ : 7; +} __attribute__((packed, aligned(4))); + +struct vfe_statsawbae_update { + uint32_t aeRegionCfg:1; + uint32_t aeSubregionCfg:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaxw_hdr_cfg { + /* Stats AXW Header Config */ + uint32_t axwHeader:8; + uint32_t /* reserved */ : 24; +} __attribute__((packed, aligned(4))); + +struct vfe_statsawb_update { + /* AWB MConfig */ + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; + + /* AWB CConfig1 */ + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; + + /* AWB CConfig2 */ + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_SyncTimerCmdType { + uint32_t hsyncCount:12; + uint32_t /* reserved */ : 20; + uint32_t pclkCount:18; + uint32_t /* reserved */ : 14; + uint32_t outputDuration:28; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_AsyncTimerCmdType { + /* config 0 */ + uint32_t inactiveCount:20; + uint32_t repeatCount:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; + /* config 1 */ + uint32_t activeCount:20; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AxiInputCmdType { + uint32_t stripeStartAddr0:32; + uint32_t stripeStartAddr1:32; + uint32_t stripeStartAddr2:32; + uint32_t stripeStartAddr3:32; + + uint32_t ySize:12; + uint32_t yOffsetDelta:12; + uint32_t /* reserved */ : 8; + + /* bus_stripe_rd_hSize */ + uint32_t /* reserved */ : 16; + uint32_t xSizeWord:10; + uint32_t /* reserved */ : 6; + + /* bus_stripe_rd_buffer_cfg */ + uint32_t burstLength:2; + uint32_t /* reserved */ : 2; + uint32_t NumOfRows:12; + uint32_t RowIncrement:12; + uint32_t /* reserved */ : 4; + + /* bus_stripe_rd_unpack_cfg */ + uint32_t mainUnpackHeight:12; + uint32_t mainUnpackWidth:13; + uint32_t mainUnpackHbiSel:3; + uint32_t mainUnpackPhase:3; + uint32_t /* reserved */ : 1; + + /* bus_stripe_rd_unpack */ + uint32_t unpackPattern:32; + + /* bus_stripe_rd_pad_size */ + uint32_t padLeft:7; + uint32_t /* reserved */ : 1; + uint32_t padRight:7; + uint32_t /* reserved */ : 1; + uint32_t padTop:7; + uint32_t /* reserved */ : 1; + uint32_t padBottom:7; + uint32_t /* reserved */ : 1; + + /* bus_stripe_rd_pad_L_unpack */ + uint32_t leftUnpackPattern0:4; + uint32_t leftUnpackPattern1:4; + uint32_t leftUnpackPattern2:4; + uint32_t leftUnpackPattern3:4; + uint32_t leftUnpackStop0:1; + uint32_t leftUnpackStop1:1; + uint32_t leftUnpackStop2:1; + uint32_t leftUnpackStop3:1; + uint32_t /* reserved */ : 12; + + /* bus_stripe_rd_pad_R_unpack */ + uint32_t rightUnpackPattern0:4; + uint32_t rightUnpackPattern1:4; + uint32_t rightUnpackPattern2:4; + uint32_t rightUnpackPattern3:4; + uint32_t rightUnpackStop0:1; + uint32_t rightUnpackStop1:1; + uint32_t rightUnpackStop2:1; + uint32_t rightUnpackStop3:1; + uint32_t /* reserved */ : 12; + + /* bus_stripe_rd_pad_tb_unpack */ + uint32_t topUnapckPattern:4; + uint32_t /* reserved */ : 12; + uint32_t bottomUnapckPattern:4; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AxiRdFragIrqEnable { + uint32_t stripeRdFragirq0Enable:1; + uint32_t stripeRdFragirq1Enable:1; + uint32_t stripeRdFragirq2Enable:1; + uint32_t stripeRdFragirq3Enable:1; + uint32_t /* reserved */ : 28; +} __attribute__((packed, aligned(4))); + +int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *); +void vfe_stats_af_stop(void); +void vfe_stop(void); +void vfe_update(void); +int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *); +int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *); +void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *); +void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *); +void vfe_start(struct vfe_cmd_start *); +void vfe_la_update(struct vfe_cmd_la_config *); +void vfe_la_config(struct vfe_cmd_la_config *); +void vfe_test_gen_start(struct vfe_cmd_test_gen_start *); +void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *); +void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *); +void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *); +void vfe_camif_frame_update(struct vfe_cmds_camif_frame *); +void vfe_color_correction_config(struct vfe_cmd_color_correction_config *); +void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *); +void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *); +void vfe_demosaic_config(struct vfe_cmd_demosaic_config *); +void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *); +void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *); +void vfe_black_level_update(struct vfe_cmd_black_level_config *); +void vfe_black_level_config(struct vfe_cmd_black_level_config *); +void vfe_asf_update(struct vfe_cmd_asf_update *); +void vfe_asf_config(struct vfe_cmd_asf_config *); +void vfe_white_balance_config(struct vfe_cmd_white_balance_config *); +void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *); +void vfe_roll_off_config(struct vfe_cmd_roll_off_config *); +void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *); +void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *); +void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *); +void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *); +void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *); +void vfe_stats_wb_exp_stop(void); +void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *); +void vfe_stats_update_af(struct vfe_cmd_stats_af_update *); +void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *); +void vfe_stats_start_af(struct vfe_cmd_stats_af_start *); +void vfe_stats_setting(struct vfe_cmd_stats_setting *); +void vfe_axi_input_config(struct vfe_cmd_axi_input_config *); +void vfe_stats_config(struct vfe_cmd_stats_setting *); +void vfe_axi_output_config(struct vfe_cmd_axi_output_config *); +void vfe_camif_config(struct vfe_cmd_camif_config *); +void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *); +void vfe_get_hw_version(struct vfe_cmd_hw_version *); +void vfe_reset(void); +void vfe_cmd_release(struct platform_device *); +void vfe_output1_ack(struct vfe_cmd_output_ack *); +void vfe_output2_ack(struct vfe_cmd_output_ack *); +#endif /* __MSM_VFE8X_REG_H__ */ diff --git a/drivers/staging/dream/camera/mt9d112.c b/drivers/staging/dream/camera/mt9d112.c new file mode 100644 index 0000000..4f938f9 --- /dev/null +++ b/drivers/staging/dream/camera/mt9d112.c @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <media/msm_camera.h> +#include <mach/gpio.h> +#include "mt9d112.h" + +/* Micron MT9D112 Registers and their values */ +/* Sensor Core Registers */ +#define REG_MT9D112_MODEL_ID 0x3000 +#define MT9D112_MODEL_ID 0x1580 + +/* SOC Registers Page 1 */ +#define REG_MT9D112_SENSOR_RESET 0x301A +#define REG_MT9D112_STANDBY_CONTROL 0x3202 +#define REG_MT9D112_MCU_BOOT 0x3386 + +struct mt9d112_work { + struct work_struct work; +}; + +static struct mt9d112_work *mt9d112_sensorw; +static struct i2c_client *mt9d112_client; + +struct mt9d112_ctrl { + const struct msm_camera_sensor_info *sensordata; +}; + + +static struct mt9d112_ctrl *mt9d112_ctrl; + +static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue); +DECLARE_MUTEX(mt9d112_sem); + + +/*============================================================= + EXTERNAL DECLARATIONS +==============================================================*/ +extern struct mt9d112_reg mt9d112_regs; + + +/*=============================================================*/ + +static int mt9d112_reset(const struct msm_camera_sensor_info *dev) +{ + int rc = 0; + + rc = gpio_request(dev->sensor_reset, "mt9d112"); + + if (!rc) { + rc = gpio_direction_output(dev->sensor_reset, 0); + mdelay(20); + rc = gpio_direction_output(dev->sensor_reset, 1); + } + + gpio_free(dev->sensor_reset); + return rc; +} + +static int32_t mt9d112_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) { + CDBG("mt9d112_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9d112_i2c_write(unsigned short saddr, + unsigned short waddr, unsigned short wdata, enum mt9d112_width width) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + switch (width) { + case WORD_LEN: { + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9d112_i2c_txdata(saddr, buf, 4); + } + break; + + case BYTE_LEN: { + buf[0] = waddr; + buf[1] = wdata; + rc = mt9d112_i2c_txdata(saddr, buf, 2); + } + break; + + default: + break; + } + + if (rc < 0) + CDBG( + "i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9d112_i2c_write_table( + struct mt9d112_i2c_reg_conf const *reg_conf_tbl, + int num_of_items_in_table) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata, + reg_conf_tbl->width); + if (rc < 0) + break; + if (reg_conf_tbl->mdelay_time != 0) + mdelay(reg_conf_tbl->mdelay_time); + reg_conf_tbl++; + } + + return rc; +} + +static int mt9d112_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) { + CDBG("mt9d112_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9d112_i2c_read(unsigned short saddr, + unsigned short raddr, unsigned short *rdata, enum mt9d112_width width) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + switch (width) { + case WORD_LEN: { + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9d112_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + } + break; + + default: + break; + } + + if (rc < 0) + CDBG("mt9d112_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9d112_set_lens_roll_off(void) +{ + int32_t rc = 0; + rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0], + mt9d112_regs.rftbl_size); + return rc; +} + +static long mt9d112_reg_init(void) +{ + int32_t array_length; + int32_t i; + long rc; + + /* PLL Setup Start */ + rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0], + mt9d112_regs.plltbl_size); + + if (rc < 0) + return rc; + /* PLL Setup End */ + + array_length = mt9d112_regs.prev_snap_reg_settings_size; + + /* Configure sensor for Preview mode and Snapshot mode */ + for (i = 0; i < array_length; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + mt9d112_regs.prev_snap_reg_settings[i].register_address, + mt9d112_regs.prev_snap_reg_settings[i].register_value, + WORD_LEN); + + if (rc < 0) + return rc; + } + + /* Configure for Noise Reduction, Saturation and Aperture Correction */ + array_length = mt9d112_regs.noise_reduction_reg_settings_size; + + for (i = 0; i < array_length; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + mt9d112_regs.noise_reduction_reg_settings[i].register_address, + mt9d112_regs.noise_reduction_reg_settings[i].register_value, + WORD_LEN); + + if (rc < 0) + return rc; + } + + /* Set Color Kill Saturation point to optimum value */ + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x35A4, + 0x0593, + WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0], + mt9d112_regs.stbl_size); + if (rc < 0) + return rc; + + rc = mt9d112_set_lens_roll_off(); + if (rc < 0) + return rc; + + return 0; +} + +static long mt9d112_set_sensor_mode(int mode) +{ + uint16_t clock; + long rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20C, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA215, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20B, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0000, WORD_LEN); + if (rc < 0) + return rc; + + clock = 0x0250; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, clock, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0001, WORD_LEN); + if (rc < 0) + return rc; + + mdelay(5); + break; + + case SENSOR_SNAPSHOT_MODE: + /* Switch to lower fps for Snapshot */ + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, 0x0120, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA120, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); + if (rc < 0) + return rc; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static long mt9d112_set_effect(int mode, int effect) +{ + uint16_t reg_addr; + uint16_t reg_val; + long rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + /* Context A Special Effects */ + reg_addr = 0x2799; + break; + + case SENSOR_SNAPSHOT_MODE: + /* Context B Special Effects */ + reg_addr = 0x279B; + break; + + default: + reg_addr = 0x2799; + break; + } + + switch (effect) { + case CAMERA_EFFECT_OFF: { + reg_val = 0x6440; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_MONO: { + reg_val = 0x6441; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_NEGATIVE: { + reg_val = 0x6443; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_SOLARIZE: { + reg_val = 0x6445; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_SEPIA: { + reg_val = 0x6442; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_PASTEL: + case CAMERA_EFFECT_MOSAIC: + case CAMERA_EFFECT_RESIZE: + return -EINVAL; + + default: { + reg_val = 0x6440; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + + return -EINVAL; + } + } + + /* Refresh Sequencer */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0005, WORD_LEN); + + return rc; +} + +static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data) +{ + uint16_t model_id = 0; + int rc = 0; + + CDBG("init entry \n"); + rc = mt9d112_reset(data); + if (rc < 0) { + CDBG("reset failed!\n"); + goto init_probe_fail; + } + + mdelay(5); + + /* Micron suggested Power up block Start: + * Put MCU into Reset - Stop MCU */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + /* Pull MCU from Reset - Start MCU */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + mdelay(5); + + /* Micron Suggested - Power up block */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + /* FUSED_DEFECT_CORRECTION */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x33F4, 0x031D, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + mdelay(5); + + /* Micron suggested Power up block End */ + /* Read the Model ID of the sensor */ + rc = mt9d112_i2c_read(mt9d112_client->addr, + REG_MT9D112_MODEL_ID, &model_id, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + CDBG("mt9d112 model_id = 0x%x\n", model_id); + + /* Check if it matches it with the value in Datasheet */ + if (model_id != MT9D112_MODEL_ID) { + rc = -EINVAL; + goto init_probe_fail; + } + + rc = mt9d112_reg_init(); + if (rc < 0) + goto init_probe_fail; + + return rc; + +init_probe_fail: + return rc; +} + +int mt9d112_sensor_init(const struct msm_camera_sensor_info *data) +{ + int rc = 0; + + mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL); + if (!mt9d112_ctrl) { + CDBG("mt9d112_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + if (data) + mt9d112_ctrl->sensordata = data; + + /* Input MCLK = 24MHz */ + msm_camio_clk_rate_set(24000000); + mdelay(5); + + msm_camio_camif_pad_reg_reset(); + + rc = mt9d112_sensor_init_probe(data); + if (rc < 0) { + CDBG("mt9d112_sensor_init failed!\n"); + goto init_fail; + } + +init_done: + return rc; + +init_fail: + kfree(mt9d112_ctrl); + return rc; +} + +static int mt9d112_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9d112_wait_queue); + return 0; +} + +int mt9d112_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cfg_data; + long rc = 0; + + if (copy_from_user(&cfg_data, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + /* down(&mt9d112_sem); */ + + CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n", + cfg_data.cfgtype, cfg_data.mode); + + switch (cfg_data.cfgtype) { + case CFG_SET_MODE: + rc = mt9d112_set_sensor_mode( + cfg_data.mode); + break; + + case CFG_SET_EFFECT: + rc = mt9d112_set_effect(cfg_data.mode, + cfg_data.cfg.effect); + break; + + case CFG_GET_AF_MAX_STEPS: + default: + rc = -EINVAL; + break; + } + + /* up(&mt9d112_sem); */ + + return rc; +} + +int mt9d112_sensor_release(void) +{ + int rc = 0; + + /* down(&mt9d112_sem); */ + + kfree(mt9d112_ctrl); + /* up(&mt9d112_sem); */ + + return rc; +} + +static int mt9d112_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + mt9d112_sensorw = + kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL); + + if (!mt9d112_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9d112_sensorw); + mt9d112_init_client(client); + mt9d112_client = client; + + CDBG("mt9d112_probe succeeded!\n"); + + return 0; + +probe_failure: + kfree(mt9d112_sensorw); + mt9d112_sensorw = NULL; + CDBG("mt9d112_probe failed!\n"); + return rc; +} + +static const struct i2c_device_id mt9d112_i2c_id[] = { + { "mt9d112", 0}, + { }, +}; + +static struct i2c_driver mt9d112_i2c_driver = { + .id_table = mt9d112_i2c_id, + .probe = mt9d112_i2c_probe, + .remove = __exit_p(mt9d112_i2c_remove), + .driver = { + .name = "mt9d112", + }, +}; + +static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&mt9d112_i2c_driver); + if (rc < 0 || mt9d112_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + /* Input MCLK = 24MHz */ + msm_camio_clk_rate_set(24000000); + mdelay(5); + + rc = mt9d112_sensor_init_probe(info); + if (rc < 0) + goto probe_done; + + s->s_init = mt9d112_sensor_init; + s->s_release = mt9d112_sensor_release; + s->s_config = mt9d112_sensor_config; + +probe_done: + CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +} + +static int __mt9d112_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9d112_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9d112_probe, + .driver = { + .name = "msm_camera_mt9d112", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9d112_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9d112_init); diff --git a/drivers/staging/dream/camera/mt9d112.h b/drivers/staging/dream/camera/mt9d112.h new file mode 100644 index 0000000..c678996 --- /dev/null +++ b/drivers/staging/dream/camera/mt9d112.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef MT9D112_H +#define MT9D112_H + +#include <linux/types.h> +#include <mach/camera.h> + +enum mt9d112_width { + WORD_LEN, + BYTE_LEN +}; + +struct mt9d112_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; + enum mt9d112_width width; + unsigned short mdelay_time; +}; + +struct mt9d112_reg { + const struct register_address_value_pair *prev_snap_reg_settings; + uint16_t prev_snap_reg_settings_size; + const struct register_address_value_pair *noise_reduction_reg_settings; + uint16_t noise_reduction_reg_settings_size; + const struct mt9d112_i2c_reg_conf *plltbl; + uint16_t plltbl_size; + const struct mt9d112_i2c_reg_conf *stbl; + uint16_t stbl_size; + const struct mt9d112_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* MT9D112_H */ diff --git a/drivers/staging/dream/camera/mt9d112_reg.c b/drivers/staging/dream/camera/mt9d112_reg.c new file mode 100644 index 0000000..c52e96f --- /dev/null +++ b/drivers/staging/dream/camera/mt9d112_reg.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include "mt9d112.h" + +struct register_address_value_pair +preview_snapshot_mode_reg_settings_array[] = { + {0x338C, 0x2703}, + {0x3390, 800}, /* Output Width (P) = 640 */ + {0x338C, 0x2705}, + {0x3390, 600}, /* Output Height (P) = 480 */ + {0x338C, 0x2707}, + {0x3390, 0x0640}, /* Output Width (S) = 1600 */ + {0x338C, 0x2709}, + {0x3390, 0x04B0}, /* Output Height (S) = 1200 */ + {0x338C, 0x270D}, + {0x3390, 0x0000}, /* Row Start (P) = 0 */ + {0x338C, 0x270F}, + {0x3390, 0x0000}, /* Column Start (P) = 0 */ + {0x338C, 0x2711}, + {0x3390, 0x04BD}, /* Row End (P) = 1213 */ + {0x338C, 0x2713}, + {0x3390, 0x064D}, /* Column End (P) = 1613 */ + {0x338C, 0x2715}, + {0x3390, 0x0000}, /* Extra Delay (P) = 0 */ + {0x338C, 0x2717}, + {0x3390, 0x2111}, /* Row Speed (P) = 8465 */ + {0x338C, 0x2719}, + {0x3390, 0x046C}, /* Read Mode (P) = 1132 */ + {0x338C, 0x271B}, + {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */ + {0x338C, 0x271D}, + {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */ + {0x338C, 0x271F}, + {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */ + {0x338C, 0x2721}, + {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */ + {0x338C, 0x2723}, + {0x3390, 659}, /* Frame Lines (P) = 679 */ + {0x338C, 0x2725}, + {0x3390, 0x0824}, /* Line Length (P) = 2084 */ + {0x338C, 0x2727}, + {0x3390, 0x2020}, + {0x338C, 0x2729}, + {0x3390, 0x2020}, + {0x338C, 0x272B}, + {0x3390, 0x1020}, + {0x338C, 0x272D}, + {0x3390, 0x2007}, + {0x338C, 0x272F}, + {0x3390, 0x0004}, /* Row Start(S) = 4 */ + {0x338C, 0x2731}, + {0x3390, 0x0004}, /* Column Start(S) = 4 */ + {0x338C, 0x2733}, + {0x3390, 0x04BB}, /* Row End(S) = 1211 */ + {0x338C, 0x2735}, + {0x3390, 0x064B}, /* Column End(S) = 1611 */ + {0x338C, 0x2737}, + {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */ + {0x338C, 0x2739}, + {0x3390, 0x2111}, /* Row Speed(S) = 8465 */ + {0x338C, 0x273B}, + {0x3390, 0x0024}, /* Read Mode(S) = 36 */ + {0x338C, 0x273D}, + {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */ + {0x338C, 0x2741}, + {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */ + {0x338C, 0x2745}, + {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */ + {0x338C, 0x2747}, + {0x3390, 0x0824}, /* Line Length(S) = 2084 */ + {0x338C, 0x2751}, + {0x3390, 0x0000}, /* Crop_X0(P) = 0 */ + {0x338C, 0x2753}, + {0x3390, 0x0320}, /* Crop_X1(P) = 800 */ + {0x338C, 0x2755}, + {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */ + {0x338C, 0x2757}, + {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */ + {0x338C, 0x275F}, + {0x3390, 0x0000}, /* Crop_X0(S) = 0 */ + {0x338C, 0x2761}, + {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */ + {0x338C, 0x2763}, + {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */ + {0x338C, 0x2765}, + {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */ + {0x338C, 0x222E}, + {0x3390, 0x00A0}, /* R9 Step = 160 */ + {0x338C, 0xA408}, + {0x3390, 0x001F}, + {0x338C, 0xA409}, + {0x3390, 0x0021}, + {0x338C, 0xA40A}, + {0x3390, 0x0025}, + {0x338C, 0xA40B}, + {0x3390, 0x0027}, + {0x338C, 0x2411}, + {0x3390, 0x00A0}, + {0x338C, 0x2413}, + {0x3390, 0x00C0}, + {0x338C, 0x2415}, + {0x3390, 0x00A0}, + {0x338C, 0x2417}, + {0x3390, 0x00C0}, + {0x338C, 0x2799}, + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */ + {0x338C, 0x279B}, + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */ +}; + +static struct register_address_value_pair +noise_reduction_reg_settings_array[] = { + {0x338C, 0xA76D}, + {0x3390, 0x0003}, + {0x338C, 0xA76E}, + {0x3390, 0x0003}, + {0x338C, 0xA76F}, + {0x3390, 0}, + {0x338C, 0xA770}, + {0x3390, 21}, + {0x338C, 0xA771}, + {0x3390, 37}, + {0x338C, 0xA772}, + {0x3390, 63}, + {0x338C, 0xA773}, + {0x3390, 100}, + {0x338C, 0xA774}, + {0x3390, 128}, + {0x338C, 0xA775}, + {0x3390, 151}, + {0x338C, 0xA776}, + {0x3390, 169}, + {0x338C, 0xA777}, + {0x3390, 186}, + {0x338C, 0xA778}, + {0x3390, 199}, + {0x338C, 0xA779}, + {0x3390, 210}, + {0x338C, 0xA77A}, + {0x3390, 220}, + {0x338C, 0xA77B}, + {0x3390, 228}, + {0x338C, 0xA77C}, + {0x3390, 234}, + {0x338C, 0xA77D}, + {0x3390, 240}, + {0x338C, 0xA77E}, + {0x3390, 244}, + {0x338C, 0xA77F}, + {0x3390, 248}, + {0x338C, 0xA780}, + {0x3390, 252}, + {0x338C, 0xA781}, + {0x3390, 255}, + {0x338C, 0xA782}, + {0x3390, 0}, + {0x338C, 0xA783}, + {0x3390, 21}, + {0x338C, 0xA784}, + {0x3390, 37}, + {0x338C, 0xA785}, + {0x3390, 63}, + {0x338C, 0xA786}, + {0x3390, 100}, + {0x338C, 0xA787}, + {0x3390, 128}, + {0x338C, 0xA788}, + {0x3390, 151}, + {0x338C, 0xA789}, + {0x3390, 169}, + {0x338C, 0xA78A}, + {0x3390, 186}, + {0x338C, 0xA78B}, + {0x3390, 199}, + {0x338C, 0xA78C}, + {0x3390, 210}, + {0x338C, 0xA78D}, + {0x3390, 220}, + {0x338C, 0xA78E}, + {0x3390, 228}, + {0x338C, 0xA78F}, + {0x3390, 234}, + {0x338C, 0xA790}, + {0x3390, 240}, + {0x338C, 0xA791}, + {0x3390, 244}, + {0x338C, 0xA793}, + {0x3390, 252}, + {0x338C, 0xA794}, + {0x3390, 255}, + {0x338C, 0xA103}, + {0x3390, 6}, +}; + +static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = { + { 0x34CE, 0x81A0, WORD_LEN, 0 }, + { 0x34D0, 0x6331, WORD_LEN, 0 }, + { 0x34D2, 0x3394, WORD_LEN, 0 }, + { 0x34D4, 0x9966, WORD_LEN, 0 }, + { 0x34D6, 0x4B25, WORD_LEN, 0 }, + { 0x34D8, 0x2670, WORD_LEN, 0 }, + { 0x34DA, 0x724C, WORD_LEN, 0 }, + { 0x34DC, 0xFFFD, WORD_LEN, 0 }, + { 0x34DE, 0x00CA, WORD_LEN, 0 }, + { 0x34E6, 0x00AC, WORD_LEN, 0 }, + { 0x34EE, 0x0EE1, WORD_LEN, 0 }, + { 0x34F6, 0x0D87, WORD_LEN, 0 }, + { 0x3500, 0xE1F7, WORD_LEN, 0 }, + { 0x3508, 0x1CF4, WORD_LEN, 0 }, + { 0x3510, 0x1D28, WORD_LEN, 0 }, + { 0x3518, 0x1F26, WORD_LEN, 0 }, + { 0x3520, 0x2220, WORD_LEN, 0 }, + { 0x3528, 0x333D, WORD_LEN, 0 }, + { 0x3530, 0x15D9, WORD_LEN, 0 }, + { 0x3538, 0xCFB8, WORD_LEN, 0 }, + { 0x354C, 0x05FE, WORD_LEN, 0 }, + { 0x3544, 0x05F8, WORD_LEN, 0 }, + { 0x355C, 0x0596, WORD_LEN, 0 }, + { 0x3554, 0x0611, WORD_LEN, 0 }, + { 0x34E0, 0x00F2, WORD_LEN, 0 }, + { 0x34E8, 0x00A8, WORD_LEN, 0 }, + { 0x34F0, 0x0F7B, WORD_LEN, 0 }, + { 0x34F8, 0x0CD7, WORD_LEN, 0 }, + { 0x3502, 0xFEDB, WORD_LEN, 0 }, + { 0x350A, 0x13E4, WORD_LEN, 0 }, + { 0x3512, 0x1F2C, WORD_LEN, 0 }, + { 0x351A, 0x1D20, WORD_LEN, 0 }, + { 0x3522, 0x2422, WORD_LEN, 0 }, + { 0x352A, 0x2925, WORD_LEN, 0 }, + { 0x3532, 0x1D04, WORD_LEN, 0 }, + { 0x353A, 0xFBF2, WORD_LEN, 0 }, + { 0x354E, 0x0616, WORD_LEN, 0 }, + { 0x3546, 0x0597, WORD_LEN, 0 }, + { 0x355E, 0x05CD, WORD_LEN, 0 }, + { 0x3556, 0x0529, WORD_LEN, 0 }, + { 0x34E4, 0x00B2, WORD_LEN, 0 }, + { 0x34EC, 0x005E, WORD_LEN, 0 }, + { 0x34F4, 0x0F43, WORD_LEN, 0 }, + { 0x34FC, 0x0E2F, WORD_LEN, 0 }, + { 0x3506, 0xF9FC, WORD_LEN, 0 }, + { 0x350E, 0x0CE4, WORD_LEN, 0 }, + { 0x3516, 0x1E1E, WORD_LEN, 0 }, + { 0x351E, 0x1B19, WORD_LEN, 0 }, + { 0x3526, 0x151B, WORD_LEN, 0 }, + { 0x352E, 0x1416, WORD_LEN, 0 }, + { 0x3536, 0x10FC, WORD_LEN, 0 }, + { 0x353E, 0xC018, WORD_LEN, 0 }, + { 0x3552, 0x06B4, WORD_LEN, 0 }, + { 0x354A, 0x0506, WORD_LEN, 0 }, + { 0x3562, 0x06AB, WORD_LEN, 0 }, + { 0x355A, 0x063A, WORD_LEN, 0 }, + { 0x34E2, 0x00E5, WORD_LEN, 0 }, + { 0x34EA, 0x008B, WORD_LEN, 0 }, + { 0x34F2, 0x0E4C, WORD_LEN, 0 }, + { 0x34FA, 0x0CA3, WORD_LEN, 0 }, + { 0x3504, 0x0907, WORD_LEN, 0 }, + { 0x350C, 0x1DFD, WORD_LEN, 0 }, + { 0x3514, 0x1E24, WORD_LEN, 0 }, + { 0x351C, 0x2529, WORD_LEN, 0 }, + { 0x3524, 0x1D20, WORD_LEN, 0 }, + { 0x352C, 0x2332, WORD_LEN, 0 }, + { 0x3534, 0x10E9, WORD_LEN, 0 }, + { 0x353C, 0x0BCB, WORD_LEN, 0 }, + { 0x3550, 0x04EF, WORD_LEN, 0 }, + { 0x3548, 0x0609, WORD_LEN, 0 }, + { 0x3560, 0x0580, WORD_LEN, 0 }, + { 0x3558, 0x05DD, WORD_LEN, 0 }, + { 0x3540, 0x0000, WORD_LEN, 0 }, + { 0x3542, 0x0000, WORD_LEN, 0 } +}; + +static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = { + { 0x341E, 0x8F09, WORD_LEN, 0 }, + { 0x341C, 0x0250, WORD_LEN, 0 }, + { 0x341E, 0x8F09, WORD_LEN, 5 }, + { 0x341E, 0x8F08, WORD_LEN, 0 } +}; + +/* Refresh Sequencer */ +static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = { + { 0x338C, 0x2799, WORD_LEN, 0}, + { 0x3390, 0x6440, WORD_LEN, 5}, + { 0x338C, 0x279B, WORD_LEN, 0}, + { 0x3390, 0x6440, WORD_LEN, 5}, + { 0x338C, 0xA103, WORD_LEN, 0}, + { 0x3390, 0x0005, WORD_LEN, 5}, + { 0x338C, 0xA103, WORD_LEN, 0}, + { 0x3390, 0x0006, WORD_LEN, 5} +}; + +struct mt9d112_reg mt9d112_regs = { + .prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0], + .prev_snap_reg_settings_size = ARRAY_SIZE(preview_snapshot_mode_reg_settings_array), + .noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0], + .noise_reduction_reg_settings_size = ARRAY_SIZE(noise_reduction_reg_settings_array), + .plltbl = pll_setup_tbl, + .plltbl_size = ARRAY_SIZE(pll_setup_tbl), + .stbl = sequencer_tbl, + .stbl_size = ARRAY_SIZE(sequencer_tbl), + .rftbl = lens_roll_off_tbl, + .rftbl_size = ARRAY_SIZE(lens_roll_off_tbl) +}; + + + diff --git a/drivers/staging/dream/camera/mt9p012.h b/drivers/staging/dream/camera/mt9p012.h new file mode 100644 index 0000000..678a002 --- /dev/null +++ b/drivers/staging/dream/camera/mt9p012.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + + +#ifndef MT9T012_H +#define MT9T012_H + +#include <linux/types.h> + +struct reg_struct { + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size ; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ +}; + + +struct mt9p012_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; +}; + + +struct mt9p012_reg { + struct reg_struct *reg_pat; + uint16_t reg_pat_size; + struct mt9p012_i2c_reg_conf *ttbl; + uint16_t ttbl_size; + struct mt9p012_i2c_reg_conf *lctbl; + uint16_t lctbl_size; + struct mt9p012_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* MT9T012_H */ diff --git a/drivers/staging/dream/camera/mt9p012_fox.c b/drivers/staging/dream/camera/mt9p012_fox.c new file mode 100644 index 0000000..70119d5 --- /dev/null +++ b/drivers/staging/dream/camera/mt9p012_fox.c @@ -0,0 +1,1305 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <media/msm_camera.h> +#include <mach/gpio.h> +#include <mach/camera.h> +#include "mt9p012.h" + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ +#define MT9P012_REG_MODEL_ID 0x0000 +#define MT9P012_MODEL_ID 0x2801 +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define REG_COARSE_INT_TIME 0x3012 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_SCALE_M 0x0404 +#define REG_FRAME_LENGTH_LINES 0x300A +#define REG_LINE_LENGTH_PCK 0x300C +#define REG_X_ADDR_START 0x3004 +#define REG_Y_ADDR_START 0x3002 +#define REG_X_ADDR_END 0x3008 +#define REG_Y_ADDR_END 0x3006 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_FINE_INTEGRATION_TIME 0x3014 +#define REG_ROW_SPEED 0x3016 +#define MT9P012_REG_RESET_REGISTER 0x301A +#define MT9P012_RESET_REGISTER_PWON 0x10CC +#define MT9P012_RESET_REGISTER_PWOFF 0x10C8 +#define REG_READ_MODE 0x3040 +#define REG_GLOBAL_GAIN 0x305E +#define REG_TEST_PATTERN_MODE 0x3070 + +#define MT9P012_REV_7 + + +enum mt9p012_test_mode { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum mt9p012_resolution { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +enum mt9p012_reg_update { + /* Sensor egisters that need to be updated during initialization */ + REG_INIT, + /* Sensor egisters that needs periodic I2C writes */ + UPDATE_PERIODIC, + /* All the sensor Registers will be updated */ + UPDATE_ALL, + /* Not valid update */ + UPDATE_INVALID +}; + +enum mt9p012_setting { + RES_PREVIEW, + RES_CAPTURE +}; + +/* actuator's Slave Address */ +#define MT9P012_AF_I2C_ADDR 0x18 + +/* AF Total steps parameters */ +#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF 32 +#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR 32 + +#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0 +#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES 0 + +/* Time in milisecs for waiting for the sensor to reset.*/ +#define MT9P012_RESET_DELAY_MSECS 66 + +/* for 20 fps preview */ +#define MT9P012_DEFAULT_CLOCK_RATE 24000000 +#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */ + +struct mt9p012_work { + struct work_struct work; +}; +static struct mt9p012_work *mt9p012_sensorw; +static struct i2c_client *mt9p012_client; + +struct mt9p012_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum mt9p012_resolution prev_res; + enum mt9p012_resolution pict_res; + enum mt9p012_resolution curr_res; + enum mt9p012_test_mode set_test; +}; + + +static struct mt9p012_ctrl *mt9p012_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue); +DECLARE_MUTEX(mt9p012_sem); + +/*============================================================= + EXTERNAL DECLARATIONS +==============================================================*/ +extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */ + + + +/*=============================================================*/ + +static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, + int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) { + CDBG("mt9p012_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9p012_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + CDBG("mt9p012_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata, + int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) { + CDBG("mt9p012_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr, + unsigned short bdata) +{ + int32_t rc = -EIO; + unsigned char buf[2]; + + memset(buf, 0, sizeof(buf)); + buf[0] = baddr; + buf[1] = bdata; + rc = mt9p012_i2c_txdata(saddr, buf, 2); + + if (rc < 0) + CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n", + saddr, baddr, bdata); + + return rc; +} + +static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr, + unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9p012_i2c_txdata(saddr, buf, 4); + + if (rc < 0) + CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9p012_i2c_write_w_table( + struct mt9p012_i2c_reg_conf *reg_conf_tbl, int num) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num; i++) { + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata); + if (rc < 0) + break; + reg_conf_tbl++; + } + + return rc; +} + +static int32_t mt9p012_test(enum mt9p012_test_mode mo) +{ + int32_t rc = 0; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + if (mo == TEST_OFF) + return 0; + else { + rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl, mt9p012_regs.ttbl_size); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + if (rc < 0) + return rc; + } + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9p012_lens_shading_enable(uint8_t is_enable) +{ + int32_t rc = 0; + + CDBG("%s: entered. enable = %d\n", __func__, is_enable); + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780, + ((uint16_t) is_enable) << 15); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + + CDBG("%s: exiting. rc = %d\n", __func__, rc); + return rc; +} + +static int32_t mt9p012_set_lc(void) +{ + int32_t rc; + + rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl, mt9p012_regs.lctbl_size); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl, mt9p012_regs.rftbl_size); + + return rc; +} + +static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ + + if (mt9p012_ctrl->prev_res == QTR_SIZE) { + divider = (uint32_t) + (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines * + mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) * 0x00000400) / + (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines * + mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck)); + + pclk_mult = + (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier * + 0x00000400) / (mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + } else { + /* full size resolution used for preview. */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ + } + + /* Verify PCLK settings and frame sizes. */ + *pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 / + 0x00000400); +} + +static uint16_t mt9p012_get_prev_lines_pf(void) +{ + if (mt9p012_ctrl->prev_res == QTR_SIZE) + return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines; + else + return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9p012_get_prev_pixels_pl(void) +{ + if (mt9p012_ctrl->prev_res == QTR_SIZE) + return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck; + else + return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint16_t mt9p012_get_pict_lines_pf(void) +{ + return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9p012_get_pict_pixels_pl(void) +{ + return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint32_t mt9p012_get_pict_max_exp_lc(void) +{ + uint16_t snapshot_lines_per_frame; + + if (mt9p012_ctrl->pict_res == QTR_SIZE) + snapshot_lines_per_frame = + mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + else + snapshot_lines_per_frame = + mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + + return snapshot_lines_per_frame * 24; +} + +static int32_t mt9p012_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q10 format */ + int32_t rc = 0; + + mt9p012_ctrl->fps_divider = fps->fps_div; + mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return -EBUSY; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_LINE_LENGTH_PCK, + (mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck * + fps->f_mult / 0x00000400)); + if (rc < 0) + return rc; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + + return rc; +} + +static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) +{ + uint16_t max_legal_gain = 0x01FF; + uint32_t line_length_ratio = 0x00000400; + enum mt9p012_setting setting; + int32_t rc = 0; + + CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__); + + if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + mt9p012_ctrl->my_reg_gain = gain; + mt9p012_ctrl->my_reg_line_count = (uint16_t)line; + } + + if (gain > max_legal_gain) { + CDBG("Max legal gain Line:%d \n", __LINE__); + gain = max_legal_gain; + } + + /* Verify no overflow */ + if (mt9p012_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + line = (uint32_t)(line * mt9p012_ctrl->fps_divider / + 0x00000400); + setting = RES_PREVIEW; + } else { + line = (uint32_t)(line * mt9p012_ctrl->pict_fps_divider / + 0x00000400); + setting = RES_CAPTURE; + } + + /* Set digital gain to 1 */ +#ifdef MT9P012_REV_7 + gain |= 0x1000; +#else + gain |= 0x0200; +#endif + + if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) { + line_length_ratio = (uint32_t) (line * 0x00000400) / + (mt9p012_regs.reg_pat[setting].frame_length_lines - 1); + } else + line_length_ratio = 0x00000400; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w( + mt9p012_client->addr, + REG_GLOBAL_GAIN, gain); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_COARSE_INT_TIME, + line); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line); + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + + return rc; +} + +static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__); + + rc = + mt9p012_write_exp_gain(gain, line); + if (rc < 0) { + CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n", + __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + 0x10CC | 0x0002); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + mdelay(5); + + /* camera_timed_wait(snapshot_wait*exposure_ratio); */ + return rc; +} + +static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate, + enum mt9p012_setting rt) +{ + int32_t rc = 0; + + switch (rupdate) { + case UPDATE_PERIODIC: + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + + struct mt9p012_i2c_reg_conf ppc_tbl[] = { + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, + {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, + + {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + (mt9p012_regs.reg_pat[rt].frame_length_lines * + mt9p012_ctrl->fps_divider / 0x00000400)}, + {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, + }; + + rc = mt9p012_i2c_write_w_table(&ppc_tbl[0], + ARRAY_SIZE(ppc_tbl)); + if (rc < 0) + return rc; + + rc = mt9p012_test(mt9p012_ctrl->set_test); + if (rc < 0) + return rc; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON | 0x0002); + if (rc < 0) + return rc; + + mdelay(5); /* 15? wait for sensor to transition*/ + + return rc; + } + break; /* UPDATE_PERIODIC */ + + case REG_INIT: + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + struct mt9p012_i2c_reg_conf ipc_tbl1[] = { + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, +#ifdef MT9P012_REV_7 + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, +#endif + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl2[] = { + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, +#ifdef MT9P012_REV_7 + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, +#endif + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl3[] = { + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, + /* Set preview or snapshot mode */ + {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, + {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + mt9p012_regs.reg_pat[rt].frame_length_lines}, + {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, + }; + + /* reset fps_divider */ + mt9p012_ctrl->fps_divider = 1 * 0x0400; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0], + ARRAY_SIZE(ipc_tbl1)); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0], + ARRAY_SIZE(ipc_tbl2)); + if (rc < 0) + return rc; + + mdelay(5); + + rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0], + ARRAY_SIZE(ipc_tbl3)); + if (rc < 0) + return rc; + + /* load lens shading */ + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = mt9p012_set_lc(); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + + if (rc < 0) + return rc; + } + break; /* case REG_INIT: */ + + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int32_t mt9p012_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case QTR_SIZE: + rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW); + if (rc < 0) + return rc; + + CDBG("mt9p012 sensor configuration done!\n"); + break; + + case FULL_SIZE: + rc = + mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + break; + + default: + return 0; + } /* switch */ + + mt9p012_ctrl->prev_res = res; + mt9p012_ctrl->curr_res = res; + mt9p012_ctrl->sensormode = mode; + + rc = + mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain, + mt9p012_ctrl->my_reg_line_count); + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + 0x10cc|0x0002); + + return rc; +} + +static int32_t mt9p012_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res; + + mt9p012_ctrl->sensormode = mode; + + return rc; +} + +static int32_t mt9p012_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res; + + mt9p012_ctrl->sensormode = mode; + + return rc; +} + +static int32_t mt9p012_power_down(void) +{ + int32_t rc = 0; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWOFF); + + mdelay(5); + return rc; +} + +static int32_t mt9p012_move_focus(int direction, int32_t num_steps) +{ + int16_t step_direction; + int16_t actual_step; + int16_t next_position; + uint8_t code_val_msb, code_val_lsb; + + if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR) + num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR; + else if (num_steps == 0) { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + if (direction == MOVE_NEAR) + step_direction = 16; /* 10bit */ + else if (direction == MOVE_FAR) + step_direction = -16; /* 10 bit */ + else { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos) + mt9p012_ctrl->curr_lens_pos = + mt9p012_ctrl->init_curr_lens_pos; + + actual_step = (int16_t)(step_direction * (int16_t)num_steps); + next_position = (int16_t)(mt9p012_ctrl->curr_lens_pos + actual_step); + + if (next_position > 1023) + next_position = 1023; + else if (next_position < 0) + next_position = 0; + + code_val_msb = next_position >> 4; + code_val_lsb = (next_position & 0x000F) << 4; + /* code_val_lsb |= mode_mask; */ + + /* Writing the digital code for current to the actuator */ + if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb) < 0) { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EBUSY; + } + + /* Storing the current lens Position */ + mt9p012_ctrl->curr_lens_pos = next_position; + + return 0; +} + +static int32_t mt9p012_set_default_focus(void) +{ + int32_t rc = 0; + uint8_t code_val_msb, code_val_lsb; + + code_val_msb = 0x00; + code_val_lsb = 0x00; + + /* Write the digital code for current to the actuator */ + rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb); + + mt9p012_ctrl->curr_lens_pos = 0; + mt9p012_ctrl->init_curr_lens_pos = 0; + + return rc; +} + +static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + uint16_t chipid; + + rc = gpio_request(data->sensor_reset, "mt9p012"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + /* RESET the sensor image part via I2C command */ + CDBG("mt9p012_sensor_init(): reseting sensor.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, 0x10CC|0x0001); + if (rc < 0) { + CDBG("sensor reset failed. rc = %d\n", rc); + goto init_probe_fail; + } + + mdelay(MT9P012_RESET_DELAY_MSECS); + + /* 3. Read sensor Model ID: */ + rc = mt9p012_i2c_read_w(mt9p012_client->addr, + MT9P012_REG_MODEL_ID, &chipid); + if (rc < 0) + goto init_probe_fail; + + /* 4. Compare sensor ID to MT9T012VC ID: */ + if (chipid != MT9P012_MODEL_ID) { + CDBG("mt9p012 wrong model_id = 0x%x\n", chipid); + rc = -ENODEV; + goto init_probe_fail; + } + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000); + if (rc < 0) { + CDBG("REV_7 write failed. rc = %d\n", rc); + goto init_probe_fail; + } + + /* RESET_REGISTER, enable parallel interface and disable serialiser */ + CDBG("mt9p012_sensor_init(): enabling parallel interface.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC); + if (rc < 0) { + CDBG("enable parallel interface failed. rc = %d\n", rc); + goto init_probe_fail; + } + + /* To disable the 2 extra lines */ + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + 0x3064, 0x0805); + + if (rc < 0) { + CDBG("disable the 2 extra lines failed. rc = %d\n", rc); + goto init_probe_fail; + } + + mdelay(MT9P012_RESET_DELAY_MSECS); + goto init_probe_done; + +init_probe_fail: + mt9p012_probe_init_done(data); +init_probe_done: + return rc; +} + +static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL); + if (!mt9p012_ctrl) { + CDBG("mt9p012_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + mt9p012_ctrl->fps_divider = 1 * 0x00000400; + mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400; + mt9p012_ctrl->set_test = TEST_OFF; + mt9p012_ctrl->prev_res = QTR_SIZE; + mt9p012_ctrl->pict_res = FULL_SIZE; + + if (data) + mt9p012_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = mt9p012_probe_init_sensor(data); + if (rc < 0) + goto init_fail1; + + if (mt9p012_ctrl->prev_res == QTR_SIZE) + rc = mt9p012_setting(REG_INIT, RES_PREVIEW); + else + rc = mt9p012_setting(REG_INIT, RES_CAPTURE); + + if (rc < 0) { + CDBG("mt9p012_setting failed. rc = %d\n", rc); + goto init_fail1; + } + + /* sensor : output enable */ + CDBG("mt9p012_sensor_open_init(): enabling output.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON); + if (rc < 0) { + CDBG("sensor output enable failed. rc = %d\n", rc); + goto init_fail1; + } + + /* TODO: enable AF actuator */ +#if 0 + CDBG("enable AF actuator, gpio = %d\n", + mt9p012_ctrl->sensordata->vcm_pwd); + rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012"); + if (!rc) + gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1); + else { + CDBG("mt9p012_ctrl gpio request failed!\n"); + goto init_fail1; + } + mdelay(20); + + rc = mt9p012_set_default_focus(); +#endif + if (rc >= 0) + goto init_done; + + /* TODO: + * gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0); + * gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); */ +init_fail1: + mt9p012_probe_init_done(data); + kfree(mt9p012_ctrl); +init_done: + return rc; +} + +static int mt9p012_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9p012_wait_queue); + return 0; +} + +static int32_t mt9p012_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = mt9p012_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = mt9p012_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = mt9p012_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +int mt9p012_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + int rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&mt9p012_sem); + + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + mt9p012_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = mt9p012_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); + rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = mt9p012_power_down(); + break; + + case CFG_MOVE_FOCUS: + CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d cdata.cfg.focus.steps=%d\n", + cdata.cfg.focus.dir, cdata.cfg.focus.steps); + rc = mt9p012_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = mt9p012_set_default_focus(); + break; + + case CFG_SET_LENS_SHADING: + CDBG("%s: CFG_SET_LENS_SHADING\n", __func__); + rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading); + break; + + case CFG_GET_AF_MAX_STEPS: + cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF; + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_EFFECT: + default: + rc = -EINVAL; + break; + } + + up(&mt9p012_sem); + return rc; +} + +int mt9p012_sensor_release(void) +{ + int rc = -EBADF; + + down(&mt9p012_sem); + + mt9p012_power_down(); + + gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, + 0); + gpio_free(mt9p012_ctrl->sensordata->sensor_reset); + + gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0); + gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); + + kfree(mt9p012_ctrl); + mt9p012_ctrl = NULL; + + CDBG("mt9p012_release completed\n"); + + up(&mt9p012_sem); + return rc; +} + +static int mt9p012_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + CDBG("mt9p012_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CDBG("i2c_check_functionality failed\n"); + goto probe_failure; + } + + mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL); + if (!mt9p012_sensorw) { + CDBG("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9p012_sensorw); + mt9p012_init_client(client); + mt9p012_client = client; + + mdelay(50); + + CDBG("mt9p012_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + CDBG("mt9p012_probe failed! rc = %d\n", rc); + return rc; +} + +static const struct i2c_device_id mt9p012_i2c_id[] = { + { "mt9p012", 0}, + { } +}; + +static struct i2c_driver mt9p012_i2c_driver = { + .id_table = mt9p012_i2c_id, + .probe = mt9p012_i2c_probe, + .remove = __exit_p(mt9p012_i2c_remove), + .driver = { + .name = "mt9p012", + }, +}; + +static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&mt9p012_i2c_driver); + if (rc < 0 || mt9p012_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE); + mdelay(20); + + rc = mt9p012_probe_init_sensor(info); + if (rc < 0) + goto probe_done; + + s->s_init = mt9p012_sensor_open_init; + s->s_release = mt9p012_sensor_release; + s->s_config = mt9p012_sensor_config; + mt9p012_probe_init_done(info); + +probe_done: + CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +} + +static int __mt9p012_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9p012_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9p012_probe, + .driver = { + .name = "msm_camera_mt9p012", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9p012_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9p012_init); diff --git a/drivers/staging/dream/camera/mt9p012_reg.c b/drivers/staging/dream/camera/mt9p012_reg.c new file mode 100644 index 0000000..e5223d6 --- /dev/null +++ b/drivers/staging/dream/camera/mt9p012_reg.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2009 QUALCOMM Incorporated. + */ + +#include "mt9p012.h" +#include <linux/kernel.h> + +/*Micron settings from Applications for lower power consumption.*/ +struct reg_struct mt9p012_reg_pat[2] = { + { /* Preview */ + /* vt_pix_clk_div REG=0x0300 */ + 6, /* 5 */ + + /* vt_sys_clk_div REG=0x0302 */ + 1, + + /* pre_pll_clk_div REG=0x0304 */ + 2, + + /* pll_multiplier REG=0x0306 */ + 60, + + /* op_pix_clk_div REG=0x0308 */ + 8, /* 10 */ + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2597, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1949, + + /* read_mode REG=0x3040 + * Preview 2x2 skipping */ + 0x00C3, + + /* x_output_size REG=0x034C */ + 1296, + + /* y_output_size REG=0x034E */ + 972, + + /* line_length_pck REG=0x300C */ + 3784, + + /* frame_length_lines REG=0x300A */ + 1057, + + /* coarse_integration_time REG=0x3012 */ + 16, + + /* fine_integration_time REG=0x3014 */ + 1764 + }, + { /*Snapshot*/ + /* vt_pix_clk_div REG=0x0300 */ + 6, + + /* vt_sys_clk_div REG=0x0302 */ + 1, + + /* pre_pll_clk_div REG=0x0304 */ + 2, + + /* pll_multiplier REG=0x0306 + * 60 for 10fps snapshot */ + 60, + + /* op_pix_clk_div REG=0x0308 */ + 8, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2615, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1967, + + /* read_mode REG=0x3040 */ + 0x0041, + + /* x_output_size REG=0x034C */ + 2608, + + /* y_output_size REG=0x034E */ + 1960, + + /* line_length_pck REG=0x300C */ + 3911, + + /* frame_length_lines REG=0x300A //10 fps snapshot */ + 2045, + + /* coarse_integration_time REG=0x3012 */ + 16, + + /* fine_integration_time REG=0x3014 */ + 882 + } +}; + + +struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = { + {0x3044, 0x0544 & 0xFBFF}, + {0x30CA, 0x0004 | 0x0001}, + {0x30D4, 0x9020 & 0x7FFF}, + {0x31E0, 0x0003 & 0xFFFE}, + {0x3180, 0x91FF & 0x7FFF}, + {0x301A, (0x10CC | 0x8000) & 0xFFF7}, + {0x301E, 0x0000}, + {0x3780, 0x0000}, +}; + + +struct mt9p012_i2c_reg_conf mt9p012_lc_tbl[] = { + /* [Lens shading 85 Percent TL84] */ + /* P_RD_P0Q0 */ + {0x360A, 0x7FEF}, + /* P_RD_P0Q1 */ + {0x360C, 0x232C}, + /* P_RD_P0Q2 */ + {0x360E, 0x7050}, + /* P_RD_P0Q3 */ + {0x3610, 0xF3CC}, + /* P_RD_P0Q4 */ + {0x3612, 0x89D1}, + /* P_RD_P1Q0 */ + {0x364A, 0xBE0D}, + /* P_RD_P1Q1 */ + {0x364C, 0x9ACB}, + /* P_RD_P1Q2 */ + {0x364E, 0x2150}, + /* P_RD_P1Q3 */ + {0x3650, 0xB26B}, + /* P_RD_P1Q4 */ + {0x3652, 0x9511}, + /* P_RD_P2Q0 */ + {0x368A, 0x2151}, + /* P_RD_P2Q1 */ + {0x368C, 0x00AD}, + /* P_RD_P2Q2 */ + {0x368E, 0x8334}, + /* P_RD_P2Q3 */ + {0x3690, 0x478E}, + /* P_RD_P2Q4 */ + {0x3692, 0x0515}, + /* P_RD_P3Q0 */ + {0x36CA, 0x0710}, + /* P_RD_P3Q1 */ + {0x36CC, 0x452D}, + /* P_RD_P3Q2 */ + {0x36CE, 0xF352}, + /* P_RD_P3Q3 */ + {0x36D0, 0x190F}, + /* P_RD_P3Q4 */ + {0x36D2, 0x4413}, + /* P_RD_P4Q0 */ + {0x370A, 0xD112}, + /* P_RD_P4Q1 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q2 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q3 */ + {0x3710, 0xDC11}, + /* P_RD_P4Q4 */ + {0x3712, 0xD776}, + /* P_GR_P0Q0 */ + {0x3600, 0x1750}, + /* P_GR_P0Q1 */ + {0x3602, 0xF0AC}, + /* P_GR_P0Q2 */ + {0x3604, 0x4711}, + /* P_GR_P0Q3 */ + {0x3606, 0x07CE}, + /* P_GR_P0Q4 */ + {0x3608, 0x96B2}, + /* P_GR_P1Q0 */ + {0x3640, 0xA9AE}, + /* P_GR_P1Q1 */ + {0x3642, 0xF9AC}, + /* P_GR_P1Q2 */ + {0x3644, 0x39F1}, + /* P_GR_P1Q3 */ + {0x3646, 0x016F}, + /* P_GR_P1Q4 */ + {0x3648, 0x8AB2}, + /* P_GR_P2Q0 */ + {0x3680, 0x1752}, + /* P_GR_P2Q1 */ + {0x3682, 0x70F0}, + /* P_GR_P2Q2 */ + {0x3684, 0x83F5}, + /* P_GR_P2Q3 */ + {0x3686, 0x8392}, + /* P_GR_P2Q4 */ + {0x3688, 0x1FD6}, + /* P_GR_P3Q0 */ + {0x36C0, 0x1131}, + /* P_GR_P3Q1 */ + {0x36C2, 0x3DAF}, + /* P_GR_P3Q2 */ + {0x36C4, 0x89B4}, + /* P_GR_P3Q3 */ + {0x36C6, 0xA391}, + /* P_GR_P3Q4 */ + {0x36C8, 0x1334}, + /* P_GR_P4Q0 */ + {0x3700, 0xDC13}, + /* P_GR_P4Q1 */ + {0x3702, 0xD052}, + /* P_GR_P4Q2 */ + {0x3704, 0x5156}, + /* P_GR_P4Q3 */ + {0x3706, 0x1F13}, + /* P_GR_P4Q4 */ + {0x3708, 0x8C38}, + /* P_BL_P0Q0 */ + {0x3614, 0x0050}, + /* P_BL_P0Q1 */ + {0x3616, 0xBD4C}, + /* P_BL_P0Q2 */ + {0x3618, 0x41B0}, + /* P_BL_P0Q3 */ + {0x361A, 0x660D}, + /* P_BL_P0Q4 */ + {0x361C, 0xC590}, + /* P_BL_P1Q0 */ + {0x3654, 0x87EC}, + /* P_BL_P1Q1 */ + {0x3656, 0xE44C}, + /* P_BL_P1Q2 */ + {0x3658, 0x302E}, + /* P_BL_P1Q3 */ + {0x365A, 0x106E}, + /* P_BL_P1Q4 */ + {0x365C, 0xB58E}, + /* P_BL_P2Q0 */ + {0x3694, 0x0DD1}, + /* P_BL_P2Q1 */ + {0x3696, 0x2A50}, + /* P_BL_P2Q2 */ + {0x3698, 0xC793}, + /* P_BL_P2Q3 */ + {0x369A, 0xE8F1}, + /* P_BL_P2Q4 */ + {0x369C, 0x4174}, + /* P_BL_P3Q0 */ + {0x36D4, 0x01EF}, + /* P_BL_P3Q1 */ + {0x36D6, 0x06CF}, + /* P_BL_P3Q2 */ + {0x36D8, 0x8D91}, + /* P_BL_P3Q3 */ + {0x36DA, 0x91F0}, + /* P_BL_P3Q4 */ + {0x36DC, 0x52EF}, + /* P_BL_P4Q0 */ + {0x3714, 0xA6D2}, + /* P_BL_P4Q1 */ + {0x3716, 0xA312}, + /* P_BL_P4Q2 */ + {0x3718, 0x2695}, + /* P_BL_P4Q3 */ + {0x371A, 0x3953}, + /* P_BL_P4Q4 */ + {0x371C, 0x9356}, + /* P_GB_P0Q0 */ + {0x361E, 0x7EAF}, + /* P_GB_P0Q1 */ + {0x3620, 0x2A4C}, + /* P_GB_P0Q2 */ + {0x3622, 0x49F0}, + {0x3624, 0xF1EC}, + /* P_GB_P0Q4 */ + {0x3626, 0xC670}, + /* P_GB_P1Q0 */ + {0x365E, 0x8E0C}, + /* P_GB_P1Q1 */ + {0x3660, 0xC2A9}, + /* P_GB_P1Q2 */ + {0x3662, 0x274F}, + /* P_GB_P1Q3 */ + {0x3664, 0xADAB}, + /* P_GB_P1Q4 */ + {0x3666, 0x8EF0}, + /* P_GB_P2Q0 */ + {0x369E, 0x09B1}, + /* P_GB_P2Q1 */ + {0x36A0, 0xAA2E}, + /* P_GB_P2Q2 */ + {0x36A2, 0xC3D3}, + /* P_GB_P2Q3 */ + {0x36A4, 0x7FAF}, + /* P_GB_P2Q4 */ + {0x36A6, 0x3F34}, + /* P_GB_P3Q0 */ + {0x36DE, 0x4C8F}, + /* P_GB_P3Q1 */ + {0x36E0, 0x886E}, + /* P_GB_P3Q2 */ + {0x36E2, 0xE831}, + /* P_GB_P3Q3 */ + {0x36E4, 0x1FD0}, + /* P_GB_P3Q4 */ + {0x36E6, 0x1192}, + /* P_GB_P4Q0 */ + {0x371E, 0xB952}, + /* P_GB_P4Q1 */ + {0x3720, 0x6DCF}, + /* P_GB_P4Q2 */ + {0x3722, 0x1B55}, + /* P_GB_P4Q3 */ + {0x3724, 0xA112}, + /* P_GB_P4Q4 */ + {0x3726, 0x82F6}, + /* POLY_ORIGIN_C */ + {0x3782, 0x0510}, + /* POLY_ORIGIN_R */ + {0x3784, 0x0390}, + /* POLY_SC_ENABLE */ + {0x3780, 0x8000}, +}; + +/* rolloff table for illuminant A */ +struct mt9p012_i2c_reg_conf mt9p012_rolloff_tbl[] = { + /* P_RD_P0Q0 */ + {0x360A, 0x7FEF}, + /* P_RD_P0Q1 */ + {0x360C, 0x232C}, + /* P_RD_P0Q2 */ + {0x360E, 0x7050}, + /* P_RD_P0Q3 */ + {0x3610, 0xF3CC}, + /* P_RD_P0Q4 */ + {0x3612, 0x89D1}, + /* P_RD_P1Q0 */ + {0x364A, 0xBE0D}, + /* P_RD_P1Q1 */ + {0x364C, 0x9ACB}, + /* P_RD_P1Q2 */ + {0x364E, 0x2150}, + /* P_RD_P1Q3 */ + {0x3650, 0xB26B}, + /* P_RD_P1Q4 */ + {0x3652, 0x9511}, + /* P_RD_P2Q0 */ + {0x368A, 0x2151}, + /* P_RD_P2Q1 */ + {0x368C, 0x00AD}, + /* P_RD_P2Q2 */ + {0x368E, 0x8334}, + /* P_RD_P2Q3 */ + {0x3690, 0x478E}, + /* P_RD_P2Q4 */ + {0x3692, 0x0515}, + /* P_RD_P3Q0 */ + {0x36CA, 0x0710}, + /* P_RD_P3Q1 */ + {0x36CC, 0x452D}, + /* P_RD_P3Q2 */ + {0x36CE, 0xF352}, + /* P_RD_P3Q3 */ + {0x36D0, 0x190F}, + /* P_RD_P3Q4 */ + {0x36D2, 0x4413}, + /* P_RD_P4Q0 */ + {0x370A, 0xD112}, + /* P_RD_P4Q1 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q2 */ + {0x370E, 0x6375}, + /* P_RD_P4Q3 */ + {0x3710, 0xDC11}, + /* P_RD_P4Q4 */ + {0x3712, 0xD776}, + /* P_GR_P0Q0 */ + {0x3600, 0x1750}, + /* P_GR_P0Q1 */ + {0x3602, 0xF0AC}, + /* P_GR_P0Q2 */ + {0x3604, 0x4711}, + /* P_GR_P0Q3 */ + {0x3606, 0x07CE}, + /* P_GR_P0Q4 */ + {0x3608, 0x96B2}, + /* P_GR_P1Q0 */ + {0x3640, 0xA9AE}, + /* P_GR_P1Q1 */ + {0x3642, 0xF9AC}, + /* P_GR_P1Q2 */ + {0x3644, 0x39F1}, + /* P_GR_P1Q3 */ + {0x3646, 0x016F}, + /* P_GR_P1Q4 */ + {0x3648, 0x8AB2}, + /* P_GR_P2Q0 */ + {0x3680, 0x1752}, + /* P_GR_P2Q1 */ + {0x3682, 0x70F0}, + /* P_GR_P2Q2 */ + {0x3684, 0x83F5}, + /* P_GR_P2Q3 */ + {0x3686, 0x8392}, + /* P_GR_P2Q4 */ + {0x3688, 0x1FD6}, + /* P_GR_P3Q0 */ + {0x36C0, 0x1131}, + /* P_GR_P3Q1 */ + {0x36C2, 0x3DAF}, + /* P_GR_P3Q2 */ + {0x36C4, 0x89B4}, + /* P_GR_P3Q3 */ + {0x36C6, 0xA391}, + /* P_GR_P3Q4 */ + {0x36C8, 0x1334}, + /* P_GR_P4Q0 */ + {0x3700, 0xDC13}, + /* P_GR_P4Q1 */ + {0x3702, 0xD052}, + /* P_GR_P4Q2 */ + {0x3704, 0x5156}, + /* P_GR_P4Q3 */ + {0x3706, 0x1F13}, + /* P_GR_P4Q4 */ + {0x3708, 0x8C38}, + /* P_BL_P0Q0 */ + {0x3614, 0x0050}, + /* P_BL_P0Q1 */ + {0x3616, 0xBD4C}, + /* P_BL_P0Q2 */ + {0x3618, 0x41B0}, + /* P_BL_P0Q3 */ + {0x361A, 0x660D}, + /* P_BL_P0Q4 */ + {0x361C, 0xC590}, + /* P_BL_P1Q0 */ + {0x3654, 0x87EC}, + /* P_BL_P1Q1 */ + {0x3656, 0xE44C}, + /* P_BL_P1Q2 */ + {0x3658, 0x302E}, + /* P_BL_P1Q3 */ + {0x365A, 0x106E}, + /* P_BL_P1Q4 */ + {0x365C, 0xB58E}, + /* P_BL_P2Q0 */ + {0x3694, 0x0DD1}, + /* P_BL_P2Q1 */ + {0x3696, 0x2A50}, + /* P_BL_P2Q2 */ + {0x3698, 0xC793}, + /* P_BL_P2Q3 */ + {0x369A, 0xE8F1}, + /* P_BL_P2Q4 */ + {0x369C, 0x4174}, + /* P_BL_P3Q0 */ + {0x36D4, 0x01EF}, + /* P_BL_P3Q1 */ + {0x36D6, 0x06CF}, + /* P_BL_P3Q2 */ + {0x36D8, 0x8D91}, + /* P_BL_P3Q3 */ + {0x36DA, 0x91F0}, + /* P_BL_P3Q4 */ + {0x36DC, 0x52EF}, + /* P_BL_P4Q0 */ + {0x3714, 0xA6D2}, + /* P_BL_P4Q1 */ + {0x3716, 0xA312}, + /* P_BL_P4Q2 */ + {0x3718, 0x2695}, + /* P_BL_P4Q3 */ + {0x371A, 0x3953}, + /* P_BL_P4Q4 */ + {0x371C, 0x9356}, + /* P_GB_P0Q0 */ + {0x361E, 0x7EAF}, + /* P_GB_P0Q1 */ + {0x3620, 0x2A4C}, + /* P_GB_P0Q2 */ + {0x3622, 0x49F0}, + {0x3624, 0xF1EC}, + /* P_GB_P0Q4 */ + {0x3626, 0xC670}, + /* P_GB_P1Q0 */ + {0x365E, 0x8E0C}, + /* P_GB_P1Q1 */ + {0x3660, 0xC2A9}, + /* P_GB_P1Q2 */ + {0x3662, 0x274F}, + /* P_GB_P1Q3 */ + {0x3664, 0xADAB}, + /* P_GB_P1Q4 */ + {0x3666, 0x8EF0}, + /* P_GB_P2Q0 */ + {0x369E, 0x09B1}, + /* P_GB_P2Q1 */ + {0x36A0, 0xAA2E}, + /* P_GB_P2Q2 */ + {0x36A2, 0xC3D3}, + /* P_GB_P2Q3 */ + {0x36A4, 0x7FAF}, + /* P_GB_P2Q4 */ + {0x36A6, 0x3F34}, + /* P_GB_P3Q0 */ + {0x36DE, 0x4C8F}, + /* P_GB_P3Q1 */ + {0x36E0, 0x886E}, + /* P_GB_P3Q2 */ + {0x36E2, 0xE831}, + /* P_GB_P3Q3 */ + {0x36E4, 0x1FD0}, + /* P_GB_P3Q4 */ + {0x36E6, 0x1192}, + /* P_GB_P4Q0 */ + {0x371E, 0xB952}, + /* P_GB_P4Q1 */ + {0x3720, 0x6DCF}, + /* P_GB_P4Q2 */ + {0x3722, 0x1B55}, + /* P_GB_P4Q3 */ + {0x3724, 0xA112}, + /* P_GB_P4Q4 */ + {0x3726, 0x82F6}, + /* POLY_ORIGIN_C */ + {0x3782, 0x0510}, + /* POLY_ORIGIN_R */ + {0x3784, 0x0390}, + /* POLY_SC_ENABLE */ + {0x3780, 0x8000}, +}; + + +struct mt9p012_reg mt9p012_regs = { + .reg_pat = &mt9p012_reg_pat[0], + .reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat), + .ttbl = &mt9p012_test_tbl[0], + .ttbl_size = ARRAY_SIZE(mt9p012_test_tbl), + .lctbl = &mt9p012_lc_tbl[0], + .lctbl_size = ARRAY_SIZE(mt9p012_lc_tbl), + .rftbl = &mt9p012_rolloff_tbl[0], + .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl) +}; + + diff --git a/drivers/staging/dream/camera/mt9t013.c b/drivers/staging/dream/camera/mt9t013.c new file mode 100644 index 0000000..88229f2 --- /dev/null +++ b/drivers/staging/dream/camera/mt9t013.c @@ -0,0 +1,1496 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <media/msm_camera.h> +#include <mach/gpio.h> +#include <mach/camera.h> +#include <asm/mach-types.h> +#include "mt9t013.h" + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ +#define MT9T013_REG_MODEL_ID 0x0000 +#define MT9T013_MODEL_ID 0x2600 +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define REG_COARSE_INT_TIME 0x3012 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_SCALE_M 0x0404 +#define REG_FRAME_LENGTH_LINES 0x300A +#define REG_LINE_LENGTH_PCK 0x300C +#define REG_X_ADDR_START 0x3004 +#define REG_Y_ADDR_START 0x3002 +#define REG_X_ADDR_END 0x3008 +#define REG_Y_ADDR_END 0x3006 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_FINE_INT_TIME 0x3014 +#define REG_ROW_SPEED 0x3016 +#define MT9T013_REG_RESET_REGISTER 0x301A +#define MT9T013_RESET_REGISTER_PWON 0x10CC +#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/ +#define REG_READ_MODE 0x3040 +#define REG_GLOBAL_GAIN 0x305E +#define REG_TEST_PATTERN_MODE 0x3070 + + +enum mt9t013_test_mode { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum mt9t013_resolution { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +enum mt9t013_reg_update { + REG_INIT, /* registers that need to be updated during initialization */ + UPDATE_PERIODIC, /* registers that needs periodic I2C writes */ + UPDATE_ALL, /* all registers will be updated */ + UPDATE_INVALID +}; + +enum mt9t013_setting { + RES_PREVIEW, + RES_CAPTURE +}; + +/* actuator's Slave Address */ +#define MT9T013_AF_I2C_ADDR 0x18 + +/* +* AF Total steps parameters +*/ +#define MT9T013_TOTAL_STEPS_NEAR_TO_FAR 30 + +/* + * Time in milisecs for waiting for the sensor to reset. + */ +#define MT9T013_RESET_DELAY_MSECS 66 + +/* for 30 fps preview */ +#define MT9T013_DEFAULT_CLOCK_RATE 24000000 +#define MT9T013_DEFAULT_MAX_FPS 26 + + +/* FIXME: Changes from here */ +struct mt9t013_work { + struct work_struct work; +}; + +static struct mt9t013_work *mt9t013_sensorw; +static struct i2c_client *mt9t013_client; + +struct mt9t013_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum mt9t013_resolution prev_res; + enum mt9t013_resolution pict_res; + enum mt9t013_resolution curr_res; + enum mt9t013_test_mode set_test; + + unsigned short imgaddr; +}; + + +static struct mt9t013_ctrl *mt9t013_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue); +DECLARE_MUTEX(mt9t013_sem); + +extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */ + +static int mt9t013_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) { + pr_err("mt9t013_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9t013_i2c_read_w(unsigned short saddr, + unsigned short raddr, unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9t013_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + pr_err("mt9t013_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9t013_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) { + pr_err("mt9t013_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9t013_i2c_write_b(unsigned short saddr, + unsigned short waddr, unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[2]; + + memset(buf, 0, sizeof(buf)); + buf[0] = waddr; + buf[1] = wdata; + rc = mt9t013_i2c_txdata(saddr, buf, 2); + + if (rc < 0) + pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9t013_i2c_write_w(unsigned short saddr, + unsigned short waddr, unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9t013_i2c_txdata(saddr, buf, 4); + + if (rc < 0) + pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9t013_i2c_write_w_table( + struct mt9t013_i2c_reg_conf *reg_conf_tbl, int num_of_items_in_table) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata); + if (rc < 0) + break; + reg_conf_tbl++; + } + + return rc; +} + +static int32_t mt9t013_test(enum mt9t013_test_mode mo) +{ + int32_t rc = 0; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + if (mo == TEST_OFF) + return 0; + else { + rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl, + mt9t013_regs.ttbl_size); + if (rc < 0) + return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + if (rc < 0) + return rc; + } + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_lc(void) +{ + int32_t rc; + + rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, mt9t013_regs.lctbl_size); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_default_focus(uint8_t af_step) +{ + int32_t rc = 0; + uint8_t code_val_msb, code_val_lsb; + code_val_msb = 0x01; + code_val_lsb = af_step; + + /* Write the digital code for current to the actuator */ + rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, + code_val_msb, code_val_lsb); + + mt9t013_ctrl->curr_lens_pos = 0; + mt9t013_ctrl->init_curr_lens_pos = 0; + return rc; +} + +static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ + + if (mt9t013_ctrl->prev_res == QTR_SIZE) { + divider = + (uint32_t)( + ((mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines * + mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck) * + 0x00000400) / + (mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines * + mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck)); + + pclk_mult = + (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier * + 0x00000400) / + (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + + } else { + /* full size resolution used for preview. */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ + } + + /* Verify PCLK settings and frame sizes. */ + *pfps = + (uint16_t) (fps * divider * pclk_mult / + 0x00000400 / 0x00000400); +} + +static uint16_t mt9t013_get_prev_lines_pf(void) +{ + if (mt9t013_ctrl->prev_res == QTR_SIZE) + return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines; + else + return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9t013_get_prev_pixels_pl(void) +{ + if (mt9t013_ctrl->prev_res == QTR_SIZE) + return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck; + else + return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint16_t mt9t013_get_pict_lines_pf(void) +{ + return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9t013_get_pict_pixels_pl(void) +{ + return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint32_t mt9t013_get_pict_max_exp_lc(void) +{ + uint16_t snapshot_lines_per_frame; + + if (mt9t013_ctrl->pict_res == QTR_SIZE) { + snapshot_lines_per_frame = + mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + } else { + snapshot_lines_per_frame = + mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + } + + return snapshot_lines_per_frame * 24; +} + +static int32_t mt9t013_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q8 format */ + int32_t rc = 0; + + mt9t013_ctrl->fps_divider = fps->fps_div; + mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return -EBUSY; + + CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n", + fps->fps_div, + (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. + frame_length_lines * + fps->fps_div/0x00000400)); + + CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n", + fps->f_mult, + (uint16_t)(mt9t013_regs.reg_pat[RES_PREVIEW]. + line_length_pck * + fps->f_mult / 0x00000400)); + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + (uint16_t) ( + mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck * + fps->f_mult / 0x00000400)); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) +{ + const uint16_t max_legal_gain = 0x01FF; + uint32_t line_length_ratio = 0x00000400; + enum mt9t013_setting setting; + int32_t rc = 0; + + if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + mt9t013_ctrl->my_reg_gain = gain; + mt9t013_ctrl->my_reg_line_count = (uint16_t) line; + } + + if (gain > max_legal_gain) + gain = max_legal_gain; + + /* Verify no overflow */ + if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + line = (uint32_t) (line * mt9t013_ctrl->fps_divider / + 0x00000400); + + setting = RES_PREVIEW; + + } else { + line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider / + 0x00000400); + + setting = RES_CAPTURE; + } + + /*Set digital gain to 1 */ + gain |= 0x0200; + + if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) { + + line_length_ratio = + (uint32_t) (line * 0x00000400) / + (mt9t013_regs.reg_pat[setting].frame_length_lines - 1); + } else + line_length_ratio = 0x00000400; + + /* There used to be PARAMETER_HOLD register write before and + * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes + * aec oscillation. Hence removed. */ + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + (uint16_t)((uint32_t) line * 0x00000400 / + line_length_ratio)); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + rc = mt9t013_write_exp_gain(gain, line); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + 0x10CC | 0x0002); + + mdelay(5); + + /* camera_timed_wait(snapshot_wait*exposure_ratio); */ + return rc; +} + +static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate, + enum mt9t013_setting rt) +{ + int32_t rc = 0; + + switch (rupdate) { + case UPDATE_PERIODIC: { + + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { +#if 0 + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + return rc; +#endif + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_sys_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs.reg_pat[rt].pre_pll_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs.reg_pat[rt].pll_multiplier); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_sys_clk_div); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs.reg_pat[rt].row_speed); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs.reg_pat[rt].x_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_END, + mt9t013_regs.reg_pat[rt].x_addr_end); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_START, + mt9t013_regs.reg_pat[rt].y_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs.reg_pat[rt].y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x046F); + else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x0027); + } else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt].read_mode); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs.reg_pat[rt].scale_m); + if (rc < 0) + return rc; + + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].x_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].y_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs.reg_pat[rt].line_length_pck); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + (mt9t013_regs.reg_pat[rt].frame_length_lines * + mt9t013_ctrl->fps_divider / 0x00000400)); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs.reg_pat[rt].coarse_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs.reg_pat[rt].fine_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + return rc; + + mdelay(5); + + return rc; + } + } + break; + + /*CAMSENSOR_REG_UPDATE_PERIODIC */ + case REG_INIT: { + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_sys_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs.reg_pat[rt].pre_pll_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs.reg_pat[rt].pll_multiplier); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_sys_clk_div); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + /* additional power saving mode ok around 38.2MHz */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3084, 0x2409); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3092, 0x0A49); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3094, 0x4949); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3096, 0x4949); + if (rc < 0) + return rc; + + /* Set preview or snapshot mode */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs.reg_pat[rt].row_speed); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs.reg_pat[rt].x_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_END, + mt9t013_regs.reg_pat[rt].x_addr_end); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_START, + mt9t013_regs.reg_pat[rt].y_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs.reg_pat[rt].y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x046F); + else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x0027); + } else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt].read_mode); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs.reg_pat[rt].scale_m); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].x_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].y_output_size); + if (rc < 0) + return 0; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs.reg_pat[rt].line_length_pck); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + mt9t013_regs.reg_pat[rt].frame_length_lines); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs.reg_pat[rt].coarse_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs.reg_pat[rt].fine_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + /* load lens shading */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + /* most likely needs to be written only once. */ + rc = mt9t013_set_lc(); + if (rc < 0) + return -EBUSY; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; + + CDBG("!!! mt9t013 !!! PowerOn is done!\n"); + mdelay(5); + return rc; + } + } /* case CAMSENSOR_REG_INIT: */ + break; + + /*CAMSENSOR_REG_INIT */ + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int32_t mt9t013_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case QTR_SIZE: + rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW); + if (rc < 0) + return rc; + CDBG("sensor configuration done!\n"); + break; + + case FULL_SIZE: + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + break; + + default: + return -EINVAL; + } /* switch */ + + mt9t013_ctrl->prev_res = res; + mt9t013_ctrl->curr_res = res; + mt9t013_ctrl->sensormode = mode; + + return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain, + mt9t013_ctrl->my_reg_line_count); +} + +static int32_t mt9t013_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; + mt9t013_ctrl->sensormode = mode; + return rc; +} + +static int32_t mt9t013_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; + mt9t013_ctrl->sensormode = mode; + return rc; +} + +static int32_t mt9t013_power_down(void) +{ + int32_t rc = 0; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc >= 0) + mdelay(5); + return rc; +} + +static int32_t mt9t013_move_focus(int direction, int32_t num_steps) +{ + int16_t step_direction; + int16_t actual_step; + int16_t next_position; + int16_t break_steps[4]; + uint8_t code_val_msb, code_val_lsb; + int16_t i; + + if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR) + num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; + else if (num_steps == 0) + return -EINVAL; + + if (direction == MOVE_NEAR) + step_direction = 4; + else if (direction == MOVE_FAR) + step_direction = -4; + else + return -EINVAL; + + if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos) + mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos; + + actual_step = + (int16_t) (step_direction * + (int16_t) num_steps); + + for (i = 0; i < 4; i++) + break_steps[i] = + actual_step / 4 * (i + 1) - actual_step / 4 * i; + + for (i = 0; i < 4; i++) { + next_position = + (int16_t) + (mt9t013_ctrl->curr_lens_pos + break_steps[i]); + + if (next_position > 255) + next_position = 255; + else if (next_position < 0) + next_position = 0; + + code_val_msb = + ((next_position >> 4) << 2) | + ((next_position << 4) >> 6); + + code_val_lsb = + ((next_position & 0x03) << 6); + + /* Writing the digital code for current to the actuator */ + if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, + code_val_msb, code_val_lsb) < 0) + return -EBUSY; + + /* Storing the current lens Position */ + mt9t013_ctrl->curr_lens_pos = next_position; + + if (i < 3) + mdelay(1); + } /* for */ + + return 0; +} + +static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int rc; + uint16_t chipid; + + rc = gpio_request(data->sensor_reset, "mt9t013"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + /* RESET the sensor image part via I2C command */ + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, 0x1009); + if (rc < 0) + goto init_probe_fail; + + /* 3. Read sensor Model ID: */ + rc = mt9t013_i2c_read_w(mt9t013_client->addr, + MT9T013_REG_MODEL_ID, &chipid); + + if (rc < 0) + goto init_probe_fail; + + CDBG("mt9t013 model_id = 0x%x\n", chipid); + + /* 4. Compare sensor ID to MT9T012VC ID: */ + if (chipid != MT9T013_MODEL_ID) { + rc = -ENODEV; + goto init_probe_fail; + } + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3064, 0x0805); + if (rc < 0) + goto init_probe_fail; + + mdelay(MT9T013_RESET_DELAY_MSECS); + + goto init_probe_done; + + /* sensor: output enable */ +#if 0 + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + + /* if this fails, the sensor is not the MT9T013 */ + rc = mt9t013_set_default_focus(0); +#endif + +init_probe_fail: + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); +init_probe_done: + return rc; +} + +static int32_t mt9t013_poweron_af(void) +{ + int32_t rc = 0; + + /* enable AF actuator */ + CDBG("enable AF actuator, gpio = %d\n", + mt9t013_ctrl->sensordata->vcm_pwd); + rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013"); + if (!rc) { + gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0); + mdelay(20); + rc = mt9t013_set_default_focus(0); + } else + pr_err("%s, gpio_request failed (%d)!\n", __func__, rc); + return rc; +} + +static void mt9t013_poweroff_af(void) +{ + gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1); + gpio_free(mt9t013_ctrl->sensordata->vcm_pwd); +} + +int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL); + if (!mt9t013_ctrl) { + pr_err("mt9t013_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + mt9t013_ctrl->fps_divider = 1 * 0x00000400; + mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400; + mt9t013_ctrl->set_test = TEST_OFF; + mt9t013_ctrl->prev_res = QTR_SIZE; + mt9t013_ctrl->pict_res = FULL_SIZE; + + if (data) + mt9t013_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = mt9t013_probe_init_sensor(data); + if (rc < 0) + goto init_fail; + + if (mt9t013_ctrl->prev_res == QTR_SIZE) + rc = mt9t013_setting(REG_INIT, RES_PREVIEW); + else + rc = mt9t013_setting(REG_INIT, RES_CAPTURE); + + if (rc >= 0) + rc = mt9t013_poweron_af(); + + if (rc < 0) + goto init_fail; + else + goto init_done; + +init_fail: + kfree(mt9t013_ctrl); +init_done: + return rc; +} + +static int mt9t013_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9t013_wait_queue); + return 0; +} + + +static int32_t mt9t013_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = mt9t013_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = mt9t013_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = mt9t013_raw_snapshot_config(mode); + break; + + default: + return -EINVAL; + } + + /* FIXME: what should we do if rc < 0? */ + if (rc >= 0) + return mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + return rc; +} + +int mt9t013_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&mt9t013_sem); + + CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = + mt9t013_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + mt9t013_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = mt9t013_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = mt9t013_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = mt9t013_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = mt9t013_set_default_focus(cdata.cfg.focus.steps); + break; + + case CFG_GET_AF_MAX_STEPS: + cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_EFFECT: + default: + rc = -EINVAL; + break; + } + + up(&mt9t013_sem); + return rc; +} + +int mt9t013_sensor_release(void) +{ + int rc = -EBADF; + + down(&mt9t013_sem); + + mt9t013_poweroff_af(); + mt9t013_power_down(); + + gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, + 0); + gpio_free(mt9t013_ctrl->sensordata->sensor_reset); + + kfree(mt9t013_ctrl); + + up(&mt9t013_sem); + CDBG("mt9t013_release completed!\n"); + return rc; +} + +static int mt9t013_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + mt9t013_sensorw = + kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL); + + if (!mt9t013_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9t013_sensorw); + mt9t013_init_client(client); + mt9t013_client = client; + mt9t013_client->addr = mt9t013_client->addr >> 1; + mdelay(50); + + CDBG("i2c probe ok\n"); + return 0; + +probe_failure: + kfree(mt9t013_sensorw); + mt9t013_sensorw = NULL; + pr_err("i2c probe failure %d\n", rc); + return rc; +} + +static const struct i2c_device_id mt9t013_i2c_id[] = { + { "mt9t013", 0}, + { } +}; + +static struct i2c_driver mt9t013_i2c_driver = { + .id_table = mt9t013_i2c_id, + .probe = mt9t013_i2c_probe, + .remove = __exit_p(mt9t013_i2c_remove), + .driver = { + .name = "mt9t013", + }, +}; + +static int mt9t013_sensor_probe( + const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + /* We expect this driver to match with the i2c device registered + * in the board file immediately. */ + int rc = i2c_add_driver(&mt9t013_i2c_driver); + if (rc < 0 || mt9t013_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); + mdelay(20); + + rc = mt9t013_probe_init_sensor(info); + if (rc < 0) { + i2c_del_driver(&mt9t013_i2c_driver); + goto probe_done; + } + + s->s_init = mt9t013_sensor_open_init; + s->s_release = mt9t013_sensor_release; + s->s_config = mt9t013_sensor_config; + mt9t013_sensor_init_done(info); + +probe_done: + return rc; +} + +static int __mt9t013_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9t013_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9t013_probe, + .driver = { + .name = "msm_camera_mt9t013", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9t013_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9t013_init); diff --git a/drivers/staging/dream/camera/mt9t013.h b/drivers/staging/dream/camera/mt9t013.h new file mode 100644 index 0000000..9bce203 --- /dev/null +++ b/drivers/staging/dream/camera/mt9t013.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef MT9T013_H +#define MT9T013_H + +#include <linux/types.h> + +struct reg_struct { + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ +}; + +struct mt9t013_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; +}; + +struct mt9t013_reg { + struct reg_struct *reg_pat; + uint16_t reg_pat_size; + struct mt9t013_i2c_reg_conf *ttbl; + uint16_t ttbl_size; + struct mt9t013_i2c_reg_conf *lctbl; + uint16_t lctbl_size; + struct mt9t013_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* #define MT9T013_H */ diff --git a/drivers/staging/dream/camera/mt9t013_reg.c b/drivers/staging/dream/camera/mt9t013_reg.c new file mode 100644 index 0000000..ba0a1d4 --- /dev/null +++ b/drivers/staging/dream/camera/mt9t013_reg.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2009 QUALCOMM Incorporated. + */ + +#include "mt9t013.h" +#include <linux/kernel.h> + +struct reg_struct const mt9t013_reg_pat[2] = { + { /* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ + /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps + * if this change */ + 8, + + /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps + * if this change */ + 1, + + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 2, + + /* pll_multiplier REG=0x0306 60 for 30fps preview, 40 + * for 20fps preview + * 46 for 30fps preview, try 47/48 to increase further */ + 46, + + /* op_pix_clk_div REG=0x0308 */ + 8, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2053, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1541, + + /* read_mode REG=0x3040 */ + 0x046C, + + /* x_output_size REG=0x034C */ + 1024, + + /* y_output_size REG=0x034E */ + 768, + + /* line_length_pck REG=0x300C */ + 2616, + + /* frame_length_lines REG=0x300A */ + 916, + + /* coarse_int_time REG=0x3012 */ + 16, + + /* fine_int_time REG=0x3014 */ + 1461 + }, + { /*Snapshot */ + /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps + * if this change */ + 8, + + /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps + * if this change */ + 1, + + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 2, + + /* pll_multiplier REG=0x0306 50 for 15fps snapshot, + * 40 for 10fps snapshot + * 46 for 30fps snapshot, try 47/48 to increase further */ + 46, + + /* op_pix_clk_div REG=0x0308 */ + 8, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2071, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1551, + + /* read_mode REG=0x3040 */ + 0x0024, + + /* x_output_size REG=0x034C */ + 2064, + + /* y_output_size REG=0x034E */ + 1544, + + /* line_length_pck REG=0x300C */ + 2952, + + /* frame_length_lines REG=0x300A */ + 1629, + + /* coarse_int_time REG=0x3012 */ + 16, + + /* fine_int_time REG=0x3014 */ + 733 + } +}; + +struct mt9t013_i2c_reg_conf mt9t013_test_tbl[] = { + { 0x3044, 0x0544 & 0xFBFF }, + { 0x30CA, 0x0004 | 0x0001 }, + { 0x30D4, 0x9020 & 0x7FFF }, + { 0x31E0, 0x0003 & 0xFFFE }, + { 0x3180, 0x91FF & 0x7FFF }, + { 0x301A, (0x10CC | 0x8000) & 0xFFF7 }, + { 0x301E, 0x0000 }, + { 0x3780, 0x0000 }, +}; + +/* [Lens shading 85 Percent TL84] */ +struct mt9t013_i2c_reg_conf mt9t013_lc_tbl[] = { + { 0x360A, 0x0290 }, /* P_RD_P0Q0 */ + { 0x360C, 0xC92D }, /* P_RD_P0Q1 */ + { 0x360E, 0x0771 }, /* P_RD_P0Q2 */ + { 0x3610, 0xE38C }, /* P_RD_P0Q3 */ + { 0x3612, 0xD74F }, /* P_RD_P0Q4 */ + { 0x364A, 0x168C }, /* P_RD_P1Q0 */ + { 0x364C, 0xCACB }, /* P_RD_P1Q1 */ + { 0x364E, 0x8C4C }, /* P_RD_P1Q2 */ + { 0x3650, 0x0BEA }, /* P_RD_P1Q3 */ + { 0x3652, 0xDC0F }, /* P_RD_P1Q4 */ + { 0x368A, 0x70B0 }, /* P_RD_P2Q0 */ + { 0x368C, 0x200B }, /* P_RD_P2Q1 */ + { 0x368E, 0x30B2 }, /* P_RD_P2Q2 */ + { 0x3690, 0xD04F }, /* P_RD_P2Q3 */ + { 0x3692, 0xACF5 }, /* P_RD_P2Q4 */ + { 0x36CA, 0xF7C9 }, /* P_RD_P3Q0 */ + { 0x36CC, 0x2AED }, /* P_RD_P3Q1 */ + { 0x36CE, 0xA652 }, /* P_RD_P3Q2 */ + { 0x36D0, 0x8192 }, /* P_RD_P3Q3 */ + { 0x36D2, 0x3A15 }, /* P_RD_P3Q4 */ + { 0x370A, 0xDA30 }, /* P_RD_P4Q0 */ + { 0x370C, 0x2E2F }, /* P_RD_P4Q1 */ + { 0x370E, 0xBB56 }, /* P_RD_P4Q2 */ + { 0x3710, 0x8195 }, /* P_RD_P4Q3 */ + { 0x3712, 0x02F9 }, /* P_RD_P4Q4 */ + { 0x3600, 0x0230 }, /* P_GR_P0Q0 */ + { 0x3602, 0x58AD }, /* P_GR_P0Q1 */ + { 0x3604, 0x18D1 }, /* P_GR_P0Q2 */ + { 0x3606, 0x260D }, /* P_GR_P0Q3 */ + { 0x3608, 0xF530 }, /* P_GR_P0Q4 */ + { 0x3640, 0x17EB }, /* P_GR_P1Q0 */ + { 0x3642, 0x3CAB }, /* P_GR_P1Q1 */ + { 0x3644, 0x87CE }, /* P_GR_P1Q2 */ + { 0x3646, 0xC02E }, /* P_GR_P1Q3 */ + { 0x3648, 0xF48F }, /* P_GR_P1Q4 */ + { 0x3680, 0x5350 }, /* P_GR_P2Q0 */ + { 0x3682, 0x7EAF }, /* P_GR_P2Q1 */ + { 0x3684, 0x4312 }, /* P_GR_P2Q2 */ + { 0x3686, 0xC652 }, /* P_GR_P2Q3 */ + { 0x3688, 0xBC15 }, /* P_GR_P2Q4 */ + { 0x36C0, 0xB8AD }, /* P_GR_P3Q0 */ + { 0x36C2, 0xBDCD }, /* P_GR_P3Q1 */ + { 0x36C4, 0xE4B2 }, /* P_GR_P3Q2 */ + { 0x36C6, 0xB50F }, /* P_GR_P3Q3 */ + { 0x36C8, 0x5B95 }, /* P_GR_P3Q4 */ + { 0x3700, 0xFC90 }, /* P_GR_P4Q0 */ + { 0x3702, 0x8C51 }, /* P_GR_P4Q1 */ + { 0x3704, 0xCED6 }, /* P_GR_P4Q2 */ + { 0x3706, 0xB594 }, /* P_GR_P4Q3 */ + { 0x3708, 0x0A39 }, /* P_GR_P4Q4 */ + { 0x3614, 0x0230 }, /* P_BL_P0Q0 */ + { 0x3616, 0x160D }, /* P_BL_P0Q1 */ + { 0x3618, 0x08D1 }, /* P_BL_P0Q2 */ + { 0x361A, 0x98AB }, /* P_BL_P0Q3 */ + { 0x361C, 0xEA50 }, /* P_BL_P0Q4 */ + { 0x3654, 0xB4EA }, /* P_BL_P1Q0 */ + { 0x3656, 0xEA6C }, /* P_BL_P1Q1 */ + { 0x3658, 0xFE08 }, /* P_BL_P1Q2 */ + { 0x365A, 0x2C6E }, /* P_BL_P1Q3 */ + { 0x365C, 0xEB0E }, /* P_BL_P1Q4 */ + { 0x3694, 0x6DF0 }, /* P_BL_P2Q0 */ + { 0x3696, 0x3ACF }, /* P_BL_P2Q1 */ + { 0x3698, 0x3E0F }, /* P_BL_P2Q2 */ + { 0x369A, 0xB2B1 }, /* P_BL_P2Q3 */ + { 0x369C, 0xC374 }, /* P_BL_P2Q4 */ + { 0x36D4, 0xF2AA }, /* P_BL_P3Q0 */ + { 0x36D6, 0x8CCC }, /* P_BL_P3Q1 */ + { 0x36D8, 0xDEF2 }, /* P_BL_P3Q2 */ + { 0x36DA, 0xFA11 }, /* P_BL_P3Q3 */ + { 0x36DC, 0x42F5 }, /* P_BL_P3Q4 */ + { 0x3714, 0xF4F1 }, /* P_BL_P4Q0 */ + { 0x3716, 0xF6F0 }, /* P_BL_P4Q1 */ + { 0x3718, 0x8FD6 }, /* P_BL_P4Q2 */ + { 0x371A, 0xEA14 }, /* P_BL_P4Q3 */ + { 0x371C, 0x6338 }, /* P_BL_P4Q4 */ + { 0x361E, 0x0350 }, /* P_GB_P0Q0 */ + { 0x3620, 0x91AE }, /* P_GB_P0Q1 */ + { 0x3622, 0x0571 }, /* P_GB_P0Q2 */ + { 0x3624, 0x100D }, /* P_GB_P0Q3 */ + { 0x3626, 0xCA70 }, /* P_GB_P0Q4 */ + { 0x365E, 0xE6CB }, /* P_GB_P1Q0 */ + { 0x3660, 0x50ED }, /* P_GB_P1Q1 */ + { 0x3662, 0x3DAE }, /* P_GB_P1Q2 */ + { 0x3664, 0xAA4F }, /* P_GB_P1Q3 */ + { 0x3666, 0xDC50 }, /* P_GB_P1Q4 */ + { 0x369E, 0x5470 }, /* P_GB_P2Q0 */ + { 0x36A0, 0x1F6E }, /* P_GB_P2Q1 */ + { 0x36A2, 0x6671 }, /* P_GB_P2Q2 */ + { 0x36A4, 0xC010 }, /* P_GB_P2Q3 */ + { 0x36A6, 0x8DF5 }, /* P_GB_P2Q4 */ + { 0x36DE, 0x0B0C }, /* P_GB_P3Q0 */ + { 0x36E0, 0x84CE }, /* P_GB_P3Q1 */ + { 0x36E2, 0x8493 }, /* P_GB_P3Q2 */ + { 0x36E4, 0xA610 }, /* P_GB_P3Q3 */ + { 0x36E6, 0x50B5 }, /* P_GB_P3Q4 */ + { 0x371E, 0x9651 }, /* P_GB_P4Q0 */ + { 0x3720, 0x1EAB }, /* P_GB_P4Q1 */ + { 0x3722, 0xAF76 }, /* P_GB_P4Q2 */ + { 0x3724, 0xE4F4 }, /* P_GB_P4Q3 */ + { 0x3726, 0x79F8 }, /* P_GB_P4Q4 */ + { 0x3782, 0x0410 }, /* POLY_ORIGIN_C */ + { 0x3784, 0x0320 }, /* POLY_ORIGIN_R */ + { 0x3780, 0x8000 } /* POLY_SC_ENABLE */ +}; + +struct mt9t013_reg mt9t013_regs = { + .reg_pat = &mt9t013_reg_pat[0], + .reg_pat_size = ARRAY_SIZE(mt9t013_reg_pat), + .ttbl = &mt9t013_test_tbl[0], + .ttbl_size = ARRAY_SIZE(mt9t013_test_tbl), + .lctbl = &mt9t013_lc_tbl[0], + .lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl), + .rftbl = &mt9t013_lc_tbl[0], /* &mt9t013_rolloff_tbl[0], */ + .rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl) +}; + + diff --git a/drivers/staging/dream/camera/s5k3e2fx.c b/drivers/staging/dream/camera/s5k3e2fx.c new file mode 100644 index 0000000..edba198 --- /dev/null +++ b/drivers/staging/dream/camera/s5k3e2fx.c @@ -0,0 +1,1310 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <media/msm_camera.h> +#include <mach/gpio.h> +#include <mach/camera.h> +#include "s5k3e2fx.h" + +#define S5K3E2FX_REG_MODEL_ID 0x0000 +#define S5K3E2FX_MODEL_ID 0x3E2F + +/* PLL Registers */ +#define REG_PRE_PLL_CLK_DIV 0x0305 +#define REG_PLL_MULTIPLIER_MSB 0x0306 +#define REG_PLL_MULTIPLIER_LSB 0x0307 +#define REG_VT_PIX_CLK_DIV 0x0301 +#define REG_VT_SYS_CLK_DIV 0x0303 +#define REG_OP_PIX_CLK_DIV 0x0309 +#define REG_OP_SYS_CLK_DIV 0x030B + +/* Data Format Registers */ +#define REG_CCP_DATA_FORMAT_MSB 0x0112 +#define REG_CCP_DATA_FORMAT_LSB 0x0113 + +/* Output Size */ +#define REG_X_OUTPUT_SIZE_MSB 0x034C +#define REG_X_OUTPUT_SIZE_LSB 0x034D +#define REG_Y_OUTPUT_SIZE_MSB 0x034E +#define REG_Y_OUTPUT_SIZE_LSB 0x034F + +/* Binning */ +#define REG_X_EVEN_INC 0x0381 +#define REG_X_ODD_INC 0x0383 +#define REG_Y_EVEN_INC 0x0385 +#define REG_Y_ODD_INC 0x0387 +/*Reserved register */ +#define REG_BINNING_ENABLE 0x3014 + +/* Frame Fotmat */ +#define REG_FRAME_LENGTH_LINES_MSB 0x0340 +#define REG_FRAME_LENGTH_LINES_LSB 0x0341 +#define REG_LINE_LENGTH_PCK_MSB 0x0342 +#define REG_LINE_LENGTH_PCK_LSB 0x0343 + +/* MSR setting */ +/* Reserved registers */ +#define REG_SHADE_CLK_ENABLE 0x30AC +#define REG_SEL_CCP 0x30C4 +#define REG_VPIX 0x3024 +#define REG_CLAMP_ON 0x3015 +#define REG_OFFSET 0x307E + +/* CDS timing settings */ +/* Reserved registers */ +#define REG_LD_START 0x3000 +#define REG_LD_END 0x3001 +#define REG_SL_START 0x3002 +#define REG_SL_END 0x3003 +#define REG_RX_START 0x3004 +#define REG_S1_START 0x3005 +#define REG_S1_END 0x3006 +#define REG_S1S_START 0x3007 +#define REG_S1S_END 0x3008 +#define REG_S3_START 0x3009 +#define REG_S3_END 0x300A +#define REG_CMP_EN_START 0x300B +#define REG_CLP_SL_START 0x300C +#define REG_CLP_SL_END 0x300D +#define REG_OFF_START 0x300E +#define REG_RMP_EN_START 0x300F +#define REG_TX_START 0x3010 +#define REG_TX_END 0x3011 +#define REG_STX_WIDTH 0x3012 +#define REG_TYPE1_AF_ENABLE 0x3130 +#define DRIVER_ENABLED 0x0001 +#define AUTO_START_ENABLED 0x0010 +#define REG_NEW_POSITION 0x3131 +#define REG_3152_RESERVED 0x3152 +#define REG_315A_RESERVED 0x315A +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204 +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205 +#define REG_FINE_INTEGRATION_TIME 0x0200 +#define REG_COARSE_INTEGRATION_TIME 0x0202 +#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203 + +/* Mode select register */ +#define S5K3E2FX_REG_MODE_SELECT 0x0100 +#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */ +#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ +#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103 +#define S5K3E2FX_SOFTWARE_RESET 0x01 +#define REG_TEST_PATTERN_MODE 0x0601 + +struct reg_struct { + uint8_t pre_pll_clk_div; /* 0x0305 */ + uint8_t pll_multiplier_msb; /* 0x0306 */ + uint8_t pll_multiplier_lsb; /* 0x0307 */ + uint8_t vt_pix_clk_div; /* 0x0301 */ + uint8_t vt_sys_clk_div; /* 0x0303 */ + uint8_t op_pix_clk_div; /* 0x0309 */ + uint8_t op_sys_clk_div; /* 0x030B */ + uint8_t ccp_data_format_msb; /* 0x0112 */ + uint8_t ccp_data_format_lsb; /* 0x0113 */ + uint8_t x_output_size_msb; /* 0x034C */ + uint8_t x_output_size_lsb; /* 0x034D */ + uint8_t y_output_size_msb; /* 0x034E */ + uint8_t y_output_size_lsb; /* 0x034F */ + uint8_t x_even_inc; /* 0x0381 */ + uint8_t x_odd_inc; /* 0x0383 */ + uint8_t y_even_inc; /* 0x0385 */ + uint8_t y_odd_inc; /* 0x0387 */ + uint8_t binning_enable; /* 0x3014 */ + uint8_t frame_length_lines_msb; /* 0x0340 */ + uint8_t frame_length_lines_lsb; /* 0x0341 */ + uint8_t line_length_pck_msb; /* 0x0342 */ + uint8_t line_length_pck_lsb; /* 0x0343 */ + uint8_t shade_clk_enable ; /* 0x30AC */ + uint8_t sel_ccp; /* 0x30C4 */ + uint8_t vpix; /* 0x3024 */ + uint8_t clamp_on; /* 0x3015 */ + uint8_t offset; /* 0x307E */ + uint8_t ld_start; /* 0x3000 */ + uint8_t ld_end; /* 0x3001 */ + uint8_t sl_start; /* 0x3002 */ + uint8_t sl_end; /* 0x3003 */ + uint8_t rx_start; /* 0x3004 */ + uint8_t s1_start; /* 0x3005 */ + uint8_t s1_end; /* 0x3006 */ + uint8_t s1s_start; /* 0x3007 */ + uint8_t s1s_end; /* 0x3008 */ + uint8_t s3_start; /* 0x3009 */ + uint8_t s3_end; /* 0x300A */ + uint8_t cmp_en_start; /* 0x300B */ + uint8_t clp_sl_start; /* 0x300C */ + uint8_t clp_sl_end; /* 0x300D */ + uint8_t off_start; /* 0x300E */ + uint8_t rmp_en_start; /* 0x300F */ + uint8_t tx_start; /* 0x3010 */ + uint8_t tx_end; /* 0x3011 */ + uint8_t stx_width; /* 0x3012 */ + uint8_t reg_3152_reserved; /* 0x3152 */ + uint8_t reg_315A_reserved; /* 0x315A */ + uint8_t analogue_gain_code_global_msb; /* 0x0204 */ + uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ + uint8_t fine_integration_time; /* 0x0200 */ + uint8_t coarse_integration_time; /* 0x0202 */ + uint32_t size_h; + uint32_t blk_l; + uint32_t size_w; + uint32_t blk_p; +}; + +struct reg_struct s5k3e2fx_reg_pat[2] = { + { /* Preview */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + 0x88, /* pll_multiplier_lsb REG=0x0307 */ + 0x0a, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x0a, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ + 0x05, /* x_output_size_msb REG=0x034C */ + 0x10, /* x_output_size_lsb REG=0x034D */ + 0x03, /* y_output_size_msb REG=0x034E */ + 0xcc, /* y_output_size_lsb REG=0x034F */ + + /* enable binning for preview */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x03, /* y_odd_inc REG=0x0387 */ + 0x06, /* binning_enable REG=0x3014 */ + + 0x03, /* frame_length_lines_msb REG=0x0340 */ + 0xde, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x04, /* vpix REG=0x3024 */ + 0x00, /* clamp_on REG=0x3015 */ + 0x02, /* offset REG=0x307E */ + 0x03, /* ld_start REG=0x3000 */ + 0x9c, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x9e, /* sl_end REG=0x3003 */ + 0x05, /* rx_start REG=0x3004 */ + 0x0f, /* s1_start REG=0x3005 */ + 0x24, /* s1_end REG=0x3006 */ + 0x7c, /* s1s_start REG=0x3007 */ + 0x9a, /* s1s_end REG=0x3008 */ + 0x10, /* s3_start REG=0x3009 */ + 0x14, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x04, /* clp_sl_start REG=0x300C */ + 0x26, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x30, /* tx_start REG=0x3010 */ + 0x4e, /* tx_end REG=0x3011 */ + 0x1E, /* stx_width REG=0x3012 */ + 0x08, /* reg_3152_reserved REG=0x3152 */ + 0x10, /* reg_315A_reserved REG=0x315A */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ + 972, + 18, + 1296, + 1436 + }, + { /* Snapshot */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + 0x88, /* pll_multiplier_lsb REG=0x0307 */ + 0x0a, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x0a, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ + 0x0a, /* x_output_size_msb REG=0x034C */ + 0x30, /* x_output_size_lsb REG=0x034D */ + 0x07, /* y_output_size_msb REG=0x034E */ + 0xa8, /* y_output_size_lsb REG=0x034F */ + + /* disable binning for snapshot */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x01, /* y_odd_inc REG=0x0387 */ + 0x00, /* binning_enable REG=0x3014 */ + + 0x07, /* frame_length_lines_msb REG=0x0340 */ + 0xb6, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x04, /* vpix REG=0x3024 */ + 0x00, /* clamp_on REG=0x3015 */ + 0x02, /* offset REG=0x307E */ + 0x03, /* ld_start REG=0x3000 */ + 0x9c, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x9e, /* sl_end REG=0x3003 */ + 0x05, /* rx_start REG=0x3004 */ + 0x0f, /* s1_start REG=0x3005 */ + 0x24, /* s1_end REG=0x3006 */ + 0x7c, /* s1s_start REG=0x3007 */ + 0x9a, /* s1s_end REG=0x3008 */ + 0x10, /* s3_start REG=0x3009 */ + 0x14, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x04, /* clp_sl_start REG=0x300C */ + 0x26, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x30, /* tx_start REG=0x3010 */ + 0x4e, /* tx_end REG=0x3011 */ + 0x1E, /* stx_width REG=0x3012 */ + 0x08, /* reg_3152_reserved REG=0x3152 */ + 0x10, /* reg_315A_reserved REG=0x315A */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ + 1960, + 14, + 2608, + 124 + } +}; + +struct s5k3e2fx_work { + struct work_struct work; +}; +static struct s5k3e2fx_work *s5k3e2fx_sensorw; +static struct i2c_client *s5k3e2fx_client; + +struct s5k3e2fx_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum msm_s_resolution prev_res; + enum msm_s_resolution pict_res; + enum msm_s_resolution curr_res; + enum msm_s_test_mode set_test; +}; + +struct s5k3e2fx_i2c_reg_conf { + unsigned short waddr; + unsigned char bdata; +}; + +static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue); +DECLARE_MUTEX(s5k3e2fx_sem); + +static int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, + int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(s5k3e2fx_client->adapter, msgs, 2) < 0) { + CDBG("s5k3e2fx_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t s5k3e2fx_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(s5k3e2fx_client->adapter, msg, 1) < 0) { + CDBG("s5k3e2fx_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr, + unsigned char bdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = bdata; + + rc = s5k3e2fx_i2c_txdata(saddr, buf, 3); + + if (rc < 0) + CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, bdata); + + return rc; +} + +static int32_t s5k3e2fx_i2c_write_table( + struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num) +{ + int i; + int32_t rc = -EIO; + for (i = 0; i < num; i++) { + if (rc < 0) + break; + reg_cfg_tbl++; + } + + return rc; +} + +static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + CDBG("s5k3e2fx_i2c_read failed!\n"); + + return rc; +} + +static int s5k3e2fx_probe_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + uint16_t chipid = 0; + + rc = gpio_request(data->sensor_reset, "s5k3e2fx"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n"); + + rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODEL_ID, &chipid); + if (rc < 0) + goto init_probe_fail; + + if (chipid != S5K3E2FX_MODEL_ID) { + CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid); + rc = -ENODEV; + goto init_probe_fail; + } + + goto init_probe_done; + +init_probe_fail: + s5k3e2fx_probe_init_done(data); +init_probe_done: + return rc; +} + +static int s5k3e2fx_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&s5k3e2fx_wait_queue); + return 0; +} + +static const struct i2c_device_id s5k3e2fx_i2c_id[] = { + { "s5k3e2fx", 0}, + { } +}; + +static int s5k3e2fx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + CDBG("s5k3e2fx_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CDBG("i2c_check_functionality failed\n"); + goto probe_failure; + } + + s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL); + if (!s5k3e2fx_sensorw) { + CDBG("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, s5k3e2fx_sensorw); + s5k3e2fx_init_client(client); + s5k3e2fx_client = client; + + mdelay(50); + + CDBG("s5k3e2fx_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + CDBG("s5k3e2fx_probe failed! rc = %d\n", rc); + return rc; +} + +static struct i2c_driver s5k3e2fx_i2c_driver = { + .id_table = s5k3e2fx_i2c_id, + .probe = s5k3e2fx_i2c_probe, + .remove = __exit_p(s5k3e2fx_i2c_remove), + .driver = { + .name = "s5k3e2fx", + }, +}; + +static int32_t s5k3e2fx_test(enum msm_s_test_mode mo) +{ + int32_t rc = 0; + + if (mo == S_TEST_OFF) + rc = 0; + else + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + + return rc; +} + +static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate, + enum msm_s_setting rt) +{ + int32_t rc = 0; + uint16_t num_lperf; + + switch (rupdate) { + case S_UPDATE_PERIODIC: + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + + struct s5k3e2fx_i2c_reg_conf tbl_1[] = { + {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, + {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, + {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, + }; + + struct s5k3e2fx_i2c_reg_conf tbl_2[] = { + {REG_FRAME_LENGTH_LINES_MSB, 0}, + {REG_FRAME_LENGTH_LINES_LSB, 0}, + {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, + {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, + {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, + {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, + {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, + {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, + {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, + {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, + {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, + {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, + {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, + {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, + {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, + {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, + {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, + {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, + {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, + {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, + {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, + {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, + {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, + {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, + {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, + {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, + {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, + {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, + {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, + }; + + rc = s5k3e2fx_i2c_write_table(&tbl_1[0], + ARRAY_SIZE(tbl_1)); + if (rc < 0) + return rc; + + num_lperf = + (uint16_t)((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8) & 0xFF00) + + s5k3e2fx_reg_pat[rt].frame_length_lines_lsb; + + num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400; + + tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8}; + tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)}; + + rc = s5k3e2fx_i2c_write_table(&tbl_2[0], + ARRAY_SIZE(tbl_2)); + if (rc < 0) + return rc; + + mdelay(5); + + rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test); + if (rc < 0) + return rc; + } + break; /* UPDATE_PERIODIC */ + + case S_REG_INIT: + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + + struct s5k3e2fx_i2c_reg_conf tbl_3[] = { + {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY}, + /* PLL setting */ + {REG_PRE_PLL_CLK_DIV, s5k3e2fx_reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER_MSB, s5k3e2fx_reg_pat[rt].pll_multiplier_msb}, + {REG_PLL_MULTIPLIER_LSB, s5k3e2fx_reg_pat[rt].pll_multiplier_lsb}, + {REG_VT_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_sys_clk_div}, + {REG_OP_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].op_sys_clk_div}, + /*Data Format */ + {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, + {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, + /*Output Size */ + {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + /* Binning */ + {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc }, + {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, + /* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, s5k3e2fx_reg_pat[rt].frame_length_lines_msb}, + {REG_FRAME_LENGTH_LINES_LSB, s5k3e2fx_reg_pat[rt].frame_length_lines_lsb}, + {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, + /* MSR setting */ + {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, + {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, + {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, + {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, + {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, + /* CDS timing setting */ + {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, + {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, + {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, + {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, + {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, + {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, + {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, + {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, + {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, + {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, + {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, + {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, + {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, + {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, + {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, + {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, + {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, + {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, + {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, + {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, + {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, + }; + + /* reset fps_divider */ + s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; + rc = s5k3e2fx_i2c_write_table(&tbl_3[0], + ARRAY_SIZE(tbl_3)); + if (rc < 0) + return rc; + } + break; /* case REG_INIT: */ + + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL); + if (!s5k3e2fx_ctrl) { + CDBG("s5k3e2fx_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400; + s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400; + s5k3e2fx_ctrl->set_test = S_TEST_OFF; + s5k3e2fx_ctrl->prev_res = S_QTR_SIZE; + s5k3e2fx_ctrl->pict_res = S_FULL_SIZE; + + if (data) + s5k3e2fx_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(24000000); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = s5k3e2fx_probe_init_sensor(data); + if (rc < 0) + goto init_fail1; + + if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE) + rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW); + else + rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE); + + if (rc < 0) { + CDBG("s5k3e2fx_setting failed. rc = %d\n", rc); + goto init_fail1; + } + + /* initialize AF */ + if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3146, 0x3A)) < 0) + goto init_fail1; + + if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3130, 0x03)) < 0) + goto init_fail1; + + goto init_done; + +init_fail1: + s5k3e2fx_probe_init_done(data); + kfree(s5k3e2fx_ctrl); +init_done: + return rc; +} + +static int32_t s5k3e2fx_power_down(void) +{ + int32_t rc = 0; + return rc; +} + +static int s5k3e2fx_sensor_release(void) +{ + int rc = -EBADF; + + down(&s5k3e2fx_sem); + + s5k3e2fx_power_down(); + + gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset, + 0); + gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset); + + kfree(s5k3e2fx_ctrl); + s5k3e2fx_ctrl = NULL; + + CDBG("s5k3e2fx_release completed\n"); + + up(&s5k3e2fx_sem); + return rc; +} + +static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /* Q10 */ + + divider = (uint32_t) + ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 / + ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) * + (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p)); + + /* Verify PCLK settings and frame sizes. */ + *pfps = (uint16_t)(fps * divider / 0x00000400); +} + +static uint16_t s5k3e2fx_get_prev_lines_pf(void) +{ + return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l); +} + +static uint16_t s5k3e2fx_get_prev_pixels_pl(void) +{ + return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p); +} + +static uint16_t s5k3e2fx_get_pict_lines_pf(void) +{ + return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l); +} + +static uint16_t s5k3e2fx_get_pict_pixels_pl(void) +{ + return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p); +} + +static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) +{ + uint32_t snapshot_lines_per_frame; + + if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE) + snapshot_lines_per_frame = + s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; + else + snapshot_lines_per_frame = 3961 * 3; + + return snapshot_lines_per_frame; +} + +static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q10 format */ + int32_t rc = 0; + + s5k3e2fx_ctrl->fps_divider = fps->fps_div; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_FRAME_LENGTH_LINES_MSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8); + if (rc < 0) + goto set_fps_done; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_FRAME_LENGTH_LINES_LSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00)); + +set_fps_done: + return rc; +} + +static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + uint16_t max_legal_gain = 0x0200; + uint32_t ll_ratio; /* Q10 */ + uint16_t ll_pck, fl_lines; + uint16_t offset = 4; + uint8_t gain_msb, gain_lsb; + uint8_t intg_t_msb, intg_t_lsb; + uint8_t ll_pck_msb, ll_pck_lsb, tmp; + + struct s5k3e2fx_i2c_reg_conf tbl[2]; + + CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__); + + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + + s5k3e2fx_ctrl->my_reg_gain = gain; + s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line; + + fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + + ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + + } else { + + fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + + ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + } + + if (gain > max_legal_gain) + gain = max_legal_gain; + + /* in Q10 */ + line = (line * s5k3e2fx_ctrl->fps_divider); + + if (fl_lines < (line / 0x400)) + ll_ratio = (line / (fl_lines - offset)); + else + ll_ratio = 0x400; + + /* update gain registers */ + gain_msb = (gain & 0xFF00) >> 8; + gain_lsb = gain & 0x00FF; + tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB; + tbl[0].bdata = gain_msb; + tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB; + tbl[1].bdata = gain_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + ll_pck = ll_pck * ll_ratio; + ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; + ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; + tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; + tbl[0].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_msb; + tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; + tbl[1].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + tmp = (ll_pck * 0x400) / ll_ratio; + intg_t_msb = (tmp & 0xFF00) >> 8; + intg_t_lsb = (tmp & 0x00FF); + tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; + tbl[0].bdata = intg_t_msb; + tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; + tbl[1].bdata = intg_t_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + +write_gain_done: + return rc; +} + +static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); + + rc = + s5k3e2fx_write_exp_gain(gain, line); + + return rc; +} + +static int32_t s5k3e2fx_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case S_QTR_SIZE: + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); + if (rc < 0) + return rc; + + CDBG("s5k3e2fx sensor configuration done!\n"); + break; + + case S_FULL_SIZE: + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + break; + + default: + return 0; + } /* switch */ + + s5k3e2fx_ctrl->prev_res = res; + s5k3e2fx_ctrl->curr_res = res; + s5k3e2fx_ctrl->sensormode = mode; + + rc = + s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, + s5k3e2fx_ctrl->my_reg_line_count); + + return rc; +} + +static int32_t s5k3e2fx_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; + s5k3e2fx_ctrl->sensormode = mode; + + return rc; +} + +static int32_t s5k3e2fx_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; + s5k3e2fx_ctrl->sensormode = mode; + + return rc; +} + +static int32_t s5k3e2fx_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = s5k3e2fx_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = s5k3e2fx_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = s5k3e2fx_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int32_t s5k3e2fx_set_default_focus(void) +{ + int32_t rc = 0; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3131, 0); + if (rc < 0) + return rc; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3132, 0); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_lens_pos = 0; + + return rc; +} + +static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) +{ + int32_t rc = 0; + int32_t i; + int16_t step_direction; + int16_t actual_step; + int16_t next_pos, pos_offset; + int16_t init_code = 50; + uint8_t next_pos_msb, next_pos_lsb; + int16_t s_move[5]; + uint32_t gain; /* Q10 format */ + + if (direction == MOVE_NEAR) + step_direction = 20; + else if (direction == MOVE_FAR) + step_direction = -20; + else { + CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + actual_step = step_direction * (int16_t)num_steps; + pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos; + gain = actual_step * 0x400 / 5; + + for (i = 0; i <= 4; i++) { + if (actual_step >= 0) + s_move[i] = ((((i+1)*gain+0x200) - (i*gain+0x200))/0x400); + else + s_move[i] = ((((i+1)*gain-0x200) - (i*gain-0x200))/0x400); + } + + /* Ring Damping Code */ + for (i = 0; i <= 4; i++) { + next_pos = (int16_t)(pos_offset + s_move[i]); + + if (next_pos > (738 + init_code)) + next_pos = 738 + init_code; + else if (next_pos < 0) + next_pos = 0; + + CDBG("next_position in damping mode = %d\n", next_pos); + /* Writing the Values to the actuator */ + if (next_pos == init_code) + next_pos = 0x00; + + next_pos_msb = next_pos >> 8; + next_pos_lsb = next_pos & 0x00FF; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, next_pos_msb); + if (rc < 0) + break; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, next_pos_lsb); + if (rc < 0) + break; + + pos_offset = next_pos; + s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code; + if (i < 4) + mdelay(3); + } + + return rc; +} + +static int s5k3e2fx_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&s5k3e2fx_sem); + + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + s5k3e2fx_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = s5k3e2fx_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = + s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); + rc = + s5k3e2fx_set_pict_exp_gain( + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = + s5k3e2fx_set_sensor_mode( + cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = s5k3e2fx_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = + s5k3e2fx_move_focus( + cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = + s5k3e2fx_set_default_focus(); + break; + + case CFG_GET_AF_MAX_STEPS: + case CFG_SET_EFFECT: + case CFG_SET_LENS_SHADING: + default: + rc = -EINVAL; + break; + } + + up(&s5k3e2fx_sem); + return rc; +} + +static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = 0; + + rc = i2c_add_driver(&s5k3e2fx_i2c_driver); + if (rc < 0 || s5k3e2fx_client == NULL) { + rc = -ENOTSUPP; + goto probe_fail; + } + + msm_camio_clk_rate_set(24000000); + mdelay(20); + + rc = s5k3e2fx_probe_init_sensor(info); + if (rc < 0) + goto probe_fail; + + s->s_init = s5k3e2fx_sensor_open_init; + s->s_release = s5k3e2fx_sensor_release; + s->s_config = s5k3e2fx_sensor_config; + s5k3e2fx_probe_init_done(info); + + return rc; + +probe_fail: + CDBG("SENSOR PROBE FAILS!\n"); + return rc; +} + +static int __s5k3e2fx_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __s5k3e2fx_probe, + .driver = { + .name = "msm_camera_s5k3e2fx", + .owner = THIS_MODULE, + }, +}; + +static int __init s5k3e2fx_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(s5k3e2fx_init); + diff --git a/drivers/staging/dream/camera/s5k3e2fx.h b/drivers/staging/dream/camera/s5k3e2fx.h new file mode 100644 index 0000000..69bc750 --- /dev/null +++ b/drivers/staging/dream/camera/s5k3e2fx.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef CAMSENSOR_S5K3E2FX +#define CAMSENSOR_S5K3E2FX + +#include <mach/board.h> +#endif /* CAMSENSOR_S5K3E2FX */ |