Commit eb7b797b authored by Brian Swetland's avatar Brian Swetland Committed by Greg Kroah-Hartman

Staging: HTC Dream: add camera support

This patch adds driver for HTC Dream camera. I guess driver is
slightly higher quality than usual for staging/ , but it is fairly big
and I don't feel like doing all the cleanups myself. Also some parts
can probably be removed, as they did not end up in shipping hardware..
Signed-off-by: default avatarPavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Iliyan Malchev <ibm@android.com>
Cc: San Mehat <san@android.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent caff4cae
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
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
/*
* 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(&region->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,
&region, 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,
&region, 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, &region[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 = &region[0];
data = &axi_data;
break;
case CMD_STATS_AF_ENABLE:
axi_data.bufnum1 =
msm_pmem_region_lookup(&sync->stats,
MSM_PMEM_AF, &region[0],
NUM_AF_STAT_OUTPUT_BUFFERS);
if (!axi_data.bufnum1) {
pr_err("%s: pmem region lookup error\n", __FUNCTION__);
return -EINVAL;
}
axi_data.region = &region[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,
&region[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,
&region[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,
&region[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,
&region[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,
&region[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 = &region[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,
&region[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 = &region[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);
/*
* 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);
}
/*
* 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);
}
/*
*
* 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)
return -1;
#if 0
/* FIXEME */
if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420)
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");
/*
* 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;
}
/*
* 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__ */
/*
* 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;
}
/*
* 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__ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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__ */
/*
* 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);
/*
* 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 */
/*
* 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)
};
/*
* 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 */
/*
* 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);
/*
* 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)
};
/*
* 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);
/*
* 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 */
/*
* 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)
};
/*
* 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);
/*
* Copyright (C) 2008-2009 QUALCOMM Incorporated.
*/
#ifndef CAMSENSOR_S5K3E2FX
#define CAMSENSOR_S5K3E2FX
#include <mach/board.h>
#endif /* CAMSENSOR_S5K3E2FX */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment