aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/dream
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dream')
-rw-r--r--drivers/staging/dream/Kconfig12
-rw-r--r--drivers/staging/dream/Makefile4
-rw-r--r--drivers/staging/dream/camera/Kconfig46
-rw-r--r--drivers/staging/dream/camera/Makefile7
-rw-r--r--drivers/staging/dream/camera/msm_camera.c2181
-rw-r--r--drivers/staging/dream/camera/msm_io7x.c291
-rw-r--r--drivers/staging/dream/camera/msm_io8x.c320
-rw-r--r--drivers/staging/dream/camera/msm_v4l2.c797
-rw-r--r--drivers/staging/dream/camera/msm_vfe7x.c701
-rw-r--r--drivers/staging/dream/camera/msm_vfe7x.h255
-rw-r--r--drivers/staging/dream/camera/msm_vfe8x.c756
-rw-r--r--drivers/staging/dream/camera/msm_vfe8x.h895
-rw-r--r--drivers/staging/dream/camera/msm_vfe8x_proc.c4003
-rw-r--r--drivers/staging/dream/camera/msm_vfe8x_proc.h1549
-rw-r--r--drivers/staging/dream/camera/mt9d112.c761
-rw-r--r--drivers/staging/dream/camera/mt9d112.h36
-rw-r--r--drivers/staging/dream/camera/mt9d112_reg.c307
-rw-r--r--drivers/staging/dream/camera/mt9p012.h51
-rw-r--r--drivers/staging/dream/camera/mt9p012_fox.c1305
-rw-r--r--drivers/staging/dream/camera/mt9p012_reg.c573
-rw-r--r--drivers/staging/dream/camera/mt9t013.c1496
-rw-r--r--drivers/staging/dream/camera/mt9t013.h48
-rw-r--r--drivers/staging/dream/camera/mt9t013_reg.c266
-rw-r--r--drivers/staging/dream/camera/s5k3e2fx.c1310
-rw-r--r--drivers/staging/dream/camera/s5k3e2fx.h9
-rw-r--r--drivers/staging/dream/gpio_axis.c180
-rw-r--r--drivers/staging/dream/gpio_event.c224
-rw-r--r--drivers/staging/dream/gpio_input.c343
-rw-r--r--drivers/staging/dream/gpio_matrix.c406
-rw-r--r--drivers/staging/dream/gpio_output.c84
-rw-r--r--drivers/staging/dream/qdsp5/Makefile17
-rw-r--r--drivers/staging/dream/qdsp5/adsp.c1163
-rw-r--r--drivers/staging/dream/qdsp5/adsp.h369
-rw-r--r--drivers/staging/dream/qdsp5/adsp_6210.c283
-rw-r--r--drivers/staging/dream/qdsp5/adsp_6220.c284
-rw-r--r--drivers/staging/dream/qdsp5/adsp_6225.c328
-rw-r--r--drivers/staging/dream/qdsp5/adsp_driver.c641
-rw-r--r--drivers/staging/dream/qdsp5/adsp_info.c121
-rw-r--r--drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c31
-rw-r--r--drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c182
-rw-r--r--drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c65
-rw-r--r--drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c54
-rw-r--r--drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c239
-rw-r--r--drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c163
-rw-r--r--drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c235
-rw-r--r--drivers/staging/dream/qdsp5/audio_aac.c1052
-rw-r--r--drivers/staging/dream/qdsp5/audio_amrnb.c873
-rw-r--r--drivers/staging/dream/qdsp5/audio_evrc.c845
-rw-r--r--drivers/staging/dream/qdsp5/audio_in.c967
-rw-r--r--drivers/staging/dream/qdsp5/audio_mp3.c971
-rw-r--r--drivers/staging/dream/qdsp5/audio_out.c851
-rw-r--r--drivers/staging/dream/qdsp5/audio_qcelp.c856
-rw-r--r--drivers/staging/dream/qdsp5/audmgr.c313
-rw-r--r--drivers/staging/dream/qdsp5/audmgr.h215
-rw-r--r--drivers/staging/dream/qdsp5/audmgr_new.h213
-rw-r--r--drivers/staging/dream/qdsp5/audpp.c429
-rw-r--r--drivers/staging/dream/qdsp5/evlog.h133
-rw-r--r--drivers/staging/dream/qdsp5/snd.c279
-rw-r--r--drivers/staging/dream/smd/Kconfig26
-rw-r--r--drivers/staging/dream/smd/Makefile6
-rw-r--r--drivers/staging/dream/smd/rpc_server_dog_keepalive.c68
-rw-r--r--drivers/staging/dream/smd/rpc_server_time_remote.c77
-rw-r--r--drivers/staging/dream/smd/smd.c1330
-rw-r--r--drivers/staging/dream/smd/smd_private.h171
-rw-r--r--drivers/staging/dream/smd/smd_qmi.c860
-rw-r--r--drivers/staging/dream/smd/smd_rpcrouter.c1274
-rw-r--r--drivers/staging/dream/smd/smd_rpcrouter.h195
-rw-r--r--drivers/staging/dream/smd/smd_rpcrouter_device.c376
-rw-r--r--drivers/staging/dream/smd/smd_rpcrouter_servers.c229
-rw-r--r--drivers/staging/dream/smd/smd_tty.c213
-rw-r--r--drivers/staging/dream/synaptics_i2c_rmi.c658
-rw-r--r--drivers/staging/dream/synaptics_i2c_rmi.h53
72 files changed, 36924 insertions, 0 deletions
diff --git a/drivers/staging/dream/Kconfig b/drivers/staging/dream/Kconfig
new file mode 100644
index 00000000000..52bd187de5a
--- /dev/null
+++ b/drivers/staging/dream/Kconfig
@@ -0,0 +1,12 @@
+source "drivers/staging/dream/smd/Kconfig"
+
+source "drivers/staging/dream/camera/Kconfig"
+
+
+config INPUT_GPIO
+ tristate "GPIO driver support"
+ help
+ Say Y here if you want to support gpio based keys, wheels etc...
+
+
+
diff --git a/drivers/staging/dream/Makefile b/drivers/staging/dream/Makefile
new file mode 100644
index 00000000000..2b791519707
--- /dev/null
+++ b/drivers/staging/dream/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MSM_ADSP) += qdsp5/ smd/
+obj-$(CONFIG_MSM_CAMERA) += camera/
+obj-$(CONFIG_INPUT_GPIO) += gpio_axis.o gpio_event.o gpio_input.o gpio_matrix.o gpio_output.o
+
diff --git a/drivers/staging/dream/camera/Kconfig b/drivers/staging/dream/camera/Kconfig
new file mode 100644
index 00000000000..0a3e903b336
--- /dev/null
+++ b/drivers/staging/dream/camera/Kconfig
@@ -0,0 +1,46 @@
+comment "Qualcomm MSM Camera And Video"
+
+menuconfig MSM_CAMERA
+ bool "Qualcomm MSM camera and video capture support"
+ depends on ARCH_MSM && VIDEO_V4L2_COMMON
+ help
+ Say Y here to enable selecting the video adapters for
+ Qualcomm msm camera and video encoding
+
+config MSM_CAMERA_DEBUG
+ bool "Qualcomm MSM camera debugging with printk"
+ depends on MSM_CAMERA
+ help
+ Enable printk() debug for msm camera
+
+config MSM_CAMERA_FLASH
+ bool "Qualcomm MSM camera flash support"
+ depends on MSM_CAMERA
+ ---help---
+ Enable support for LED flash for msm camera
+
+
+comment "Camera Sensor Selection"
+config MT9T013
+ bool "Sensor mt9t013 (BAYER 3M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 3M Bayer Sensor with AutoFocus
+
+config MT9D112
+ bool "Sensor mt9d112 (YUV 2M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 2M YUV Sensor
+
+config MT9P012
+ bool "Sensor mt9p012 (BAYER 5M)"
+ depends on MSM_CAMERA
+ ---help---
+ MICRON 5M Bayer Sensor with Autofocus
+
+config S5K3E2FX
+ bool "Sensor s5k3e2fx (Samsung 5M)"
+ depends on MSM_CAMERA
+ ---help---
+ Samsung 5M with Autofocus
diff --git a/drivers/staging/dream/camera/Makefile b/drivers/staging/dream/camera/Makefile
new file mode 100644
index 00000000000..4429ae5fcaf
--- /dev/null
+++ b/drivers/staging/dream/camera/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o
+obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o
+obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o
+obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o
+obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o
+obj-$(CONFIG_ARCH_MSM) += msm_vfe7x.o msm_io7x.o
+obj-$(CONFIG_ARCH_QSD) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o
diff --git a/drivers/staging/dream/camera/msm_camera.c b/drivers/staging/dream/camera/msm_camera.c
new file mode 100644
index 00000000000..88165998698
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_camera.c
@@ -0,0 +1,2181 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+//FIXME: most allocations need not be GFP_ATOMIC
+/* FIXME: management of mutexes */
+/* FIXME: msm_pmem_region_lookup return values */
+/* FIXME: way too many copy to/from user */
+/* FIXME: does region->active mean free */
+/* FIXME: check limits on command lenghts passed from userspace */
+/* FIXME: __msm_release: which queues should we flush when opencnt != 0 */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <mach/board.h>
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/android_pmem.h>
+#include <linux/poll.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+#define MSM_MAX_CAMERA_SENSORS 5
+
+#define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \
+ __func__, __LINE__, ((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+#define ERR_COPY_TO_USER() ERR_USER_COPY(1)
+
+static struct class *msm_class;
+static dev_t msm_devno;
+static LIST_HEAD(msm_sensors);
+
+#define __CONTAINS(r, v, l, field) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = __v >= __r->field && \
+ __e <= __r->field + __r->len; \
+ res; \
+})
+
+#define CONTAINS(r1, r2, field) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->field, __r2->len, field); \
+})
+
+#define IN_RANGE(r, v, field) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->field) && \
+ (__vv < (__r->field + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2, field) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->field) __v = __r2->field; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v, field) || \
+ IN_RANGE(__r1, __e, field)); \
+ res; \
+})
+
+#define MSM_DRAIN_QUEUE_NOSYNC(sync, name) do { \
+ struct msm_queue_cmd *qcmd = NULL; \
+ CDBG("%s: draining queue "#name"\n", __func__); \
+ while (!list_empty(&(sync)->name)) { \
+ qcmd = list_first_entry(&(sync)->name, \
+ struct msm_queue_cmd, list); \
+ list_del_init(&qcmd->list); \
+ kfree(qcmd); \
+ }; \
+} while(0)
+
+#define MSM_DRAIN_QUEUE(sync, name) do { \
+ unsigned long flags; \
+ spin_lock_irqsave(&(sync)->name##_lock, flags); \
+ MSM_DRAIN_QUEUE_NOSYNC(sync, name); \
+ spin_unlock_irqrestore(&(sync)->name##_lock, flags); \
+} while(0)
+
+static int check_overlap(struct hlist_head *ptype,
+ unsigned long paddr,
+ unsigned long len)
+{
+ struct msm_pmem_region *region;
+ struct msm_pmem_region t = { .paddr = paddr, .len = len };
+ struct hlist_node *node;
+
+ hlist_for_each_entry(region, node, ptype, list) {
+ if (CONTAINS(region, &t, paddr) ||
+ CONTAINS(&t, region, paddr) ||
+ OVERLAPS(region, &t, paddr)) {
+ printk(KERN_ERR
+ " region (PHYS %p len %ld)"
+ " clashes with registered region"
+ " (paddr %p len %ld)\n",
+ (void *)t.paddr, t.len,
+ (void *)region->paddr, region->len);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int msm_pmem_table_add(struct hlist_head *ptype,
+ struct msm_pmem_info *info)
+{
+ struct file *file;
+ unsigned long paddr;
+ unsigned long vstart;
+ unsigned long len;
+ int rc;
+ struct msm_pmem_region *region;
+
+ rc = get_pmem_file(info->fd, &paddr, &vstart, &len, &file);
+ if (rc < 0) {
+ pr_err("msm_pmem_table_add: get_pmem_file fd %d error %d\n",
+ info->fd, rc);
+ return rc;
+ }
+
+ if (check_overlap(ptype, paddr, len) < 0)
+ return -EINVAL;
+
+ CDBG("%s: type = %d, paddr = 0x%lx, vaddr = 0x%lx\n",
+ __func__,
+ info->type, paddr, (unsigned long)info->vaddr);
+
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ INIT_HLIST_NODE(&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);
diff --git a/drivers/staging/dream/camera/msm_io7x.c b/drivers/staging/dream/camera/msm_io7x.c
new file mode 100644
index 00000000000..55c020bb7af
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_io7x.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2008-2009 QUALCOMM Incorporated
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7
+#define APPS_RESET_OFFSET 0x00000210
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+extern int clk_set_flags(struct clk *clk, unsigned long flags);
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = -1;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk = clk_get(NULL, "vfe_mdc_clk");
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk = clk_get(NULL, "mdc_clk");
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk = clk_get(NULL, "vfe_clk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = -1;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk;
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+
+ if (clk != ERR_PTR(-ENOENT))
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camio_ext = camdev->ioext;
+
+ appio = request_mem_region(camio_ext.appphy,
+ camio_ext.appsz, pdev->name);
+ if (!appio) {
+ rc = -EBUSY;
+ goto enable_fail;
+ }
+
+ appbase = ioremap(camio_ext.appphy,
+ camio_ext.appsz);
+ if (!appbase) {
+ rc = -ENOMEM;
+ goto apps_no_mem;
+ }
+
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio) {
+ rc = -EBUSY;
+ goto mdc_busy;
+ }
+
+ mdcbase = ioremap(camio_ext.mdcphy,
+ camio_ext.mdcsz);
+ if (!mdcbase) {
+ rc = -ENOMEM;
+ goto mdc_no_mem;
+ }
+
+ camdev->camera_gpio_on();
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ return 0;
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+mdc_busy:
+ iounmap(appbase);
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+
+ camdev->camera_gpio_off();
+
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+
+ mask = CAM_SEL_BMSK |
+ CAM_PCLK_SRC_SEL_BMSK |
+ CAM_PCLK_INVERT_BMSK;
+
+ value = 1 << CAM_SEL_SHFT |
+ 3 << CAM_PCLK_SRC_SEL_SHFT |
+ 0 << CAM_PCLK_INVERT_SHFT;
+
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+ mdelay(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ uint32_t val;
+
+ val = readl(appbase + 0x00000210);
+ val |= 0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000210);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+ struct clk *clk = NULL;
+
+ clk = camio_vfe_clk;
+
+ if (clk != NULL && clk != ERR_PTR(-ENOENT)) {
+ switch (srctype) {
+ case MSM_CAMIO_CLK_SRC_INTERNAL:
+ clk_set_flags(clk, 0x00000100 << 1);
+ break;
+
+ case MSM_CAMIO_CLK_SRC_EXTERNAL:
+ clk_set_flags(clk, 0x00000100);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_VFE_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_VFE_CLK);
+}
diff --git a/drivers/staging/dream/camera/msm_io8x.c b/drivers/staging/dream/camera/msm_io8x.c
new file mode 100644
index 00000000000..895161ae2e1
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_io8x.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2008-2009 QUALCOMM Incorporated
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+
+#define CAMIF_CFG_RMSK 0x1fffff
+#define CAM_SEL_BMSK 0x2
+#define CAM_PCLK_SRC_SEL_BMSK 0x60000
+#define CAM_PCLK_INVERT_BMSK 0x80000
+#define CAM_PAD_REG_SW_RESET_BMSK 0x100000
+
+#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000
+#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000
+#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80
+
+#define CAM_SEL_SHFT 0x1
+#define CAM_PCLK_SRC_SEL_SHFT 0x11
+#define CAM_PCLK_INVERT_SHFT 0x13
+#define CAM_PAD_REG_SW_RESET_SHFT 0x14
+
+#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10
+#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF
+#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7
+#define APPS_RESET_OFFSET 0x00000210
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_axi_clk;
+static struct msm_camera_io_ext camio_ext;
+static struct resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+extern int clk_set_flags(struct clk *clk, unsigned long flags);
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ camio_vfe_mdc_clk =
+ clk = clk_get(NULL, "vfe_mdc_clk");
+ break;
+
+ case CAMIO_MDC_CLK:
+ camio_mdc_clk =
+ clk = clk_get(NULL, "mdc_clk");
+ break;
+
+ case CAMIO_VFE_CLK:
+ camio_vfe_clk =
+ clk = clk_get(NULL, "vfe_clk");
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ camio_vfe_axi_clk =
+ clk = clk_get(NULL, "vfe_axi_clk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk))
+ clk_enable(clk);
+ else
+ rc = -1;
+
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_VFE_MDC_CLK:
+ clk = camio_vfe_mdc_clk;
+ break;
+
+ case CAMIO_MDC_CLK:
+ clk = camio_mdc_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ clk = camio_vfe_axi_clk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ } else
+ rc = -1;
+
+ return rc;
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_mdc_clk;
+
+ /* TODO: check return */
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camio_ext = camdev->ioext;
+
+ appio = request_mem_region(camio_ext.appphy,
+ camio_ext.appsz, pdev->name);
+ if (!appio) {
+ rc = -EBUSY;
+ goto enable_fail;
+ }
+
+ appbase = ioremap(camio_ext.appphy,
+ camio_ext.appsz);
+ if (!appbase) {
+ rc = -ENOMEM;
+ goto apps_no_mem;
+ }
+
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio) {
+ rc = -EBUSY;
+ goto mdc_busy;
+ }
+
+ mdcbase = ioremap(camio_ext.mdcphy,
+ camio_ext.mdcsz);
+ if (!mdcbase) {
+ rc = -ENOMEM;
+ goto mdc_no_mem;
+ }
+
+ camdev->camera_gpio_on();
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_AXI_CLK);
+ return 0;
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+mdc_busy:
+ iounmap(appbase);
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+
+ camdev->camera_gpio_off();
+
+ msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+
+ mask = CAM_SEL_BMSK |
+ CAM_PCLK_SRC_SEL_BMSK |
+ CAM_PCLK_INVERT_BMSK |
+ EXT_CAM_HSYNC_POL_SEL_BMSK |
+ EXT_CAM_VSYNC_POL_SEL_BMSK |
+ MDDI_CLK_CHICKEN_BIT_BMSK;
+
+ value = 1 << CAM_SEL_SHFT |
+ 3 << CAM_PCLK_SRC_SEL_SHFT |
+ 0 << CAM_PCLK_INVERT_SHFT |
+ 0 << EXT_CAM_HSYNC_POL_SEL_SHFT |
+ 0 << EXT_CAM_VSYNC_POL_SEL_SHFT |
+ 0 << MDDI_CLK_CHICKEN_BIT_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+
+ mdelay(10);
+
+ /* todo: check return */
+ if (camio_vfe_clk)
+ clk_set_rate(camio_vfe_clk, 96000000);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ uint32_t val;
+
+ val = readl(appbase + 0x00000210);
+ val |= 0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000210);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+
+ reg = (readl(mdcbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ writel((reg & (~mask)) | (value & mask), mdcbase);
+ mdelay(10);
+}
+
+void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype)
+{
+ struct clk *clk = NULL;
+
+ clk = camio_vfe_clk;
+
+ if (clk != NULL) {
+ switch (srctype) {
+ case MSM_CAMIO_CLK_SRC_INTERNAL:
+ clk_set_flags(clk, 0x00000100 << 1);
+ break;
+
+ case MSM_CAMIO_CLK_SRC_EXTERNAL:
+ clk_set_flags(clk, 0x00000100);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void msm_camio_clk_axi_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_axi_clk;
+ /* todo: check return */
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
diff --git a/drivers/staging/dream/camera/msm_v4l2.c b/drivers/staging/dream/camera/msm_v4l2.c
new file mode 100644
index 00000000000..6a7d46cf11e
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_v4l2.c
@@ -0,0 +1,797 @@
+/*
+ *
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <media/v4l2-dev.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include <media/v4l2-ioctl.h>
+/*#include <linux/platform_device.h>*/
+
+#define MSM_V4L2_START_SNAPSHOT _IOWR('V', BASE_VIDIOC_PRIVATE+1, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_GET_PICTURE _IOWR('V', BASE_VIDIOC_PRIVATE+2, \
+ struct v4l2_buffer)
+
+#define MSM_V4L2_DEVICE_NAME "msm_v4l2"
+
+#define MSM_V4L2_PROC_NAME "msm_v4l2"
+
+#define MSM_V4L2_DEVNUM_MPEG2 0
+#define MSM_V4L2_DEVNUM_YUV 20
+
+/* HVGA-P (portrait) and HVGA-L (landscape) */
+#define MSM_V4L2_WIDTH 480
+#define MSM_V4L2_HEIGHT 320
+
+#if 1
+#define D(fmt, args...) printk(KERN_INFO "msm_v4l2: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define PREVIEW_FRAMES_NUM 4
+
+struct msm_v4l2_device {
+ struct list_head read_queue;
+ struct v4l2_format current_cap_format;
+ struct v4l2_format current_pix_format;
+ struct video_device *pvdev;
+ struct msm_v4l2_driver *drv;
+ uint8_t opencnt;
+
+ spinlock_t read_queue_lock;
+};
+
+static struct msm_v4l2_device *g_pmsm_v4l2_dev;
+
+
+static DEFINE_MUTEX(msm_v4l2_opencnt_lock);
+
+static int msm_v4l2_open(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->open(
+ g_pmsm_v4l2_dev->drv->sync,
+ MSM_APPS_ID_V4L2);
+ }
+ g_pmsm_v4l2_dev->opencnt++;
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static int msm_v4l2_release(struct file *f)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+ mutex_lock(&msm_v4l2_opencnt_lock);
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ g_pmsm_v4l2_dev->opencnt--;
+ if (!g_pmsm_v4l2_dev->opencnt) {
+ rc = g_pmsm_v4l2_dev->drv->release(
+ g_pmsm_v4l2_dev->drv->sync);
+ }
+ }
+ mutex_unlock(&msm_v4l2_opencnt_lock);
+ return rc;
+}
+
+static unsigned int msm_v4l2_poll(struct file *f, struct poll_table_struct *w)
+{
+ return g_pmsm_v4l2_dev->drv->drv_poll(g_pmsm_v4l2_dev->drv->sync, f, w);
+}
+
+static long msm_v4l2_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("msm_v4l2_ioctl, cmd = %d, %d\n", cmd, __LINE__);
+
+ switch (cmd) {
+ case MSM_V4L2_START_SNAPSHOT:
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_ioctl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n",
+ cmd);
+ ctrlcmd->type = MSM_V4L2_SNAPSHOT;
+ return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ case MSM_V4L2_GET_PICTURE:
+ D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd);
+ ctrlcmd = (struct msm_ctrl_cmd *)arg;
+ return g_pmsm_v4l2_dev->drv->get_pict(
+ g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ default:
+ D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd);
+ return video_ioctl2(filep, cmd, arg);
+ }
+}
+
+static void msm_v4l2_release_dev(struct video_device *d)
+{
+ D("%s\n", __func__);
+}
+
+static int msm_v4l2_querycap(struct file *f,
+ void *pctx, struct v4l2_capability *pcaps)
+{
+ D("%s\n", __func__);
+ strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver));
+ strncpy(pcaps->card,
+ MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card));
+ pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id *pnorm)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_queryctrl(struct file *f,
+ void *pctx, struct v4l2_queryctrl *pqctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_queryctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_QUERY_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_queryctrl);
+ ctrlcmd->value = pqctrl;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_g_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_GET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_ctrl: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_SET_CTRL;
+ ctrlcmd->length = sizeof(struct v4l2_control);
+ ctrlcmd->value = c;
+ ctrlcmd->timeout_ms = 10000;
+
+ D("%s\n", __func__);
+
+ rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+ if (rc < 0)
+ return -1;
+
+ return ctrlcmd->status;
+}
+
+static int msm_v4l2_reqbufs(struct file *f,
+ void *pctx, struct v4l2_requestbuffers *b)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_pmem_info pmem_buf;
+#if 0
+ __u32 width = 0;
+ __u32 height = 0;
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.width; */
+ width = 640;
+ /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.height; */
+ height = 480;
+
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ __u32 y_pad = pb->bytesused % 4;
+
+ /* V4L2 videodev will do the copy_from_user. */
+
+ memset(&pmem_buf, 0, sizeof(struct msm_pmem_info));
+ pmem_buf.type = MSM_PMEM_OUTPUT2;
+ pmem_buf.vaddr = (void *)pb->m.userptr;
+ pmem_buf.y_off = 0;
+ pmem_buf.fd = (int)pb->reserved;
+ /* pmem_buf.cbcr_off = (y_size + y_pad); */
+ pmem_buf.cbcr_off = (pb->bytesused + y_pad);
+
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf);
+
+ return 0;
+}
+
+static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ /*
+ __u32 y_size = 0;
+ __u32 y_pad = 0;
+ __u32 width = 0;
+ __u32 height = 0;
+ */
+
+ __u32 y_pad = 0;
+
+ struct msm_pmem_info meminfo;
+ struct msm_frame frame;
+ static int cnt;
+
+ if ((pb->flags >> 16) & 0x0001) {
+ /* this is for previwe */
+#if 0
+ width = 640;
+ height = 480;
+
+ /* V4L2 videodev will do the copy_from_user. */
+ D("%s: width = %d, height = %d\n", __func__, width, height);
+ y_size = width * height;
+ y_pad = y_size % 4;
+#endif
+
+ y_pad = pb->bytesused % 4;
+
+ if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ /* this qbuf is actually for releasing */
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ g_pmsm_v4l2_dev->drv->put_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+
+ return 0;
+ }
+
+ D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n",
+ pb->bytesused);
+
+ meminfo.type = MSM_PMEM_OUTPUT2;
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (pb->bytesused + y_pad);
+ if (cnt == PREVIEW_FRAMES_NUM - 1)
+ meminfo.active = 0;
+ else
+ meminfo.active = 1;
+ cnt++;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ } else if ((pb->flags) & 0x0001) {
+ /* this is for snapshot */
+
+ __u32 y_size = 0;
+
+ if ((pb->flags >> 8) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_THUMBAIL;
+ } else if ((pb->flags >> 9) & 0x01) {
+
+ y_size = pb->bytesused;
+
+ meminfo.type = MSM_PMEM_MAINIMG;
+ }
+
+ y_pad = y_size % 4;
+
+ meminfo.fd = (int)pb->reserved;
+ meminfo.vaddr = (void *)pb->m.userptr;
+ meminfo.y_off = 0;
+ /* meminfo.cbcr_off = (y_size + y_pad); */
+ meminfo.cbcr_off = (y_size + y_pad);
+ meminfo.active = 1;
+ g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync,
+ &meminfo);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb)
+{
+ struct msm_frame frame;
+ D("%s\n", __func__);
+
+ /* V4L2 videodev will do the copy_to_user. */
+ if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+
+ D("%s, %d\n", __func__, __LINE__);
+
+ g_pmsm_v4l2_dev->drv->get_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+
+ pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */
+ pb->reserved = (int)frame.fd;
+ /* pb->length = (int)frame.cbcr_off; */
+
+ pb->bytesused = frame.cbcr_off;
+
+ } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) {
+ __u32 y_pad = pb->bytesused % 4;
+
+ frame.buffer = pb->m.userptr;
+ frame.y_off = 0;
+ /* frame.cbcr_off = (y_size + y_pad); */
+ frame.cbcr_off = (pb->bytesused + y_pad);
+ frame.fd = pb->reserved;
+
+ g_pmsm_v4l2_dev->drv->put_frame(
+ g_pmsm_v4l2_dev->drv->sync,
+ &frame);
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_ON;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(
+ g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ D("%s after drv->ctrl \n", __func__);
+
+ return 0;
+}
+
+static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_STREAM_OFF;
+ ctrlcmd->timeout_ms = 10000;
+ ctrlcmd->length = 0;
+ ctrlcmd->value = NULL;
+
+
+ D("%s\n", __func__);
+
+ g_pmsm_v4l2_dev->drv->ctrl(
+ g_pmsm_v4l2_dev->drv->sync,
+ ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_enum_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_fmtdesc *pfmtdesc)
+{
+ D("%s\n", __func__);
+
+ switch (pfmtdesc->index) {
+ case 0:
+ pfmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmtdesc->flags = 0;
+ strncpy(pfmtdesc->description, "YUV 4:2:0",
+ sizeof(pfmtdesc->description));
+ pfmtdesc->pixelformat = V4L2_PIX_FMT_YVU420;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ struct msm_ctrl_cmd *ctrlcmd;
+
+ D("%s\n", __func__);
+
+ ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!ctrlcmd) {
+ CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd->length = sizeof(struct v4l2_format);
+ ctrlcmd->value = pfmt;
+ ctrlcmd->timeout_ms = 10000;
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(ctrlcmd);
+ return -1;
+ }
+
+#if 0
+ /* FIXEME */
+ if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420) {
+ kfree(ctrlcmd);
+ return -EINVAL;
+ }
+#endif
+
+ /* Ok, but check other params, too. */
+
+#if 0
+ memcpy(&g_pmsm_v4l2_dev->current_pix_format.fmt.pix, pfmt,
+ sizeof(struct v4l2_format));
+#endif
+
+ g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd);
+
+ return 0;
+}
+
+static int msm_v4l2_g_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ pfmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
+ pfmt->fmt.pix.width = MSM_V4L2_WIDTH;
+ pfmt->fmt.pix.height = MSM_V4L2_HEIGHT;
+ pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
+ pfmt->fmt.pix.field = V4L2_FIELD_ANY;
+ pfmt->fmt.pix.bytesperline = 0;
+ pfmt->fmt.pix.sizeimage = 0;
+ pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ pfmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int msm_v4l2_s_fmt_overlay(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_overlay(struct file *f, void *pctx, unsigned int i)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_g_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+static int msm_v4l2_s_jpegcomp(struct file *f,
+ void *pctx, struct v4l2_jpegcompression *pcomp)
+{
+ D("%s\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int msm_v4l2_read_proc(char *pbuf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ len += snprintf(pbuf, strlen("stats\n") + 1, "stats\n");
+
+ if (g_pmsm_v4l2_dev) {
+ len += snprintf(pbuf, strlen("mode: ") + 1, "mode: ");
+
+ if (g_pmsm_v4l2_dev->current_cap_format.type
+ == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ len += snprintf(pbuf, strlen("capture\n") + 1,
+ "capture\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, 21, "resolution: %dx%d\n",
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ width,
+ g_pmsm_v4l2_dev->current_cap_format.fmt.pix.
+ height);
+
+ len += snprintf(pbuf,
+ strlen("pixel format: ") + 1, "pixel format: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.pixelformat
+ == V4L2_PIX_FMT_YVU420)
+ len += snprintf(pbuf, strlen("yvu420\n") + 1,
+ "yvu420\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+
+ len += snprintf(pbuf, strlen("colorspace: ") + 1,
+ "colorspace: ");
+ if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.colorspace
+ == V4L2_COLORSPACE_JPEG)
+ len += snprintf(pbuf, strlen("jpeg\n") + 1, "jpeg\n");
+ else
+ len += snprintf(pbuf, strlen("unknown\n") + 1,
+ "unknown\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+#endif
+
+static const struct v4l2_file_operations msm_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_v4l2_open,
+ .poll = msm_v4l2_poll,
+ .release = msm_v4l2_release,
+ .ioctl = msm_v4l2_ioctl,
+};
+
+static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev)
+{
+ pmsm_v4l2_dev->read_queue_lock =
+ __SPIN_LOCK_UNLOCKED(pmsm_v4l2_dev->read_queue_lock);
+ INIT_LIST_HEAD(&pmsm_v4l2_dev->read_queue);
+}
+
+static int msm_v4l2_try_fmt_cap(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+static int mm_v4l2_try_fmt_type_private(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ /* FIXME */
+ return 0;
+}
+
+/*
+ * should the following structure be used instead of the code in the function?
+ * static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
+ * .vidioc_querycap = ....
+ * }
+ */
+static const struct v4l2_ioctl_ops msm_ioctl_ops = {
+ .vidioc_querycap = msm_v4l2_querycap,
+ .vidioc_s_std = msm_v4l2_s_std,
+
+ .vidioc_queryctrl = msm_v4l2_queryctrl,
+ .vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_s_ctrl = msm_v4l2_s_ctrl,
+
+ .vidioc_reqbufs = msm_v4l2_reqbufs,
+ .vidioc_querybuf = msm_v4l2_querybuf,
+ .vidioc_qbuf = msm_v4l2_qbuf,
+ .vidioc_dqbuf = msm_v4l2_dqbuf,
+
+ .vidioc_streamon = msm_v4l2_streamon,
+ .vidioc_streamoff = msm_v4l2_streamoff,
+
+ .vidioc_enum_fmt_vid_overlay = msm_v4l2_enum_fmt_overlay,
+ .vidioc_enum_fmt_vid_cap = msm_v4l2_enum_fmt_cap,
+
+ .vidioc_try_fmt_vid_cap = msm_v4l2_try_fmt_cap,
+ .vidioc_try_fmt_type_private = mm_v4l2_try_fmt_type_private,
+
+ .vidioc_g_fmt_vid_cap = msm_v4l2_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = msm_v4l2_s_fmt_cap,
+ .vidioc_g_fmt_vid_overlay = msm_v4l2_g_fmt_overlay,
+ .vidioc_s_fmt_vid_overlay = msm_v4l2_s_fmt_overlay,
+ .vidioc_overlay = msm_v4l2_overlay,
+
+ .vidioc_g_jpegcomp = msm_v4l2_g_jpegcomp,
+ .vidioc_s_jpegcomp = msm_v4l2_s_jpegcomp,
+};
+
+static int msm_v4l2_video_dev_init(struct video_device *pvd)
+{
+ strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name));
+ pvd->vfl_type = 1;
+ pvd->fops = &msm_v4l2_fops;
+ pvd->release = msm_v4l2_release_dev;
+ pvd->minor = -1;
+ pvd->ioctl_ops = &msm_ioctl_ops;
+ return msm_v4l2_register(g_pmsm_v4l2_dev->drv);
+}
+
+static int __init msm_v4l2_init(void)
+{
+ int rc = -ENOMEM;
+ struct video_device *pvdev = NULL;
+ struct msm_v4l2_device *pmsm_v4l2_dev = NULL;
+ D("%s\n", __func__);
+
+ pvdev = video_device_alloc();
+ if (pvdev == NULL)
+ return rc;
+
+ pmsm_v4l2_dev =
+ kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL);
+ if (pmsm_v4l2_dev == NULL) {
+ video_device_release(pvdev);
+ return rc;
+ }
+
+ msm_v4l2_dev_init(pmsm_v4l2_dev);
+
+ g_pmsm_v4l2_dev = pmsm_v4l2_dev;
+ g_pmsm_v4l2_dev->pvdev = pvdev;
+
+ g_pmsm_v4l2_dev->drv =
+ kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL);
+ if (!g_pmsm_v4l2_dev->drv) {
+ video_device_release(pvdev);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ rc = msm_v4l2_video_dev_init(pvdev);
+ if (rc < 0) {
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev->drv);
+ kfree(pmsm_v4l2_dev);
+ return rc;
+ }
+
+ if (video_register_device(pvdev, VFL_TYPE_GRABBER,
+ MSM_V4L2_DEVNUM_YUV)) {
+ D("failed to register device\n");
+ video_device_release(pvdev);
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+ return -ENOENT;
+ }
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(MSM_V4L2_PROC_NAME,
+ 0, NULL, msm_v4l2_read_proc, NULL);
+#endif
+
+ return 0;
+}
+
+static void __exit msm_v4l2_exit(void)
+{
+ struct video_device *pvdev = g_pmsm_v4l2_dev->pvdev;
+ D("%s\n", __func__);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(MSM_V4L2_PROC_NAME, NULL);
+#endif
+ video_unregister_device(pvdev);
+ video_device_release(pvdev);
+
+ msm_v4l2_unregister(g_pmsm_v4l2_dev->drv);
+
+ kfree(g_pmsm_v4l2_dev->drv);
+ g_pmsm_v4l2_dev->drv = NULL;
+
+ kfree(g_pmsm_v4l2_dev);
+ g_pmsm_v4l2_dev = NULL;
+}
+
+module_init(msm_v4l2_init);
+module_exit(msm_v4l2_exit);
+
+MODULE_DESCRIPTION("MSM V4L2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/dream/camera/msm_vfe7x.c b/drivers/staging/dream/camera/msm_vfe7x.c
new file mode 100644
index 00000000000..5de96c5d635
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe7x.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <mach/msm_adsp.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include "msm_vfe7x.h"
+
+#define QDSP_CMDQUEUE QDSP_vfeCommandQueue
+
+#define VFE_RESET_CMD 0
+#define VFE_START_CMD 1
+#define VFE_STOP_CMD 2
+#define VFE_FRAME_ACK 20
+#define STATS_AF_ACK 21
+#define STATS_WE_ACK 22
+
+#define MSG_STOP_ACK 1
+#define MSG_SNAPSHOT 2
+#define MSG_OUTPUT1 6
+#define MSG_OUTPUT2 7
+#define MSG_STATS_AF 8
+#define MSG_STATS_WE 9
+
+static struct msm_adsp_module *qcam_mod;
+static struct msm_adsp_module *vfe_mod;
+static struct msm_vfe_callback *resp;
+static void *extdata;
+static uint32_t extlen;
+
+struct mutex vfe_lock;
+static void *vfe_syncdata;
+static uint8_t vfestopped;
+
+static struct stop_event stopevent;
+
+static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type,
+ void *data, void **ext, int32_t *elen)
+{
+ switch (type) {
+ case VFE_MSG_OUTPUT1:
+ case VFE_MSG_OUTPUT2: {
+ pinfo->y_phy = ((struct vfe_endframe *)data)->y_address;
+ pinfo->cbcr_phy =
+ ((struct vfe_endframe *)data)->cbcr_address;
+
+ CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+
+ ((struct vfe_frame_extra *)extdata)->bl_evencol =
+ ((struct vfe_endframe *)data)->blacklevelevencolumn;
+
+ ((struct vfe_frame_extra *)extdata)->bl_oddcol =
+ ((struct vfe_endframe *)data)->blackleveloddcolumn;
+
+ ((struct vfe_frame_extra *)extdata)->g_def_p_cnt =
+ ((struct vfe_endframe *)data)->greendefectpixelcount;
+
+ ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt =
+ ((struct vfe_endframe *)data)->redbluedefectpixelcount;
+
+ *ext = extdata;
+ *elen = extlen;
+ }
+ break;
+
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_WE:
+ pinfo->sbuf_phy = *(uint32_t *)data;
+ break;
+
+ default:
+ break;
+ } /* switch */
+}
+
+static void vfe_7x_ops(void *driver_data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ uint32_t evt_buf[3];
+ struct msm_vfe_resp *rp;
+ void *data;
+
+ len = (id == (uint16_t)-1) ? 0 : len;
+ data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len, vfe_syncdata);
+
+ if (!data) {
+ pr_err("rp: cannot allocate buffer\n");
+ return;
+ }
+ rp = (struct msm_vfe_resp *)data;
+ rp->evt_msg.len = len;
+
+ if (id == ((uint16_t)-1)) {
+ /* event */
+ rp->type = VFE_EVENT;
+ rp->evt_msg.type = MSM_CAMERA_EVT;
+ getevent(evt_buf, sizeof(evt_buf));
+ rp->evt_msg.msg_id = evt_buf[0];
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata);
+ } else {
+ /* messages */
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+ rp->evt_msg.data = rp + 1;
+ getevent(rp->evt_msg.data, len);
+
+ switch (rp->evt_msg.msg_id) {
+ case MSG_SNAPSHOT:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_OUTPUT1:
+ rp->type = VFE_MSG_OUTPUT1;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT1,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_OUTPUT2:
+ rp->type = VFE_MSG_OUTPUT2;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT2,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_STATS_AF:
+ rp->type = VFE_MSG_STATS_AF;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_STATS_WE:
+ rp->type = VFE_MSG_STATS_WE;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE,
+ rp->evt_msg.data, NULL, NULL);
+
+ CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy);
+ break;
+
+ case MSG_STOP_ACK:
+ rp->type = VFE_MSG_GENERAL;
+ stopevent.state = 1;
+ wake_up(&stopevent.wait);
+ break;
+
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata);
+ }
+}
+
+static struct msm_adsp_ops vfe_7x_sync = {
+ .event = vfe_7x_ops,
+};
+
+static int vfe_7x_enable(struct camera_enable_cmd *enable)
+{
+ int rc = -EFAULT;
+
+ if (!strcmp(enable->name, "QCAMTASK"))
+ rc = msm_adsp_enable(qcam_mod);
+ else if (!strcmp(enable->name, "VFETASK"))
+ rc = msm_adsp_enable(vfe_mod);
+
+ return rc;
+}
+
+static int vfe_7x_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev __attribute__((unused)))
+{
+ int rc = -EFAULT;
+
+ if (!strcmp(enable->name, "QCAMTASK"))
+ rc = msm_adsp_disable(qcam_mod);
+ else if (!strcmp(enable->name, "VFETASK"))
+ rc = msm_adsp_disable(vfe_mod);
+
+ return rc;
+}
+
+static int vfe_7x_stop(void)
+{
+ int rc = 0;
+ uint32_t stopcmd = VFE_STOP_CMD;
+ rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE,
+ &stopcmd, sizeof(uint32_t));
+ if (rc < 0) {
+ CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc);
+ return rc;
+ }
+
+ stopevent.state = 0;
+ rc = wait_event_timeout(stopevent.wait,
+ stopevent.state != 0,
+ msecs_to_jiffies(stopevent.timeout));
+
+ return rc;
+}
+
+static void vfe_7x_release(struct platform_device *pdev)
+{
+ mutex_lock(&vfe_lock);
+ vfe_syncdata = NULL;
+ mutex_unlock(&vfe_lock);
+
+ if (!vfestopped) {
+ CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__);
+ vfe_7x_stop();
+ } else
+ vfestopped = 0;
+
+ msm_adsp_disable(qcam_mod);
+ msm_adsp_disable(vfe_mod);
+
+ msm_adsp_put(qcam_mod);
+ msm_adsp_put(vfe_mod);
+
+ msm_camio_disable(pdev);
+
+ kfree(extdata);
+ extlen = 0;
+}
+
+static int vfe_7x_init(struct msm_vfe_callback *presp,
+ struct platform_device *dev)
+{
+ int rc = 0;
+
+ init_waitqueue_head(&stopevent.wait);
+ stopevent.timeout = 200;
+ stopevent.state = 0;
+
+ if (presp && presp->vfe_resp)
+ resp = presp;
+ else
+ return -EFAULT;
+
+ /* Bring up all the required GPIOs and Clocks */
+ rc = msm_camio_enable(dev);
+ if (rc < 0)
+ return rc;
+
+ msm_camio_camif_pad_reg_reset();
+
+ extlen = sizeof(struct vfe_frame_extra);
+
+ extdata =
+ kmalloc(sizeof(extlen), GFP_ATOMIC);
+ if (!extdata) {
+ rc = -ENOMEM;
+ goto init_fail;
+ }
+
+ rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL);
+ if (rc) {
+ rc = -EBUSY;
+ goto get_qcam_fail;
+ }
+
+ rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL);
+ if (rc) {
+ rc = -EBUSY;
+ goto get_vfe_fail;
+ }
+
+ return 0;
+
+get_vfe_fail:
+ msm_adsp_put(qcam_mod);
+get_qcam_fail:
+ kfree(extdata);
+init_fail:
+ extlen = 0;
+ return rc;
+}
+
+static int vfe_7x_config_axi(int mode,
+ struct axidata *ad, struct axiout *ao)
+{
+ struct msm_pmem_region *regptr;
+ unsigned long *bptr;
+ int cnt;
+
+ int rc = 0;
+
+ if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+ regptr = ad->region;
+
+ CDBG("bufnum1 = %d\n", ad->bufnum1);
+ CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->y_off, regptr->cbcr_off);
+
+ bptr = &ao->output1buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+ *bptr = regptr->paddr + regptr->y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+ *bptr = regptr->paddr + regptr->y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->cbcr_off;
+ bptr++;
+ }
+ } /* if OUTPUT1 or Both */
+
+ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+ regptr = &(ad->region[ad->bufnum1]);
+
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+ CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->y_off, regptr->cbcr_off);
+
+ bptr = &ao->output2buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+ *bptr = regptr->paddr + regptr->y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+ *bptr = regptr->paddr + regptr->y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->cbcr_off;
+ bptr++;
+ }
+ }
+
+ return rc;
+}
+
+static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ struct msm_pmem_region *regptr;
+ unsigned char buf[256];
+
+ struct vfe_stats_ack sack;
+ struct axidata *axid;
+ uint32_t i;
+
+ struct vfe_stats_we_cfg *scfg = NULL;
+ struct vfe_stats_af_cfg *sfcfg = NULL;
+
+ struct axiout *axio = NULL;
+ void *cmd_data = NULL;
+ void *cmd_data_alloc = NULL;
+ long rc = 0;
+ struct msm_vfe_command_7k *vfecmd;
+
+ vfecmd =
+ kmalloc(sizeof(struct msm_vfe_command_7k),
+ GFP_ATOMIC);
+ if (!vfecmd) {
+ pr_err("vfecmd alloc failed!\n");
+ return -ENOMEM;
+ }
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(struct msm_vfe_command_7k))) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+ }
+
+ switch (cmd->cmd_type) {
+ case CMD_STATS_ENABLE:
+ case CMD_STATS_AXI_CFG: {
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ scfg =
+ kmalloc(sizeof(struct vfe_stats_we_cfg),
+ GFP_ATOMIC);
+ if (!scfg) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+
+ if (copy_from_user(scfg,
+ (void __user *)(vfecmd->value),
+ vfecmd->length)) {
+
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n",
+ axid->bufnum1, scfg->wb_expstatsenable);
+
+ if (axid->bufnum1 > 0) {
+ regptr = axid->region;
+
+ for (i = 0; i < axid->bufnum1; i++) {
+
+ CDBG("STATS_ENABLE, phy = 0x%lx\n",
+ regptr->paddr);
+
+ scfg->wb_expstatoutputbuffer[i] =
+ (void *)regptr->paddr;
+ regptr++;
+ }
+
+ cmd_data = scfg;
+
+ } else {
+ rc = -EINVAL;
+ goto config_done;
+ }
+ }
+ break;
+
+ case CMD_STATS_AF_ENABLE:
+ case CMD_STATS_AF_AXI_CFG: {
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ sfcfg =
+ kmalloc(sizeof(struct vfe_stats_af_cfg),
+ GFP_ATOMIC);
+
+ if (!sfcfg) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+
+ if (copy_from_user(sfcfg,
+ (void __user *)(vfecmd->value),
+ vfecmd->length)) {
+
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n",
+ axid->bufnum1, sfcfg->af_enable);
+
+ if (axid->bufnum1 > 0) {
+ regptr = axid->region;
+
+ for (i = 0; i < axid->bufnum1; i++) {
+
+ CDBG("STATS_ENABLE, phy = 0x%lx\n",
+ regptr->paddr);
+
+ sfcfg->af_outbuf[i] =
+ (void *)regptr->paddr;
+
+ regptr++;
+ }
+
+ cmd_data = sfcfg;
+
+ } else {
+ rc = -EINVAL;
+ goto config_done;
+ }
+ }
+ break;
+
+ case CMD_FRAME_BUF_RELEASE: {
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe_outputack fack;
+ if (!data) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ fack.header = VFE_FRAME_ACK;
+
+ fack.output2newybufferaddress =
+ (void *)(p + b->y_off);
+
+ fack.output2newcbcrbufferaddress =
+ (void *)(p + b->cbcr_off);
+
+ vfecmd->queue = QDSP_CMDQUEUE;
+ vfecmd->length = sizeof(struct vfe_outputack);
+ cmd_data = &fack;
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+
+ case CMD_STATS_BUF_RELEASE: {
+ CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n");
+ if (!data) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ sack.header = STATS_WE_ACK;
+ sack.bufaddr = (void *)*(uint32_t *)data;
+
+ vfecmd->queue = QDSP_CMDQUEUE;
+ vfecmd->length = sizeof(struct vfe_stats_ack);
+ cmd_data = &sack;
+ }
+ break;
+
+ case CMD_STATS_AF_BUF_RELEASE: {
+ CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n");
+ if (!data) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ sack.header = STATS_AF_ACK;
+ sack.bufaddr = (void *)*(uint32_t *)data;
+
+ vfecmd->queue = QDSP_CMDQUEUE;
+ vfecmd->length = sizeof(struct vfe_stats_ack);
+ cmd_data = &sack;
+ }
+ break;
+
+ case CMD_GENERAL:
+ case CMD_STATS_DISABLE: {
+ if (vfecmd->length > 256) {
+ cmd_data_alloc =
+ cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC);
+ if (!cmd_data) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+ } else
+ cmd_data = buf;
+
+ if (copy_from_user(cmd_data,
+ (void __user *)(vfecmd->value),
+ vfecmd->length)) {
+
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ if (vfecmd->queue == QDSP_CMDQUEUE) {
+ switch (*(uint32_t *)cmd_data) {
+ case VFE_RESET_CMD:
+ msm_camio_vfe_blk_reset();
+ msm_camio_camif_pad_reg_reset_2();
+ vfestopped = 0;
+ break;
+
+ case VFE_START_CMD:
+ msm_camio_camif_pad_reg_reset_2();
+ vfestopped = 0;
+ break;
+
+ case VFE_STOP_CMD:
+ vfestopped = 1;
+ goto config_send;
+
+ default:
+ break;
+ }
+ } /* QDSP_CMDQUEUE */
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT1: {
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+
+ if (copy_from_user(axio, (void *)(vfecmd->value),
+ sizeof(struct axiout))) {
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ vfe_7x_config_axi(OUTPUT_1, axid, axio);
+
+ cmd_data = axio;
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT2:
+ case CMD_RAW_PICT_AXI_CFG: {
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd->value),
+ sizeof(struct axiout))) {
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ vfe_7x_config_axi(OUTPUT_2, axid, axio);
+ cmd_data = axio;
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP_O1_AND_O2: {
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+
+ axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ goto config_failure;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd->value),
+ sizeof(struct axiout))) {
+ rc = -EFAULT;
+ goto config_done;
+ }
+
+ vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio);
+
+ cmd_data = axio;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ if (vfestopped)
+ goto config_done;
+
+config_send:
+ CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data);
+ rc = msm_adsp_write(vfe_mod, vfecmd->queue,
+ cmd_data, vfecmd->length);
+
+config_done:
+ if (cmd_data_alloc != NULL)
+ kfree(cmd_data_alloc);
+
+config_failure:
+ kfree(scfg);
+ kfree(axio);
+ kfree(vfecmd);
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ mutex_init(&vfe_lock);
+ fptr->vfe_init = vfe_7x_init;
+ fptr->vfe_enable = vfe_7x_enable;
+ fptr->vfe_config = vfe_7x_config;
+ fptr->vfe_disable = vfe_7x_disable;
+ fptr->vfe_release = vfe_7x_release;
+ vfe_syncdata = data;
+}
diff --git a/drivers/staging/dream/camera/msm_vfe7x.h b/drivers/staging/dream/camera/msm_vfe7x.h
new file mode 100644
index 00000000000..be3e9ad8f52
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe7x.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+#ifndef __MSM_VFE7X_H__
+#define __MSM_VFE7X_H__
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+
+struct vfe_frame_extra {
+ uint32_t bl_evencol;
+ uint32_t bl_oddcol;
+ uint16_t g_def_p_cnt;
+ uint16_t r_b_def_p_cnt;
+};
+
+struct vfe_endframe {
+ uint32_t y_address;
+ uint32_t cbcr_address;
+
+ unsigned int blacklevelevencolumn:23;
+ uint16_t reserved1:9;
+ unsigned int blackleveloddcolumn:23;
+ uint16_t reserved2:9;
+
+ uint16_t greendefectpixelcount:8;
+ uint16_t reserved3:8;
+ uint16_t redbluedefectpixelcount:8;
+ uint16_t reserved4:8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_outputack {
+ uint32_t header;
+ void *output2newybufferaddress;
+ void *output2newcbcrbufferaddress;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_ack {
+ uint32_t header;
+ /* MUST BE 64 bit ALIGNED */
+ void *bufaddr;
+} __attribute__((packed, aligned(4)));
+
+/* AXI Output Config Command sent to DSP */
+struct axiout {
+ uint32_t cmdheader:32;
+ int outputmode:3;
+ uint8_t format:2;
+ uint32_t /* reserved */ : 27;
+
+ /* AXI Output 1 Y Configuration, Part 1 */
+ uint32_t out1yimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1yimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 Y Configuration, Part 2 */
+ uint8_t out1yburstlen:2;
+ uint32_t out1ynumrows:12;
+ uint32_t out1yrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 CbCr Configuration, Part 1 */
+ uint32_t out1cbcrimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1cbcrimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 1 CbCr Configuration, Part 2 */
+ uint8_t out1cbcrburstlen:2;
+ uint32_t out1cbcrnumrows:12;
+ uint32_t out1cbcrrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 Y Configuration, Part 1 */
+ uint32_t out2yimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2yimagewidthin64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 Y Configuration, Part 2 */
+ uint8_t out2yburstlen:2;
+ uint32_t out2ynumrows:12;
+ uint32_t out2yrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 CbCr Configuration, Part 1 */
+ uint32_t out2cbcrimageheight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2cbcrimagewidtein64bitwords:10;
+ uint32_t /* reserved */ : 6;
+
+ /* AXI Output 2 CbCr Configuration, Part 2 */
+ uint8_t out2cbcrburstlen:2;
+ uint32_t out2cbcrnumrows:12;
+ uint32_t out2cbcrrowincin64bitincs:12;
+ uint32_t /* reserved */ : 6;
+
+ /* Address configuration:
+ * output1 phisycal address */
+ unsigned long output1buffer1_y_phy;
+ unsigned long output1buffer1_cbcr_phy;
+ unsigned long output1buffer2_y_phy;
+ unsigned long output1buffer2_cbcr_phy;
+ unsigned long output1buffer3_y_phy;
+ unsigned long output1buffer3_cbcr_phy;
+ unsigned long output1buffer4_y_phy;
+ unsigned long output1buffer4_cbcr_phy;
+ unsigned long output1buffer5_y_phy;
+ unsigned long output1buffer5_cbcr_phy;
+ unsigned long output1buffer6_y_phy;
+ unsigned long output1buffer6_cbcr_phy;
+ unsigned long output1buffer7_y_phy;
+ unsigned long output1buffer7_cbcr_phy;
+ unsigned long output1buffer8_y_phy;
+ unsigned long output1buffer8_cbcr_phy;
+
+ /* output2 phisycal address */
+ unsigned long output2buffer1_y_phy;
+ unsigned long output2buffer1_cbcr_phy;
+ unsigned long output2buffer2_y_phy;
+ unsigned long output2buffer2_cbcr_phy;
+ unsigned long output2buffer3_y_phy;
+ unsigned long output2buffer3_cbcr_phy;
+ unsigned long output2buffer4_y_phy;
+ unsigned long output2buffer4_cbcr_phy;
+ unsigned long output2buffer5_y_phy;
+ unsigned long output2buffer5_cbcr_phy;
+ unsigned long output2buffer6_y_phy;
+ unsigned long output2buffer6_cbcr_phy;
+ unsigned long output2buffer7_y_phy;
+ unsigned long output2buffer7_cbcr_phy;
+ unsigned long output2buffer8_y_phy;
+ unsigned long output2buffer8_cbcr_phy;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_we_cfg {
+ uint32_t header;
+
+ /* White Balance/Exposure Statistic Selection */
+ uint8_t wb_expstatsenable:1;
+ uint8_t wb_expstatbuspriorityselection:1;
+ unsigned int wb_expstatbuspriorityvalue:4;
+ unsigned int /* reserved */ : 26;
+
+ /* White Balance/Exposure Statistic Configuration, Part 1 */
+ uint8_t exposurestatregions:1;
+ uint8_t exposurestatsubregions:1;
+ unsigned int /* reserved */ : 14;
+
+ unsigned int whitebalanceminimumy:8;
+ unsigned int whitebalancemaximumy:8;
+
+ /* White Balance/Exposure Statistic Configuration, Part 2 */
+ uint8_t wb_expstatslopeofneutralregionline[
+ NUM_WB_EXP_NEUTRAL_REGION_LINES];
+
+ /* White Balance/Exposure Statistic Configuration, Part 3 */
+ unsigned int wb_expstatcrinterceptofneutralregionline2:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int wb_expstatcbinterceptofneutralreginnline1:12;
+ unsigned int /* reserved */ : 4;
+
+ /* White Balance/Exposure Statistic Configuration, Part 4 */
+ unsigned int wb_expstatcrinterceptofneutralregionline4:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int wb_expstatcbinterceptofneutralregionline3:12;
+ unsigned int /* reserved */ : 4;
+
+ /* White Balance/Exposure Statistic Output Buffer Header */
+ unsigned int wb_expmetricheaderpattern:8;
+ unsigned int /* reserved */ : 24;
+
+ /* White Balance/Exposure Statistic Output Buffers-MUST
+ * BE 64 bit ALIGNED */
+ void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS];
+} __attribute__((packed, aligned(4)));
+
+struct vfe_stats_af_cfg {
+ uint32_t header;
+
+ /* Autofocus Statistic Selection */
+ uint8_t af_enable:1;
+ uint8_t af_busprioritysel:1;
+ unsigned int af_buspriorityval:4;
+ unsigned int /* reserved */ : 26;
+
+ /* Autofocus Statistic Configuration, Part 1 */
+ unsigned int af_singlewinvoffset:12;
+ unsigned int /* reserved */ : 4;
+ unsigned int af_singlewinhoffset:12;
+ unsigned int /* reserved */ : 3;
+ uint8_t af_winmode:1;
+
+ /* Autofocus Statistic Configuration, Part 2 */
+ unsigned int af_singglewinvh:11;
+ unsigned int /* reserved */ : 5;
+ unsigned int af_singlewinhw:11;
+ unsigned int /* reserved */ : 5;
+
+ /* Autofocus Statistic Configuration, Parts 3-6 */
+ uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS];
+
+ /* Autofocus Statistic Configuration, Part 7 */
+ signed int af_metrichpfcoefa00:5;
+ signed int af_metrichpfcoefa04:5;
+ unsigned int af_metricmaxval:11;
+ uint8_t af_metricsel:1;
+ unsigned int /* reserved */ : 10;
+
+ /* Autofocus Statistic Configuration, Part 8 */
+ signed int af_metrichpfcoefa20:5;
+ signed int af_metrichpfcoefa21:5;
+ signed int af_metrichpfcoefa22:5;
+ signed int af_metrichpfcoefa23:5;
+ signed int af_metrichpfcoefa24:5;
+ unsigned int /* reserved */ : 7;
+
+ /* Autofocus Statistic Output Buffer Header */
+ unsigned int af_metrichp:8;
+ unsigned int /* reserved */ : 24;
+
+ /* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */
+ void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS];
+} __attribute__((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */
+
+struct msm_camera_frame_msg {
+ unsigned long output_y_address;
+ unsigned long output_cbcr_address;
+
+ unsigned int blacklevelevenColumn:23;
+ uint16_t reserved1:9;
+ unsigned int blackleveloddColumn:23;
+ uint16_t reserved2:9;
+
+ uint16_t greendefectpixelcount:8;
+ uint16_t reserved3:8;
+ uint16_t redbluedefectpixelcount:8;
+ uint16_t reserved4:8;
+} __attribute__((packed, aligned(4)));
+
+/* New one for 7k */
+struct msm_vfe_command_7k {
+ uint16_t queue;
+ uint16_t length;
+ void *value;
+};
+
+struct stop_event {
+ wait_queue_head_t wait;
+ int state;
+ int timeout;
+};
+
+
+#endif /* __MSM_VFE7X_H__ */
diff --git a/drivers/staging/dream/camera/msm_vfe8x.c b/drivers/staging/dream/camera/msm_vfe8x.c
new file mode 100644
index 00000000000..03de6ec2eb4
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe8x.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include "msm_vfe8x_proc.h"
+
+#define ON 1
+#define OFF 0
+
+struct mutex vfe_lock;
+static void *vfe_syncdata;
+
+static int vfe_enable(struct camera_enable_cmd *enable)
+{
+ int rc = 0;
+ return rc;
+}
+
+static int vfe_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev)
+{
+ int rc = 0;
+
+ vfe_stop();
+
+ msm_camio_disable(dev);
+ return rc;
+}
+
+static void vfe_release(struct platform_device *dev)
+{
+ msm_camio_disable(dev);
+ vfe_cmd_release(dev);
+
+ mutex_lock(&vfe_lock);
+ vfe_syncdata = NULL;
+ mutex_unlock(&vfe_lock);
+}
+
+static void vfe_config_axi(int mode,
+ struct axidata *ad, struct vfe_cmd_axi_output_config *ao)
+{
+ struct msm_pmem_region *regptr;
+ int i, j;
+ uint32_t *p1, *p2;
+
+ if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) {
+ regptr = ad->region;
+ for (i = 0;
+ i < ad->bufnum1; i++) {
+
+ p1 = &(ao->output1.outputY.outFragments[i][0]);
+ p2 = &(ao->output1.outputCbcr.outFragments[i][0]);
+
+ for (j = 0;
+ j < ao->output1.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->y_off;
+ p1++;
+
+ *p2 = regptr->paddr + regptr->cbcr_off;
+ p2++;
+ }
+ regptr++;
+ }
+ } /* if OUTPUT1 or Both */
+
+ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+
+ regptr = &(ad->region[ad->bufnum1]);
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+
+ for (i = 0;
+ i < ad->bufnum2; i++) {
+
+ p1 = &(ao->output2.outputY.outFragments[i][0]);
+ p2 = &(ao->output2.outputCbcr.outFragments[i][0]);
+
+ CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, cbcr_off = %d\n",
+ regptr->paddr, regptr->y_off, regptr->cbcr_off);
+
+ for (j = 0;
+ j < ao->output2.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->y_off;
+ CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+ p1++;
+
+ *p2 = regptr->paddr + regptr->cbcr_off;
+ CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+ p2++;
+ }
+ regptr++;
+ }
+ }
+}
+
+static int vfe_proc_general(struct msm_vfe_command_8k *cmd)
+{
+ int rc = 0;
+
+ CDBG("vfe_proc_general: cmdID = %d\n", cmd->id);
+
+ switch (cmd->id) {
+ case VFE_CMD_ID_RESET:
+ msm_camio_vfe_blk_reset();
+ msm_camio_camif_pad_reg_reset_2();
+ vfe_reset();
+ break;
+
+ case VFE_CMD_ID_START: {
+ struct vfe_cmd_start start;
+ if (copy_from_user(&start,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ /* msm_camio_camif_pad_reg_reset_2(); */
+ msm_camio_camif_pad_reg_reset();
+ vfe_start(&start);
+ }
+ break;
+
+ case VFE_CMD_ID_CAMIF_CONFIG: {
+ struct vfe_cmd_camif_config camif;
+ if (copy_from_user(&camif,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_camif_config(&camif);
+ }
+ break;
+
+ case VFE_CMD_ID_BLACK_LEVEL_CONFIG: {
+ struct vfe_cmd_black_level_config bl;
+ if (copy_from_user(&bl,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_black_level_config(&bl);
+ }
+ break;
+
+ case VFE_CMD_ID_ROLL_OFF_CONFIG: {
+ struct vfe_cmd_roll_off_config rolloff;
+ if (copy_from_user(&rolloff,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_roll_off_config(&rolloff);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: {
+ struct vfe_cmd_demux_channel_gain_config demuxc;
+ if (copy_from_user(&demuxc,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ /* demux is always enabled. */
+ vfe_demux_channel_gain_config(&demuxc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_CONFIG: {
+ struct vfe_cmd_demosaic_config demosaic;
+ if (copy_from_user(&demosaic,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_demosaic_config(&demosaic);
+ }
+ break;
+
+ case VFE_CMD_ID_FOV_CROP_CONFIG:
+ case VFE_CMD_ID_FOV_CROP_UPDATE: {
+ struct vfe_cmd_fov_crop_config fov;
+ if (copy_from_user(&fov,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_fov_crop_config(&fov);
+ }
+ break;
+
+ case VFE_CMD_ID_MAIN_SCALER_CONFIG:
+ case VFE_CMD_ID_MAIN_SCALER_UPDATE: {
+ struct vfe_cmd_main_scaler_config mainds;
+ if (copy_from_user(&mainds,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_main_scaler_config(&mainds);
+ }
+ break;
+
+ case VFE_CMD_ID_WHITE_BALANCE_CONFIG:
+ case VFE_CMD_ID_WHITE_BALANCE_UPDATE: {
+ struct vfe_cmd_white_balance_config wb;
+ if (copy_from_user(&wb,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_white_balance_config(&wb);
+ }
+ break;
+
+ case VFE_CMD_ID_COLOR_CORRECTION_CONFIG:
+ case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: {
+ struct vfe_cmd_color_correction_config cc;
+ if (copy_from_user(&cc,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_color_correction_config(&cc);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_CONFIG: {
+ struct vfe_cmd_la_config la;
+ if (copy_from_user(&la,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_la_config(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_CONFIG: {
+ struct vfe_cmd_rgb_gamma_config rgb;
+ if (copy_from_user(&rgb,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ rc = vfe_rgb_gamma_config(&rgb);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_ENHAN_CONFIG:
+ case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: {
+ struct vfe_cmd_chroma_enhan_config chrom;
+ if (copy_from_user(&chrom,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_chroma_enhan_config(&chrom);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG:
+ case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: {
+ struct vfe_cmd_chroma_suppression_config chromsup;
+ if (copy_from_user(&chromsup,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_chroma_sup_config(&chromsup);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_CONFIG: {
+ struct vfe_cmd_asf_config asf;
+ if (copy_from_user(&asf,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_asf_config(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2Y_CONFIG:
+ case VFE_CMD_ID_SCALER2Y_UPDATE: {
+ struct vfe_cmd_scaler2_config ds2y;
+ if (copy_from_user(&ds2y,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_scaler2y_config(&ds2y);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2CbCr_CONFIG:
+ case VFE_CMD_ID_SCALER2CbCr_UPDATE: {
+ struct vfe_cmd_scaler2_config ds2cbcr;
+ if (copy_from_user(&ds2cbcr,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_scaler2cbcr_config(&ds2cbcr);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: {
+ struct vfe_cmd_chroma_subsample_config sub;
+ if (copy_from_user(&sub,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_chroma_subsample_config(&sub);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_CONFIG: {
+ struct vfe_cmd_frame_skip_config fskip;
+ if (copy_from_user(&fskip,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_frame_skip_config(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: {
+ struct vfe_cmd_output_clamp_config clamp;
+ if (copy_from_user(&clamp,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_output_clamp_config(&clamp);
+ }
+ break;
+
+ /* module update commands */
+ case VFE_CMD_ID_BLACK_LEVEL_UPDATE: {
+ struct vfe_cmd_black_level_config blk;
+ if (copy_from_user(&blk,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_black_level_update(&blk);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: {
+ struct vfe_cmd_demux_channel_gain_config dmu;
+ if (copy_from_user(&dmu,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_demux_channel_gain_update(&dmu);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: {
+ struct vfe_cmd_demosaic_bpc_update demo_bpc;
+ if (copy_from_user(&demo_bpc,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_demosaic_bpc_update(&demo_bpc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: {
+ struct vfe_cmd_demosaic_abf_update demo_abf;
+ if (copy_from_user(&demo_abf,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_demosaic_abf_update(&demo_abf);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_UPDATE: {
+ struct vfe_cmd_la_config la;
+ if (copy_from_user(&la,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_la_update(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_UPDATE: {
+ struct vfe_cmd_rgb_gamma_config rgb;
+ if (copy_from_user(&rgb,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ rc = vfe_rgb_gamma_update(&rgb);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_UPDATE: {
+ struct vfe_cmd_asf_update asf;
+ if (copy_from_user(&asf,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_asf_update(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_UPDATE: {
+ struct vfe_cmd_frame_skip_update fskip;
+ if (copy_from_user(&fskip,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_frame_skip_update(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_CAMIF_FRAME_UPDATE: {
+ struct vfe_cmds_camif_frame fup;
+ if (copy_from_user(&fup,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_camif_frame_update(&fup);
+ }
+ break;
+
+ /* stats update commands */
+ case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: {
+ struct vfe_cmd_stats_af_update afup;
+ if (copy_from_user(&afup,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_stats_update_af(&afup);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_UPDATE: {
+ struct vfe_cmd_stats_wb_exp_update wbexp;
+ if (copy_from_user(&wbexp,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_stats_update_wb_exp(&wbexp);
+ }
+ break;
+
+ /* control of start, stop, update, etc... */
+ case VFE_CMD_ID_STOP:
+ vfe_stop();
+ break;
+
+ case VFE_CMD_ID_GET_HW_VERSION:
+ break;
+
+ /* stats */
+ case VFE_CMD_ID_STATS_SETTING: {
+ struct vfe_cmd_stats_setting stats;
+ if (copy_from_user(&stats,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_stats_setting(&stats);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_AUTOFOCUS_START: {
+ struct vfe_cmd_stats_af_start af;
+ if (copy_from_user(&af,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_stats_start_af(&af);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_AUTOFOCUS_STOP:
+ vfe_stats_af_stop();
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_START: {
+ struct vfe_cmd_stats_wb_exp_start awexp;
+ if (copy_from_user(&awexp,
+ (void __user *) cmd->value, cmd->length))
+ rc = -EFAULT;
+
+ vfe_stats_start_wb_exp(&awexp);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_STOP:
+ vfe_stats_wb_exp_stop();
+ break;
+
+ case VFE_CMD_ID_ASYNC_TIMER_SETTING:
+ break;
+
+ case VFE_CMD_ID_UPDATE:
+ vfe_update();
+ break;
+
+ /* test gen */
+ case VFE_CMD_ID_TEST_GEN_START:
+ break;
+
+/*
+ acknowledge from upper layer
+ these are not in general command.
+
+ case VFE_CMD_ID_OUTPUT1_ACK:
+ break;
+ case VFE_CMD_ID_OUTPUT2_ACK:
+ break;
+ case VFE_CMD_ID_EPOCH1_ACK:
+ break;
+ case VFE_CMD_ID_EPOCH2_ACK:
+ break;
+ case VFE_CMD_ID_STATS_AUTOFOCUS_ACK:
+ break;
+ case VFE_CMD_ID_STATS_WB_EXP_ACK:
+ break;
+*/
+
+ default:
+ break;
+ } /* switch */
+
+ return rc;
+}
+
+static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ struct msm_pmem_region *regptr;
+ struct msm_vfe_command_8k vfecmd;
+
+ uint32_t i;
+
+ void *cmd_data = NULL;
+ long rc = 0;
+
+ struct vfe_cmd_axi_output_config *axio = NULL;
+ struct vfe_cmd_stats_setting *scfg = NULL;
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BUF_RELEASE) {
+
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(struct msm_vfe_command_8k)))
+ return -EFAULT;
+ }
+
+ CDBG("vfe_config: cmdType = %d\n", cmd->cmd_type);
+
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe_proc_general(&vfecmd);
+ break;
+
+ case CMD_STATS_ENABLE:
+ case CMD_STATS_AXI_CFG: {
+ struct axidata *axid;
+
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+
+ scfg =
+ kmalloc(sizeof(struct vfe_cmd_stats_setting),
+ GFP_ATOMIC);
+ if (!scfg)
+ return -ENOMEM;
+
+ if (copy_from_user(scfg,
+ (void __user *)(vfecmd.value),
+ vfecmd.length)) {
+
+ kfree(scfg);
+ return -EFAULT;
+ }
+
+ regptr = axid->region;
+ if (axid->bufnum1 > 0) {
+ for (i = 0; i < axid->bufnum1; i++) {
+ scfg->awbBuffer[i] =
+ (uint32_t)(regptr->paddr);
+ regptr++;
+ }
+ }
+
+ if (axid->bufnum2 > 0) {
+ for (i = 0; i < axid->bufnum2; i++) {
+ scfg->afBuffer[i] =
+ (uint32_t)(regptr->paddr);
+ regptr++;
+ }
+ }
+
+ vfe_stats_config(scfg);
+ }
+ break;
+
+ case CMD_STATS_AF_AXI_CFG: {
+ }
+ break;
+
+ case CMD_FRAME_BUF_RELEASE: {
+ /* preview buffer release */
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe_cmd_output_ack fack;
+
+ if (!data)
+ return -EFAULT;
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ b->path = MSM_FRAME_ENC;
+
+ fack.ybufaddr[0] =
+ (uint32_t)(p + b->y_off);
+
+ fack.chromabufaddr[0] =
+ (uint32_t)(p + b->cbcr_off);
+
+ if (b->path == MSM_FRAME_PREV_1)
+ vfe_output1_ack(&fack);
+
+ if (b->path == MSM_FRAME_ENC ||
+ b->path == MSM_FRAME_PREV_2)
+ vfe_output2_ack(&fack);
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE: {
+ }
+ break;
+
+ case CMD_STATS_BUF_RELEASE: {
+ struct vfe_cmd_stats_wb_exp_ack sack;
+
+ if (!data)
+ return -EFAULT;
+
+ sack.nextWbExpOutputBufferAddr = *(uint32_t *)data;
+ vfe_stats_wb_exp_ack(&sack);
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT1: {
+ struct axidata *axid;
+
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+
+ axio =
+ kmalloc(sizeof(struct vfe_cmd_axi_output_config),
+ GFP_ATOMIC);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ sizeof(struct vfe_cmd_axi_output_config))) {
+ kfree(axio);
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_1, axid, axio);
+ vfe_axi_output_config(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_OUT2:
+ case CMD_RAW_PICT_AXI_CFG: {
+ struct axidata *axid;
+
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+
+ axio =
+ kmalloc(sizeof(struct vfe_cmd_axi_output_config),
+ GFP_ATOMIC);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ sizeof(struct vfe_cmd_axi_output_config))) {
+ kfree(axio);
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_2, axid, axio);
+
+ axio->outputDataSize = 0;
+ vfe_axi_output_config(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP_O1_AND_O2: {
+ struct axidata *axid;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+
+ axio =
+ kmalloc(sizeof(struct vfe_cmd_axi_output_config),
+ GFP_ATOMIC);
+ if (!axio)
+ return -ENOMEM;
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ sizeof(struct vfe_cmd_axi_output_config))) {
+ kfree(axio);
+ return -EFAULT;
+ }
+
+ vfe_config_axi(OUTPUT_1_AND_2,
+ axid, axio);
+ vfe_axi_output_config(axio);
+ cmd_data = axio;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ kfree(scfg);
+
+ kfree(axio);
+
+/*
+ if (cmd->length > 256 &&
+ cmd_data &&
+ (cmd->cmd_type == CMD_GENERAL ||
+ cmd->cmd_type == CMD_STATS_DISABLE)) {
+ kfree(cmd_data);
+ }
+*/
+ return rc;
+}
+
+static int vfe_init(struct msm_vfe_callback *presp,
+ struct platform_device *dev)
+{
+ int rc = 0;
+
+ rc = vfe_cmd_init(presp, dev, vfe_syncdata);
+ if (rc < 0)
+ return rc;
+
+ /* Bring up all the required GPIOs and Clocks */
+ return msm_camio_enable(dev);
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ mutex_init(&vfe_lock);
+ fptr->vfe_init = vfe_init;
+ fptr->vfe_enable = vfe_enable;
+ fptr->vfe_config = vfe_config;
+ fptr->vfe_disable = vfe_disable;
+ fptr->vfe_release = vfe_release;
+ vfe_syncdata = data;
+}
diff --git a/drivers/staging/dream/camera/msm_vfe8x.h b/drivers/staging/dream/camera/msm_vfe8x.h
new file mode 100644
index 00000000000..28a70a9e5ed
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe8x.h
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+#ifndef __MSM_VFE8X_H__
+#define __MSM_VFE8X_H__
+
+#define TRUE 1
+#define FALSE 0
+#define boolean uint8_t
+
+enum VFE_STATE {
+ VFE_STATE_IDLE,
+ VFE_STATE_ACTIVE
+};
+
+enum vfe_cmd_id {
+ /*
+ *Important! Command_ID are arranged in order.
+ *Don't change!*/
+ VFE_CMD_ID_START,
+ VFE_CMD_ID_RESET,
+
+ /* bus and camif config */
+ VFE_CMD_ID_AXI_INPUT_CONFIG,
+ VFE_CMD_ID_CAMIF_CONFIG,
+ VFE_CMD_ID_AXI_OUTPUT_CONFIG,
+
+ /* module config */
+ VFE_CMD_ID_BLACK_LEVEL_CONFIG,
+ VFE_CMD_ID_ROLL_OFF_CONFIG,
+ VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG,
+ VFE_CMD_ID_DEMOSAIC_CONFIG,
+ VFE_CMD_ID_FOV_CROP_CONFIG,
+ VFE_CMD_ID_MAIN_SCALER_CONFIG,
+ VFE_CMD_ID_WHITE_BALANCE_CONFIG,
+ VFE_CMD_ID_COLOR_CORRECTION_CONFIG,
+ VFE_CMD_ID_LA_CONFIG,
+ VFE_CMD_ID_RGB_GAMMA_CONFIG,
+ VFE_CMD_ID_CHROMA_ENHAN_CONFIG,
+ VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG,
+ VFE_CMD_ID_ASF_CONFIG,
+ VFE_CMD_ID_SCALER2Y_CONFIG,
+ VFE_CMD_ID_SCALER2CbCr_CONFIG,
+ VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG,
+ VFE_CMD_ID_FRAME_SKIP_CONFIG,
+ VFE_CMD_ID_OUTPUT_CLAMP_CONFIG,
+
+ /* test gen */
+ VFE_CMD_ID_TEST_GEN_START,
+
+ VFE_CMD_ID_UPDATE,
+
+ /* ackownledge from upper layer */
+ VFE_CMD_ID_OUTPUT1_ACK,
+ VFE_CMD_ID_OUTPUT2_ACK,
+ VFE_CMD_ID_EPOCH1_ACK,
+ VFE_CMD_ID_EPOCH2_ACK,
+ VFE_CMD_ID_STATS_AUTOFOCUS_ACK,
+ VFE_CMD_ID_STATS_WB_EXP_ACK,
+
+ /* module update commands */
+ VFE_CMD_ID_BLACK_LEVEL_UPDATE,
+ VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE,
+ VFE_CMD_ID_DEMOSAIC_BPC_UPDATE,
+ VFE_CMD_ID_DEMOSAIC_ABF_UPDATE,
+ VFE_CMD_ID_FOV_CROP_UPDATE,
+ VFE_CMD_ID_WHITE_BALANCE_UPDATE,
+ VFE_CMD_ID_COLOR_CORRECTION_UPDATE,
+ VFE_CMD_ID_LA_UPDATE,
+ VFE_CMD_ID_RGB_GAMMA_UPDATE,
+ VFE_CMD_ID_CHROMA_ENHAN_UPDATE,
+ VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE,
+ VFE_CMD_ID_MAIN_SCALER_UPDATE,
+ VFE_CMD_ID_SCALER2CbCr_UPDATE,
+ VFE_CMD_ID_SCALER2Y_UPDATE,
+ VFE_CMD_ID_ASF_UPDATE,
+ VFE_CMD_ID_FRAME_SKIP_UPDATE,
+ VFE_CMD_ID_CAMIF_FRAME_UPDATE,
+
+ /* stats update commands */
+ VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE,
+ VFE_CMD_ID_STATS_WB_EXP_UPDATE,
+
+ /* control of start, stop, update, etc... */
+ VFE_CMD_ID_STOP,
+ VFE_CMD_ID_GET_HW_VERSION,
+
+ /* stats */
+ VFE_CMD_ID_STATS_SETTING,
+ VFE_CMD_ID_STATS_AUTOFOCUS_START,
+ VFE_CMD_ID_STATS_AUTOFOCUS_STOP,
+ VFE_CMD_ID_STATS_WB_EXP_START,
+ VFE_CMD_ID_STATS_WB_EXP_STOP,
+
+ VFE_CMD_ID_ASYNC_TIMER_SETTING,
+
+ /* max id */
+ VFE_CMD_ID_MAX
+};
+
+struct vfe_cmd_hw_version {
+ uint32_t minorVersion;
+ uint32_t majorVersion;
+ uint32_t coreVersion;
+};
+
+enum VFE_CAMIF_SYNC_EDGE {
+ VFE_CAMIF_SYNC_EDGE_ActiveHigh,
+ VFE_CAMIF_SYNC_EDGE_ActiveLow
+};
+
+enum VFE_CAMIF_SYNC_MODE {
+ VFE_CAMIF_SYNC_MODE_APS,
+ VFE_CAMIF_SYNC_MODE_EFS,
+ VFE_CAMIF_SYNC_MODE_ELS,
+ VFE_CAMIF_SYNC_MODE_ILLEGAL
+};
+
+struct vfe_cmds_camif_efs {
+ uint8_t efsendofline;
+ uint8_t efsstartofline;
+ uint8_t efsendofframe;
+ uint8_t efsstartofframe;
+};
+
+struct vfe_cmds_camif_frame {
+ uint16_t pixelsPerLine;
+ uint16_t linesPerFrame;
+};
+
+struct vfe_cmds_camif_window {
+ uint16_t firstpixel;
+ uint16_t lastpixel;
+ uint16_t firstline;
+ uint16_t lastline;
+};
+
+enum CAMIF_SUBSAMPLE_FRAME_SKIP {
+ CAMIF_SUBSAMPLE_FRAME_SKIP_0,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_AllFrames,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_2Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_3Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_4Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_5Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_6Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_7Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_8Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_9Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_10Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_11Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_12Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_13Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_14Frame,
+ CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_15Frame
+};
+
+struct vfe_cmds_camif_subsample {
+ uint16_t pixelskipmask;
+ uint16_t lineskipmask;
+ enum CAMIF_SUBSAMPLE_FRAME_SKIP frameskip;
+ uint8_t frameskipmode;
+ uint8_t pixelskipwrap;
+};
+
+struct vfe_cmds_camif_epoch {
+ uint8_t enable;
+ uint16_t lineindex;
+};
+
+struct vfe_cmds_camif_cfg {
+ enum VFE_CAMIF_SYNC_EDGE vSyncEdge;
+ enum VFE_CAMIF_SYNC_EDGE hSyncEdge;
+ enum VFE_CAMIF_SYNC_MODE syncMode;
+ uint8_t vfeSubSampleEnable;
+ uint8_t busSubSampleEnable;
+ uint8_t irqSubSampleEnable;
+ uint8_t binningEnable;
+ uint8_t misrEnable;
+};
+
+struct vfe_cmd_camif_config {
+ struct vfe_cmds_camif_cfg camifConfig;
+ struct vfe_cmds_camif_efs EFS;
+ struct vfe_cmds_camif_frame frame;
+ struct vfe_cmds_camif_window window;
+ struct vfe_cmds_camif_subsample subsample;
+ struct vfe_cmds_camif_epoch epoch1;
+ struct vfe_cmds_camif_epoch epoch2;
+};
+
+enum VFE_AXI_OUTPUT_MODE {
+ VFE_AXI_OUTPUT_MODE_Output1,
+ VFE_AXI_OUTPUT_MODE_Output2,
+ VFE_AXI_OUTPUT_MODE_Output1AndOutput2,
+ VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2,
+ VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1,
+ VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2,
+ VFE_AXI_LAST_OUTPUT_MODE_ENUM
+};
+
+enum VFE_RAW_WR_PATH_SEL {
+ VFE_RAW_OUTPUT_DISABLED,
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH,
+ VFE_RAW_OUTPUT_VIEW_CBCR_PATH,
+ VFE_RAW_OUTPUT_PATH_INVALID
+};
+
+enum VFE_RAW_PIXEL_DATA_SIZE {
+ VFE_RAW_PIXEL_DATA_SIZE_8BIT,
+ VFE_RAW_PIXEL_DATA_SIZE_10BIT,
+ VFE_RAW_PIXEL_DATA_SIZE_12BIT,
+};
+
+#define VFE_AXI_OUTPUT_BURST_LENGTH 4
+#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4
+#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3
+
+struct vfe_cmds_axi_out_per_component {
+ uint16_t imageWidth;
+ uint16_t imageHeight;
+ uint16_t outRowCount;
+ uint16_t outRowIncrement;
+ uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT]
+ [VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+struct vfe_cmds_axi_per_output_path {
+ uint8_t fragmentCount;
+ struct vfe_cmds_axi_out_per_component outputY;
+ struct vfe_cmds_axi_out_per_component outputCbcr;
+};
+
+enum VFE_AXI_BURST_LENGTH {
+ VFE_AXI_BURST_LENGTH_IS_2 = 2,
+ VFE_AXI_BURST_LENGTH_IS_4 = 4,
+ VFE_AXI_BURST_LENGTH_IS_8 = 8,
+ VFE_AXI_BURST_LENGTH_IS_16 = 16
+};
+
+struct vfe_cmd_axi_output_config {
+ enum VFE_AXI_BURST_LENGTH burstLength;
+ enum VFE_AXI_OUTPUT_MODE outputMode;
+ enum VFE_RAW_PIXEL_DATA_SIZE outputDataSize;
+ struct vfe_cmds_axi_per_output_path output1;
+ struct vfe_cmds_axi_per_output_path output2;
+};
+
+struct vfe_cmd_fov_crop_config {
+ uint8_t enable;
+ uint16_t firstPixel;
+ uint16_t lastPixel;
+ uint16_t firstLine;
+ uint16_t lastLine;
+};
+
+struct vfe_cmds_main_scaler_stripe_init {
+ uint16_t MNCounterInit;
+ uint16_t phaseInit;
+};
+
+struct vfe_cmds_scaler_one_dimension {
+ uint8_t enable;
+ uint16_t inputSize;
+ uint16_t outputSize;
+ uint32_t phaseMultiplicationFactor;
+ uint8_t interpolationResolution;
+};
+
+struct vfe_cmd_main_scaler_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+ struct vfe_cmds_main_scaler_stripe_init MNInitH;
+ struct vfe_cmds_main_scaler_stripe_init MNInitV;
+};
+
+struct vfe_cmd_scaler2_config {
+ uint8_t enable;
+ struct vfe_cmds_scaler_one_dimension hconfig;
+ struct vfe_cmds_scaler_one_dimension vconfig;
+};
+
+struct vfe_cmd_frame_skip_config {
+ uint8_t output1Period;
+ uint32_t output1Pattern;
+ uint8_t output2Period;
+ uint32_t output2Pattern;
+};
+
+struct vfe_cmd_frame_skip_update {
+ uint32_t output1Pattern;
+ uint32_t output2Pattern;
+};
+
+struct vfe_cmd_output_clamp_config {
+ uint8_t minCh0;
+ uint8_t minCh1;
+ uint8_t minCh2;
+ uint8_t maxCh0;
+ uint8_t maxCh1;
+ uint8_t maxCh2;
+};
+
+struct vfe_cmd_chroma_subsample_config {
+ uint8_t enable;
+ uint8_t cropEnable;
+ uint8_t vsubSampleEnable;
+ uint8_t hsubSampleEnable;
+ uint8_t vCosited;
+ uint8_t hCosited;
+ uint8_t vCositedPhase;
+ uint8_t hCositedPhase;
+ uint16_t cropWidthFirstPixel;
+ uint16_t cropWidthLastPixel;
+ uint16_t cropHeightFirstLine;
+ uint16_t cropHeightLastLine;
+};
+
+enum VFE_START_INPUT_SOURCE {
+ VFE_START_INPUT_SOURCE_CAMIF,
+ VFE_START_INPUT_SOURCE_TESTGEN,
+ VFE_START_INPUT_SOURCE_AXI,
+ VFE_START_INPUT_SOURCE_INVALID
+};
+
+enum VFE_START_OPERATION_MODE {
+ VFE_START_OPERATION_MODE_CONTINUOUS,
+ VFE_START_OPERATION_MODE_SNAPSHOT
+};
+
+enum VFE_START_PIXEL_PATTERN {
+ VFE_BAYER_RGRGRG,
+ VFE_BAYER_GRGRGR,
+ VFE_BAYER_BGBGBG,
+ VFE_BAYER_GBGBGB,
+ VFE_YUV_YCbYCr,
+ VFE_YUV_YCrYCb,
+ VFE_YUV_CbYCrY,
+ VFE_YUV_CrYCbY
+};
+
+enum VFE_BUS_RD_INPUT_PIXEL_PATTERN {
+ VFE_BAYER_RAW,
+ VFE_YUV_INTERLEAVED,
+ VFE_YUV_PSEUDO_PLANAR_Y,
+ VFE_YUV_PSEUDO_PLANAR_CBCR
+};
+
+enum VFE_YUV_INPUT_COSITING_MODE {
+ VFE_YUV_COSITED,
+ VFE_YUV_INTERPOLATED
+};
+
+struct vfe_cmd_start {
+ enum VFE_START_INPUT_SOURCE inputSource;
+ enum VFE_START_OPERATION_MODE operationMode;
+ uint8_t snapshotCount;
+ enum VFE_START_PIXEL_PATTERN pixel;
+ enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode;
+};
+
+struct vfe_cmd_output_ack {
+ uint32_t ybufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+ uint32_t chromabufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME];
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_setting {
+ uint16_t frameHDimension;
+ uint16_t frameVDimension;
+ uint8_t afBusPrioritySelection;
+ uint8_t afBusPriority;
+ uint8_t awbBusPrioritySelection;
+ uint8_t awbBusPriority;
+ uint8_t histBusPrioritySelection;
+ uint8_t histBusPriority;
+ uint32_t afBuffer[VFE_STATS_BUFFER_COUNT];
+ uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT];
+ uint32_t histBuffer[VFE_STATS_BUFFER_COUNT];
+};
+
+struct vfe_cmd_stats_af_start {
+ uint8_t enable;
+ uint8_t windowMode;
+ uint16_t windowHOffset;
+ uint16_t windowVOffset;
+ uint16_t windowWidth;
+ uint16_t windowHeight;
+ uint8_t gridForMultiWindows[16];
+ uint8_t metricSelection;
+ int16_t metricMax;
+ int8_t highPassCoef[7];
+ int8_t bufferHeader;
+};
+
+struct vfe_cmd_stats_af_update {
+ uint8_t windowMode;
+ uint16_t windowHOffset;
+ uint16_t windowVOffset;
+ uint16_t windowWidth;
+ uint16_t windowHeight;
+};
+
+struct vfe_cmd_stats_wb_exp_start {
+ uint8_t enable;
+ uint8_t wbExpRegions;
+ uint8_t wbExpSubRegion;
+ uint8_t awbYMin;
+ uint8_t awbYMax;
+ int8_t awbMCFG[4];
+ int16_t awbCCFG[4];
+ int8_t axwHeader;
+};
+
+struct vfe_cmd_stats_wb_exp_update {
+ uint8_t wbExpRegions;
+ uint8_t wbExpSubRegion;
+ int8_t awbYMin;
+ int8_t awbYMax;
+ int8_t awbMCFG[4];
+ int16_t awbCCFG[4];
+};
+
+struct vfe_cmd_stats_af_ack {
+ uint32_t nextAFOutputBufferAddr;
+};
+
+struct vfe_cmd_stats_wb_exp_ack {
+ uint32_t nextWbExpOutputBufferAddr;
+};
+
+struct vfe_cmd_black_level_config {
+ uint8_t enable;
+ uint16_t evenEvenAdjustment;
+ uint16_t evenOddAdjustment;
+ uint16_t oddEvenAdjustment;
+ uint16_t oddOddAdjustment;
+};
+
+/* 13*1 */
+#define VFE_ROLL_OFF_INIT_TABLE_SIZE 13
+/* 13*16 */
+#define VFE_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+struct vfe_cmd_roll_off_config {
+ uint8_t enable;
+ uint16_t gridWidth;
+ uint16_t gridHeight;
+ uint16_t yDelta;
+ uint8_t gridXIndex;
+ uint8_t gridYIndex;
+ uint16_t gridPixelXIndex;
+ uint16_t gridPixelYIndex;
+ uint16_t yDeltaAccum;
+ uint16_t initTableR[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE];
+ int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+ int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE];
+};
+
+struct vfe_cmd_demux_channel_gain_config {
+ uint16_t ch0EvenGain;
+ uint16_t ch0OddGain;
+ uint16_t ch1Gain;
+ uint16_t ch2Gain;
+};
+
+struct vfe_cmds_demosaic_abf {
+ uint8_t enable;
+ uint8_t forceOn;
+ uint8_t shift;
+ uint16_t lpThreshold;
+ uint16_t max;
+ uint16_t min;
+ uint8_t ratio;
+};
+
+struct vfe_cmds_demosaic_bpc {
+ uint8_t enable;
+ uint16_t fmaxThreshold;
+ uint16_t fminThreshold;
+ uint16_t redDiffThreshold;
+ uint16_t blueDiffThreshold;
+ uint16_t greenDiffThreshold;
+};
+
+struct vfe_cmd_demosaic_config {
+ uint8_t enable;
+ uint8_t slopeShift;
+ struct vfe_cmds_demosaic_abf abfConfig;
+ struct vfe_cmds_demosaic_bpc bpcConfig;
+};
+
+struct vfe_cmd_demosaic_bpc_update {
+ struct vfe_cmds_demosaic_bpc bpcUpdate;
+};
+
+struct vfe_cmd_demosaic_abf_update {
+ struct vfe_cmds_demosaic_abf abfUpdate;
+};
+
+struct vfe_cmd_white_balance_config {
+ uint8_t enable;
+ uint16_t ch2Gain;
+ uint16_t ch1Gain;
+ uint16_t ch0Gain;
+};
+
+enum VFE_COLOR_CORRECTION_COEF_QFACTOR {
+ COEF_IS_Q7_SIGNED,
+ COEF_IS_Q8_SIGNED,
+ COEF_IS_Q9_SIGNED,
+ COEF_IS_Q10_SIGNED
+};
+
+struct vfe_cmd_color_correction_config {
+ uint8_t enable;
+ enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor;
+ int16_t C0;
+ int16_t C1;
+ int16_t C2;
+ int16_t C3;
+ int16_t C4;
+ int16_t C5;
+ int16_t C6;
+ int16_t C7;
+ int16_t C8;
+ int16_t K0;
+ int16_t K1;
+ int16_t K2;
+};
+
+#define VFE_LA_TABLE_LENGTH 256
+struct vfe_cmd_la_config {
+ uint8_t enable;
+ int16_t table[VFE_LA_TABLE_LENGTH];
+};
+
+#define VFE_GAMMA_TABLE_LENGTH 256
+enum VFE_RGB_GAMMA_TABLE_SELECT {
+ RGB_GAMMA_CH0_SELECTED,
+ RGB_GAMMA_CH1_SELECTED,
+ RGB_GAMMA_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_SELECTED,
+ RGB_GAMMA_CH0_CH2_SELECTED,
+ RGB_GAMMA_CH1_CH2_SELECTED,
+ RGB_GAMMA_CH0_CH1_CH2_SELECTED
+};
+
+struct vfe_cmd_rgb_gamma_config {
+ uint8_t enable;
+ enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect;
+ int16_t table[VFE_GAMMA_TABLE_LENGTH];
+};
+
+struct vfe_cmd_chroma_enhan_config {
+ uint8_t enable;
+ int16_t am;
+ int16_t ap;
+ int16_t bm;
+ int16_t bp;
+ int16_t cm;
+ int16_t cp;
+ int16_t dm;
+ int16_t dp;
+ int16_t kcr;
+ int16_t kcb;
+ int16_t RGBtoYConversionV0;
+ int16_t RGBtoYConversionV1;
+ int16_t RGBtoYConversionV2;
+ uint8_t RGBtoYConversionOffset;
+};
+
+struct vfe_cmd_chroma_suppression_config {
+ uint8_t enable;
+ uint8_t m1;
+ uint8_t m3;
+ uint8_t n1;
+ uint8_t n3;
+ uint8_t nn1;
+ uint8_t mm1;
+};
+
+struct vfe_cmd_asf_config {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+ uint16_t cropFirstPixel;
+ uint16_t cropLastPixel;
+ uint16_t cropFirstLine;
+ uint16_t cropLastLine;
+};
+
+struct vfe_cmd_asf_update {
+ uint8_t enable;
+ uint8_t smoothFilterEnabled;
+ uint8_t sharpMode;
+ uint8_t smoothCoefCenter;
+ uint8_t smoothCoefSurr;
+ uint8_t normalizeFactor;
+ uint8_t sharpK1;
+ uint8_t sharpK2;
+ uint8_t sharpThreshE1;
+ int8_t sharpThreshE2;
+ int8_t sharpThreshE3;
+ int8_t sharpThreshE4;
+ int8_t sharpThreshE5;
+ int8_t filter1Coefficients[9];
+ int8_t filter2Coefficients[9];
+ uint8_t cropEnable;
+};
+
+enum VFE_TEST_GEN_SYNC_EDGE {
+ VFE_TEST_GEN_SYNC_EDGE_ActiveHigh,
+ VFE_TEST_GEN_SYNC_EDGE_ActiveLow
+};
+
+struct vfe_cmd_test_gen_start {
+ uint8_t pixelDataSelect;
+ uint8_t systematicDataSelect;
+ enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge;
+ enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge;
+ uint16_t numFrame;
+ enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize;
+ uint16_t imageWidth;
+ uint16_t imageHeight;
+ uint32_t startOfFrameOffset;
+ uint32_t endOfFrameNOffset;
+ uint16_t startOfLineOffset;
+ uint16_t endOfLineNOffset;
+ uint16_t hbi;
+ uint8_t vblEnable;
+ uint16_t vbl;
+ uint8_t startOfFrameDummyLine;
+ uint8_t endOfFrameDummyLine;
+ uint8_t unicolorBarEnable;
+ uint8_t colorBarsSplitEnable;
+ uint8_t unicolorBarSelect;
+ enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern;
+ uint8_t colorBarsRotatePeriod;
+ uint16_t testGenRandomSeed;
+};
+
+struct vfe_cmd_bus_pm_start {
+ uint8_t output2YWrPmEnable;
+ uint8_t output2CbcrWrPmEnable;
+ uint8_t output1YWrPmEnable;
+ uint8_t output1CbcrWrPmEnable;
+};
+
+struct vfe_cmd_camif_frame_update {
+ struct vfe_cmds_camif_frame camifFrame;
+};
+
+struct vfe_cmd_sync_timer_setting {
+ uint8_t whichSyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t hsyncCount;
+ uint32_t pclkCount;
+ uint32_t outputDuration;
+};
+
+struct vfe_cmd_async_timer_setting {
+ uint8_t whichAsyncTimer;
+ uint8_t operation;
+ uint8_t polarity;
+ uint16_t repeatCount;
+ uint16_t inactiveCount;
+ uint32_t activeCount;
+};
+
+struct vfe_frame_skip_counts {
+ uint32_t totalFrameCount;
+ uint32_t output1Count;
+ uint32_t output2Count;
+};
+
+enum VFE_AXI_RD_UNPACK_HBI_SEL {
+ VFE_AXI_RD_HBI_32_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_64_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_128_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_256_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_512_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_1024_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_2048_CLOCK_CYCLES,
+ VFE_AXI_RD_HBI_4096_CLOCK_CYCLES
+};
+
+struct vfe_cmd_axi_input_config {
+ uint32_t fragAddr[4];
+ uint8_t totalFragmentCount;
+ uint16_t ySize;
+ uint16_t xOffset;
+ uint16_t xSize;
+ uint16_t rowIncrement;
+ uint16_t numOfRows;
+ enum VFE_AXI_BURST_LENGTH burstLength;
+ uint8_t unpackPhase;
+ enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi;
+ enum VFE_RAW_PIXEL_DATA_SIZE pixelSize;
+ uint8_t padRepeatCountLeft;
+ uint8_t padRepeatCountRight;
+ uint8_t padRepeatCountTop;
+ uint8_t padRepeatCountBottom;
+ uint8_t padLeftComponentSelectCycle0;
+ uint8_t padLeftComponentSelectCycle1;
+ uint8_t padLeftComponentSelectCycle2;
+ uint8_t padLeftComponentSelectCycle3;
+ uint8_t padLeftStopCycle0;
+ uint8_t padLeftStopCycle1;
+ uint8_t padLeftStopCycle2;
+ uint8_t padLeftStopCycle3;
+ uint8_t padRightComponentSelectCycle0;
+ uint8_t padRightComponentSelectCycle1;
+ uint8_t padRightComponentSelectCycle2;
+ uint8_t padRightComponentSelectCycle3;
+ uint8_t padRightStopCycle0;
+ uint8_t padRightStopCycle1;
+ uint8_t padRightStopCycle2;
+ uint8_t padRightStopCycle3;
+ uint8_t padTopLineCount;
+ uint8_t padBottomLineCount;
+};
+
+struct vfe_interrupt_status {
+ uint8_t camifErrorIrq;
+ uint8_t camifSofIrq;
+ uint8_t camifEolIrq;
+ uint8_t camifEofIrq;
+ uint8_t camifEpoch1Irq;
+ uint8_t camifEpoch2Irq;
+ uint8_t camifOverflowIrq;
+ uint8_t ceIrq;
+ uint8_t regUpdateIrq;
+ uint8_t resetAckIrq;
+ uint8_t encYPingpongIrq;
+ uint8_t encCbcrPingpongIrq;
+ uint8_t viewYPingpongIrq;
+ uint8_t viewCbcrPingpongIrq;
+ uint8_t rdPingpongIrq;
+ uint8_t afPingpongIrq;
+ uint8_t awbPingpongIrq;
+ uint8_t histPingpongIrq;
+ uint8_t encIrq;
+ uint8_t viewIrq;
+ uint8_t busOverflowIrq;
+ uint8_t afOverflowIrq;
+ uint8_t awbOverflowIrq;
+ uint8_t syncTimer0Irq;
+ uint8_t syncTimer1Irq;
+ uint8_t syncTimer2Irq;
+ uint8_t asyncTimer0Irq;
+ uint8_t asyncTimer1Irq;
+ uint8_t asyncTimer2Irq;
+ uint8_t asyncTimer3Irq;
+ uint8_t axiErrorIrq;
+ uint8_t violationIrq;
+ uint8_t anyErrorIrqs;
+ uint8_t anyOutput1PathIrqs;
+ uint8_t anyOutput2PathIrqs;
+ uint8_t anyOutputPathIrqs;
+ uint8_t anyAsyncTimerIrqs;
+ uint8_t anySyncTimerIrqs;
+ uint8_t anyIrqForActiveStatesOnly;
+};
+
+enum VFE_MESSAGE_ID {
+ VFE_MSG_ID_RESET_ACK,
+ VFE_MSG_ID_START_ACK,
+ VFE_MSG_ID_STOP_ACK,
+ VFE_MSG_ID_UPDATE_ACK,
+ VFE_MSG_ID_OUTPUT1,
+ VFE_MSG_ID_OUTPUT2,
+ VFE_MSG_ID_SNAPSHOT_DONE,
+ VFE_MSG_ID_STATS_AUTOFOCUS,
+ VFE_MSG_ID_STATS_WB_EXP,
+ VFE_MSG_ID_EPOCH1,
+ VFE_MSG_ID_EPOCH2,
+ VFE_MSG_ID_SYNC_TIMER0_DONE,
+ VFE_MSG_ID_SYNC_TIMER1_DONE,
+ VFE_MSG_ID_SYNC_TIMER2_DONE,
+ VFE_MSG_ID_ASYNC_TIMER0_DONE,
+ VFE_MSG_ID_ASYNC_TIMER1_DONE,
+ VFE_MSG_ID_ASYNC_TIMER2_DONE,
+ VFE_MSG_ID_ASYNC_TIMER3_DONE,
+ VFE_MSG_ID_AF_OVERFLOW,
+ VFE_MSG_ID_AWB_OVERFLOW,
+ VFE_MSG_ID_AXI_ERROR,
+ VFE_MSG_ID_CAMIF_OVERFLOW,
+ VFE_MSG_ID_VIOLATION,
+ VFE_MSG_ID_CAMIF_ERROR,
+ VFE_MSG_ID_BUS_OVERFLOW,
+};
+
+struct vfe_msg_stats_autofocus {
+ uint32_t afBuffer;
+ uint32_t frameCounter;
+};
+
+struct vfe_msg_stats_wb_exp {
+ uint32_t awbBuffer;
+ uint32_t frameCounter;
+};
+
+struct vfe_frame_bpc_info {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+};
+
+struct vfe_frame_asf_info {
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+};
+
+struct vfe_msg_camif_status {
+ uint8_t camifState;
+ uint32_t pixelCount;
+ uint32_t lineCount;
+};
+
+struct vfe_bus_pm_per_path {
+ uint32_t yWrPmStats0;
+ uint32_t yWrPmStats1;
+ uint32_t cbcrWrPmStats0;
+ uint32_t cbcrWrPmStats1;
+};
+
+struct vfe_bus_performance_monitor {
+ struct vfe_bus_pm_per_path encPathPmInfo;
+ struct vfe_bus_pm_per_path viewPathPmInfo;
+};
+
+struct vfe_irq_thread_msg {
+ uint32_t vfeIrqStatus;
+ uint32_t camifStatus;
+ uint32_t demosaicStatus;
+ uint32_t asfMaxEdge;
+ struct vfe_bus_performance_monitor pmInfo;
+};
+
+struct vfe_msg_output {
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+ struct vfe_bus_pm_per_path pmData;
+};
+
+struct vfe_message {
+ enum VFE_MESSAGE_ID _d;
+ union {
+ struct vfe_msg_output msgOutput1;
+ struct vfe_msg_output msgOutput2;
+ struct vfe_msg_stats_autofocus msgStatsAf;
+ struct vfe_msg_stats_wb_exp msgStatsWbExp;
+ struct vfe_msg_camif_status msgCamifError;
+ struct vfe_bus_performance_monitor msgBusOverflow;
+ } _u;
+};
+
+/* New one for 8k */
+struct msm_vfe_command_8k {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+struct vfe_frame_extra {
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+ struct vfe_bus_pm_per_path pmData;
+};
+#endif /* __MSM_VFE8X_H__ */
diff --git a/drivers/staging/dream/camera/msm_vfe8x_proc.c b/drivers/staging/dream/camera/msm_vfe8x_proc.c
new file mode 100644
index 00000000000..10aef0e59ba
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe8x_proc.c
@@ -0,0 +1,4003 @@
+/*
+* Copyright (C) 2008-2009 QUALCOMM Incorporated.
+*/
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include "msm_vfe8x_proc.h"
+#include <media/msm_camera.h>
+
+struct msm_vfe8x_ctrl {
+ /* bit 1:0 ENC_IRQ_MASK = 0x11:
+ * generate IRQ when both y and cbcr frame is ready. */
+
+ /* bit 1:0 VIEW_IRQ_MASK= 0x11:
+ * generate IRQ when both y and cbcr frame is ready. */
+ struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal;
+ struct vfe_module_enable vfeModuleEnableLocal;
+ struct vfe_camif_cfg_data vfeCamifConfigLocal;
+ struct vfe_interrupt_mask vfeImaskLocal;
+ struct vfe_stats_cmd_data vfeStatsCmdLocal;
+ struct vfe_bus_cfg_data vfeBusConfigLocal;
+ struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal;
+ struct vfe_bus_cmd_data vfeBusCmdLocal;
+ enum vfe_interrupt_name vfeInterruptNameLocal;
+ uint32_t vfeLaBankSel;
+ struct vfe_gamma_lut_sel vfeGammaLutSel;
+
+ boolean vfeStartAckPendingFlag;
+ boolean vfeStopAckPending;
+ boolean vfeResetAckPending;
+ boolean vfeUpdateAckPending;
+
+ enum VFE_AXI_OUTPUT_MODE axiOutputMode;
+ enum VFE_START_OPERATION_MODE vfeOperationMode;
+
+ uint32_t vfeSnapShotCount;
+ uint32_t vfeRequestedSnapShotCount;
+ boolean vfeStatsPingPongReloadFlag;
+ uint32_t vfeFrameId;
+
+ struct vfe_cmd_frame_skip_config vfeFrameSkip;
+ uint32_t vfeFrameSkipPattern;
+ uint8_t vfeFrameSkipCount;
+ uint8_t vfeFrameSkipPeriod;
+
+ boolean vfeTestGenStartFlag;
+ uint32_t vfeImaskPacked;
+ uint32_t vfeImaskCompositePacked;
+ enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize;
+ struct vfe_irq_thread_msg vfeIrqThreadMsgLocal;
+
+ struct vfe_output_path_combo viewPath;
+ struct vfe_output_path_combo encPath;
+ struct vfe_frame_skip_counts vfeDroppedFrameCounts;
+ struct vfe_stats_control afStatsControl;
+ struct vfe_stats_control awbStatsControl;
+
+ enum VFE_STATE vstate;
+
+ spinlock_t ack_lock;
+ spinlock_t state_lock;
+ spinlock_t io_lock;
+
+ struct msm_vfe_callback *resp;
+ uint32_t extlen;
+ void *extdata;
+
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+
+ int vfeirq;
+ void __iomem *vfebase;
+
+ void *syncdata;
+};
+static struct msm_vfe8x_ctrl *ctrl;
+static irqreturn_t vfe_parse_irq(int irq_num, void *data);
+
+struct isr_queue_cmd {
+ struct list_head list;
+ struct vfe_interrupt_status vfeInterruptStatus;
+ struct vfe_frame_asf_info vfeAsfFrameInfo;
+ struct vfe_frame_bpc_info vfeBpcFrameInfo;
+ struct vfe_msg_camif_status vfeCamifStatusLocal;
+ struct vfe_bus_performance_monitor vfePmData;
+};
+
+static void vfe_prog_hw(uint8_t *hwreg,
+ uint32_t *inptr, uint32_t regcnt)
+{
+ /* unsigned long flags; */
+ uint32_t i;
+ uint32_t *p;
+
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->io_lock, flags); */
+
+ p = (uint32_t *)(hwreg);
+ for (i = 0; i < (regcnt >> 2); i++)
+ writel(*inptr++, p++);
+ /* *p++ = *inptr++; */
+
+ /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */
+}
+
+static void vfe_read_reg_values(uint8_t *hwreg,
+ uint32_t *dest, uint32_t count)
+{
+ /* unsigned long flags; */
+ uint32_t *temp;
+ uint32_t i;
+
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->io_lock, flags); */
+
+ temp = (uint32_t *)(hwreg);
+ for (i = 0; i < count; i++)
+ *dest++ = *temp++;
+
+ /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */
+}
+
+static struct vfe_irqenable vfe_read_irq_mask(void)
+{
+ /* unsigned long flags; */
+ uint32_t *temp;
+ struct vfe_irqenable rc;
+
+ memset(&rc, 0, sizeof(rc));
+
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->io_lock, flags); */
+ temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_MASK);
+
+ rc = *((struct vfe_irqenable *)temp);
+ /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */
+
+ return rc;
+}
+
+static void
+vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath,
+ struct vfe_output_path_combo *epath)
+{
+ vpath->yPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR);
+ vpath->yPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR);
+ vpath->cbcrPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR);
+ vpath->cbcrPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR);
+
+ epath->yPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR);
+ epath->yPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR);
+ epath->cbcrPath.hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR);
+ epath->cbcrPath.hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR);
+}
+
+static void vfe_axi_output(struct vfe_cmd_axi_output_config *in,
+ struct vfe_output_path_combo *out1,
+ struct vfe_output_path_combo *out2, uint16_t out)
+{
+ struct vfe_axi_out_cfg cmd;
+
+ uint16_t temp;
+ uint32_t burstLength;
+
+ /* force it to burst length 4, hardware does not support it. */
+ burstLength = 1;
+
+ /* AXI Output 2 Y Configuration*/
+ /* VFE_BUS_ENC_Y_WR_PING_ADDR */
+ cmd.out2YPingAddr = out2->yPath.addressBuffer[0];
+
+ /* VFE_BUS_ENC_Y_WR_PONG_ADDR */
+ cmd.out2YPongAddr = out2->yPath.addressBuffer[1];
+
+ /* VFE_BUS_ENC_Y_WR_IMAGE_SIZE */
+ cmd.out2YImageHeight = in->output2.outputY.imageHeight;
+ /* convert the image width and row increment to be in
+ * unit of 64bit (8 bytes) */
+ temp = (in->output2.outputY.imageWidth + (out - 1)) /
+ out;
+ cmd.out2YImageWidthin64bit = temp;
+
+ /* VFE_BUS_ENC_Y_WR_BUFFER_CFG */
+ cmd.out2YBurstLength = burstLength;
+ cmd.out2YNumRows = in->output2.outputY.outRowCount;
+ temp = (in->output2.outputY.outRowIncrement + (out - 1)) /
+ out;
+ cmd.out2YRowIncrementIn64bit = temp;
+
+ /* AXI Output 2 Cbcr Configuration*/
+ /* VFE_BUS_ENC_Cbcr_WR_PING_ADDR */
+ cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0];
+
+ /* VFE_BUS_ENC_Cbcr_WR_PONG_ADDR */
+ cmd.out2CbcrPongAddr = out2->cbcrPath.addressBuffer[1];
+
+ /* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */
+ cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight;
+ temp = (in->output2.outputCbcr.imageWidth + (out - 1)) /
+ out;
+ cmd.out2CbcrImageWidthIn64bit = temp;
+
+ /* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */
+ cmd.out2CbcrBurstLength = burstLength;
+ cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount;
+ temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) /
+ out;
+ cmd.out2CbcrRowIncrementIn64bit = temp;
+
+ /* AXI Output 1 Y Configuration */
+ /* VFE_BUS_VIEW_Y_WR_PING_ADDR */
+ cmd.out1YPingAddr = out1->yPath.addressBuffer[0];
+
+ /* VFE_BUS_VIEW_Y_WR_PONG_ADDR */
+ cmd.out1YPongAddr = out1->yPath.addressBuffer[1];
+
+ /* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */
+ cmd.out1YImageHeight = in->output1.outputY.imageHeight;
+ temp = (in->output1.outputY.imageWidth + (out - 1)) /
+ out;
+ cmd.out1YImageWidthin64bit = temp;
+
+ /* VFE_BUS_VIEW_Y_WR_BUFFER_CFG */
+ cmd.out1YBurstLength = burstLength;
+ cmd.out1YNumRows = in->output1.outputY.outRowCount;
+
+ temp =
+ (in->output1.outputY.outRowIncrement +
+ (out - 1)) / out;
+ cmd.out1YRowIncrementIn64bit = temp;
+
+ /* AXI Output 1 Cbcr Configuration*/
+ cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0];
+
+ /* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR */
+ cmd.out1CbcrPongAddr =
+ out1->cbcrPath.addressBuffer[1];
+
+ /* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */
+ cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight;
+ temp = (in->output1.outputCbcr.imageWidth +
+ (out - 1)) / out;
+ cmd.out1CbcrImageWidthIn64bit = temp;
+
+ cmd.out1CbcrBurstLength = burstLength;
+ cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount;
+ temp =
+ (in->output1.outputCbcr.outRowIncrement +
+ (out - 1)) / out;
+
+ cmd.out1CbcrRowIncrementIn64bit = temp;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in)
+{
+ struct vfe_axi_bus_cfg cmd;
+
+ cmd.stripeRdPathEn = in->stripeRdPathEn;
+ cmd.encYWrPathEn = in->encYWrPathEn;
+ cmd.encCbcrWrPathEn = in->encCbcrWrPathEn;
+ cmd.viewYWrPathEn = in->viewYWrPathEn;
+ cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn;
+ cmd.rawPixelDataSize = (uint32_t)in->rawPixelDataSize;
+ cmd.rawWritePathSelect = (uint32_t)in->rawWritePathSelect;
+
+ /* program vfe_bus_cfg */
+ writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG);
+}
+
+static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in)
+{
+ struct VFE_CAMIFConfigType cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.VSyncEdge =
+ in->camifCfgFromCmd.vSyncEdge;
+
+ cfg.HSyncEdge =
+ in->camifCfgFromCmd.hSyncEdge;
+
+ cfg.syncMode =
+ in->camifCfgFromCmd.syncMode;
+
+ cfg.vfeSubsampleEnable =
+ in->camifCfgFromCmd.vfeSubSampleEnable;
+
+ cfg.busSubsampleEnable =
+ in->camifCfgFromCmd.busSubSampleEnable;
+
+ cfg.camif2vfeEnable =
+ in->camif2OutputEnable;
+
+ cfg.camif2busEnable =
+ in->camif2BusEnable;
+
+ cfg.irqSubsampleEnable =
+ in->camifCfgFromCmd.irqSubSampleEnable;
+
+ cfg.binningEnable =
+ in->camifCfgFromCmd.binningEnable;
+
+ cfg.misrEnable =
+ in->camifCfgFromCmd.misrEnable;
+
+ /* program camif_config */
+ writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG);
+}
+
+static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in)
+{
+ struct vfe_buscmd cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.stripeReload = in->stripeReload;
+ cmd.busPingpongReload = in->busPingpongReload;
+ cmd.statsPingpongReload = in->statsPingpongReload;
+
+ writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD);
+
+ CDBG("bus command = 0x%x\n", (*((uint32_t *)&cmd)));
+
+ /* this is needed, as the control bits are pulse based.
+ * Don't want to reload bus pingpong again. */
+ in->busPingpongReload = 0;
+ in->statsPingpongReload = 0;
+ in->stripeReload = 0;
+}
+
+static void vfe_reg_module_cfg(struct vfe_module_enable *in)
+{
+ struct vfe_mod_enable ena;
+
+ memset(&ena, 0, sizeof(ena));
+
+ ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable;
+ ena.lensRollOffEnable = in->lensRollOffEnable;
+ ena.demuxEnable = in->demuxEnable;
+ ena.chromaUpsampleEnable = in->chromaUpsampleEnable;
+ ena.demosaicEnable = in->demosaicEnable;
+ ena.statsEnable = in->statsEnable;
+ ena.cropEnable = in->cropEnable;
+ ena.mainScalerEnable = in->mainScalerEnable;
+ ena.whiteBalanceEnable = in->whiteBalanceEnable;
+ ena.colorCorrectionEnable = in->colorCorrectionEnable;
+ ena.yHistEnable = in->yHistEnable;
+ ena.skinToneEnable = in->skinToneEnable;
+ ena.lumaAdaptationEnable = in->lumaAdaptationEnable;
+ ena.rgbLUTEnable = in->rgbLUTEnable;
+ ena.chromaEnhanEnable = in->chromaEnhanEnable;
+ ena.asfEnable = in->asfEnable;
+ ena.chromaSuppressionEnable = in->chromaSuppressionEnable;
+ ena.chromaSubsampleEnable = in->chromaSubsampleEnable;
+ ena.scaler2YEnable = in->scaler2YEnable;
+ ena.scaler2CbcrEnable = in->scaler2CbcrEnable;
+
+ writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG);
+}
+
+static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT;
+
+ value += (uint32_t)bankSel;
+ /* CDBG("dmi cfg input bank is 0x%x\n", bankSel); */
+
+ writel(value, ctrl->vfebase + VFE_DMI_CFG);
+ writel(0, ctrl->vfebase + VFE_DMI_ADDR);
+}
+
+static void vfe_write_lens_roll_off_table(
+ struct vfe_cmd_roll_off_config *in)
+{
+ uint16_t i;
+ uint32_t data;
+
+ uint16_t *initGr = in->initTableGr;
+ uint16_t *initGb = in->initTableGb;
+ uint16_t *initB = in->initTableB;
+ uint16_t *initR = in->initTableR;
+
+ int16_t *pDeltaGr = in->deltaTableGr;
+ int16_t *pDeltaGb = in->deltaTableGb;
+ int16_t *pDeltaB = in->deltaTableB;
+ int16_t *pDeltaR = in->deltaTableR;
+
+ vfe_program_dmi_cfg(ROLLOFF_RAM);
+
+ /* first pack and write init table */
+ for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) {
+ data = (((uint32_t)(*initR)) & 0x0000FFFF) |
+ (((uint32_t)(*initGr)) << 16);
+ initR++;
+ initGr++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+ data = (((uint32_t)(*initB)) & 0x0000FFFF) |
+ (((uint32_t)(*initGr))<<16);
+ initB++;
+ initGb++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+
+ /* there are gaps between the init table and delta table,
+ * set the offset for delta table. */
+ writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET,
+ ctrl->vfebase + VFE_DMI_ADDR);
+
+ /* pack and write delta table */
+ for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) {
+ data = (((int32_t)(*pDeltaR)) & 0x0000FFFF) |
+ (((int32_t)(*pDeltaGr))<<16);
+ pDeltaR++;
+ pDeltaGr++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+ data = (((int32_t)(*pDeltaB)) & 0x0000FFFF) |
+ (((int32_t)(*pDeltaGb))<<16);
+ pDeltaB++;
+ pDeltaGb++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+
+ /* After DMI transfer, to make it safe, need to set the
+ * DMI_CFG to unselect any SRAM
+ */
+ /* unselect the SRAM Bank. */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_set_default_reg_values(void)
+{
+ writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_0);
+ writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_1);
+ writel(0xFFFFF, ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+ /* default frame drop period and pattern */
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_CFG);
+ writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_CFG);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+ writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+ writel(0, ctrl->vfebase + VFE_CLAMP_MIN_CFG);
+ writel(0xFFFFFF, ctrl->vfebase + VFE_CLAMP_MAX_CFG);
+}
+
+static void vfe_config_demux(uint32_t period, uint32_t even, uint32_t odd)
+{
+ writel(period, ctrl->vfebase + VFE_DEMUX_CFG);
+ writel(even, ctrl->vfebase + VFE_DEMUX_EVEN_CFG);
+ writel(odd, ctrl->vfebase + VFE_DEMUX_ODD_CFG);
+}
+
+static void vfe_pm_stop(void)
+{
+ writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static void vfe_program_bus_rd_irq_en(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN);
+}
+
+static void vfe_camif_go(void)
+{
+ writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND);
+}
+
+static void vfe_camif_stop_immediately(void)
+{
+ writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND);
+ writel(0, ctrl->vfebase + VFE_CGC_OVERRIDE);
+}
+
+static void vfe_program_reg_update_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD);
+}
+
+static void vfe_program_bus_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_BUS_CMD);
+}
+
+static void vfe_program_global_reset_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
+
+static void vfe_program_axi_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_AXI_CMD);
+}
+
+static void vfe_program_irq_composite_mask(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+}
+
+static inline void vfe_program_irq_mask(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_IRQ_MASK);
+}
+
+static void vfe_program_chroma_upsample_cfg(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG);
+}
+
+static uint32_t vfe_read_axi_status(void)
+{
+ return readl(ctrl->vfebase + VFE_AXI_STATUS);
+}
+
+static uint32_t vfe_read_pm_status_in_raw_capture(void)
+{
+ return readl(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PM_STATS_1);
+}
+
+static void
+vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl,
+ struct vfe_stats_control *awbControl)
+{
+ afControl->hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ afControl->hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+ awbControl->hwRegPingAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ awbControl->hwRegPongAddress = (uint8_t *)
+ (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static uint32_t vfe_read_camif_status(void)
+{
+ return readl(ctrl->vfebase + CAMIF_STATUS);
+}
+
+static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in)
+{
+ struct VFE_GammaLutSelect_ConfigCmdType cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0BankSelect = in->ch0BankSelect;
+ cmd.ch1BankSelect = in->ch1BankSelect;
+ cmd.ch2BankSelect = in->ch2BankSelect;
+ CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd));
+ vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in)
+{
+ struct VFE_StatsCmdType stats;
+ memset(&stats, 0, sizeof(stats));
+
+ stats.autoFocusEnable = in->autoFocusEnable;
+ stats.axwEnable = in->axwEnable;
+ stats.histEnable = in->histEnable;
+ stats.clearHistEnable = in->clearHistEnable;
+ stats.histAutoClearEnable = in->histAutoClearEnable;
+ stats.colorConversionEnable = in->colorConversionEnable;
+
+ writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD);
+}
+
+static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+ struct VFE_Bus_Pm_ConfigCmdType cmd;
+ memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType));
+
+ cmd.output2YWrPmEnable = in->output2YWrPmEnable;
+ cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable;
+ cmd.output1YWrPmEnable = in->output1YWrPmEnable;
+ cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in)
+{
+ in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn;
+ in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn;
+ in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn;
+ in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn;
+
+ if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable)
+ ctrl->viewPath.pmEnabled = TRUE;
+
+ if (in->output2CbcrWrPmEnable || in->output2YWrPmEnable)
+ ctrl->encPath.pmEnabled = TRUE;
+
+ vfe_pm_start(in);
+
+ writel(VFE_PERFORMANCE_MONITOR_GO, ctrl->vfebase + VFE_BUS_PM_CMD);
+}
+
+static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data)
+{
+ struct vfe_irqenable packedData;
+
+ memset(&packedData, 0, sizeof(packedData));
+
+ packedData.camifErrorIrq = data.camifErrorIrq;
+ packedData.camifSofIrq = data.camifSofIrq;
+ packedData.camifEolIrq = data.camifEolIrq;
+ packedData.camifEofIrq = data.camifEofIrq;
+ packedData.camifEpoch1Irq = data.camifEpoch1Irq;
+ packedData.camifEpoch2Irq = data.camifEpoch2Irq;
+ packedData.camifOverflowIrq = data.camifOverflowIrq;
+ packedData.ceIrq = data.ceIrq;
+ packedData.regUpdateIrq = data.regUpdateIrq;
+ packedData.resetAckIrq = data.resetAckIrq;
+ packedData.encYPingpongIrq = data.encYPingpongIrq;
+ packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq;
+ packedData.viewYPingpongIrq = data.viewYPingpongIrq;
+ packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq;
+ packedData.rdPingpongIrq = data.rdPingpongIrq;
+ packedData.afPingpongIrq = data.afPingpongIrq;
+ packedData.awbPingpongIrq = data.awbPingpongIrq;
+ packedData.histPingpongIrq = data.histPingpongIrq;
+ packedData.encIrq = data.encIrq;
+ packedData.viewIrq = data.viewIrq;
+ packedData.busOverflowIrq = data.busOverflowIrq;
+ packedData.afOverflowIrq = data.afOverflowIrq;
+ packedData.awbOverflowIrq = data.awbOverflowIrq;
+ packedData.syncTimer0Irq = data.syncTimer0Irq;
+ packedData.syncTimer1Irq = data.syncTimer1Irq;
+ packedData.syncTimer2Irq = data.syncTimer2Irq;
+ packedData.asyncTimer0Irq = data.asyncTimer0Irq;
+ packedData.asyncTimer1Irq = data.asyncTimer1Irq;
+ packedData.asyncTimer2Irq = data.asyncTimer2Irq;
+ packedData.asyncTimer3Irq = data.asyncTimer3Irq;
+ packedData.axiErrorIrq = data.axiErrorIrq;
+ packedData.violationIrq = data.violationIrq;
+
+ return *((uint32_t *)&packedData);
+}
+
+static uint32_t
+vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data)
+{
+ struct VFE_Irq_Composite_MaskType packedData;
+
+ memset(&packedData, 0, sizeof(packedData));
+
+ packedData.encIrqComMaskBits = data.encIrqComMask;
+ packedData.viewIrqComMaskBits = data.viewIrqComMask;
+ packedData.ceDoneSelBits = data.ceDoneSel;
+
+ return *((uint32_t *)&packedData);
+}
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+ switch (type) {
+ case VFE_MSG_OUTPUT1: {
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOutput1.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOutput1.cbcrBuffer;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo =
+ ((struct vfe_message *)data)->_u.msgOutput1.asfInfo;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter =
+ ((struct vfe_message *)data)->_u.msgOutput1.frameCounter;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->pmData =
+ ((struct vfe_message *)data)->_u.msgOutput1.pmData;
+
+ *ext = ctrl->extdata;
+ *elen = ctrl->extlen;
+ }
+ break;
+
+ case VFE_MSG_OUTPUT2: {
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.cbcrBuffer;
+
+ CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", pinfo->y_phy);
+ CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n",
+ pinfo->cbcr_phy);
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.asfInfo;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter =
+ ((struct vfe_message *)data)->_u.msgOutput2.frameCounter;
+
+ ((struct vfe_frame_extra *)ctrl->extdata)->pmData =
+ ((struct vfe_message *)data)->_u.msgOutput2.pmData;
+
+ *ext = ctrl->extdata;
+ *elen = ctrl->extlen;
+ }
+ break;
+
+ case VFE_MSG_STATS_AF:
+ pinfo->sbuf_phy =
+ ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer;
+ break;
+
+ case VFE_MSG_STATS_WE:
+ pinfo->sbuf_phy =
+ ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer;
+ break;
+
+ default:
+ break;
+ } /* switch */
+}
+
+static void
+vfe_proc_ops(enum VFE_MESSAGE_ID id, void *msg, size_t len)
+{
+ struct msm_vfe_resp *rp;
+
+ /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before
+ * SNAPSHOT_DONE. We don't send such messages to user */
+
+ CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n",
+ ctrl->vfeOperationMode, id);
+
+ if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) &&
+ (id == VFE_MSG_ID_OUTPUT1 || id == VFE_MSG_ID_OUTPUT2)) {
+ return;
+ }
+
+ rp = ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), ctrl->syncdata);
+ if (!rp) {
+ CDBG("rp: cannot allocate buffer\n");
+ return;
+ }
+
+ CDBG("vfe_proc_ops, msgId = %d\n", id);
+
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+ rp->evt_msg.len = len;
+ rp->evt_msg.data = msg;
+
+ switch (rp->evt_msg.msg_id) {
+ case VFE_MSG_ID_SNAPSHOT_DONE:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case VFE_MSG_ID_OUTPUT1:
+ rp->type = VFE_MSG_OUTPUT1;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT1,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case VFE_MSG_ID_OUTPUT2:
+ rp->type = VFE_MSG_OUTPUT2;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT2,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case VFE_MSG_ID_STATS_AUTOFOCUS:
+ rp->type = VFE_MSG_STATS_AF;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AF,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case VFE_MSG_ID_STATS_WB_EXP:
+ rp->type = VFE_MSG_STATS_WE;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_WE,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+
+ ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata);
+}
+
+static void vfe_send_msg_no_payload(enum VFE_MESSAGE_ID id)
+{
+ struct vfe_message *msg;
+
+ msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ msg->_d = id;
+ vfe_proc_ops(id, msg, 0);
+}
+
+static void vfe_send_bus_overflow_msg(void)
+{
+ struct vfe_message *msg;
+ msg =
+ kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ msg->_d = VFE_MSG_ID_BUS_OVERFLOW;
+#if 0
+ memcpy(&(msg->_u.msgBusOverflow),
+ &ctrl->vfePmData, sizeof(ctrl->vfePmData));
+#endif
+
+ vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW,
+ msg, sizeof(struct vfe_message));
+}
+
+static void vfe_send_camif_error_msg(void)
+{
+#if 0
+ struct vfe_message *msg;
+ msg =
+ kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ msg->_d = VFE_MSG_ID_CAMIF_ERROR;
+ memcpy(&(msg->_u.msgCamifError),
+ &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal));
+
+ vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR,
+ msg, sizeof(struct vfe_message));
+#endif
+}
+
+static void vfe_process_error_irq(
+ struct vfe_interrupt_status *irqstatus)
+{
+ /* all possible error irq. Note error irqs are not enabled, it is
+ * checked only when other interrupts are present. */
+ if (irqstatus->afOverflowIrq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_AF_OVERFLOW);
+
+ if (irqstatus->awbOverflowIrq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_AWB_OVERFLOW);
+
+ if (irqstatus->axiErrorIrq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_AXI_ERROR);
+
+ if (irqstatus->busOverflowIrq)
+ vfe_send_bus_overflow_msg();
+
+ if (irqstatus->camifErrorIrq)
+ vfe_send_camif_error_msg();
+
+ if (irqstatus->camifOverflowIrq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_CAMIF_OVERFLOW);
+
+ if (irqstatus->violationIrq)
+ ;
+}
+
+static void vfe_process_camif_sof_irq(void)
+{
+ /* increment the frame id number. */
+ ctrl->vfeFrameId++;
+
+ CDBG("camif_sof_irq, frameId = %d\n",
+ ctrl->vfeFrameId);
+
+ /* In snapshot mode, if frame skip is programmed,
+ * need to check it accordingly to stop camif at
+ * correct frame boundary. For the dropped frames,
+ * there won't be any output path irqs, but there is
+ * still SOF irq, which can help us determine when
+ * to stop the camif.
+ */
+ if (ctrl->vfeOperationMode) {
+ if ((1 << ctrl->vfeFrameSkipCount) &
+ ctrl->vfeFrameSkipPattern) {
+
+ ctrl->vfeSnapShotCount--;
+ if (ctrl->vfeSnapShotCount == 0)
+ /* terminate vfe pipeline at frame boundary. */
+ writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ ctrl->vfebase + CAMIF_COMMAND);
+ }
+
+ /* update frame skip counter for bit checking. */
+ ctrl->vfeFrameSkipCount++;
+ if (ctrl->vfeFrameSkipCount ==
+ (ctrl->vfeFrameSkipPeriod + 1))
+ ctrl->vfeFrameSkipCount = 0;
+ }
+}
+
+static int vfe_get_af_pingpong_status(void)
+{
+ uint32_t busPingPongStatus;
+ int rc = 0;
+
+ busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+ if ((busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT) == 0)
+ return -EFAULT;
+
+ return rc;
+}
+
+static uint32_t vfe_read_af_buf_addr(boolean pipo)
+{
+ if (pipo == FALSE)
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ else
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static void
+vfe_update_af_buf_addr(boolean pipo, uint32_t addr)
+{
+ if (pipo == FALSE)
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ else
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+}
+
+static void
+vfe_send_af_stats_msg(uint32_t afBufAddress)
+{
+ /* unsigned long flags; */
+ struct vfe_message *msg;
+ msg =
+ kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ /* fill message with right content. */
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ if (ctrl->vstate != VFE_STATE_ACTIVE) {
+ kfree(msg);
+ goto af_stats_done;
+ }
+
+ msg->_d = VFE_MSG_ID_STATS_AUTOFOCUS;
+ msg->_u.msgStatsAf.afBuffer = afBufAddress;
+ msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId;
+
+ vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS,
+ msg, sizeof(struct vfe_message));
+
+ ctrl->afStatsControl.ackPending = TRUE;
+
+af_stats_done:
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return;
+}
+
+static void vfe_process_stats_af_irq(void)
+{
+ boolean bufferAvailable;
+
+ if (!(ctrl->afStatsControl.ackPending)) {
+
+ /* read hardware status. */
+ ctrl->afStatsControl.pingPongStatus =
+ vfe_get_af_pingpong_status();
+
+ bufferAvailable =
+ (ctrl->afStatsControl.pingPongStatus) ^ 1;
+
+ ctrl->afStatsControl.bufToRender =
+ vfe_read_af_buf_addr(bufferAvailable);
+
+ /* update the same buffer address (ping or pong) */
+ vfe_update_af_buf_addr(bufferAvailable,
+ ctrl->afStatsControl.nextFrameAddrBuf);
+
+ vfe_send_af_stats_msg(ctrl->afStatsControl.bufToRender);
+ } else
+ ctrl->afStatsControl.droppedStatsFrameCount++;
+}
+
+static boolean vfe_get_awb_pingpong_status(void)
+{
+ uint32_t busPingPongStatus;
+
+ busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+ if ((busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static uint32_t
+vfe_read_awb_buf_addr(boolean pingpong)
+{
+ if (pingpong == FALSE)
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ else
+ return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_update_awb_buf_addr(
+ boolean pingpong, uint32_t addr)
+{
+ if (pingpong == FALSE)
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ else
+ writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+}
+
+static void vfe_send_awb_stats_msg(uint32_t awbBufAddress)
+{
+ /* unsigned long flags; */
+ struct vfe_message *msg;
+
+ msg =
+ kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ /* fill message with right content. */
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ if (ctrl->vstate != VFE_STATE_ACTIVE) {
+ kfree(msg);
+ goto awb_stats_done;
+ }
+
+ msg->_d = VFE_MSG_ID_STATS_WB_EXP;
+ msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress;
+ msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId;
+
+ vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP,
+ msg, sizeof(struct vfe_message));
+
+ ctrl->awbStatsControl.ackPending = TRUE;
+
+awb_stats_done:
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return;
+}
+
+static void vfe_process_stats_awb_irq(void)
+{
+ boolean bufferAvailable;
+
+ if (!(ctrl->awbStatsControl.ackPending)) {
+
+ ctrl->awbStatsControl.pingPongStatus =
+ vfe_get_awb_pingpong_status();
+
+ bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1;
+
+ ctrl->awbStatsControl.bufToRender =
+ vfe_read_awb_buf_addr(bufferAvailable);
+
+ vfe_update_awb_buf_addr(bufferAvailable,
+ ctrl->awbStatsControl.nextFrameAddrBuf);
+
+ vfe_send_awb_stats_msg(ctrl->awbStatsControl.bufToRender);
+
+ } else
+ ctrl->awbStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe_process_sync_timer_irq(
+ struct vfe_interrupt_status *irqstatus)
+{
+ if (irqstatus->syncTimer0Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER0_DONE);
+
+ if (irqstatus->syncTimer1Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER1_DONE);
+
+ if (irqstatus->syncTimer2Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER2_DONE);
+}
+
+static void vfe_process_async_timer_irq(
+ struct vfe_interrupt_status *irqstatus)
+{
+
+ if (irqstatus->asyncTimer0Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE);
+
+ if (irqstatus->asyncTimer1Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER1_DONE);
+
+ if (irqstatus->asyncTimer2Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER2_DONE);
+
+ if (irqstatus->asyncTimer3Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER3_DONE);
+}
+
+static void vfe_send_violation_msg(void)
+{
+ vfe_send_msg_no_payload(VFE_MSG_ID_VIOLATION);
+}
+
+static void vfe_send_async_timer_msg(void)
+{
+ vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE);
+}
+
+static void vfe_write_gamma_table(uint8_t channel,
+ boolean bank, int16_t *pTable)
+{
+ uint16_t i;
+
+ enum VFE_DMI_RAM_SEL dmiRamSel = NO_MEM_SELECTED;
+
+ switch (channel) {
+ case 0:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH0_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH0_BANK1;
+ break;
+
+ case 1:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH1_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH1_BANK1;
+ break;
+
+ case 2:
+ if (bank == 0)
+ dmiRamSel = RGBLUT_RAM_CH2_BANK0;
+ else
+ dmiRamSel = RGBLUT_RAM_CH2_BANK1;
+ break;
+
+ default:
+ break;
+ }
+
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) {
+ writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, need to set the DMI_CFG to unselect any SRAM
+ unselect the SRAM Bank. */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+}
+
+static void vfe_prog_hw_testgen_cmd(uint32_t value)
+{
+ writel(value, ctrl->vfebase + VFE_HW_TESTGEN_CMD);
+}
+
+static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out)
+{
+ uint32_t *temp;
+
+ memset(out, 0, sizeof(struct vfe_irq_thread_msg));
+
+ temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_STATUS);
+ out->vfeIrqStatus = readl(temp);
+
+ temp = (uint32_t *)(ctrl->vfebase + CAMIF_STATUS);
+ out->camifStatus = readl(temp);
+ writel(0x7, ctrl->vfebase + CAMIF_COMMAND);
+ writel(0x3, ctrl->vfebase + CAMIF_COMMAND);
+ CDBG("camifStatus = 0x%x\n", out->camifStatus);
+
+/*
+ temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS);
+ out->demosaicStatus = readl(temp);
+
+ temp = (uint32_t *)(ctrl->vfebase + VFE_ASF_MAX_EDGE);
+ out->asfMaxEdge = readl(temp);
+
+ temp = (uint32_t *)(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PM_STATS_0);
+*/
+
+#if 0
+ out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++);
+ out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++);
+ out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++);
+ out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++);
+ out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp);
+#endif /* if 0 Jeff */
+}
+
+static struct vfe_interrupt_status
+vfe_parse_interrupt_status(uint32_t irqStatusIn)
+{
+ struct vfe_irqenable hwstat;
+ struct vfe_interrupt_status ret;
+ boolean temp;
+
+ memset(&hwstat, 0, sizeof(hwstat));
+ memset(&ret, 0, sizeof(ret));
+
+ hwstat = *((struct vfe_irqenable *)(&irqStatusIn));
+
+ ret.camifErrorIrq = hwstat.camifErrorIrq;
+ ret.camifSofIrq = hwstat.camifSofIrq;
+ ret.camifEolIrq = hwstat.camifEolIrq;
+ ret.camifEofIrq = hwstat.camifEofIrq;
+ ret.camifEpoch1Irq = hwstat.camifEpoch1Irq;
+ ret.camifEpoch2Irq = hwstat.camifEpoch2Irq;
+ ret.camifOverflowIrq = hwstat.camifOverflowIrq;
+ ret.ceIrq = hwstat.ceIrq;
+ ret.regUpdateIrq = hwstat.regUpdateIrq;
+ ret.resetAckIrq = hwstat.resetAckIrq;
+ ret.encYPingpongIrq = hwstat.encYPingpongIrq;
+ ret.encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq;
+ ret.viewYPingpongIrq = hwstat.viewYPingpongIrq;
+ ret.viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq;
+ ret.rdPingpongIrq = hwstat.rdPingpongIrq;
+ ret.afPingpongIrq = hwstat.afPingpongIrq;
+ ret.awbPingpongIrq = hwstat.awbPingpongIrq;
+ ret.histPingpongIrq = hwstat.histPingpongIrq;
+ ret.encIrq = hwstat.encIrq;
+ ret.viewIrq = hwstat.viewIrq;
+ ret.busOverflowIrq = hwstat.busOverflowIrq;
+ ret.afOverflowIrq = hwstat.afOverflowIrq;
+ ret.awbOverflowIrq = hwstat.awbOverflowIrq;
+ ret.syncTimer0Irq = hwstat.syncTimer0Irq;
+ ret.syncTimer1Irq = hwstat.syncTimer1Irq;
+ ret.syncTimer2Irq = hwstat.syncTimer2Irq;
+ ret.asyncTimer0Irq = hwstat.asyncTimer0Irq;
+ ret.asyncTimer1Irq = hwstat.asyncTimer1Irq;
+ ret.asyncTimer2Irq = hwstat.asyncTimer2Irq;
+ ret.asyncTimer3Irq = hwstat.asyncTimer3Irq;
+ ret.axiErrorIrq = hwstat.axiErrorIrq;
+ ret.violationIrq = hwstat.violationIrq;
+
+ /* logic OR of any error bits
+ * although each irq corresponds to a bit, the data type here is a
+ * boolean already. hence use logic operation.
+ */
+ temp =
+ ret.camifErrorIrq ||
+ ret.camifOverflowIrq ||
+ ret.afOverflowIrq ||
+ ret.awbPingpongIrq ||
+ ret.busOverflowIrq ||
+ ret.axiErrorIrq ||
+ ret.violationIrq;
+
+ ret.anyErrorIrqs = temp;
+
+ /* logic OR of any output path bits*/
+ temp =
+ ret.encYPingpongIrq ||
+ ret.encCbcrPingpongIrq ||
+ ret.encIrq;
+
+ ret.anyOutput2PathIrqs = temp;
+
+ temp =
+ ret.viewYPingpongIrq ||
+ ret.viewCbcrPingpongIrq ||
+ ret.viewIrq;
+
+ ret.anyOutput1PathIrqs = temp;
+
+ ret.anyOutputPathIrqs =
+ ret.anyOutput1PathIrqs ||
+ ret.anyOutput2PathIrqs;
+
+ /* logic OR of any sync timer bits*/
+ temp =
+ ret.syncTimer0Irq ||
+ ret.syncTimer1Irq ||
+ ret.syncTimer2Irq;
+
+ ret.anySyncTimerIrqs = temp;
+
+ /* logic OR of any async timer bits*/
+ temp =
+ ret.asyncTimer0Irq ||
+ ret.asyncTimer1Irq ||
+ ret.asyncTimer2Irq ||
+ ret.asyncTimer3Irq;
+
+ ret.anyAsyncTimerIrqs = temp;
+
+ /* bool for all interrupts that are not allowed in idle state */
+ temp =
+ ret.anyErrorIrqs ||
+ ret.anyOutputPathIrqs ||
+ ret.anySyncTimerIrqs ||
+ ret.regUpdateIrq ||
+ ret.awbPingpongIrq ||
+ ret.afPingpongIrq ||
+ ret.camifSofIrq ||
+ ret.camifEpoch2Irq ||
+ ret.camifEpoch1Irq;
+
+ ret.anyIrqForActiveStatesOnly =
+ temp;
+
+ return ret;
+}
+
+static struct vfe_frame_asf_info
+vfe_get_asf_frame_info(struct vfe_irq_thread_msg *in)
+{
+ struct vfe_asf_info asfInfoTemp;
+ struct vfe_frame_asf_info rc;
+
+ memset(&rc, 0, sizeof(rc));
+ memset(&asfInfoTemp, 0, sizeof(asfInfoTemp));
+
+ asfInfoTemp =
+ *((struct vfe_asf_info *)(&(in->asfMaxEdge)));
+
+ rc.asfHbiCount = asfInfoTemp.HBICount;
+ rc.asfMaxEdge = asfInfoTemp.maxEdge;
+
+ return rc;
+}
+
+static struct vfe_frame_bpc_info
+vfe_get_demosaic_frame_info(struct vfe_irq_thread_msg *in)
+{
+ struct vfe_bps_info bpcInfoTemp;
+ struct vfe_frame_bpc_info rc;
+
+ memset(&rc, 0, sizeof(rc));
+ memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp));
+
+ bpcInfoTemp =
+ *((struct vfe_bps_info *)(&(in->demosaicStatus)));
+
+ rc.greenDefectPixelCount =
+ bpcInfoTemp.greenBadPixelCount;
+
+ rc.redBlueDefectPixelCount =
+ bpcInfoTemp.RedBlueBadPixelCount;
+
+ return rc;
+}
+
+static struct vfe_msg_camif_status
+vfe_get_camif_status(struct vfe_irq_thread_msg *in)
+{
+ struct vfe_camif_stats camifStatusTemp;
+ struct vfe_msg_camif_status rc;
+
+ memset(&rc, 0, sizeof(rc));
+ memset(&camifStatusTemp, 0, sizeof(camifStatusTemp));
+
+ camifStatusTemp =
+ *((struct vfe_camif_stats *)(&(in->camifStatus)));
+
+ rc.camifState = (boolean)camifStatusTemp.camifHalt;
+ rc.lineCount = camifStatusTemp.lineCount;
+ rc.pixelCount = camifStatusTemp.pixelCount;
+
+ return rc;
+}
+
+static struct vfe_bus_performance_monitor
+vfe_get_performance_monitor_data(struct vfe_irq_thread_msg *in)
+{
+ struct vfe_bus_performance_monitor rc;
+ memset(&rc, 0, sizeof(rc));
+
+ rc.encPathPmInfo.yWrPmStats0 =
+ in->pmInfo.encPathPmInfo.yWrPmStats0;
+ rc.encPathPmInfo.yWrPmStats1 =
+ in->pmInfo.encPathPmInfo.yWrPmStats1;
+ rc.encPathPmInfo.cbcrWrPmStats0 =
+ in->pmInfo.encPathPmInfo.cbcrWrPmStats0;
+ rc.encPathPmInfo.cbcrWrPmStats1 =
+ in->pmInfo.encPathPmInfo.cbcrWrPmStats1;
+ rc.viewPathPmInfo.yWrPmStats0 =
+ in->pmInfo.viewPathPmInfo.yWrPmStats0;
+ rc.viewPathPmInfo.yWrPmStats1 =
+ in->pmInfo.viewPathPmInfo.yWrPmStats1;
+ rc.viewPathPmInfo.cbcrWrPmStats0 =
+ in->pmInfo.viewPathPmInfo.cbcrWrPmStats0;
+ rc.viewPathPmInfo.cbcrWrPmStats1 =
+ in->pmInfo.viewPathPmInfo.cbcrWrPmStats1;
+
+ return rc;
+}
+
+static void vfe_process_reg_update_irq(void)
+{
+ CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n",
+ ctrl->vfeStartAckPendingFlag);
+ if (ctrl->vfeStartAckPendingFlag == TRUE) {
+ vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ } else
+ vfe_send_msg_no_payload(VFE_MSG_ID_UPDATE_ACK);
+}
+
+static void vfe_process_reset_irq(void)
+{
+ /* unsigned long flags; */
+
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ ctrl->vstate = VFE_STATE_IDLE;
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+
+ if (ctrl->vfeStopAckPending == TRUE) {
+ ctrl->vfeStopAckPending = FALSE;
+ vfe_send_msg_no_payload(VFE_MSG_ID_STOP_ACK);
+ } else {
+ vfe_set_default_reg_values();
+ vfe_send_msg_no_payload(VFE_MSG_ID_RESET_ACK);
+ }
+}
+
+static void vfe_process_pingpong_irq(struct vfe_output_path *in,
+ uint8_t fragmentCount)
+{
+ uint16_t circularIndex;
+ uint32_t nextFragmentAddr;
+
+ /* get next fragment address from circular buffer */
+ circularIndex = (in->fragIndex) % (2 * fragmentCount);
+ nextFragmentAddr = in->addressBuffer[circularIndex];
+
+ in->fragIndex = circularIndex + 1;
+
+ /* use next fragment to program hardware ping/pong address. */
+ if (in->hwCurrentFlag == ping) {
+ writel(nextFragmentAddr, in->hwRegPingAddress);
+ in->hwCurrentFlag = pong;
+
+ } else {
+ writel(nextFragmentAddr, in->hwRegPongAddress);
+ in->hwCurrentFlag = ping;
+ }
+}
+
+static void vfe_send_output2_msg(
+ struct vfe_msg_output *pPayload)
+{
+ /* unsigned long flags; */
+ struct vfe_message *msg;
+
+ msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ /* fill message with right content. */
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ if (ctrl->vstate != VFE_STATE_ACTIVE) {
+ kfree(msg);
+ goto output2_msg_done;
+ }
+
+ msg->_d = VFE_MSG_ID_OUTPUT2;
+
+ memcpy(&(msg->_u.msgOutput2),
+ (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT2,
+ msg, sizeof(struct vfe_message));
+
+ ctrl->encPath.ackPending = TRUE;
+
+ if (!(ctrl->vfeRequestedSnapShotCount <= 3) &&
+ (ctrl->vfeOperationMode ==
+ VFE_START_OPERATION_MODE_SNAPSHOT))
+ ctrl->encPath.ackPending = TRUE;
+
+output2_msg_done:
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return;
+}
+
+static void vfe_send_output1_msg(
+ struct vfe_msg_output *pPayload)
+{
+ /* unsigned long flags; */
+ struct vfe_message *msg;
+
+ msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ if (ctrl->vstate != VFE_STATE_ACTIVE) {
+ kfree(msg);
+ goto output1_msg_done;
+ }
+
+ msg->_d = VFE_MSG_ID_OUTPUT1;
+ memmove(&(msg->_u),
+ (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT1,
+ msg, sizeof(struct vfe_message));
+
+ ctrl->viewPath.ackPending = TRUE;
+
+ if (!(ctrl->vfeRequestedSnapShotCount <= 3) &&
+ (ctrl->vfeOperationMode ==
+ VFE_START_OPERATION_MODE_SNAPSHOT))
+ ctrl->viewPath.ackPending = TRUE;
+
+output1_msg_done:
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return;
+}
+
+static void vfe_send_output_msg(boolean whichOutputPath,
+ uint32_t yPathAddr, uint32_t cbcrPathAddr)
+{
+ struct vfe_msg_output msgPayload;
+
+ msgPayload.yBuffer = yPathAddr;
+ msgPayload.cbcrBuffer = cbcrPathAddr;
+
+ /* asf info is common for both output1 and output2 */
+#if 0
+ msgPayload.asfInfo.asfHbiCount = ctrl->vfeAsfFrameInfo.asfHbiCount;
+ msgPayload.asfInfo.asfMaxEdge = ctrl->vfeAsfFrameInfo.asfMaxEdge;
+
+ /* demosaic info is common for both output1 and output2 */
+ msgPayload.bpcInfo.greenDefectPixelCount =
+ ctrl->vfeBpcFrameInfo.greenDefectPixelCount;
+ msgPayload.bpcInfo.redBlueDefectPixelCount =
+ ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount;
+#endif /* if 0 */
+
+ /* frame ID is common for both paths. */
+ msgPayload.frameCounter = ctrl->vfeFrameId;
+
+ if (whichOutputPath) {
+ /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */
+ vfe_send_output2_msg(&msgPayload);
+ } else {
+ /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */
+ vfe_send_output1_msg(&msgPayload);
+ }
+}
+
+static void vfe_process_frame_done_irq_multi_frag(
+ struct vfe_output_path_combo *in)
+{
+ uint32_t yAddress, cbcrAddress;
+ uint16_t idx;
+ uint32_t *ptrY;
+ uint32_t *ptrCbcr;
+ const uint32_t *ptrSrc;
+ uint8_t i;
+
+ if (!in->ackPending) {
+
+ idx = (in->currentFrame) * (in->fragCount);
+
+ /* Send output message. */
+ yAddress = in->yPath.addressBuffer[idx];
+ cbcrAddress = in->cbcrPath.addressBuffer[idx];
+
+ /* copy next frame to current frame. */
+ ptrSrc = in->nextFrameAddrBuf;
+ ptrY = (uint32_t *)&(in->yPath.addressBuffer[idx]);
+ ptrCbcr = (uint32_t *)&(in->cbcrPath.addressBuffer[idx]);
+
+ /* Copy Y address */
+ for (i = 0; i < in->fragCount; i++)
+ *ptrY++ = *ptrSrc++;
+
+ /* Copy Cbcr address */
+ for (i = 0; i < in->fragCount; i++)
+ *ptrCbcr++ = *ptrSrc++;
+
+ vfe_send_output_msg(in->whichOutputPath, yAddress, cbcrAddress);
+
+ } else {
+ if (in->whichOutputPath == 0)
+ ctrl->vfeDroppedFrameCounts.output1Count++;
+
+ if (in->whichOutputPath == 1)
+ ctrl->vfeDroppedFrameCounts.output2Count++;
+ }
+
+ /* toggle current frame. */
+ in->currentFrame = in->currentFrame^1;
+
+ if (ctrl->vfeOperationMode)
+ in->snapshotPendingCount--;
+}
+
+static void vfe_process_frame_done_irq_no_frag_io(
+ struct vfe_output_path_combo *in, uint32_t *pNextAddr,
+ uint32_t *pdestRenderAddr)
+{
+ uint32_t busPingPongStatus;
+ uint32_t tempAddress;
+
+ /* 1. read hw status register. */
+ busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+ CDBG("hardware status is 0x%x\n", busPingPongStatus);
+
+ /* 2. determine ping or pong */
+ /* use cbcr status */
+ busPingPongStatus = busPingPongStatus & (1<<(in->cbcrStatusBit));
+
+ /* 3. read out address and update address */
+ if (busPingPongStatus == 0) {
+ /* hw is working on ping, render pong buffer */
+ /* a. read out pong address */
+ /* read out y address. */
+ tempAddress = readl(in->yPath.hwRegPongAddress);
+
+ CDBG("pong 1 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr++ = tempAddress;
+ /* read out cbcr address. */
+ tempAddress = readl(in->cbcrPath.hwRegPongAddress);
+
+ CDBG("pong 2 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr = tempAddress;
+
+ /* b. update pong address */
+ writel(*pNextAddr++, in->yPath.hwRegPongAddress);
+ writel(*pNextAddr, in->cbcrPath.hwRegPongAddress);
+ } else {
+ /* hw is working on pong, render ping buffer */
+
+ /* a. read out ping address */
+ tempAddress = readl(in->yPath.hwRegPingAddress);
+ CDBG("ping 1 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr++ = tempAddress;
+ tempAddress = readl(in->cbcrPath.hwRegPingAddress);
+
+ CDBG("ping 2 addr = 0x%x\n", tempAddress);
+ *pdestRenderAddr = tempAddress;
+
+ /* b. update ping address */
+ writel(*pNextAddr++, in->yPath.hwRegPingAddress);
+ CDBG("NextAddress = 0x%x\n", *pNextAddr);
+ writel(*pNextAddr, in->cbcrPath.hwRegPingAddress);
+ }
+}
+
+static void vfe_process_frame_done_irq_no_frag(
+ struct vfe_output_path_combo *in)
+{
+ uint32_t addressToRender[2];
+ static uint32_t fcnt;
+
+ if (fcnt++ < 3)
+ return;
+
+ if (!in->ackPending) {
+ vfe_process_frame_done_irq_no_frag_io(in,
+ in->nextFrameAddrBuf, addressToRender);
+
+ /* use addressToRender to send out message. */
+ vfe_send_output_msg(in->whichOutputPath,
+ addressToRender[0], addressToRender[1]);
+
+ } else {
+ /* ackPending is still there, accumulate dropped frame count.
+ * These count can be read through ioctrl command. */
+ CDBG("waiting frame ACK\n");
+
+ if (in->whichOutputPath == 0)
+ ctrl->vfeDroppedFrameCounts.output1Count++;
+
+ if (in->whichOutputPath == 1)
+ ctrl->vfeDroppedFrameCounts.output2Count++;
+ }
+
+ /* in case of multishot when upper layer did not ack, there will still
+ * be a snapshot done msg sent out, even though the number of frames
+ * sent out may be less than the desired number of frames. snapshot
+ * done msg would be helpful to indicate that vfe pipeline has stop,
+ * and in good known state.
+ */
+ if (ctrl->vfeOperationMode)
+ in->snapshotPendingCount--;
+}
+
+static void vfe_process_output_path_irq(
+ struct vfe_interrupt_status *irqstatus)
+{
+ /* unsigned long flags; */
+
+ /* process the view path interrupts */
+ if (irqstatus->anyOutput1PathIrqs) {
+ if (ctrl->viewPath.multiFrag) {
+
+ if (irqstatus->viewCbcrPingpongIrq)
+ vfe_process_pingpong_irq(
+ &(ctrl->viewPath.cbcrPath),
+ ctrl->viewPath.fragCount);
+
+ if (irqstatus->viewYPingpongIrq)
+ vfe_process_pingpong_irq(
+ &(ctrl->viewPath.yPath),
+ ctrl->viewPath.fragCount);
+
+ if (irqstatus->viewIrq)
+ vfe_process_frame_done_irq_multi_frag(
+ &ctrl->viewPath);
+
+ } else {
+ /* typical case for no fragment,
+ only frame done irq is enabled. */
+ if (irqstatus->viewIrq)
+ vfe_process_frame_done_irq_no_frag(
+ &ctrl->viewPath);
+ }
+ }
+
+ /* process the encoder path interrupts */
+ if (irqstatus->anyOutput2PathIrqs) {
+ if (ctrl->encPath.multiFrag) {
+ if (irqstatus->encCbcrPingpongIrq)
+ vfe_process_pingpong_irq(
+ &(ctrl->encPath.cbcrPath),
+ ctrl->encPath.fragCount);
+
+ if (irqstatus->encYPingpongIrq)
+ vfe_process_pingpong_irq(&(ctrl->encPath.yPath),
+ ctrl->encPath.fragCount);
+
+ if (irqstatus->encIrq)
+ vfe_process_frame_done_irq_multi_frag(
+ &ctrl->encPath);
+
+ } else {
+ if (irqstatus->encIrq)
+ vfe_process_frame_done_irq_no_frag(
+ &ctrl->encPath);
+ }
+ }
+
+ if (ctrl->vfeOperationMode) {
+ if ((ctrl->encPath.snapshotPendingCount == 0) &&
+ (ctrl->viewPath.snapshotPendingCount == 0)) {
+
+ /* @todo This is causing issues, further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ ctrl->vstate = VFE_STATE_IDLE;
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+
+ vfe_send_msg_no_payload(VFE_MSG_ID_SNAPSHOT_DONE);
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+ vfe_pm_stop();
+ }
+ }
+}
+
+static void vfe_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+
+ struct isr_queue_cmd *qcmd = NULL;
+
+ CDBG("=== vfe_do_tasklet start === \n");
+
+ spin_lock_irqsave(&ctrl->tasklet_lock, flags);
+ qcmd = list_first_entry(&ctrl->tasklet_q,
+ struct isr_queue_cmd, list);
+
+ if (!qcmd) {
+ spin_unlock_irqrestore(&ctrl->tasklet_lock, flags);
+ return;
+ }
+
+ list_del(&qcmd->list);
+ spin_unlock_irqrestore(&ctrl->tasklet_lock, flags);
+
+ if (qcmd->vfeInterruptStatus.regUpdateIrq) {
+ CDBG("irq regUpdateIrq\n");
+ vfe_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus.resetAckIrq) {
+ CDBG("irq resetAckIrq\n");
+ vfe_process_reset_irq();
+ }
+
+ spin_lock_irqsave(&ctrl->state_lock, flags);
+ if (ctrl->vstate != VFE_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&ctrl->state_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ctrl->state_lock, flags);
+
+#if 0
+ if (qcmd->vfeInterruptStatus.camifEpoch1Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH1);
+
+ if (qcmd->vfeInterruptStatus.camifEpoch2Irq)
+ vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH2);
+#endif /* Jeff */
+
+ /* next, check output path related interrupts. */
+ if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) {
+ CDBG("irq anyOutputPathIrqs\n");
+ vfe_process_output_path_irq(&qcmd->vfeInterruptStatus);
+ }
+
+ if (qcmd->vfeInterruptStatus.afPingpongIrq)
+ vfe_process_stats_af_irq();
+
+ if (qcmd->vfeInterruptStatus.awbPingpongIrq)
+ vfe_process_stats_awb_irq();
+
+ /* any error irqs*/
+ if (qcmd->vfeInterruptStatus.anyErrorIrqs)
+ vfe_process_error_irq(&qcmd->vfeInterruptStatus);
+
+#if 0
+ if (qcmd->vfeInterruptStatus.anySyncTimerIrqs)
+ vfe_process_sync_timer_irq();
+
+ if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs)
+ vfe_process_async_timer_irq();
+#endif /* Jeff */
+
+ if (qcmd->vfeInterruptStatus.camifSofIrq) {
+ CDBG("irq camifSofIrq\n");
+ vfe_process_camif_sof_irq();
+ }
+
+ kfree(qcmd);
+ CDBG("=== vfe_do_tasklet end === \n");
+}
+
+DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0);
+
+static irqreturn_t vfe_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t irqStatusLocal;
+ struct vfe_irq_thread_msg irq;
+ struct isr_queue_cmd *qcmd;
+
+ CDBG("vfe_parse_irq\n");
+
+ vfe_read_irq_status(&irq);
+
+ if (irq.vfeIrqStatus == 0) {
+ CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n");
+ return IRQ_HANDLED;
+ }
+
+ qcmd = kzalloc(sizeof(struct isr_queue_cmd),
+ GFP_ATOMIC);
+ if (!qcmd) {
+ CDBG("vfe_parse_irq: qcmd malloc failed!\n");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&ctrl->ack_lock, flags);
+
+ if (ctrl->vfeStopAckPending)
+ irqStatusLocal =
+ (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus);
+ else
+ irqStatusLocal =
+ ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) &
+ irq.vfeIrqStatus);
+
+ spin_unlock_irqrestore(&ctrl->ack_lock, flags);
+
+ /* first parse the interrupt status to local data structures. */
+ qcmd->vfeInterruptStatus = vfe_parse_interrupt_status(irqStatusLocal);
+ qcmd->vfeAsfFrameInfo = vfe_get_asf_frame_info(&irq);
+ qcmd->vfeBpcFrameInfo = vfe_get_demosaic_frame_info(&irq);
+ qcmd->vfeCamifStatusLocal = vfe_get_camif_status(&irq);
+ qcmd->vfePmData = vfe_get_performance_monitor_data(&irq);
+
+ spin_lock_irqsave(&ctrl->tasklet_lock, flags);
+ list_add_tail(&qcmd->list, &ctrl->tasklet_q);
+ spin_unlock_irqrestore(&ctrl->tasklet_lock, flags);
+ tasklet_schedule(&vfe_tasklet);
+
+ /* clear the pending interrupt of the same kind.*/
+ writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ return IRQ_HANDLED;
+}
+
+int vfe_cmd_init(struct msm_vfe_callback *presp,
+ struct platform_device *pdev, void *sdata)
+{
+ struct resource *vfemem, *vfeirq, *vfeio;
+ int rc;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ CDBG("no mem resource?\n");
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ CDBG("no irq resource?\n");
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ CDBG("VFE region already claimed\n");
+ return -EBUSY;
+ }
+
+ ctrl =
+ kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+
+ ctrl->vfeirq = vfeirq->start;
+
+ ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!ctrl->vfebase) {
+ rc = -ENOMEM;
+ goto cmd_init_failed2;
+ }
+
+ rc = request_irq(ctrl->vfeirq, vfe_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+ if (rc < 0)
+ goto cmd_init_failed2;
+
+ if (presp && presp->vfe_resp)
+ ctrl->resp = presp;
+ else {
+ rc = -EINVAL;
+ goto cmd_init_failed3;
+ }
+
+ ctrl->extdata =
+ kmalloc(sizeof(struct vfe_frame_extra), GFP_KERNEL);
+ if (!ctrl->extdata) {
+ rc = -ENOMEM;
+ goto cmd_init_failed3;
+ }
+
+ spin_lock_init(&ctrl->ack_lock);
+ spin_lock_init(&ctrl->state_lock);
+ spin_lock_init(&ctrl->io_lock);
+
+ ctrl->extlen = sizeof(struct vfe_frame_extra);
+
+ spin_lock_init(&ctrl->tasklet_lock);
+ INIT_LIST_HEAD(&ctrl->tasklet_q);
+
+ ctrl->syncdata = sdata;
+ return 0;
+
+cmd_init_failed3:
+ disable_irq(ctrl->vfeirq);
+ free_irq(ctrl->vfeirq, 0);
+ iounmap(ctrl->vfebase);
+cmd_init_failed2:
+ kfree(ctrl);
+cmd_init_failed1:
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ return rc;
+}
+
+void vfe_cmd_release(struct platform_device *dev)
+{
+ struct resource *mem;
+
+ disable_irq(ctrl->vfeirq);
+ free_irq(ctrl->vfeirq, 0);
+
+ iounmap(ctrl->vfebase);
+ mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ ctrl->extlen = 0;
+
+ kfree(ctrl->extdata);
+ kfree(ctrl);
+}
+
+void vfe_stats_af_stop(void)
+{
+ ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE;
+ ctrl->vfeImaskLocal.afPingpongIrq = FALSE;
+}
+
+void vfe_stop(void)
+{
+ boolean vfeAxiBusy;
+ uint32_t vfeAxiStauts;
+
+ /* for reset hw modules, and send msg when reset_irq comes.*/
+ ctrl->vfeStopAckPending = TRUE;
+
+ ctrl->vfeStatsPingPongReloadFlag = FALSE;
+ vfe_pm_stop();
+
+ /* disable all interrupts. */
+ vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS);
+
+ /* in either continuous or snapshot mode, stop command can be issued
+ * at any time.
+ */
+ vfe_camif_stop_immediately();
+ vfe_program_axi_cmd(AXI_HALT);
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+
+ vfeAxiBusy = TRUE;
+
+ while (vfeAxiBusy) {
+ vfeAxiStauts = vfe_read_axi_status();
+ if ((vfeAxiStauts & AXI_STATUS_BUSY_MASK) != 0)
+ vfeAxiBusy = FALSE;
+ }
+
+ vfe_program_axi_cmd(AXI_HALT_CLEAR);
+
+ /* clear all pending interrupts */
+ writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* enable reset_ack and async timer interrupt only while stopping
+ * the pipeline.
+ */
+ vfe_program_irq_mask(VFE_IMASK_WHILE_STOPPING);
+
+ vfe_program_global_reset_cmd(VFE_RESET_UPON_STOP_CMD);
+}
+
+void vfe_update(void)
+{
+ ctrl->vfeModuleEnableLocal.statsEnable =
+ ctrl->vfeStatsCmdLocal.autoFocusEnable |
+ ctrl->vfeStatsCmdLocal.axwEnable;
+
+ vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+ vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+ if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) &&
+ (ctrl->vfeStatsPingPongReloadFlag == FALSE)) {
+ ctrl->vfeStatsPingPongReloadFlag = TRUE;
+
+ ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+ vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+ }
+
+ vfe_program_reg_update_cmd(VFE_REG_UPDATE_TRIGGER);
+}
+
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in)
+{
+ int rc = 0;
+
+ ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+ switch (in->channelSelect) {
+ case RGB_GAMMA_CH0_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ vfe_write_gamma_table(0,
+ ctrl->vfeGammaLutSel.ch0BankSelect, in->table);
+ break;
+
+ case RGB_GAMMA_CH1_SELECTED:
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ vfe_write_gamma_table(1,
+ ctrl->vfeGammaLutSel.ch1BankSelect, in->table);
+ break;
+
+ case RGB_GAMMA_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(2,
+ ctrl->vfeGammaLutSel.ch2BankSelect, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH1_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+ ctrl->vfeGammaLutSel.ch0BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch1BankSelect ^= 1;
+ ctrl->vfeGammaLutSel.ch2BankSelect ^= 1;
+ vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect,
+ in->table);
+ vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect,
+ in->table);
+ vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect,
+ in->table);
+ break;
+
+ default:
+ return -EINVAL;
+ } /* switch */
+
+ /* update the gammaLutSel register. */
+ vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+ return rc;
+}
+
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in)
+{
+ int rc = 0;
+
+ ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable;
+
+ switch (in->channelSelect) {
+ case RGB_GAMMA_CH0_SELECTED:
+vfe_write_gamma_table(0, 0, in->table);
+break;
+
+ case RGB_GAMMA_CH1_SELECTED:
+ vfe_write_gamma_table(1, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH2_SELECTED:
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(1, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH2_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH1_CH2_SELECTED:
+ vfe_write_gamma_table(1, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ case RGB_GAMMA_CH0_CH1_CH2_SELECTED:
+ vfe_write_gamma_table(0, 0, in->table);
+ vfe_write_gamma_table(1, 0, in->table);
+ vfe_write_gamma_table(2, 0, in->table);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch */
+
+ return rc;
+}
+
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *in)
+{
+ ctrl->afStatsControl.nextFrameAddrBuf = in->nextAFOutputBufferAddr;
+ ctrl->afStatsControl.ackPending = FALSE;
+}
+
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in)
+{
+ ctrl->awbStatsControl.nextFrameAddrBuf = in->nextWbExpOutputBufferAddr;
+ ctrl->awbStatsControl.ackPending = FALSE;
+}
+
+void vfe_output2_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ CDBG("output2_ack: ack addr = 0x%x\n", in->ybufaddr[0]);
+
+ psrc = in->ybufaddr;
+ for (i = 0; i < ctrl->encPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ psrc = in->chromabufaddr;
+ for (i = 0; i < ctrl->encPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->encPath.ackPending = FALSE;
+}
+
+void vfe_output1_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ pdest = ctrl->viewPath.nextFrameAddrBuf;
+
+ psrc = in->ybufaddr;
+ for (i = 0; i < ctrl->viewPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ psrc = in->chromabufaddr;
+ for (i = 0; i < ctrl->viewPath.fragCount; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->viewPath.ackPending = FALSE;
+}
+
+void vfe_start(struct vfe_cmd_start *in)
+{
+ unsigned long flags;
+ uint32_t pmstatus = 0;
+ boolean rawmode;
+ uint32_t demperiod = 0;
+ uint32_t demeven = 0;
+ uint32_t demodd = 0;
+
+ /* derived from other commands. (camif config, axi output config,
+ * etc)
+ */
+ struct vfe_cfg hwcfg;
+ struct vfe_upsample_cfg chromupcfg;
+
+ CDBG("vfe_start operationMode = %d\n", in->operationMode);
+
+ memset(&hwcfg, 0, sizeof(hwcfg));
+ memset(&chromupcfg, 0, sizeof(chromupcfg));
+
+ switch (in->pixel) {
+ case VFE_BAYER_RGRGRG:
+ demperiod = 1;
+ demeven = 0xC9;
+ demodd = 0xAC;
+ break;
+
+ case VFE_BAYER_GRGRGR:
+ demperiod = 1;
+ demeven = 0x9C;
+ demodd = 0xCA;
+ break;
+
+ case VFE_BAYER_BGBGBG:
+ demperiod = 1;
+ demeven = 0xCA;
+ demodd = 0x9C;
+ break;
+
+ case VFE_BAYER_GBGBGB:
+ demperiod = 1;
+ demeven = 0xAC;
+ demodd = 0xC9;
+ break;
+
+ case VFE_YUV_YCbYCr:
+ demperiod = 3;
+ demeven = 0x9CAC;
+ demodd = 0x9CAC;
+ break;
+
+ case VFE_YUV_YCrYCb:
+ demperiod = 3;
+ demeven = 0xAC9C;
+ demodd = 0xAC9C;
+ break;
+
+ case VFE_YUV_CbYCrY:
+ demperiod = 3;
+ demeven = 0xC9CA;
+ demodd = 0xC9CA;
+ break;
+
+ case VFE_YUV_CrYCbY:
+ demperiod = 3;
+ demeven = 0xCAC9;
+ demodd = 0xCAC9;
+ break;
+
+ default:
+ return;
+ }
+
+ vfe_config_demux(demperiod, demeven, demodd);
+
+ vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel);
+
+ /* save variables to local. */
+ ctrl->vfeOperationMode = in->operationMode;
+ if (ctrl->vfeOperationMode ==
+ VFE_START_OPERATION_MODE_SNAPSHOT) {
+ /* in snapshot mode, initialize snapshot count*/
+ ctrl->vfeSnapShotCount = in->snapshotCount;
+
+ /* save the requested count, this is temporarily done, to
+ help with HJR / multishot. */
+ ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount;
+
+ CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount);
+
+ /* Assumption is to have the same pattern and period for both
+ paths, if both paths are used. */
+ if (ctrl->viewPath.pathEnabled) {
+ ctrl->viewPath.snapshotPendingCount =
+ in->snapshotCount;
+
+ ctrl->vfeFrameSkipPattern =
+ ctrl->vfeFrameSkip.output1Pattern;
+ ctrl->vfeFrameSkipPeriod =
+ ctrl->vfeFrameSkip.output1Period;
+ }
+
+ if (ctrl->encPath.pathEnabled) {
+ ctrl->encPath.snapshotPendingCount =
+ in->snapshotCount;
+
+ ctrl->vfeFrameSkipPattern =
+ ctrl->vfeFrameSkip.output2Pattern;
+ ctrl->vfeFrameSkipPeriod =
+ ctrl->vfeFrameSkip.output2Period;
+ }
+ }
+
+ /* enable color conversion for bayer sensor
+ if stats enabled, need to do color conversion. */
+ if (in->pixel <= VFE_BAYER_GBGBGB)
+ ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE;
+
+ vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal);
+
+ if (in->pixel >= VFE_YUV_YCbYCr)
+ ctrl->vfeModuleEnableLocal.chromaUpsampleEnable = TRUE;
+
+ ctrl->vfeModuleEnableLocal.demuxEnable = TRUE;
+
+ /* if any stats module is enabled, the main bit is enabled. */
+ ctrl->vfeModuleEnableLocal.statsEnable =
+ ctrl->vfeStatsCmdLocal.autoFocusEnable |
+ ctrl->vfeStatsCmdLocal.axwEnable;
+
+ vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal);
+
+ /* in case of offline processing, do not need to config camif. Having
+ * bus output enabled in camif_config register might confuse the
+ * hardware?
+ */
+ if (in->inputSource != VFE_START_INPUT_SOURCE_AXI) {
+ vfe_reg_camif_config(&ctrl->vfeCamifConfigLocal);
+ } else {
+ /* offline processing, enable axi read */
+ ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE;
+ ctrl->vfeBusCmdLocal.stripeReload = TRUE;
+ ctrl->vfeBusConfigLocal.rawPixelDataSize =
+ ctrl->axiInputDataSize;
+ }
+
+ vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal);
+
+ /* directly from start command */
+ hwcfg.pixelPattern = in->pixel;
+ hwcfg.inputSource = in->inputSource;
+ writel(*(uint32_t *)&hwcfg, ctrl->vfebase + VFE_CFG);
+
+ /* regardless module enabled or not, it does not hurt
+ * to program the cositing mode. */
+ chromupcfg.chromaCositingForYCbCrInputs =
+ in->yuvInputCositingMode;
+
+ writel(*(uint32_t *)&(chromupcfg),
+ ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG);
+
+ /* MISR to monitor the axi read. */
+ writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0);
+
+ /* clear all pending interrupts. */
+ writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* define how composite interrupt work. */
+ ctrl->vfeImaskCompositePacked =
+ vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal);
+
+ vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked);
+
+ /* enable all necessary interrupts. */
+ ctrl->vfeImaskLocal.camifSofIrq = TRUE;
+ ctrl->vfeImaskLocal.regUpdateIrq = TRUE;
+ ctrl->vfeImaskLocal.resetAckIrq = TRUE;
+
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+ vfe_program_irq_mask(ctrl->vfeImaskPacked);
+
+ /* enable bus performance monitor */
+ vfe_8k_pm_start(&ctrl->vfeBusPmConfigLocal);
+
+ /* trigger vfe reg update */
+ ctrl->vfeStartAckPendingFlag = TRUE;
+
+ /* write bus command to trigger reload of ping pong buffer. */
+ ctrl->vfeBusCmdLocal.busPingpongReload = TRUE;
+
+ if (ctrl->vfeModuleEnableLocal.statsEnable == TRUE) {
+ ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE;
+ ctrl->vfeStatsPingPongReloadFlag = TRUE;
+ }
+
+ writel(VFE_REG_UPDATE_TRIGGER,
+ ctrl->vfebase + VFE_REG_UPDATE_CMD);
+
+ /* program later than the reg update. */
+ vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal);
+
+ if ((in->inputSource ==
+ VFE_START_INPUT_SOURCE_CAMIF) ||
+ (in->inputSource ==
+ VFE_START_INPUT_SOURCE_TESTGEN))
+ writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND);
+
+ /* start test gen if it is enabled */
+ if (ctrl->vfeTestGenStartFlag == TRUE) {
+ ctrl->vfeTestGenStartFlag = FALSE;
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_GO);
+ }
+
+ CDBG("ctrl->axiOutputMode = %d\n", ctrl->axiOutputMode);
+ if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2) {
+ /* raw dump mode */
+ rawmode = TRUE;
+
+ while (rawmode) {
+ pmstatus =
+ readl(ctrl->vfebase +
+ VFE_BUS_ENC_CBCR_WR_PM_STATS_1);
+
+ if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0)
+ rawmode = FALSE;
+ }
+
+ vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ }
+
+ spin_lock_irqsave(&ctrl->state_lock, flags);
+ ctrl->vstate = VFE_STATE_ACTIVE;
+ spin_unlock_irqrestore(&ctrl->state_lock, flags);
+}
+
+void vfe_la_update(struct vfe_cmd_la_config *in)
+{
+ int16_t *pTable;
+ enum VFE_DMI_RAM_SEL dmiRamSel;
+ int i;
+
+ pTable = in->table;
+ ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+ /* toggle the bank to be used. */
+ ctrl->vfeLaBankSel ^= 1;
+
+ if (ctrl->vfeLaBankSel == 0)
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+ else
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+ /* configure the DMI_CFG to select right sram */
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+ writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, to make it safe, need to set
+ * the DMI_CFG to unselect any SRAM */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+ writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+}
+
+void vfe_la_config(struct vfe_cmd_la_config *in)
+{
+ uint16_t i;
+ int16_t *pTable;
+ enum VFE_DMI_RAM_SEL dmiRamSel;
+
+ pTable = in->table;
+ ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable;
+
+ if (ctrl->vfeLaBankSel == 0)
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0;
+ else
+ dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1;
+
+ /* configure the DMI_CFG to select right sram */
+ vfe_program_dmi_cfg(dmiRamSel);
+
+ for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) {
+ writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO);
+ pTable++;
+ }
+
+ /* After DMI transfer, to make it safe, need to set the
+ * DMI_CFG to unselect any SRAM */
+ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG);
+
+ /* can only be bank 0 or bank 1 for now. */
+ writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG);
+ CDBG("VFE Luma adaptation bank selection is 0x%x\n",
+ *(uint32_t *)&ctrl->vfeLaBankSel);
+}
+
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in)
+{
+ struct VFE_TestGen_ConfigCmdType cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.numFrame = in->numFrame;
+ cmd.pixelDataSelect = in->pixelDataSelect;
+ cmd.systematicDataSelect = in->systematicDataSelect;
+ cmd.pixelDataSize = (uint32_t)in->pixelDataSize;
+ cmd.hsyncEdge = (uint32_t)in->hsyncEdge;
+ cmd.vsyncEdge = (uint32_t)in->vsyncEdge;
+ cmd.imageWidth = in->imageWidth;
+ cmd.imageHeight = in->imageHeight;
+ cmd.sofOffset = in->startOfFrameOffset;
+ cmd.eofNOffset = in->endOfFrameNOffset;
+ cmd.solOffset = in->startOfLineOffset;
+ cmd.eolNOffset = in->endOfLineNOffset;
+ cmd.hBlankInterval = in->hbi;
+ cmd.vBlankInterval = in->vbl;
+ cmd.vBlankIntervalEnable = in->vblEnable;
+ cmd.sofDummy = in->startOfFrameDummyLine;
+ cmd.eofDummy = in->endOfFrameDummyLine;
+ cmd.unicolorBarSelect = in->unicolorBarSelect;
+ cmd.unicolorBarEnable = in->unicolorBarEnable;
+ cmd.splitEnable = in->colorBarsSplitEnable;
+ cmd.pixelPattern = (uint32_t)in->colorBarsPixelPattern;
+ cmd.rotatePeriod = in->colorBarsRotatePeriod;
+ cmd.randomSeed = in->testGenRandomSeed;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG,
+ (uint32_t *) &cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in)
+{
+ struct VFE_FRAME_SKIP_UpdateCmdType cmd;
+
+ cmd.yPattern = in->output1Pattern;
+ cmd.cbcrPattern = in->output1Pattern;
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd.yPattern = in->output2Pattern;
+ cmd.cbcrPattern = in->output2Pattern;
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in)
+{
+ struct vfe_frame_skip_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeFrameSkip = *in;
+
+ cmd.output2YPeriod = in->output2Period;
+ cmd.output2CbCrPeriod = in->output2Period;
+ cmd.output2YPattern = in->output2Pattern;
+ cmd.output2CbCrPattern = in->output2Pattern;
+ cmd.output1YPeriod = in->output1Period;
+ cmd.output1CbCrPeriod = in->output1Period;
+ cmd.output1YPattern = in->output1Pattern;
+ cmd.output1CbCrPattern = in->output1Pattern;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in)
+{
+ struct vfe_output_clamp_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.yChanMax = in->maxCh0;
+ cmd.cbChanMax = in->maxCh1;
+ cmd.crChanMax = in->maxCh2;
+
+ cmd.yChanMin = in->minCh0;
+ cmd.cbChanMin = in->minCh1;
+ cmd.crChanMin = in->minCh2;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd,
+ sizeof(cmd));
+}
+
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in)
+{
+ struct vfe_camifframe_update cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.pixelsPerLine = in->pixelsPerLine;
+ cmd.linesPerFrame = in->linesPerFrame;
+
+ vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd,
+ sizeof(cmd));
+}
+
+void vfe_color_correction_config(
+ struct vfe_cmd_color_correction_config *in)
+{
+ struct vfe_color_correction_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ctrl->vfeModuleEnableLocal.colorCorrectionEnable = in->enable;
+
+ cmd.c0 = in->C0;
+ cmd.c1 = in->C1;
+ cmd.c2 = in->C2;
+ cmd.c3 = in->C3;
+ cmd.c4 = in->C4;
+ cmd.c5 = in->C5;
+ cmd.c6 = in->C6;
+ cmd.c7 = in->C7;
+ cmd.c8 = in->C8;
+
+ cmd.k0 = in->K0;
+ cmd.k1 = in->K1;
+ cmd.k2 = in->K2;
+
+ cmd.coefQFactor = in->coefQFactor;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in)
+{
+struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_abf_cfg cmdabf;
+ uint32_t temp;
+
+ memset(&cmd, 0, sizeof(cmd));
+ temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+ cmd = *((struct vfe_demosaic_cfg *)(&temp));
+ cmd.abfEnable = in->abfUpdate.enable;
+ cmd.forceAbfOn = in->abfUpdate.forceOn;
+ cmd.abfShift = in->abfUpdate.shift;
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmdabf.lpThreshold = in->abfUpdate.lpThreshold;
+ cmdabf.ratio = in->abfUpdate.ratio;
+ cmdabf.minValue = in->abfUpdate.min;
+ cmdabf.maxValue = in->abfUpdate.max;
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+ (uint32_t *)&cmdabf, sizeof(cmdabf));
+}
+
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in)
+{
+ struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_bpc_cfg cmdbpc;
+ uint32_t temp;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG);
+
+ cmd = *((struct vfe_demosaic_cfg *)(&temp));
+ cmd.badPixelCorrEnable = in->bpcUpdate.enable;
+ cmd.fminThreshold = in->bpcUpdate.fminThreshold;
+ cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold;
+ cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold;
+ cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+ (uint32_t *)&cmdbpc, sizeof(cmdbpc));
+}
+
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in)
+{
+ struct vfe_demosaic_cfg cmd;
+ struct vfe_demosaic_bpc_cfg cmd_bpc;
+ struct vfe_demosaic_abf_cfg cmd_abf;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd_bpc, 0, sizeof(cmd_bpc));
+ memset(&cmd_abf, 0, sizeof(cmd_abf));
+
+ ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable;
+
+ cmd.abfEnable = in->abfConfig.enable;
+ cmd.badPixelCorrEnable = in->bpcConfig.enable;
+ cmd.forceAbfOn = in->abfConfig.forceOn;
+ cmd.abfShift = in->abfConfig.shift;
+ cmd.fminThreshold = in->bpcConfig.fminThreshold;
+ cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold;
+ cmd.slopeShift = in->slopeShift;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd_abf.lpThreshold = in->abfConfig.lpThreshold;
+ cmd_abf.ratio = in->abfConfig.ratio;
+ cmd_abf.minValue = in->abfConfig.min;
+ cmd_abf.maxValue = in->abfConfig.max;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0,
+ (uint32_t *)&cmd_abf, sizeof(cmd_abf));
+
+ cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold;
+ cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold;
+ cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0,
+ (uint32_t *)&cmd_bpc, sizeof(cmd_bpc));
+}
+
+void vfe_demux_channel_gain_update(
+ struct vfe_cmd_demux_channel_gain_config *in)
+{
+ struct vfe_demux_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0EvenGain = in->ch0EvenGain;
+ cmd.ch0OddGain = in->ch0OddGain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_demux_channel_gain_config(
+ struct vfe_cmd_demux_channel_gain_config *in)
+{
+ struct vfe_demux_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.ch0EvenGain = in->ch0EvenGain;
+ cmd.ch0OddGain = in->ch0OddGain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_update(struct vfe_cmd_black_level_config *in)
+{
+ struct vfe_blacklevel_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+ cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+ cmd.evenOddAdjustment = in->evenOddAdjustment;
+ cmd.oddEvenAdjustment = in->oddEvenAdjustment;
+ cmd.oddOddAdjustment = in->oddOddAdjustment;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_black_level_config(struct vfe_cmd_black_level_config *in)
+{
+ struct vfe_blacklevel_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable;
+
+ cmd.evenEvenAdjustment = in->evenEvenAdjustment;
+ cmd.evenOddAdjustment = in->evenOddAdjustment;
+ cmd.oddEvenAdjustment = in->oddEvenAdjustment;
+ cmd.oddOddAdjustment = in->oddOddAdjustment;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_update(struct vfe_cmd_asf_update *in)
+{
+ struct vfe_asf_update cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+ cmd.smoothEnable = in->smoothFilterEnabled;
+ cmd.sharpMode = in->sharpMode;
+ cmd.smoothCoeff1 = in->smoothCoefCenter;
+ cmd.smoothCoeff0 = in->smoothCoefSurr;
+ cmd.cropEnable = in->cropEnable;
+ cmd.sharpThresholdE1 = in->sharpThreshE1;
+ cmd.sharpDegreeK1 = in->sharpK1;
+ cmd.sharpDegreeK2 = in->sharpK2;
+ cmd.normalizeFactor = in->normalizeFactor;
+ cmd.sharpThresholdE2 = in->sharpThreshE2;
+ cmd.sharpThresholdE3 = in->sharpThreshE3;
+ cmd.sharpThresholdE4 = in->sharpThreshE4;
+ cmd.sharpThresholdE5 = in->sharpThreshE5;
+ cmd.F1Coeff0 = in->filter1Coefficients[0];
+ cmd.F1Coeff1 = in->filter1Coefficients[1];
+ cmd.F1Coeff2 = in->filter1Coefficients[2];
+ cmd.F1Coeff3 = in->filter1Coefficients[3];
+ cmd.F1Coeff4 = in->filter1Coefficients[4];
+ cmd.F1Coeff5 = in->filter1Coefficients[5];
+ cmd.F1Coeff6 = in->filter1Coefficients[6];
+ cmd.F1Coeff7 = in->filter1Coefficients[7];
+ cmd.F1Coeff8 = in->filter1Coefficients[8];
+ cmd.F2Coeff0 = in->filter2Coefficients[0];
+ cmd.F2Coeff1 = in->filter2Coefficients[1];
+ cmd.F2Coeff2 = in->filter2Coefficients[2];
+ cmd.F2Coeff3 = in->filter2Coefficients[3];
+ cmd.F2Coeff4 = in->filter2Coefficients[4];
+ cmd.F2Coeff5 = in->filter2Coefficients[5];
+ cmd.F2Coeff6 = in->filter2Coefficients[6];
+ cmd.F2Coeff7 = in->filter2Coefficients[7];
+ cmd.F2Coeff8 = in->filter2Coefficients[8];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_asf_config(struct vfe_cmd_asf_config *in)
+{
+ struct vfe_asf_update cmd;
+ struct vfe_asfcrop_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->vfeModuleEnableLocal.asfEnable = in->enable;
+
+ cmd.smoothEnable = in->smoothFilterEnabled;
+ cmd.sharpMode = in->sharpMode;
+ cmd.smoothCoeff0 = in->smoothCoefCenter;
+ cmd.smoothCoeff1 = in->smoothCoefSurr;
+ cmd.cropEnable = in->cropEnable;
+ cmd.sharpThresholdE1 = in->sharpThreshE1;
+ cmd.sharpDegreeK1 = in->sharpK1;
+ cmd.sharpDegreeK2 = in->sharpK2;
+ cmd.normalizeFactor = in->normalizeFactor;
+ cmd.sharpThresholdE2 = in->sharpThreshE2;
+ cmd.sharpThresholdE3 = in->sharpThreshE3;
+ cmd.sharpThresholdE4 = in->sharpThreshE4;
+ cmd.sharpThresholdE5 = in->sharpThreshE5;
+ cmd.F1Coeff0 = in->filter1Coefficients[0];
+ cmd.F1Coeff1 = in->filter1Coefficients[1];
+ cmd.F1Coeff2 = in->filter1Coefficients[2];
+ cmd.F1Coeff3 = in->filter1Coefficients[3];
+ cmd.F1Coeff4 = in->filter1Coefficients[4];
+ cmd.F1Coeff5 = in->filter1Coefficients[5];
+ cmd.F1Coeff6 = in->filter1Coefficients[6];
+ cmd.F1Coeff7 = in->filter1Coefficients[7];
+ cmd.F1Coeff8 = in->filter1Coefficients[8];
+ cmd.F2Coeff0 = in->filter2Coefficients[0];
+ cmd.F2Coeff1 = in->filter2Coefficients[1];
+ cmd.F2Coeff2 = in->filter2Coefficients[2];
+ cmd.F2Coeff3 = in->filter2Coefficients[3];
+ cmd.F2Coeff4 = in->filter2Coefficients[4];
+ cmd.F2Coeff5 = in->filter2Coefficients[5];
+ cmd.F2Coeff6 = in->filter2Coefficients[6];
+ cmd.F2Coeff7 = in->filter2Coefficients[7];
+ cmd.F2Coeff8 = in->filter2Coefficients[8];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.firstLine = in->cropFirstLine;
+ cmd2.lastLine = in->cropLastLine;
+ cmd2.firstPixel = in->cropFirstPixel;
+ cmd2.lastPixel = in->cropLastPixel;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in)
+{
+ struct vfe_wb_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.whiteBalanceEnable =
+ in->enable;
+
+ cmd.ch0Gain = in->ch0Gain;
+ cmd.ch1Gain = in->ch1Gain;
+ cmd.ch2Gain = in->ch2Gain;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in)
+{
+ struct vfe_chroma_suppress_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable;
+
+ cmd.m1 = in->m1;
+ cmd.m3 = in->m3;
+ cmd.n1 = in->n1;
+ cmd.n3 = in->n3;
+ cmd.mm1 = in->mm1;
+ cmd.nn1 = in->nn1;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in)
+{
+ struct vfe_rolloff_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable;
+
+ cmd.gridWidth = in->gridWidth;
+ cmd.gridHeight = in->gridHeight;
+ cmd.yDelta = in->yDelta;
+ cmd.gridX = in->gridXIndex;
+ cmd.gridY = in->gridYIndex;
+ cmd.pixelX = in->gridPixelXIndex;
+ cmd.pixelY = in->gridPixelYIndex;
+ cmd.yDeltaAccum = in->yDeltaAccum;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ vfe_write_lens_roll_off_table(in);
+}
+
+void vfe_chroma_subsample_config(
+ struct vfe_cmd_chroma_subsample_config *in)
+{
+ struct vfe_chromasubsample_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable;
+
+ cmd.hCositedPhase = in->hCositedPhase;
+ cmd.vCositedPhase = in->vCositedPhase;
+ cmd.hCosited = in->hCosited;
+ cmd.vCosited = in->vCosited;
+ cmd.hsubSampleEnable = in->hsubSampleEnable;
+ cmd.vsubSampleEnable = in->vsubSampleEnable;
+ cmd.cropEnable = in->cropEnable;
+ cmd.cropWidthLastPixel = in->cropWidthLastPixel;
+ cmd.cropWidthFirstPixel = in->cropWidthFirstPixel;
+ cmd.cropHeightLastLine = in->cropHeightLastLine;
+ cmd.cropHeightFirstLine = in->cropHeightFirstLine;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in)
+{
+ struct vfe_chroma_enhance_cfg cmd;
+ struct vfe_color_convert_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable;
+
+ cmd.ap = in->ap;
+ cmd.am = in->am;
+ cmd.bp = in->bp;
+ cmd.bm = in->bm;
+ cmd.cp = in->cp;
+ cmd.cm = in->cm;
+ cmd.dp = in->dp;
+ cmd.dm = in->dm;
+ cmd.kcb = in->kcb;
+ cmd.kcr = in->kcr;
+
+ cmd2.v0 = in->RGBtoYConversionV0;
+ cmd2.v1 = in->RGBtoYConversionV1;
+ cmd2.v2 = in->RGBtoYConversionV2;
+ cmd2.ConvertOffset = in->RGBtoYConversionOffset;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in)
+{
+ struct vfe_scaler2_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in)
+{
+ struct vfe_scaler2_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in)
+{
+ struct vfe_main_scaler_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable;
+
+ cmd.hEnable = in->hconfig.enable;
+ cmd.vEnable = in->vconfig.enable;
+ cmd.inWidth = in->hconfig.inputSize;
+ cmd.outWidth = in->hconfig.outputSize;
+ cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor;
+ cmd.horizInterResolution = in->hconfig.interpolationResolution;
+ cmd.horizMNInit = in->MNInitH.MNCounterInit;
+ cmd.horizPhaseInit = in->MNInitH.phaseInit;
+ cmd.inHeight = in->vconfig.inputSize;
+ cmd.outHeight = in->vconfig.outputSize;
+ cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor;
+ cmd.vertInterResolution = in->vconfig.interpolationResolution;
+ cmd.vertMNInit = in->MNInitV.MNCounterInit;
+ cmd.vertPhaseInit = in->MNInitV.phaseInit;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_wb_exp_stop(void)
+{
+ ctrl->vfeStatsCmdLocal.axwEnable = FALSE;
+ ctrl->vfeImaskLocal.awbPingpongIrq = FALSE;
+}
+
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in)
+{
+ struct vfe_statsawb_update cmd;
+ struct vfe_statsawbae_update cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ cmd.m1 = in->awbMCFG[0];
+ cmd.m2 = in->awbMCFG[1];
+ cmd.m3 = in->awbMCFG[2];
+ cmd.m4 = in->awbMCFG[3];
+ cmd.c1 = in->awbCCFG[0];
+ cmd.c2 = in->awbCCFG[1];
+ cmd.c3 = in->awbCCFG[2];
+ cmd.c4 = in->awbCCFG[3];
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.aeRegionCfg = in->wbExpRegions;
+ cmd2.aeSubregionCfg = in->wbExpSubRegion;
+ cmd2.awbYMin = in->awbYMin;
+ cmd2.awbYMax = in->awbYMax;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in)
+{
+ struct vfe_statsaf_update cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.windowVOffset = in->windowVOffset;
+ cmd.windowHOffset = in->windowHOffset;
+ cmd.windowMode = in->windowMode;
+ cmd.windowHeight = in->windowHeight;
+ cmd.windowWidth = in->windowWidth;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in)
+{
+ struct vfe_statsawb_update cmd;
+ struct vfe_statsawbae_update cmd2;
+ struct vfe_statsaxw_hdr_cfg cmd3;
+
+ ctrl->vfeStatsCmdLocal.axwEnable = in->enable;
+ ctrl->vfeImaskLocal.awbPingpongIrq = TRUE;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+ memset(&cmd3, 0, sizeof(cmd3));
+
+ cmd.m1 = in->awbMCFG[0];
+ cmd.m2 = in->awbMCFG[1];
+ cmd.m3 = in->awbMCFG[2];
+ cmd.m4 = in->awbMCFG[3];
+ cmd.c1 = in->awbCCFG[0];
+ cmd.c2 = in->awbCCFG[1];
+ cmd.c3 = in->awbCCFG[2];
+ cmd.c4 = in->awbCCFG[3];
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.aeRegionCfg = in->wbExpRegions;
+ cmd2.aeSubregionCfg = in->wbExpSubRegion;
+ cmd2.awbYMin = in->awbYMin;
+ cmd2.awbYMax = in->awbYMax;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+
+ cmd3.axwHeader = in->axwHeader;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER,
+ (uint32_t *)&cmd3, sizeof(cmd3));
+}
+
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in)
+{
+ struct vfe_statsaf_update cmd;
+ struct vfe_statsaf_cfg cmd2;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable;
+ctrl->vfeImaskLocal.afPingpongIrq = TRUE;
+
+ cmd.windowVOffset = in->windowVOffset;
+ cmd.windowHOffset = in->windowHOffset;
+ cmd.windowMode = in->windowMode;
+ cmd.windowHeight = in->windowHeight;
+ cmd.windowWidth = in->windowWidth;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ cmd2.a00 = in->highPassCoef[0];
+ cmd2.a04 = in->highPassCoef[1];
+ cmd2.a20 = in->highPassCoef[2];
+ cmd2.a21 = in->highPassCoef[3];
+ cmd2.a22 = in->highPassCoef[4];
+ cmd2.a23 = in->highPassCoef[5];
+ cmd2.a24 = in->highPassCoef[6];
+ cmd2.fvMax = in->metricMax;
+ cmd2.fvMetric = in->metricSelection;
+ cmd2.afHeader = in->bufferHeader;
+ cmd2.entry00 = in->gridForMultiWindows[0];
+ cmd2.entry01 = in->gridForMultiWindows[1];
+ cmd2.entry02 = in->gridForMultiWindows[2];
+ cmd2.entry03 = in->gridForMultiWindows[3];
+ cmd2.entry10 = in->gridForMultiWindows[4];
+ cmd2.entry11 = in->gridForMultiWindows[5];
+ cmd2.entry12 = in->gridForMultiWindows[6];
+ cmd2.entry13 = in->gridForMultiWindows[7];
+ cmd2.entry20 = in->gridForMultiWindows[8];
+ cmd2.entry21 = in->gridForMultiWindows[9];
+ cmd2.entry22 = in->gridForMultiWindows[10];
+ cmd2.entry23 = in->gridForMultiWindows[11];
+ cmd2.entry30 = in->gridForMultiWindows[12];
+ cmd2.entry31 = in->gridForMultiWindows[13];
+ cmd2.entry32 = in->gridForMultiWindows[14];
+ cmd2.entry33 = in->gridForMultiWindows[15];
+
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+}
+
+void vfe_stats_setting(struct vfe_cmd_stats_setting *in)
+{
+ struct vfe_statsframe cmd1;
+ struct vfe_busstats_wrprio cmd2;
+
+ memset(&cmd1, 0, sizeof(cmd1));
+ memset(&cmd2, 0, sizeof(cmd2));
+
+ ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0];
+ ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1];
+ ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2];
+
+ ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0];
+ ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1];
+ ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2];
+
+ cmd1.lastPixel = in->frameHDimension;
+ cmd1.lastLine = in->frameVDimension;
+ vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE,
+ (uint32_t *)&cmd1, sizeof(cmd1));
+
+ cmd2.afBusPriority = in->afBusPriority;
+ cmd2.awbBusPriority = in->awbBusPriority;
+ cmd2.histBusPriority = in->histBusPriority;
+ cmd2.afBusPriorityEn = in->afBusPrioritySelection;
+ cmd2.awbBusPriorityEn = in->awbBusPrioritySelection;
+ cmd2.histBusPriorityEn = in->histBusPrioritySelection;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY,
+ (uint32_t *)&cmd2, sizeof(cmd2));
+
+ /* Program the bus ping pong address for statistics modules. */
+ writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+ writel(in->awbBuffer[0],
+ ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ writel(in->awbBuffer[1],
+ ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+ writel(in->histBuffer[0],
+ ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+ writel(in->histBuffer[1],
+ ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+}
+
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in)
+{
+ struct VFE_AxiInputCmdType cmd;
+ uint32_t xSizeWord, axiRdUnpackPattern;
+ uint8_t axiInputPpw;
+ uint32_t busPingpongRdIrqEnable;
+
+ ctrl->vfeImaskLocal.rdPingpongIrq = TRUE;
+
+ switch (in->pixelSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_10BIT;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_12BIT;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ default:
+ ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_8BIT;
+ break;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ switch (in->pixelSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ axiInputPpw = 6;
+ axiRdUnpackPattern = 0xD43210;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ axiInputPpw = 5;
+ axiRdUnpackPattern = 0xC3210;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ default:
+ axiInputPpw = 8;
+ axiRdUnpackPattern = 0xF6543210;
+ break;
+ }
+
+ xSizeWord =
+ ((((in->xOffset % axiInputPpw) + in->xSize) +
+ (axiInputPpw-1)) / axiInputPpw) - 1;
+
+ cmd.stripeStartAddr0 = in->fragAddr[0];
+ cmd.stripeStartAddr1 = in->fragAddr[1];
+ cmd.stripeStartAddr2 = in->fragAddr[2];
+ cmd.stripeStartAddr3 = in->fragAddr[3];
+ cmd.ySize = in->ySize;
+ cmd.yOffsetDelta = 0;
+ cmd.xSizeWord = xSizeWord;
+ cmd.burstLength = 1;
+ cmd.NumOfRows = in->numOfRows;
+ cmd.RowIncrement =
+ (in->rowIncrement + (axiInputPpw-1))/axiInputPpw;
+ cmd.mainUnpackHeight = in->ySize;
+ cmd.mainUnpackWidth = in->xSize - 1;
+ cmd.mainUnpackHbiSel = (uint32_t)in->unpackHbi;
+ cmd.mainUnpackPhase = in->unpackPhase;
+ cmd.unpackPattern = axiRdUnpackPattern;
+ cmd.padLeft = in->padRepeatCountLeft;
+ cmd.padRight = in->padRepeatCountRight;
+ cmd.padTop = in->padRepeatCountTop;
+ cmd.padBottom = in->padRepeatCountBottom;
+ cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0;
+ cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1;
+ cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2;
+ cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3;
+ cmd.leftUnpackStop0 = in->padLeftStopCycle0;
+ cmd.leftUnpackStop1 = in->padLeftStopCycle1;
+ cmd.leftUnpackStop2 = in->padLeftStopCycle2;
+ cmd.leftUnpackStop3 = in->padLeftStopCycle3;
+ cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0;
+ cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1;
+ cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2;
+ cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3;
+ cmd.rightUnpackStop0 = in->padRightStopCycle0;
+ cmd.rightUnpackStop1 = in->padRightStopCycle1;
+ cmd.rightUnpackStop2 = in->padRightStopCycle2;
+ cmd.rightUnpackStop3 = in->padRightStopCycle3;
+ cmd.topUnapckPattern = in->padTopLineCount;
+ cmd.bottomUnapckPattern = in->padBottomLineCount;
+
+ /* program vfe_bus_cfg */
+ vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0,
+ (uint32_t *)&cmd, sizeof(cmd));
+
+ /* hacking code, put it to default value */
+ busPingpongRdIrqEnable = 0xf;
+
+ writel(busPingpongRdIrqEnable,
+ ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN);
+}
+
+void vfe_stats_config(struct vfe_cmd_stats_setting *in)
+{
+ ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0];
+ ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1];
+ ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2];
+
+ ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0];
+ ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1];
+ ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2];
+
+ vfe_stats_setting(in);
+}
+
+void vfe_axi_output_config(
+ struct vfe_cmd_axi_output_config *in)
+{
+ /* local variable */
+ uint32_t *pcircle;
+ uint32_t *pdest;
+ uint32_t *psrc;
+ uint8_t i;
+ uint8_t fcnt;
+ uint16_t axioutpw = 8;
+
+ /* parameters check, condition and usage mode check */
+ ctrl->encPath.fragCount = in->output2.fragmentCount;
+ if (ctrl->encPath.fragCount > 1)
+ ctrl->encPath.multiFrag = TRUE;
+
+ ctrl->viewPath.fragCount = in->output1.fragmentCount;
+ if (ctrl->viewPath.fragCount > 1)
+ ctrl->viewPath.multiFrag = TRUE;
+
+ /* VFE_BUS_CFG. raw data size */
+ ctrl->vfeBusConfigLocal.rawPixelDataSize = in->outputDataSize;
+
+ switch (in->outputDataSize) {
+ case VFE_RAW_PIXEL_DATA_SIZE_8BIT:
+ axioutpw = 8;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_10BIT:
+ axioutpw = 6;
+ break;
+
+ case VFE_RAW_PIXEL_DATA_SIZE_12BIT:
+ axioutpw = 5;
+ break;
+ }
+
+ ctrl->axiOutputMode = in->outputMode;
+
+ CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode);
+
+ switch (ctrl->axiOutputMode) {
+ case VFE_AXI_OUTPUT_MODE_Output1: {
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.encIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE;
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output2: {
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.viewIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output1AndOutput2: {
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_DISABLED;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2: {
+ /* For raw snapshot, we need both ping and pong buffer
+ * initialized to the same address. Otherwise, if we
+ * leave the pong buffer to NULL, there will be axi_error.
+ * Note that ideally we should deal with this at upper layer,
+ * which is in msm_vfe8x.c */
+ if (!in->output2.outputCbcr.outFragments[1][0]) {
+ in->output2.outputCbcr.outFragments[1][0] =
+ in->output2.outputCbcr.outFragments[0][0];
+ }
+
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = FALSE;
+ ctrl->vfeImaskLocal.viewIrq = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1: {
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_VIEW_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */
+ break;
+
+ case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2: {
+ ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE;
+ ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE;
+ ctrl->vfeBusConfigLocal.rawWritePathSelect =
+ VFE_RAW_OUTPUT_ENC_CBCR_PATH;
+
+ ctrl->encPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.encIrq = TRUE;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_CBCR_ONLY;
+
+ ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE;
+ ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE;
+
+ ctrl->viewPath.pathEnabled = TRUE;
+ ctrl->vfeImaskLocal.viewIrq = TRUE;
+
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE;
+ ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encYWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn &&
+ ctrl->encPath.multiFrag)
+ ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewYWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE;
+
+ if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn &&
+ ctrl->viewPath.multiFrag)
+ ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE;
+ } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */
+ break;
+
+ case VFE_AXI_LAST_OUTPUT_MODE_ENUM:
+ break;
+ } /* switch */
+
+ /* Save the addresses for each path. */
+ /* output2 path */
+ fcnt = ctrl->encPath.fragCount;
+
+ pcircle = ctrl->encPath.yPath.addressBuffer;
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ psrc = &(in->output2.outputY.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputY.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputY.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ pcircle = ctrl->encPath.cbcrPath.addressBuffer;
+
+ psrc = &(in->output2.outputCbcr.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputCbcr.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output2.outputCbcr.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ vfe_set_bus_pipo_addr(&ctrl->viewPath, &ctrl->encPath);
+
+ ctrl->encPath.ackPending = FALSE;
+ ctrl->encPath.currentFrame = ping;
+ ctrl->encPath.whichOutputPath = 1;
+ ctrl->encPath.yPath.fragIndex = 2;
+ ctrl->encPath.cbcrPath.fragIndex = 2;
+ ctrl->encPath.yPath.hwCurrentFlag = ping;
+ ctrl->encPath.cbcrPath.hwCurrentFlag = ping;
+
+ /* output1 path */
+ pcircle = ctrl->viewPath.yPath.addressBuffer;
+ pdest = ctrl->viewPath.nextFrameAddrBuf;
+ fcnt = ctrl->viewPath.fragCount;
+
+ psrc = &(in->output1.outputY.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputY.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputY.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ pcircle = ctrl->viewPath.cbcrPath.addressBuffer;
+
+ psrc = &(in->output1.outputCbcr.outFragments[0][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputCbcr.outFragments[1][0]);
+ for (i = 0; i < fcnt; i++)
+ *pcircle++ = *psrc++;
+
+ psrc = &(in->output1.outputCbcr.outFragments[2][0]);
+ for (i = 0; i < fcnt; i++)
+ *pdest++ = *psrc++;
+
+ ctrl->viewPath.ackPending = FALSE;
+ ctrl->viewPath.currentFrame = ping;
+ ctrl->viewPath.whichOutputPath = 0;
+ ctrl->viewPath.yPath.fragIndex = 2;
+ ctrl->viewPath.cbcrPath.fragIndex = 2;
+ ctrl->viewPath.yPath.hwCurrentFlag = ping;
+ ctrl->viewPath.cbcrPath.hwCurrentFlag = ping;
+
+ /* call to program the registers. */
+ vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw);
+}
+
+void vfe_camif_config(struct vfe_cmd_camif_config *in)
+{
+ struct vfe_camifcfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine);
+ CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame);
+ CDBG("camif.window firstpixel = %d\n", in->window.firstpixel);
+ CDBG("camif.window lastpixel = %d\n", in->window.lastpixel);
+ CDBG("camif.window firstline = %d\n", in->window.firstline);
+ CDBG("camif.window lastline = %d\n", in->window.lastline);
+
+ /* determine if epoch interrupt needs to be enabled. */
+ if ((in->epoch1.enable == TRUE) &&
+ (in->epoch1.lineindex <=
+ in->frame.linesPerFrame))
+ ctrl->vfeImaskLocal.camifEpoch1Irq = 1;
+
+ if ((in->epoch2.enable == TRUE) &&
+ (in->epoch2.lineindex <=
+ in->frame.linesPerFrame)) {
+ ctrl->vfeImaskLocal.camifEpoch2Irq = 1;
+ }
+
+ /* save the content to program CAMIF_CONFIG seperately. */
+ ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig;
+
+ /* EFS_Config */
+ cmd.efsEndOfLine = in->EFS.efsendofline;
+ cmd.efsStartOfLine = in->EFS.efsstartofline;
+ cmd.efsEndOfFrame = in->EFS.efsendofframe;
+ cmd.efsStartOfFrame = in->EFS.efsstartofframe;
+
+ /* Frame Config */
+ cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine;
+ cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame;
+
+ /* Window Width Config */
+ cmd.windowWidthCfgLastPixel = in->window.lastpixel;
+ cmd.windowWidthCfgFirstPixel = in->window.firstpixel;
+
+ /* Window Height Config */
+ cmd.windowHeightCfglastLine = in->window.lastline;
+ cmd.windowHeightCfgfirstLine = in->window.firstline;
+
+ /* Subsample 1 Config */
+ cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask;
+ cmd.subsample1CfgLineSkip = in->subsample.lineskipmask;
+
+ /* Subsample 2 Config */
+ cmd.subsample2CfgFrameSkip = in->subsample.frameskip;
+ cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode;
+ cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap;
+
+ /* Epoch Interrupt */
+ cmd.epoch1Line = in->epoch1.lineindex;
+ cmd.epoch2Line = in->epoch2.lineindex;
+
+ vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in)
+{
+ struct vfe_fov_crop_cfg cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctrl->vfeModuleEnableLocal.cropEnable = in->enable;
+
+ /* FOV Corp, Part 1 */
+ cmd.lastPixel = in->lastPixel;
+ cmd.firstPixel = in->firstPixel;
+
+ /* FOV Corp, Part 2 */
+ cmd.lastLine = in->lastLine;
+ cmd.firstLine = in->firstLine;
+
+ vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG,
+ (uint32_t *)&cmd, sizeof(cmd));
+}
+
+void vfe_get_hw_version(struct vfe_cmd_hw_version *out)
+{
+ uint32_t vfeHwVersionPacked;
+ struct vfe_hw_ver ver;
+
+ vfeHwVersionPacked = readl(ctrl->vfebase + VFE_HW_VERSION);
+
+ ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked);
+
+ out->coreVersion = ver.coreVersion;
+ out->minorVersion = ver.minorVersion;
+ out->majorVersion = ver.majorVersion;
+}
+
+static void vfe_reset_internal_variables(void)
+{
+ unsigned long flags;
+
+ /* local variables to program the hardware. */
+ ctrl->vfeImaskPacked = 0;
+ ctrl->vfeImaskCompositePacked = 0;
+
+ /* FALSE = disable, 1 = enable. */
+ memset(&ctrl->vfeModuleEnableLocal, 0,
+ sizeof(ctrl->vfeModuleEnableLocal));
+
+ /* 0 = disable, 1 = enable */
+ memset(&ctrl->vfeCamifConfigLocal, 0,
+ sizeof(ctrl->vfeCamifConfigLocal));
+ /* 0 = disable, 1 = enable */
+ memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal));
+ memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal));
+ memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal));
+ memset(&ctrl->vfeBusPmConfigLocal, 0,
+ sizeof(ctrl->vfeBusPmConfigLocal));
+ memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal));
+ memset(&ctrl->vfeInterruptNameLocal, 0,
+ sizeof(ctrl->vfeInterruptNameLocal));
+ memset(&ctrl->vfeDroppedFrameCounts, 0,
+ sizeof(ctrl->vfeDroppedFrameCounts));
+ memset(&ctrl->vfeIrqThreadMsgLocal, 0,
+ sizeof(ctrl->vfeIrqThreadMsgLocal));
+
+ /* state control variables */
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ ctrl->vfeStopAckPending = FALSE;
+ ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0;
+ ctrl->vfeIrqCompositeMaskLocal.encIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+ ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask =
+ VFE_COMP_IRQ_BOTH_Y_CBCR;
+
+ spin_lock_irqsave(&ctrl->state_lock, flags);
+ ctrl->vstate = VFE_STATE_IDLE;
+ spin_unlock_irqrestore(&ctrl->state_lock, flags);
+
+ ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM;
+ /* 0 for continuous mode, 1 for snapshot mode */
+ ctrl->vfeOperationMode = VFE_START_OPERATION_MODE_CONTINUOUS;
+ ctrl->vfeSnapShotCount = 0;
+ ctrl->vfeStatsPingPongReloadFlag = FALSE;
+ /* this is unsigned 32 bit integer. */
+ ctrl->vfeFrameId = 0;
+ ctrl->vfeFrameSkip.output1Pattern = 0xffffffff;
+ ctrl->vfeFrameSkip.output1Period = 31;
+ ctrl->vfeFrameSkip.output2Pattern = 0xffffffff;
+ ctrl->vfeFrameSkip.output2Period = 31;
+ ctrl->vfeFrameSkipPattern = 0xffffffff;
+ ctrl->vfeFrameSkipCount = 0;
+ ctrl->vfeFrameSkipPeriod = 31;
+
+ memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath));
+ memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath));
+
+ ctrl->encPath.whichOutputPath = 1;
+ ctrl->encPath.cbcrStatusBit = 5;
+ ctrl->viewPath.whichOutputPath = 0;
+ ctrl->viewPath.cbcrStatusBit = 7;
+
+ ctrl->vfeTestGenStartFlag = FALSE;
+
+ /* default to bank 0. */
+ ctrl->vfeLaBankSel = 0;
+
+ /* default to bank 0 for all channels. */
+ memset(&ctrl->vfeGammaLutSel, 0, sizeof(ctrl->vfeGammaLutSel));
+
+ /* Stats control variables. */
+ memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl));
+ memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl));
+ vfe_set_stats_pingpong_address(&ctrl->afStatsControl,
+ &ctrl->awbStatsControl);
+}
+
+void vfe_reset(void)
+{
+ vfe_reset_internal_variables();
+
+ ctrl->vfeImaskLocal.resetAckIrq = TRUE;
+ ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal);
+
+ /* disable all interrupts. */
+ writel(VFE_DISABLE_ALL_IRQS,
+ ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK);
+
+ /* clear all pending interrupts*/
+ writel(VFE_CLEAR_ALL_IRQS,
+ ctrl->vfebase + VFE_IRQ_CLEAR);
+
+ /* enable reset_ack interrupt. */
+ writel(ctrl->vfeImaskPacked,
+ ctrl->vfebase + VFE_IRQ_MASK);
+
+ writel(VFE_RESET_UPON_RESET_CMD,
+ ctrl->vfebase + VFE_GLOBAL_RESET_CMD);
+}
diff --git a/drivers/staging/dream/camera/msm_vfe8x_proc.h b/drivers/staging/dream/camera/msm_vfe8x_proc.h
new file mode 100644
index 00000000000..91828569a4d
--- /dev/null
+++ b/drivers/staging/dream/camera/msm_vfe8x_proc.h
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#ifndef __MSM_VFE8X_REG_H__
+#define __MSM_VFE8X_REG_H__
+
+#include <mach/msm_iomap.h>
+#include <mach/camera.h>
+#include "msm_vfe8x.h"
+
+/* at start of camif, bit 1:0 = 0x01:enable
+ * image data capture at frame boundary. */
+#define CAMIF_COMMAND_START 0x00000005
+
+/* bit 2= 0x1:clear the CAMIF_STATUS register
+ * value. */
+#define CAMIF_COMMAND_CLEAR 0x00000004
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x10:
+ * disable image data capture immediately. */
+#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002
+
+/* at stop of vfe pipeline, for now it is assumed
+ * that camif will stop at any time. Bit 1:0 = 0x00:
+ * disable image data capture at frame boundary */
+#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000
+
+/* to halt axi bridge */
+#define AXI_HALT 0x00000001
+
+/* clear the halt bit. */
+#define AXI_HALT_CLEAR 0x00000000
+
+/* reset the pipeline when stop command is issued.
+ * (without reset the register.) bit 26-31 = 0,
+ * domain reset, bit 0-9 = 1 for module reset, except
+ * register module. */
+#define VFE_RESET_UPON_STOP_CMD 0x000003ef
+
+/* reset the pipeline when reset command.
+ * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */
+#define VFE_RESET_UPON_RESET_CMD 0x000003ff
+
+/* bit 5 is for axi status idle or busy.
+ * 1 = halted, 0 = busy */
+#define AXI_STATUS_BUSY_MASK 0x00000020
+
+/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present
+ * for frame done interrupt */
+#define VFE_COMP_IRQ_BOTH_Y_CBCR 3
+
+/* bit 1 = 1, only cbcr irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_CBCR_ONLY 2
+
+/* bit 0 = 1, only y irq triggers frame done interrupt */
+#define VFE_COMP_IRQ_Y_ONLY 1
+
+/* bit 0 = 1, PM go; bit1 = 1, PM stop */
+#define VFE_PERFORMANCE_MONITOR_GO 0x00000001
+#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002
+
+/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */
+#define VFE_TEST_GEN_GO 0x00000001
+#define VFE_TEST_GEN_STOP 0x00000002
+
+/* the chroma is assumed to be interpolated between
+ * the luma samples. JPEG 4:2:2 */
+#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+
+/* constants for irq registers */
+#define VFE_DISABLE_ALL_IRQS 0
+/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+/* imask for while waiting for stop ack, driver has already
+ * requested stop, waiting for reset irq,
+ * bit 29,28,27,26 for async timer, bit 9 for reset */
+#define VFE_IMASK_WHILE_STOPPING 0x3c000200
+
+/* when normal case, don't want to block error status.
+ * bit 0,6,20,21,22,30,31 */
+#define VFE_IMASK_ERROR_ONLY 0xC0700041
+#define VFE_REG_UPDATE_TRIGGER 1
+#define VFE_PM_BUF_MAX_CNT_MASK 0xFF
+#define VFE_DMI_CFG_DEFAULT 0x00000100
+#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32
+#define VFE_AF_PINGPONG_STATUS_BIT 0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT 0x200
+
+/* VFE I/O registers */
+enum {
+ VFE_HW_VERSION = 0x00000000,
+ VFE_GLOBAL_RESET_CMD = 0x00000004,
+ VFE_MODULE_RESET = 0x00000008,
+ VFE_CGC_OVERRIDE = 0x0000000C,
+ VFE_MODULE_CFG = 0x00000010,
+ VFE_CFG = 0x00000014,
+ VFE_IRQ_MASK = 0x00000018,
+ VFE_IRQ_CLEAR = 0x0000001C,
+VFE_IRQ_STATUS = 0x00000020,
+VFE_IRQ_COMPOSITE_MASK = 0x00000024,
+VFE_BUS_CMD = 0x00000028,
+VFE_BUS_CFG = 0x0000002C,
+VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030,
+VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034,
+VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038,
+VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C,
+VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040,
+VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044,
+VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048,
+VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C,
+VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050,
+VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054,
+VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058,
+VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C,
+VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060,
+VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064,
+VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068,
+VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C,
+VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070,
+VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074,
+VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078,
+VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C,
+VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080,
+VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084,
+VFE_BUS_STATS_WR_PRIORITY = 0x00000088,
+VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C,
+VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090,
+VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094,
+VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098,
+VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C,
+VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0,
+VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4,
+VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8,
+VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC,
+VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0,
+VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4,
+VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8,
+VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC,
+VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0,
+VFE_BUS_PINGPONG_STATUS = 0x000000C4,
+VFE_BUS_PM_CMD = 0x000000C8,
+VFE_BUS_PM_CFG = 0x000000CC,
+VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0,
+VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4,
+VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8,
+VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC,
+VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0,
+VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4,
+VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8,
+VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC,
+VFE_BUS_MISR_CFG = 0x000000F4,
+VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8,
+VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC,
+VFE_BUS_MISR_RD_VAL = 0x00000100,
+VFE_AXI_CMD = 0x00000104,
+VFE_AXI_CFG = 0x00000108,
+VFE_AXI_STATUS = 0x0000010C,
+CAMIF_COMMAND = 0x00000110,
+CAMIF_CONFIG = 0x00000114,
+CAMIF_EFS_CONFIG = 0x00000118,
+CAMIF_FRAME_CONFIG = 0x0000011C,
+CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120,
+CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124,
+CAMIF_SUBSAMPLE1_CONFIG = 0x00000128,
+CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C,
+CAMIF_EPOCH_IRQ = 0x00000130,
+CAMIF_STATUS = 0x00000134,
+CAMIF_MISR = 0x00000138,
+VFE_SYNC_TIMER_CMD = 0x0000013C,
+VFE_SYNC_TIMER0_LINE_START = 0x00000140,
+VFE_SYNC_TIMER0_PIXEL_START = 0x00000144,
+VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148,
+VFE_SYNC_TIMER1_LINE_START = 0x0000014C,
+VFE_SYNC_TIMER1_PIXEL_START = 0x00000150,
+VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154,
+VFE_SYNC_TIMER2_LINE_START = 0x00000158,
+VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C,
+VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160,
+VFE_SYNC_TIMER_POLARITY = 0x00000164,
+VFE_ASYNC_TIMER_CMD = 0x00000168,
+VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C,
+VFE_ASYNC_TIMER0_CFG_1 = 0x00000170,
+VFE_ASYNC_TIMER1_CFG_0 = 0x00000174,
+VFE_ASYNC_TIMER1_CFG_1 = 0x00000178,
+VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C,
+VFE_ASYNC_TIMER2_CFG_1 = 0x00000180,
+VFE_ASYNC_TIMER3_CFG_0 = 0x00000184,
+VFE_ASYNC_TIMER3_CFG_1 = 0x00000188,
+VFE_TIMER_SEL = 0x0000018C,
+VFE_REG_UPDATE_CMD = 0x00000190,
+VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194,
+VFE_BLACK_EVEN_ODD_VALUE = 0x00000198,
+VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C,
+VFE_BLACK_ODD_ODD_VALUE = 0x000001A0,
+VFE_ROLLOFF_CFG_0 = 0x000001A4,
+VFE_ROLLOFF_CFG_1 = 0x000001A8,
+VFE_ROLLOFF_CFG_2 = 0x000001AC,
+VFE_DEMUX_CFG = 0x000001B0,
+VFE_DEMUX_GAIN_0 = 0x000001B4,
+VFE_DEMUX_GAIN_1 = 0x000001B8,
+VFE_DEMUX_EVEN_CFG = 0x000001BC,
+VFE_DEMUX_ODD_CFG = 0x000001C0,
+VFE_DEMOSAIC_CFG = 0x000001C4,
+VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8,
+VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC,
+VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0,
+VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4,
+VFE_DEMOSAIC_STATUS = 0x000001D8,
+VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC,
+VFE_CROP_WIDTH_CFG = 0x000001E0,
+VFE_CROP_HEIGHT_CFG = 0x000001E4,
+VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8,
+VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC,
+VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0,
+VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4,
+VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8,
+VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC,
+VFE_COLOR_CORRECT_COEFF_6 = 0x00000200,
+VFE_COLOR_CORRECT_COEFF_7 = 0x00000204,
+VFE_COLOR_CORRECT_COEFF_8 = 0x00000208,
+VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C,
+VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210,
+VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214,
+VFE_COLOR_CORRECT_COEFF_Q = 0x00000218,
+VFE_LA_CFG = 0x0000021C,
+VFE_LUT_BANK_SEL = 0x00000220,
+VFE_CHROMA_ENHAN_A = 0x00000224,
+VFE_CHROMA_ENHAN_B = 0x00000228,
+VFE_CHROMA_ENHAN_C = 0x0000022C,
+VFE_CHROMA_ENHAN_D = 0x00000230,
+VFE_CHROMA_ENHAN_K = 0x00000234,
+VFE_COLOR_CONVERT_COEFF_0 = 0x00000238,
+VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C,
+VFE_COLOR_CONVERT_COEFF_2 = 0x00000240,
+VFE_COLOR_CONVERT_OFFSET = 0x00000244,
+VFE_ASF_CFG = 0x00000248,
+VFE_ASF_SHARP_CFG_0 = 0x0000024C,
+VFE_ASF_SHARP_CFG_1 = 0x00000250,
+VFE_ASF_SHARP_COEFF_0 = 0x00000254,
+VFE_ASF_SHARP_COEFF_1 = 0x00000258,
+VFE_ASF_SHARP_COEFF_2 = 0x0000025C,
+VFE_ASF_SHARP_COEFF_3 = 0x00000260,
+VFE_ASF_MAX_EDGE = 0x00000264,
+VFE_ASF_CROP_WIDTH_CFG = 0x00000268,
+VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C,
+VFE_SCALE_CFG = 0x00000270,
+VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274,
+VFE_SCALE_H_PHASE_CFG = 0x00000278,
+VFE_SCALE_H_STRIPE_CFG = 0x0000027C,
+VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280,
+VFE_SCALE_V_PHASE_CFG = 0x00000284,
+VFE_SCALE_V_STRIPE_CFG = 0x00000288,
+VFE_SCALE_Y_CFG = 0x0000028C,
+VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290,
+VFE_SCALE_Y_H_PHASE_CFG = 0x00000294,
+VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298,
+VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C,
+VFE_SCALE_CBCR_CFG = 0x000002A0,
+VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4,
+VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8,
+VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC,
+VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0,
+VFE_WB_CFG = 0x000002B4,
+VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8,
+VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC,
+VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0,
+VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4,
+VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8,
+VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC,
+VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0,
+VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4,
+VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8,
+VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC,
+VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0,
+VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4,
+VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8,
+VFE_CLAMP_MAX_CFG = 0x000002EC,
+VFE_CLAMP_MIN_CFG = 0x000002F0,
+VFE_STATS_CMD = 0x000002F4,
+VFE_STATS_AF_CFG = 0x000002F8,
+VFE_STATS_AF_DIM = 0x000002FC,
+VFE_STATS_AF_GRID_0 = 0x00000300,
+VFE_STATS_AF_GRID_1 = 0x00000304,
+VFE_STATS_AF_GRID_2 = 0x00000308,
+VFE_STATS_AF_GRID_3 = 0x0000030C,
+VFE_STATS_AF_HEADER = 0x00000310,
+VFE_STATS_AF_COEF0 = 0x00000314,
+VFE_STATS_AF_COEF1 = 0x00000318,
+VFE_STATS_AWBAE_CFG = 0x0000031C,
+VFE_STATS_AXW_HEADER = 0x00000320,
+VFE_STATS_AWB_MCFG = 0x00000324,
+VFE_STATS_AWB_CCFG1 = 0x00000328,
+VFE_STATS_AWB_CCFG2 = 0x0000032C,
+VFE_STATS_HIST_HEADER = 0x00000330,
+VFE_STATS_HIST_INNER_OFFSET = 0x00000334,
+VFE_STATS_HIST_INNER_DIM = 0x00000338,
+VFE_STATS_FRAME_SIZE = 0x0000033C,
+VFE_DMI_CFG = 0x00000340,
+VFE_DMI_ADDR = 0x00000344,
+VFE_DMI_DATA_HI = 0x00000348,
+VFE_DMI_DATA_LO = 0x0000034C,
+VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350,
+VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354,
+VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358,
+VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C,
+VFE_TESTBUS_SEL = 0x00000360,
+VFE_TESTGEN_CFG = 0x00000364,
+VFE_SW_TESTGEN_CMD = 0x00000368,
+VFE_HW_TESTGEN_CMD = 0x0000036C,
+VFE_HW_TESTGEN_CFG = 0x00000370,
+VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374,
+VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378,
+VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C,
+VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380,
+VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384,
+VFE_HW_TESTGEN_HBI_CFG = 0x00000388,
+VFE_HW_TESTGEN_VBL_CFG = 0x0000038C,
+VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390,
+VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394,
+VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398,
+VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C,
+VFE_SPARE = 0x000003A0,
+};
+
+#define ping 0x0
+#define pong 0x1
+
+struct vfe_bus_cfg_data {
+ boolean stripeRdPathEn;
+ boolean encYWrPathEn;
+ boolean encCbcrWrPathEn;
+ boolean viewYWrPathEn;
+ boolean viewCbcrWrPathEn;
+ enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize;
+ enum VFE_RAW_WR_PATH_SEL rawWritePathSelect;
+};
+
+struct vfe_camif_cfg_data {
+ boolean camif2OutputEnable;
+ boolean camif2BusEnable;
+ struct vfe_cmds_camif_cfg camifCfgFromCmd;
+};
+
+struct vfe_irq_composite_mask_config {
+ uint8_t encIrqComMask;
+ uint8_t viewIrqComMask;
+ uint8_t ceDoneSel;
+};
+
+/* define a structure for each output path.*/
+struct vfe_output_path {
+ uint32_t addressBuffer[8];
+ uint16_t fragIndex;
+ boolean hwCurrentFlag;
+ uint8_t *hwRegPingAddress;
+ uint8_t *hwRegPongAddress;
+};
+
+struct vfe_output_path_combo {
+ boolean whichOutputPath;
+ boolean pathEnabled;
+ boolean multiFrag;
+ uint8_t fragCount;
+ boolean ackPending;
+ uint8_t currentFrame;
+ uint32_t nextFrameAddrBuf[8];
+ struct vfe_output_path yPath;
+ struct vfe_output_path cbcrPath;
+ uint8_t snapshotPendingCount;
+ boolean pmEnabled;
+ uint8_t cbcrStatusBit;
+};
+
+struct vfe_stats_control {
+ boolean ackPending;
+ uint32_t addressBuffer[2];
+ uint32_t nextFrameAddrBuf;
+ boolean pingPongStatus;
+ uint8_t *hwRegPingAddress;
+ uint8_t *hwRegPongAddress;
+ uint32_t droppedStatsFrameCount;
+ uint32_t bufToRender;
+};
+
+struct vfe_gamma_lut_sel {
+ boolean ch0BankSelect;
+ boolean ch1BankSelect;
+ boolean ch2BankSelect;
+};
+
+struct vfe_interrupt_mask {
+ boolean camifErrorIrq;
+ boolean camifSofIrq;
+ boolean camifEolIrq;
+ boolean camifEofIrq;
+ boolean camifEpoch1Irq;
+ boolean camifEpoch2Irq;
+ boolean camifOverflowIrq;
+ boolean ceIrq;
+ boolean regUpdateIrq;
+ boolean resetAckIrq;
+ boolean encYPingpongIrq;
+ boolean encCbcrPingpongIrq;
+ boolean viewYPingpongIrq;
+ boolean viewCbcrPingpongIrq;
+ boolean rdPingpongIrq;
+ boolean afPingpongIrq;
+ boolean awbPingpongIrq;
+ boolean histPingpongIrq;
+ boolean encIrq;
+ boolean viewIrq;
+ boolean busOverflowIrq;
+ boolean afOverflowIrq;
+ boolean awbOverflowIrq;
+ boolean syncTimer0Irq;
+ boolean syncTimer1Irq;
+ boolean syncTimer2Irq;
+ boolean asyncTimer0Irq;
+ boolean asyncTimer1Irq;
+ boolean asyncTimer2Irq;
+ boolean asyncTimer3Irq;
+ boolean axiErrorIrq;
+ boolean violationIrq;
+};
+
+enum vfe_interrupt_name {
+ CAMIF_ERROR_IRQ,
+ CAMIF_SOF_IRQ,
+ CAMIF_EOL_IRQ,
+ CAMIF_EOF_IRQ,
+ CAMIF_EPOCH1_IRQ,
+ CAMIF_EPOCH2_IRQ,
+ CAMIF_OVERFLOW_IRQ,
+ CE_IRQ,
+ REG_UPDATE_IRQ,
+ RESET_ACK_IRQ,
+ ENC_Y_PINGPONG_IRQ,
+ ENC_CBCR_PINGPONG_IRQ,
+ VIEW_Y_PINGPONG_IRQ,
+ VIEW_CBCR_PINGPONG_IRQ,
+ RD_PINGPONG_IRQ,
+ AF_PINGPONG_IRQ,
+ AWB_PINGPONG_IRQ,
+ HIST_PINGPONG_IRQ,
+ ENC_IRQ,
+ VIEW_IRQ,
+ BUS_OVERFLOW_IRQ,
+ AF_OVERFLOW_IRQ,
+ AWB_OVERFLOW_IRQ,
+ SYNC_TIMER0_IRQ,
+ SYNC_TIMER1_IRQ,
+ SYNC_TIMER2_IRQ,
+ ASYNC_TIMER0_IRQ,
+ ASYNC_TIMER1_IRQ,
+ ASYNC_TIMER2_IRQ,
+ ASYNC_TIMER3_IRQ,
+ AXI_ERROR_IRQ,
+ VIOLATION_IRQ
+};
+
+enum VFE_DMI_RAM_SEL {
+ NO_MEM_SELECTED = 0,
+ ROLLOFF_RAM = 0x1,
+ RGBLUT_RAM_CH0_BANK0 = 0x2,
+ RGBLUT_RAM_CH0_BANK1 = 0x3,
+ RGBLUT_RAM_CH1_BANK0 = 0x4,
+ RGBLUT_RAM_CH1_BANK1 = 0x5,
+ RGBLUT_RAM_CH2_BANK0 = 0x6,
+ RGBLUT_RAM_CH2_BANK1 = 0x7,
+ STATS_HIST_CB_EVEN_RAM = 0x8,
+ STATS_HIST_CB_ODD_RAM = 0x9,
+ STATS_HIST_CR_EVEN_RAM = 0xa,
+ STATS_HIST_CR_ODD_RAM = 0xb,
+ RGBLUT_CHX_BANK0 = 0xc,
+ RGBLUT_CHX_BANK1 = 0xd,
+ LUMA_ADAPT_LUT_RAM_BANK0 = 0xe,
+ LUMA_ADAPT_LUT_RAM_BANK1 = 0xf
+};
+
+struct vfe_module_enable {
+ boolean blackLevelCorrectionEnable;
+ boolean lensRollOffEnable;
+ boolean demuxEnable;
+ boolean chromaUpsampleEnable;
+ boolean demosaicEnable;
+ boolean statsEnable;
+ boolean cropEnable;
+ boolean mainScalerEnable;
+ boolean whiteBalanceEnable;
+ boolean colorCorrectionEnable;
+ boolean yHistEnable;
+ boolean skinToneEnable;
+ boolean lumaAdaptationEnable;
+ boolean rgbLUTEnable;
+ boolean chromaEnhanEnable;
+ boolean asfEnable;
+ boolean chromaSuppressionEnable;
+ boolean chromaSubsampleEnable;
+ boolean scaler2YEnable;
+ boolean scaler2CbcrEnable;
+};
+
+struct vfe_bus_cmd_data {
+ boolean stripeReload;
+ boolean busPingpongReload;
+ boolean statsPingpongReload;
+};
+
+struct vfe_stats_cmd_data {
+ boolean autoFocusEnable;
+ boolean axwEnable;
+ boolean histEnable;
+ boolean clearHistEnable;
+ boolean histAutoClearEnable;
+ boolean colorConversionEnable;
+};
+
+struct vfe_hw_ver {
+ uint32_t minorVersion:8;
+ uint32_t majorVersion:8;
+ uint32_t coreVersion:4;
+ uint32_t /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_cfg {
+ uint32_t pixelPattern:3;
+ uint32_t /* reserved */ : 13;
+ uint32_t inputSource:2;
+ uint32_t /* reserved */ : 14;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_buscmd {
+ uint32_t stripeReload:1;
+ uint32_t /* reserved */ : 3;
+ uint32_t busPingpongReload:1;
+ uint32_t statsPingpongReload:1;
+ uint32_t /* reserved */ : 26;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_Irq_Composite_MaskType {
+ uint32_t encIrqComMaskBits:2;
+ uint32_t viewIrqComMaskBits:2;
+ uint32_t ceDoneSelBits:5;
+ uint32_t /* reserved */ : 23;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_mod_enable {
+ uint32_t blackLevelCorrectionEnable:1;
+ uint32_t lensRollOffEnable:1;
+ uint32_t demuxEnable:1;
+ uint32_t chromaUpsampleEnable:1;
+ uint32_t demosaicEnable:1;
+ uint32_t statsEnable:1;
+ uint32_t cropEnable:1;
+ uint32_t mainScalerEnable:1;
+ uint32_t whiteBalanceEnable:1;
+ uint32_t colorCorrectionEnable:1;
+ uint32_t yHistEnable:1;
+ uint32_t skinToneEnable:1;
+ uint32_t lumaAdaptationEnable:1;
+ uint32_t rgbLUTEnable:1;
+ uint32_t chromaEnhanEnable:1;
+ uint32_t asfEnable:1;
+ uint32_t chromaSuppressionEnable:1;
+ uint32_t chromaSubsampleEnable:1;
+ uint32_t scaler2YEnable:1;
+ uint32_t scaler2CbcrEnable:1;
+ uint32_t /* reserved */ : 14;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_irqenable {
+ uint32_t camifErrorIrq:1;
+ uint32_t camifSofIrq:1;
+ uint32_t camifEolIrq:1;
+ uint32_t camifEofIrq:1;
+ uint32_t camifEpoch1Irq:1;
+ uint32_t camifEpoch2Irq:1;
+ uint32_t camifOverflowIrq:1;
+ uint32_t ceIrq:1;
+ uint32_t regUpdateIrq:1;
+ uint32_t resetAckIrq:1;
+ uint32_t encYPingpongIrq:1;
+ uint32_t encCbcrPingpongIrq:1;
+ uint32_t viewYPingpongIrq:1;
+ uint32_t viewCbcrPingpongIrq:1;
+ uint32_t rdPingpongIrq:1;
+ uint32_t afPingpongIrq:1;
+ uint32_t awbPingpongIrq:1;
+ uint32_t histPingpongIrq:1;
+ uint32_t encIrq:1;
+ uint32_t viewIrq:1;
+ uint32_t busOverflowIrq:1;
+ uint32_t afOverflowIrq:1;
+ uint32_t awbOverflowIrq:1;
+ uint32_t syncTimer0Irq:1;
+ uint32_t syncTimer1Irq:1;
+ uint32_t syncTimer2Irq:1;
+ uint32_t asyncTimer0Irq:1;
+ uint32_t asyncTimer1Irq:1;
+ uint32_t asyncTimer2Irq:1;
+ uint32_t asyncTimer3Irq:1;
+ uint32_t axiErrorIrq:1;
+ uint32_t violationIrq:1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_upsample_cfg {
+ uint32_t chromaCositingForYCbCrInputs:1;
+ uint32_t /* reserved */ : 31;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_CAMIFConfigType {
+ /* CAMIF Config */
+ uint32_t /* reserved */ : 1;
+ uint32_t VSyncEdge:1;
+ uint32_t HSyncEdge:1;
+ uint32_t syncMode:2;
+ uint32_t vfeSubsampleEnable:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t busSubsampleEnable:1;
+ uint32_t camif2vfeEnable:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t camif2busEnable:1;
+ uint32_t irqSubsampleEnable:1;
+ uint32_t binningEnable:1;
+ uint32_t /* reserved */ : 18;
+ uint32_t misrEnable:1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camifcfg {
+ /* EFS_Config */
+ uint32_t efsEndOfLine:8;
+ uint32_t efsStartOfLine:8;
+ uint32_t efsEndOfFrame:8;
+ uint32_t efsStartOfFrame:8;
+ /* Frame Config */
+ uint32_t frameConfigPixelsPerLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t frameConfigLinesPerFrame:14;
+ uint32_t /* reserved */ : 2;
+ /* Window Width Config */
+ uint32_t windowWidthCfgLastPixel:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t windowWidthCfgFirstPixel:14;
+ uint32_t /* reserved */ : 2;
+ /* Window Height Config */
+ uint32_t windowHeightCfglastLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t windowHeightCfgfirstLine:14;
+ uint32_t /* reserved */ : 2;
+ /* Subsample 1 Config */
+ uint32_t subsample1CfgPixelSkip:16;
+ uint32_t subsample1CfgLineSkip:16;
+ /* Subsample 2 Config */
+ uint32_t subsample2CfgFrameSkip:4;
+ uint32_t subsample2CfgFrameSkipMode:1;
+ uint32_t subsample2CfgPixelSkipWrap:1;
+ uint32_t /* reserved */ : 26;
+ /* Epoch Interrupt */
+ uint32_t epoch1Line:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t epoch2Line:14;
+ uint32_t /* reserved */ : 2;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camifframe_update {
+ uint32_t pixelsPerLine:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t linesPerFrame:14;
+ uint32_t /* reserved */ : 2;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_axi_bus_cfg {
+ uint32_t stripeRdPathEn:1;
+ uint32_t /* reserved */ : 3;
+ uint32_t encYWrPathEn:1;
+ uint32_t encCbcrWrPathEn:1;
+ uint32_t viewYWrPathEn:1;
+ uint32_t viewCbcrWrPathEn:1;
+ uint32_t rawPixelDataSize:2;
+ uint32_t rawWritePathSelect:2;
+ uint32_t /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_axi_out_cfg {
+ uint32_t out2YPingAddr:32;
+ uint32_t out2YPongAddr:32;
+ uint32_t out2YImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2YImageWidthin64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out2YBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out2YNumRows:12;
+ uint32_t out2YRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2CbcrPingAddr:32;
+ uint32_t out2CbcrPongAddr:32;
+ uint32_t out2CbcrImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out2CbcrImageWidthIn64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out2CbcrBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out2CbcrNumRows:12;
+ uint32_t out2CbcrRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1YPingAddr:32;
+ uint32_t out1YPongAddr:32;
+ uint32_t out1YImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1YImageWidthin64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out1YBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out1YNumRows:12;
+ uint32_t out1YRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1CbcrPingAddr:32;
+ uint32_t out1CbcrPongAddr:32;
+ uint32_t out1CbcrImageHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t out1CbcrImageWidthIn64bit:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t out1CbcrBurstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t out1CbcrNumRows:12;
+ uint32_t out1CbcrRowIncrementIn64bit:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_output_clamp_cfg {
+ /* Output Clamp Maximums */
+ uint32_t yChanMax:8;
+ uint32_t cbChanMax:8;
+ uint32_t crChanMax:8;
+ uint32_t /* reserved */ : 8;
+ /* Output Clamp Minimums */
+ uint32_t yChanMin:8;
+ uint32_t cbChanMin:8;
+ uint32_t crChanMin:8;
+ uint32_t /* reserved */ : 8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_fov_crop_cfg {
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstPixel:12;
+ uint32_t /* reserved */ : 4;
+
+ /* FOV Corp, Part 2 */
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_FRAME_SKIP_UpdateCmdType {
+ uint32_t yPattern:32;
+ uint32_t cbcrPattern:32;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_frame_skip_cfg {
+ /* Frame Drop Enc (output2) */
+ uint32_t output2YPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output2CbCrPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output2YPattern:32;
+ uint32_t output2CbCrPattern:32;
+ /* Frame Drop View (output1) */
+ uint32_t output1YPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output1CbCrPeriod:5;
+ uint32_t /* reserved */ : 27;
+ uint32_t output1YPattern:32;
+ uint32_t output1CbCrPattern:32;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_main_scaler_cfg {
+ /* Scaler Enable Config */
+ uint32_t hEnable:1;
+ uint32_t vEnable:1;
+ uint32_t /* reserved */ : 30;
+ /* Scale H Image Size Config */
+ uint32_t inWidth:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outWidth:12;
+ uint32_t /* reserved */ : 4;
+ /* Scale H Phase Config */
+ uint32_t horizPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t horizInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scale H Stripe Config */
+ uint32_t horizMNInit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t horizPhaseInit:15;
+ uint32_t /* reserved */ : 1;
+ /* Scale V Image Size Config */
+ uint32_t inHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outHeight:12;
+ uint32_t /* reserved */ : 4;
+ /* Scale V Phase Config */
+ uint32_t vertPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t vertInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scale V Stripe Config */
+ uint32_t vertMNInit:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t vertPhaseInit:15;
+ uint32_t /* reserved */ : 1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_scaler2_cfg {
+ /* Scaler Enable Config */
+ uint32_t hEnable:1;
+ uint32_t vEnable:1;
+ uint32_t /* reserved */ : 30;
+ /* Scaler H Image Size Config */
+ uint32_t inWidth:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outWidth:12;
+ uint32_t /* reserved */ : 4;
+ /* Scaler H Phase Config */
+ uint32_t horizPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t horizInterResolution:2;
+ uint32_t /* reserved */ : 10;
+ /* Scaler V Image Size Config */
+ uint32_t inHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t outHeight:12;
+ uint32_t /* reserved */ : 4;
+ /* Scaler V Phase Config */
+ uint32_t vertPhaseMult:18;
+ uint32_t /* reserved */ : 2;
+ uint32_t vertInterResolution:2;
+ uint32_t /* reserved */ : 10;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_rolloff_cfg {
+ /* Rolloff 0 Config */
+ uint32_t gridWidth:9;
+ uint32_t gridHeight:9;
+ uint32_t yDelta:9;
+ uint32_t /* reserved */ : 5;
+ /* Rolloff 1 Config*/
+ uint32_t gridX:4;
+ uint32_t gridY:4;
+ uint32_t pixelX:9;
+ uint32_t /* reserved */ : 3;
+ uint32_t pixelY:9;
+ uint32_t /* reserved */ : 3;
+ /* Rolloff 2 Config */
+ uint32_t yDeltaAccum:12;
+ uint32_t /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asf_update {
+ /* ASF Config Command */
+ uint32_t smoothEnable:1;
+ uint32_t sharpMode:2;
+ uint32_t /* reserved */ : 1;
+ uint32_t smoothCoeff1:4;
+ uint32_t smoothCoeff0:8;
+ uint32_t pipeFlushCount:12;
+ uint32_t pipeFlushOvd:1;
+ uint32_t flushHaltOvd:1;
+ uint32_t cropEnable:1;
+ uint32_t /* reserved */ : 1;
+ /* Sharpening Config 0 */
+ uint32_t sharpThresholdE1:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t sharpDegreeK1:5;
+ uint32_t /* reserved */ : 3;
+ uint32_t sharpDegreeK2:5;
+ uint32_t /* reserved */ : 3;
+ uint32_t normalizeFactor:7;
+ uint32_t /* reserved */ : 1;
+ /* Sharpening Config 1 */
+ uint32_t sharpThresholdE2:8;
+ uint32_t sharpThresholdE3:8;
+ uint32_t sharpThresholdE4:8;
+ uint32_t sharpThresholdE5:8;
+ /* Sharpening Coefficients 0 */
+ uint32_t F1Coeff0:6;
+ uint32_t F1Coeff1:6;
+ uint32_t F1Coeff2:6;
+ uint32_t F1Coeff3:6;
+ uint32_t F1Coeff4:6;
+ uint32_t /* reserved */ : 2;
+ /* Sharpening Coefficients 1 */
+ uint32_t F1Coeff5:6;
+ uint32_t F1Coeff6:6;
+ uint32_t F1Coeff7:6;
+ uint32_t F1Coeff8:7;
+ uint32_t /* reserved */ : 7;
+ /* Sharpening Coefficients 2 */
+ uint32_t F2Coeff0:6;
+ uint32_t F2Coeff1:6;
+ uint32_t F2Coeff2:6;
+ uint32_t F2Coeff3:6;
+ uint32_t F2Coeff4:6;
+ uint32_t /* reserved */ : 2;
+ /* Sharpening Coefficients 3 */
+ uint32_t F2Coeff5:6;
+ uint32_t F2Coeff6:6;
+ uint32_t F2Coeff7:6;
+ uint32_t F2Coeff8:7;
+ uint32_t /* reserved */ : 7;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asfcrop_cfg {
+ /* ASF Crop Width Config */
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstPixel:12;
+ uint32_t /* reserved */ : 4;
+ /* ASP Crop Height Config */
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t firstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chroma_suppress_cfg {
+ /* Chroma Suppress 0 Config */
+ uint32_t m1:8;
+ uint32_t m3:8;
+ uint32_t n1:3;
+ uint32_t /* reserved */ : 1;
+ uint32_t n3:3;
+ uint32_t /* reserved */ : 9;
+ /* Chroma Suppress 1 Config */
+ uint32_t mm1:8;
+ uint32_t nn1:3;
+ uint32_t /* reserved */ : 21;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chromasubsample_cfg {
+ /* Chroma Subsample Selection */
+ uint32_t hCositedPhase:1;
+ uint32_t vCositedPhase:1;
+ uint32_t hCosited:1;
+ uint32_t vCosited:1;
+ uint32_t hsubSampleEnable:1;
+ uint32_t vsubSampleEnable:1;
+ uint32_t cropEnable:1;
+ uint32_t /* reserved */ : 25;
+ uint32_t cropWidthLastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropWidthFirstPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropHeightLastLine:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t cropHeightFirstLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_blacklevel_cfg {
+ /* Black Even-Even Value Config */
+ uint32_t evenEvenAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Even-Odd Value Config */
+ uint32_t evenOddAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Odd-Even Value Config */
+ uint32_t oddEvenAdjustment:9;
+ uint32_t /* reserved */ : 23;
+ /* Black Odd-Odd Value Config */
+ uint32_t oddOddAdjustment:9;
+ uint32_t /* reserved */ : 23;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demux_cfg {
+ /* Demux Gain 0 Config */
+ uint32_t ch0EvenGain:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t ch0OddGain:10;
+ uint32_t /* reserved */ : 6;
+ /* Demux Gain 1 Config */
+ uint32_t ch1Gain:10;
+ uint32_t /* reserved */ : 6;
+ uint32_t ch2Gain:10;
+ uint32_t /* reserved */ : 6;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_bps_info {
+ uint32_t greenBadPixelCount:8;
+ uint32_t /* reserved */ : 8;
+ uint32_t RedBlueBadPixelCount:8;
+ uint32_t /* reserved */ : 8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_cfg {
+ /* Demosaic Config */
+ uint32_t abfEnable:1;
+ uint32_t badPixelCorrEnable:1;
+ uint32_t forceAbfOn:1;
+ uint32_t /* reserved */ : 1;
+ uint32_t abfShift:4;
+ uint32_t fminThreshold:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t fmaxThreshold:7;
+ uint32_t /* reserved */ : 5;
+ uint32_t slopeShift:3;
+ uint32_t /* reserved */ : 1;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_bpc_cfg {
+ /* Demosaic BPC Config 0 */
+ uint32_t blueDiffThreshold:12;
+ uint32_t redDiffThreshold:12;
+ uint32_t /* reserved */ : 8;
+ /* Demosaic BPC Config 1 */
+ uint32_t greenDiffThreshold:12;
+ uint32_t /* reserved */ : 20;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_demosaic_abf_cfg {
+ /* Demosaic ABF Config 0 */
+ uint32_t lpThreshold:10;
+ uint32_t /* reserved */ : 22;
+ /* Demosaic ABF Config 1 */
+ uint32_t ratio:4;
+ uint32_t minValue:10;
+ uint32_t /* reserved */ : 2;
+ uint32_t maxValue:10;
+ uint32_t /* reserved */ : 6;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_color_correction_cfg {
+ /* Color Corr. Coefficient 0 Config */
+ uint32_t c0:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 1 Config */
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 2 Config */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 3 Config */
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 4 Config */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 5 Config */
+ uint32_t c5:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 6 Config */
+ uint32_t c6:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 7 Config */
+ uint32_t c7:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Coefficient 8 Config */
+ uint32_t c8:12;
+ uint32_t /* reserved */ : 20;
+ /* Color Corr. Offset 0 Config */
+ uint32_t k0:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Offset 1 Config */
+ uint32_t k1:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Offset 2 Config */
+ uint32_t k2:11;
+ uint32_t /* reserved */ : 21;
+ /* Color Corr. Coefficient Q Config */
+ uint32_t coefQFactor:2;
+ uint32_t /* reserved */ : 30;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_LumaAdaptation_ConfigCmdType {
+ /* LA Config */
+ uint32_t lutBankSelect:1;
+ uint32_t /* reserved */ : 31;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_wb_cfg {
+ /* WB Config */
+ uint32_t ch0Gain:9;
+ uint32_t ch1Gain:9;
+ uint32_t ch2Gain:9;
+ uint32_t /* reserved */ : 5;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_GammaLutSelect_ConfigCmdType {
+ /* LUT Bank Select Config */
+ uint32_t ch0BankSelect:1;
+ uint32_t ch1BankSelect:1;
+ uint32_t ch2BankSelect:1;
+ uint32_t /* reserved */ : 29;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_chroma_enhance_cfg {
+ /* Chroma Enhance A Config */
+ uint32_t ap:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t am:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance B Config */
+ uint32_t bp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t bm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance C Config */
+ uint32_t cp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t cm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance D Config */
+ uint32_t dp:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t dm:11;
+ uint32_t /* reserved */ : 5;
+ /* Chroma Enhance K Config */
+ uint32_t kcb:11;
+ uint32_t /* reserved */ : 5;
+ uint32_t kcr:11;
+ uint32_t /* reserved */ : 5;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_color_convert_cfg {
+ /* Conversion Coefficient 0 */
+ uint32_t v0:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Coefficient 1 */
+ uint32_t v1:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Coefficient 2 */
+ uint32_t v2:12;
+ uint32_t /* reserved */ : 20;
+ /* Conversion Offset */
+ uint32_t ConvertOffset:8;
+ uint32_t /* reserved */ : 24;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_SyncTimer_ConfigCmdType {
+ /* Timer Line Start Config */
+ uint32_t timerLineStart:12;
+ uint32_t /* reserved */ : 20;
+ /* Timer Pixel Start Config */
+ uint32_t timerPixelStart:18;
+ uint32_t /* reserved */ : 14;
+ /* Timer Pixel Duration Config */
+ uint32_t timerPixelDuration:28;
+ uint32_t /* reserved */ : 4;
+ /* Sync Timer Polarity Config */
+ uint32_t timer0Polarity:1;
+ uint32_t timer1Polarity:1;
+ uint32_t timer2Polarity:1;
+ uint32_t /* reserved */ : 29;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AsyncTimer_ConfigCmdType {
+ /* Async Timer Config 0 */
+ uint32_t inactiveLength:20;
+ uint32_t numRepetition:10;
+ uint32_t /* reserved */ : 1;
+ uint32_t polarity:1;
+ /* Async Timer Config 1 */
+ uint32_t activeLength:20;
+ uint32_t /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AWBAEStatistics_ConfigCmdType {
+ /* AWB autoexposure Config */
+ uint32_t aeRegionConfig:1;
+ uint32_t aeSubregionConfig:1;
+ uint32_t /* reserved */ : 14;
+ uint32_t awbYMin:8;
+ uint32_t awbYMax:8;
+ /* AXW Header */
+ uint32_t axwHeader:8;
+ uint32_t /* reserved */ : 24;
+ /* AWB Mconfig */
+ uint32_t m4:8;
+ uint32_t m3:8;
+ uint32_t m2:8;
+ uint32_t m1:8;
+ /* AWB Cconfig */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 4;
+ /* AWB Cconfig 2 */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_TestGen_ConfigCmdType {
+ /* HW Test Gen Config */
+ uint32_t numFrame:10;
+ uint32_t /* reserved */ : 2;
+ uint32_t pixelDataSelect:1;
+ uint32_t systematicDataSelect:1;
+ uint32_t /* reserved */ : 2;
+ uint32_t pixelDataSize:2;
+ uint32_t hsyncEdge:1;
+ uint32_t vsyncEdge:1;
+ uint32_t /* reserved */ : 12;
+ /* HW Test Gen Image Config */
+ uint32_t imageWidth:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t imageHeight:14;
+ uint32_t /* reserved */ : 2;
+ /* SOF Offset Config */
+ uint32_t sofOffset:24;
+ uint32_t /* reserved */ : 8;
+ /* EOF NOffset Config */
+ uint32_t eofNOffset:24;
+ uint32_t /* reserved */ : 8;
+ /* SOL Offset Config */
+ uint32_t solOffset:9;
+ uint32_t /* reserved */ : 23;
+ /* EOL NOffset Config */
+ uint32_t eolNOffset:9;
+ uint32_t /* reserved */ : 23;
+ /* HBI Config */
+ uint32_t hBlankInterval:14;
+ uint32_t /* reserved */ : 18;
+ /* VBL Config */
+ uint32_t vBlankInterval:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t vBlankIntervalEnable:1;
+ uint32_t /* reserved */ : 15;
+ /* SOF Dummy Line Config */
+ uint32_t sofDummy:8;
+ uint32_t /* reserved */ : 24;
+ /* EOF Dummy Line Config */
+ uint32_t eofDummy:8;
+ uint32_t /* reserved */ : 24;
+ /* Color Bars Config */
+ uint32_t unicolorBarSelect:3;
+ uint32_t /* reserved */ : 1;
+ uint32_t unicolorBarEnable:1;
+ uint32_t splitEnable:1;
+ uint32_t pixelPattern:2;
+ uint32_t rotatePeriod:6;
+ uint32_t /* reserved */ : 18;
+ /* Random Config */
+ uint32_t randomSeed:16;
+ uint32_t /* reserved */ : 16;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_Bus_Pm_ConfigCmdType {
+ /* VFE Bus Performance Monitor Config */
+ uint32_t output2YWrPmEnable:1;
+ uint32_t output2CbcrWrPmEnable:1;
+ uint32_t output1YWrPmEnable:1;
+ uint32_t output1CbcrWrPmEnable:1;
+ uint32_t /* reserved */ : 28;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_asf_info {
+ /* asf max edge */
+ uint32_t maxEdge:13;
+ uint32_t /* reserved */ : 3;
+ /* HBi count */
+ uint32_t HBICount:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_camif_stats {
+ uint32_t pixelCount:14;
+ uint32_t /* reserved */ : 2;
+ uint32_t lineCount:14;
+ uint32_t /* reserved */ : 1;
+ uint32_t camifHalt:1;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_StatsCmdType {
+ uint32_t autoFocusEnable:1;
+ uint32_t axwEnable:1;
+ uint32_t histEnable:1;
+ uint32_t clearHistEnable:1;
+ uint32_t histAutoClearEnable:1;
+ uint32_t colorConversionEnable:1;
+ uint32_t /* reserved */ : 26;
+} __attribute__((packed, aligned(4)));
+
+
+struct vfe_statsframe {
+ uint32_t lastPixel:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t lastLine:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_busstats_wrprio {
+ uint32_t afBusPriority:4;
+ uint32_t awbBusPriority:4;
+ uint32_t histBusPriority:4;
+ uint32_t afBusPriorityEn:1;
+ uint32_t awbBusPriorityEn:1;
+ uint32_t histBusPriorityEn:1;
+ uint32_t /* reserved */ : 17;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaf_update {
+ /* VFE_STATS_AF_CFG */
+ uint32_t windowVOffset:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t windowHOffset:12;
+ uint32_t /* reserved */ : 3;
+ uint32_t windowMode:1;
+
+ /* VFE_STATS_AF_DIM */
+ uint32_t windowHeight:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t windowWidth:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaf_cfg {
+ /* VFE_STATS_AF_GRID_0 */
+ uint32_t entry00:8;
+ uint32_t entry01:8;
+ uint32_t entry02:8;
+ uint32_t entry03:8;
+
+ /* VFE_STATS_AF_GRID_1 */
+ uint32_t entry10:8;
+ uint32_t entry11:8;
+ uint32_t entry12:8;
+ uint32_t entry13:8;
+
+ /* VFE_STATS_AF_GRID_2 */
+ uint32_t entry20:8;
+ uint32_t entry21:8;
+ uint32_t entry22:8;
+ uint32_t entry23:8;
+
+ /* VFE_STATS_AF_GRID_3 */
+ uint32_t entry30:8;
+ uint32_t entry31:8;
+ uint32_t entry32:8;
+ uint32_t entry33:8;
+
+ /* VFE_STATS_AF_HEADER */
+ uint32_t afHeader:8;
+ uint32_t /* reserved */ : 24;
+ /* VFE_STATS_AF_COEF0 */
+ uint32_t a00:5;
+ uint32_t a04:5;
+ uint32_t fvMax:11;
+ uint32_t fvMetric:1;
+ uint32_t /* reserved */ : 10;
+
+ /* VFE_STATS_AF_COEF1 */
+ uint32_t a20:5;
+ uint32_t a21:5;
+ uint32_t a22:5;
+ uint32_t a23:5;
+ uint32_t a24:5;
+ uint32_t /* reserved */ : 7;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsawbae_update {
+ uint32_t aeRegionCfg:1;
+ uint32_t aeSubregionCfg:1;
+ uint32_t /* reserved */ : 14;
+ uint32_t awbYMin:8;
+ uint32_t awbYMax:8;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsaxw_hdr_cfg {
+ /* Stats AXW Header Config */
+ uint32_t axwHeader:8;
+ uint32_t /* reserved */ : 24;
+} __attribute__((packed, aligned(4)));
+
+struct vfe_statsawb_update {
+ /* AWB MConfig */
+ uint32_t m4:8;
+ uint32_t m3:8;
+ uint32_t m2:8;
+ uint32_t m1:8;
+
+ /* AWB CConfig1 */
+ uint32_t c2:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c1:12;
+ uint32_t /* reserved */ : 4;
+
+ /* AWB CConfig2 */
+ uint32_t c4:12;
+ uint32_t /* reserved */ : 4;
+ uint32_t c3:12;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_SyncTimerCmdType {
+ uint32_t hsyncCount:12;
+ uint32_t /* reserved */ : 20;
+ uint32_t pclkCount:18;
+ uint32_t /* reserved */ : 14;
+ uint32_t outputDuration:28;
+ uint32_t /* reserved */ : 4;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AsyncTimerCmdType {
+ /* config 0 */
+ uint32_t inactiveCount:20;
+ uint32_t repeatCount:10;
+ uint32_t /* reserved */ : 1;
+ uint32_t polarity:1;
+ /* config 1 */
+ uint32_t activeCount:20;
+ uint32_t /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AxiInputCmdType {
+ uint32_t stripeStartAddr0:32;
+ uint32_t stripeStartAddr1:32;
+ uint32_t stripeStartAddr2:32;
+ uint32_t stripeStartAddr3:32;
+
+ uint32_t ySize:12;
+ uint32_t yOffsetDelta:12;
+ uint32_t /* reserved */ : 8;
+
+ /* bus_stripe_rd_hSize */
+ uint32_t /* reserved */ : 16;
+ uint32_t xSizeWord:10;
+ uint32_t /* reserved */ : 6;
+
+ /* bus_stripe_rd_buffer_cfg */
+ uint32_t burstLength:2;
+ uint32_t /* reserved */ : 2;
+ uint32_t NumOfRows:12;
+ uint32_t RowIncrement:12;
+ uint32_t /* reserved */ : 4;
+
+ /* bus_stripe_rd_unpack_cfg */
+ uint32_t mainUnpackHeight:12;
+ uint32_t mainUnpackWidth:13;
+ uint32_t mainUnpackHbiSel:3;
+ uint32_t mainUnpackPhase:3;
+ uint32_t /* reserved */ : 1;
+
+ /* bus_stripe_rd_unpack */
+ uint32_t unpackPattern:32;
+
+ /* bus_stripe_rd_pad_size */
+ uint32_t padLeft:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padRight:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padTop:7;
+ uint32_t /* reserved */ : 1;
+ uint32_t padBottom:7;
+ uint32_t /* reserved */ : 1;
+
+ /* bus_stripe_rd_pad_L_unpack */
+ uint32_t leftUnpackPattern0:4;
+ uint32_t leftUnpackPattern1:4;
+ uint32_t leftUnpackPattern2:4;
+ uint32_t leftUnpackPattern3:4;
+ uint32_t leftUnpackStop0:1;
+ uint32_t leftUnpackStop1:1;
+ uint32_t leftUnpackStop2:1;
+ uint32_t leftUnpackStop3:1;
+ uint32_t /* reserved */ : 12;
+
+ /* bus_stripe_rd_pad_R_unpack */
+ uint32_t rightUnpackPattern0:4;
+ uint32_t rightUnpackPattern1:4;
+ uint32_t rightUnpackPattern2:4;
+ uint32_t rightUnpackPattern3:4;
+ uint32_t rightUnpackStop0:1;
+ uint32_t rightUnpackStop1:1;
+ uint32_t rightUnpackStop2:1;
+ uint32_t rightUnpackStop3:1;
+ uint32_t /* reserved */ : 12;
+
+ /* bus_stripe_rd_pad_tb_unpack */
+ uint32_t topUnapckPattern:4;
+ uint32_t /* reserved */ : 12;
+ uint32_t bottomUnapckPattern:4;
+ uint32_t /* reserved */ : 12;
+} __attribute__((packed, aligned(4)));
+
+struct VFE_AxiRdFragIrqEnable {
+ uint32_t stripeRdFragirq0Enable:1;
+ uint32_t stripeRdFragirq1Enable:1;
+ uint32_t stripeRdFragirq2Enable:1;
+ uint32_t stripeRdFragirq3Enable:1;
+ uint32_t /* reserved */ : 28;
+} __attribute__((packed, aligned(4)));
+
+int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *);
+void vfe_stats_af_stop(void);
+void vfe_stop(void);
+void vfe_update(void);
+int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *);
+int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *);
+void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *);
+void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *);
+void vfe_start(struct vfe_cmd_start *);
+void vfe_la_update(struct vfe_cmd_la_config *);
+void vfe_la_config(struct vfe_cmd_la_config *);
+void vfe_test_gen_start(struct vfe_cmd_test_gen_start *);
+void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *);
+void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *);
+void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *);
+void vfe_camif_frame_update(struct vfe_cmds_camif_frame *);
+void vfe_color_correction_config(struct vfe_cmd_color_correction_config *);
+void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *);
+void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *);
+void vfe_demosaic_config(struct vfe_cmd_demosaic_config *);
+void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *);
+void vfe_black_level_update(struct vfe_cmd_black_level_config *);
+void vfe_black_level_config(struct vfe_cmd_black_level_config *);
+void vfe_asf_update(struct vfe_cmd_asf_update *);
+void vfe_asf_config(struct vfe_cmd_asf_config *);
+void vfe_white_balance_config(struct vfe_cmd_white_balance_config *);
+void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *);
+void vfe_roll_off_config(struct vfe_cmd_roll_off_config *);
+void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *);
+void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *);
+void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *);
+void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *);
+void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *);
+void vfe_stats_wb_exp_stop(void);
+void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *);
+void vfe_stats_update_af(struct vfe_cmd_stats_af_update *);
+void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *);
+void vfe_stats_start_af(struct vfe_cmd_stats_af_start *);
+void vfe_stats_setting(struct vfe_cmd_stats_setting *);
+void vfe_axi_input_config(struct vfe_cmd_axi_input_config *);
+void vfe_stats_config(struct vfe_cmd_stats_setting *);
+void vfe_axi_output_config(struct vfe_cmd_axi_output_config *);
+void vfe_camif_config(struct vfe_cmd_camif_config *);
+void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *);
+void vfe_get_hw_version(struct vfe_cmd_hw_version *);
+void vfe_reset(void);
+void vfe_cmd_release(struct platform_device *);
+void vfe_output1_ack(struct vfe_cmd_output_ack *);
+void vfe_output2_ack(struct vfe_cmd_output_ack *);
+#endif /* __MSM_VFE8X_REG_H__ */
diff --git a/drivers/staging/dream/camera/mt9d112.c b/drivers/staging/dream/camera/mt9d112.c
new file mode 100644
index 00000000000..4f938f9dfc4
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9d112.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include "mt9d112.h"
+
+/* Micron MT9D112 Registers and their values */
+/* Sensor Core Registers */
+#define REG_MT9D112_MODEL_ID 0x3000
+#define MT9D112_MODEL_ID 0x1580
+
+/* SOC Registers Page 1 */
+#define REG_MT9D112_SENSOR_RESET 0x301A
+#define REG_MT9D112_STANDBY_CONTROL 0x3202
+#define REG_MT9D112_MCU_BOOT 0x3386
+
+struct mt9d112_work {
+ struct work_struct work;
+};
+
+static struct mt9d112_work *mt9d112_sensorw;
+static struct i2c_client *mt9d112_client;
+
+struct mt9d112_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+};
+
+
+static struct mt9d112_ctrl *mt9d112_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue);
+DECLARE_MUTEX(mt9d112_sem);
+
+
+/*=============================================================
+ EXTERNAL DECLARATIONS
+==============================================================*/
+extern struct mt9d112_reg mt9d112_regs;
+
+
+/*=============================================================*/
+
+static int mt9d112_reset(const struct msm_camera_sensor_info *dev)
+{
+ int rc = 0;
+
+ rc = gpio_request(dev->sensor_reset, "mt9d112");
+
+ if (!rc) {
+ rc = gpio_direction_output(dev->sensor_reset, 0);
+ mdelay(20);
+ rc = gpio_direction_output(dev->sensor_reset, 1);
+ }
+
+ gpio_free(dev->sensor_reset);
+ return rc;
+}
+
+static int32_t mt9d112_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) {
+ CDBG("mt9d112_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9d112_i2c_write(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata, enum mt9d112_width width)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ switch (width) {
+ case WORD_LEN: {
+ buf[0] = (waddr & 0xFF00)>>8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00)>>8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9d112_i2c_txdata(saddr, buf, 4);
+ }
+ break;
+
+ case BYTE_LEN: {
+ buf[0] = waddr;
+ buf[1] = wdata;
+ rc = mt9d112_i2c_txdata(saddr, buf, 2);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (rc < 0)
+ CDBG(
+ "i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int32_t mt9d112_i2c_write_table(
+ struct mt9d112_i2c_reg_conf const *reg_conf_tbl,
+ int num_of_items_in_table)
+{
+ int i;
+ int32_t rc = -EIO;
+
+ for (i = 0; i < num_of_items_in_table; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ reg_conf_tbl->waddr, reg_conf_tbl->wdata,
+ reg_conf_tbl->width);
+ if (rc < 0)
+ break;
+ if (reg_conf_tbl->mdelay_time != 0)
+ mdelay(reg_conf_tbl->mdelay_time);
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int mt9d112_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9d112_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9d112_i2c_read(unsigned short saddr,
+ unsigned short raddr, unsigned short *rdata, enum mt9d112_width width)
+{
+ int32_t rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ switch (width) {
+ case WORD_LEN: {
+ buf[0] = (raddr & 0xFF00)>>8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9d112_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (rc < 0)
+ CDBG("mt9d112_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int32_t mt9d112_set_lens_roll_off(void)
+{
+ int32_t rc = 0;
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0],
+ mt9d112_regs.rftbl_size);
+ return rc;
+}
+
+static long mt9d112_reg_init(void)
+{
+ int32_t array_length;
+ int32_t i;
+ long rc;
+
+ /* PLL Setup Start */
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0],
+ mt9d112_regs.plltbl_size);
+
+ if (rc < 0)
+ return rc;
+ /* PLL Setup End */
+
+ array_length = mt9d112_regs.prev_snap_reg_settings_size;
+
+ /* Configure sensor for Preview mode and Snapshot mode */
+ for (i = 0; i < array_length; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ mt9d112_regs.prev_snap_reg_settings[i].register_address,
+ mt9d112_regs.prev_snap_reg_settings[i].register_value,
+ WORD_LEN);
+
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Configure for Noise Reduction, Saturation and Aperture Correction */
+ array_length = mt9d112_regs.noise_reduction_reg_settings_size;
+
+ for (i = 0; i < array_length; i++) {
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ mt9d112_regs.noise_reduction_reg_settings[i].register_address,
+ mt9d112_regs.noise_reduction_reg_settings[i].register_value,
+ WORD_LEN);
+
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Set Color Kill Saturation point to optimum value */
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x35A4,
+ 0x0593,
+ WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0],
+ mt9d112_regs.stbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_set_lens_roll_off();
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static long mt9d112_set_sensor_mode(int mode)
+{
+ uint16_t clock;
+ long rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA20C, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0004, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA215, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0004, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA20B, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0000, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ clock = 0x0250;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x341C, clock, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0001, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ /* Switch to lower fps for Snapshot */
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x341C, 0x0120, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA120, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static long mt9d112_set_effect(int mode, int effect)
+{
+ uint16_t reg_addr;
+ uint16_t reg_val;
+ long rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ /* Context A Special Effects */
+ reg_addr = 0x2799;
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ /* Context B Special Effects */
+ reg_addr = 0x279B;
+ break;
+
+ default:
+ reg_addr = 0x2799;
+ break;
+ }
+
+ switch (effect) {
+ case CAMERA_EFFECT_OFF: {
+ reg_val = 0x6440;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_MONO: {
+ reg_val = 0x6441;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_NEGATIVE: {
+ reg_val = 0x6443;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_SOLARIZE: {
+ reg_val = 0x6445;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_SEPIA: {
+ reg_val = 0x6442;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+
+ case CAMERA_EFFECT_PASTEL:
+ case CAMERA_EFFECT_MOSAIC:
+ case CAMERA_EFFECT_RESIZE:
+ return -EINVAL;
+
+ default: {
+ reg_val = 0x6440;
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, reg_addr, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, reg_val, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ return -EINVAL;
+ }
+ }
+
+ /* Refresh Sequencer */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0005, WORD_LEN);
+
+ return rc;
+}
+
+static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data)
+{
+ uint16_t model_id = 0;
+ int rc = 0;
+
+ CDBG("init entry \n");
+ rc = mt9d112_reset(data);
+ if (rc < 0) {
+ CDBG("reset failed!\n");
+ goto init_probe_fail;
+ }
+
+ mdelay(5);
+
+ /* Micron suggested Power up block Start:
+ * Put MCU into Reset - Stop MCU */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* Pull MCU from Reset - Start MCU */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(5);
+
+ /* Micron Suggested - Power up block */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* FUSED_DEFECT_CORRECTION */
+ rc = mt9d112_i2c_write(mt9d112_client->addr,
+ 0x33F4, 0x031D, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(5);
+
+ /* Micron suggested Power up block End */
+ /* Read the Model ID of the sensor */
+ rc = mt9d112_i2c_read(mt9d112_client->addr,
+ REG_MT9D112_MODEL_ID, &model_id, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ CDBG("mt9d112 model_id = 0x%x\n", model_id);
+
+ /* Check if it matches it with the value in Datasheet */
+ if (model_id != MT9D112_MODEL_ID) {
+ rc = -EINVAL;
+ goto init_probe_fail;
+ }
+
+ rc = mt9d112_reg_init();
+ if (rc < 0)
+ goto init_probe_fail;
+
+ return rc;
+
+init_probe_fail:
+ return rc;
+}
+
+int mt9d112_sensor_init(const struct msm_camera_sensor_info *data)
+{
+ int rc = 0;
+
+ mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL);
+ if (!mt9d112_ctrl) {
+ CDBG("mt9d112_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ if (data)
+ mt9d112_ctrl->sensordata = data;
+
+ /* Input MCLK = 24MHz */
+ msm_camio_clk_rate_set(24000000);
+ mdelay(5);
+
+ msm_camio_camif_pad_reg_reset();
+
+ rc = mt9d112_sensor_init_probe(data);
+ if (rc < 0) {
+ CDBG("mt9d112_sensor_init failed!\n");
+ goto init_fail;
+ }
+
+init_done:
+ return rc;
+
+init_fail:
+ kfree(mt9d112_ctrl);
+ return rc;
+}
+
+static int mt9d112_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9d112_wait_queue);
+ return 0;
+}
+
+int mt9d112_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cfg_data;
+ long rc = 0;
+
+ if (copy_from_user(&cfg_data,
+ (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ /* down(&mt9d112_sem); */
+
+ CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n",
+ cfg_data.cfgtype, cfg_data.mode);
+
+ switch (cfg_data.cfgtype) {
+ case CFG_SET_MODE:
+ rc = mt9d112_set_sensor_mode(
+ cfg_data.mode);
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = mt9d112_set_effect(cfg_data.mode,
+ cfg_data.cfg.effect);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ /* up(&mt9d112_sem); */
+
+ return rc;
+}
+
+int mt9d112_sensor_release(void)
+{
+ int rc = 0;
+
+ /* down(&mt9d112_sem); */
+
+ kfree(mt9d112_ctrl);
+ /* up(&mt9d112_sem); */
+
+ return rc;
+}
+
+static int mt9d112_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ rc = -ENOTSUPP;
+ goto probe_failure;
+ }
+
+ mt9d112_sensorw =
+ kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL);
+
+ if (!mt9d112_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9d112_sensorw);
+ mt9d112_init_client(client);
+ mt9d112_client = client;
+
+ CDBG("mt9d112_probe succeeded!\n");
+
+ return 0;
+
+probe_failure:
+ kfree(mt9d112_sensorw);
+ mt9d112_sensorw = NULL;
+ CDBG("mt9d112_probe failed!\n");
+ return rc;
+}
+
+static const struct i2c_device_id mt9d112_i2c_id[] = {
+ { "mt9d112", 0},
+ { },
+};
+
+static struct i2c_driver mt9d112_i2c_driver = {
+ .id_table = mt9d112_i2c_id,
+ .probe = mt9d112_i2c_probe,
+ .remove = __exit_p(mt9d112_i2c_remove),
+ .driver = {
+ .name = "mt9d112",
+ },
+};
+
+static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&mt9d112_i2c_driver);
+ if (rc < 0 || mt9d112_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ /* Input MCLK = 24MHz */
+ msm_camio_clk_rate_set(24000000);
+ mdelay(5);
+
+ rc = mt9d112_sensor_init_probe(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = mt9d112_sensor_init;
+ s->s_release = mt9d112_sensor_release;
+ s->s_config = mt9d112_sensor_config;
+
+probe_done:
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ return rc;
+}
+
+static int __mt9d112_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9d112_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9d112_probe,
+ .driver = {
+ .name = "msm_camera_mt9d112",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9d112_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9d112_init);
diff --git a/drivers/staging/dream/camera/mt9d112.h b/drivers/staging/dream/camera/mt9d112.h
new file mode 100644
index 00000000000..c678996f9e2
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9d112.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#ifndef MT9D112_H
+#define MT9D112_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+enum mt9d112_width {
+ WORD_LEN,
+ BYTE_LEN
+};
+
+struct mt9d112_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+ enum mt9d112_width width;
+ unsigned short mdelay_time;
+};
+
+struct mt9d112_reg {
+ const struct register_address_value_pair *prev_snap_reg_settings;
+ uint16_t prev_snap_reg_settings_size;
+ const struct register_address_value_pair *noise_reduction_reg_settings;
+ uint16_t noise_reduction_reg_settings_size;
+ const struct mt9d112_i2c_reg_conf *plltbl;
+ uint16_t plltbl_size;
+ const struct mt9d112_i2c_reg_conf *stbl;
+ uint16_t stbl_size;
+ const struct mt9d112_i2c_reg_conf *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* MT9D112_H */
diff --git a/drivers/staging/dream/camera/mt9d112_reg.c b/drivers/staging/dream/camera/mt9d112_reg.c
new file mode 100644
index 00000000000..c52e96f4714
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9d112_reg.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include "mt9d112.h"
+
+struct register_address_value_pair
+preview_snapshot_mode_reg_settings_array[] = {
+ {0x338C, 0x2703},
+ {0x3390, 800}, /* Output Width (P) = 640 */
+ {0x338C, 0x2705},
+ {0x3390, 600}, /* Output Height (P) = 480 */
+ {0x338C, 0x2707},
+ {0x3390, 0x0640}, /* Output Width (S) = 1600 */
+ {0x338C, 0x2709},
+ {0x3390, 0x04B0}, /* Output Height (S) = 1200 */
+ {0x338C, 0x270D},
+ {0x3390, 0x0000}, /* Row Start (P) = 0 */
+ {0x338C, 0x270F},
+ {0x3390, 0x0000}, /* Column Start (P) = 0 */
+ {0x338C, 0x2711},
+ {0x3390, 0x04BD}, /* Row End (P) = 1213 */
+ {0x338C, 0x2713},
+ {0x3390, 0x064D}, /* Column End (P) = 1613 */
+ {0x338C, 0x2715},
+ {0x3390, 0x0000}, /* Extra Delay (P) = 0 */
+ {0x338C, 0x2717},
+ {0x3390, 0x2111}, /* Row Speed (P) = 8465 */
+ {0x338C, 0x2719},
+ {0x3390, 0x046C}, /* Read Mode (P) = 1132 */
+ {0x338C, 0x271B},
+ {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */
+ {0x338C, 0x271D},
+ {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */
+ {0x338C, 0x271F},
+ {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */
+ {0x338C, 0x2721},
+ {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */
+ {0x338C, 0x2723},
+ {0x3390, 659}, /* Frame Lines (P) = 679 */
+ {0x338C, 0x2725},
+ {0x3390, 0x0824}, /* Line Length (P) = 2084 */
+ {0x338C, 0x2727},
+ {0x3390, 0x2020},
+ {0x338C, 0x2729},
+ {0x3390, 0x2020},
+ {0x338C, 0x272B},
+ {0x3390, 0x1020},
+ {0x338C, 0x272D},
+ {0x3390, 0x2007},
+ {0x338C, 0x272F},
+ {0x3390, 0x0004}, /* Row Start(S) = 4 */
+ {0x338C, 0x2731},
+ {0x3390, 0x0004}, /* Column Start(S) = 4 */
+ {0x338C, 0x2733},
+ {0x3390, 0x04BB}, /* Row End(S) = 1211 */
+ {0x338C, 0x2735},
+ {0x3390, 0x064B}, /* Column End(S) = 1611 */
+ {0x338C, 0x2737},
+ {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */
+ {0x338C, 0x2739},
+ {0x3390, 0x2111}, /* Row Speed(S) = 8465 */
+ {0x338C, 0x273B},
+ {0x3390, 0x0024}, /* Read Mode(S) = 36 */
+ {0x338C, 0x273D},
+ {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */
+ {0x338C, 0x2741},
+ {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */
+ {0x338C, 0x2745},
+ {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */
+ {0x338C, 0x2747},
+ {0x3390, 0x0824}, /* Line Length(S) = 2084 */
+ {0x338C, 0x2751},
+ {0x3390, 0x0000}, /* Crop_X0(P) = 0 */
+ {0x338C, 0x2753},
+ {0x3390, 0x0320}, /* Crop_X1(P) = 800 */
+ {0x338C, 0x2755},
+ {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */
+ {0x338C, 0x2757},
+ {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */
+ {0x338C, 0x275F},
+ {0x3390, 0x0000}, /* Crop_X0(S) = 0 */
+ {0x338C, 0x2761},
+ {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */
+ {0x338C, 0x2763},
+ {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */
+ {0x338C, 0x2765},
+ {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */
+ {0x338C, 0x222E},
+ {0x3390, 0x00A0}, /* R9 Step = 160 */
+ {0x338C, 0xA408},
+ {0x3390, 0x001F},
+ {0x338C, 0xA409},
+ {0x3390, 0x0021},
+ {0x338C, 0xA40A},
+ {0x3390, 0x0025},
+ {0x338C, 0xA40B},
+ {0x3390, 0x0027},
+ {0x338C, 0x2411},
+ {0x3390, 0x00A0},
+ {0x338C, 0x2413},
+ {0x3390, 0x00C0},
+ {0x338C, 0x2415},
+ {0x3390, 0x00A0},
+ {0x338C, 0x2417},
+ {0x3390, 0x00C0},
+ {0x338C, 0x2799},
+ {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */
+ {0x338C, 0x279B},
+ {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */
+};
+
+static struct register_address_value_pair
+noise_reduction_reg_settings_array[] = {
+ {0x338C, 0xA76D},
+ {0x3390, 0x0003},
+ {0x338C, 0xA76E},
+ {0x3390, 0x0003},
+ {0x338C, 0xA76F},
+ {0x3390, 0},
+ {0x338C, 0xA770},
+ {0x3390, 21},
+ {0x338C, 0xA771},
+ {0x3390, 37},
+ {0x338C, 0xA772},
+ {0x3390, 63},
+ {0x338C, 0xA773},
+ {0x3390, 100},
+ {0x338C, 0xA774},
+ {0x3390, 128},
+ {0x338C, 0xA775},
+ {0x3390, 151},
+ {0x338C, 0xA776},
+ {0x3390, 169},
+ {0x338C, 0xA777},
+ {0x3390, 186},
+ {0x338C, 0xA778},
+ {0x3390, 199},
+ {0x338C, 0xA779},
+ {0x3390, 210},
+ {0x338C, 0xA77A},
+ {0x3390, 220},
+ {0x338C, 0xA77B},
+ {0x3390, 228},
+ {0x338C, 0xA77C},
+ {0x3390, 234},
+ {0x338C, 0xA77D},
+ {0x3390, 240},
+ {0x338C, 0xA77E},
+ {0x3390, 244},
+ {0x338C, 0xA77F},
+ {0x3390, 248},
+ {0x338C, 0xA780},
+ {0x3390, 252},
+ {0x338C, 0xA781},
+ {0x3390, 255},
+ {0x338C, 0xA782},
+ {0x3390, 0},
+ {0x338C, 0xA783},
+ {0x3390, 21},
+ {0x338C, 0xA784},
+ {0x3390, 37},
+ {0x338C, 0xA785},
+ {0x3390, 63},
+ {0x338C, 0xA786},
+ {0x3390, 100},
+ {0x338C, 0xA787},
+ {0x3390, 128},
+ {0x338C, 0xA788},
+ {0x3390, 151},
+ {0x338C, 0xA789},
+ {0x3390, 169},
+ {0x338C, 0xA78A},
+ {0x3390, 186},
+ {0x338C, 0xA78B},
+ {0x3390, 199},
+ {0x338C, 0xA78C},
+ {0x3390, 210},
+ {0x338C, 0xA78D},
+ {0x3390, 220},
+ {0x338C, 0xA78E},
+ {0x3390, 228},
+ {0x338C, 0xA78F},
+ {0x3390, 234},
+ {0x338C, 0xA790},
+ {0x3390, 240},
+ {0x338C, 0xA791},
+ {0x3390, 244},
+ {0x338C, 0xA793},
+ {0x3390, 252},
+ {0x338C, 0xA794},
+ {0x3390, 255},
+ {0x338C, 0xA103},
+ {0x3390, 6},
+};
+
+static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = {
+ { 0x34CE, 0x81A0, WORD_LEN, 0 },
+ { 0x34D0, 0x6331, WORD_LEN, 0 },
+ { 0x34D2, 0x3394, WORD_LEN, 0 },
+ { 0x34D4, 0x9966, WORD_LEN, 0 },
+ { 0x34D6, 0x4B25, WORD_LEN, 0 },
+ { 0x34D8, 0x2670, WORD_LEN, 0 },
+ { 0x34DA, 0x724C, WORD_LEN, 0 },
+ { 0x34DC, 0xFFFD, WORD_LEN, 0 },
+ { 0x34DE, 0x00CA, WORD_LEN, 0 },
+ { 0x34E6, 0x00AC, WORD_LEN, 0 },
+ { 0x34EE, 0x0EE1, WORD_LEN, 0 },
+ { 0x34F6, 0x0D87, WORD_LEN, 0 },
+ { 0x3500, 0xE1F7, WORD_LEN, 0 },
+ { 0x3508, 0x1CF4, WORD_LEN, 0 },
+ { 0x3510, 0x1D28, WORD_LEN, 0 },
+ { 0x3518, 0x1F26, WORD_LEN, 0 },
+ { 0x3520, 0x2220, WORD_LEN, 0 },
+ { 0x3528, 0x333D, WORD_LEN, 0 },
+ { 0x3530, 0x15D9, WORD_LEN, 0 },
+ { 0x3538, 0xCFB8, WORD_LEN, 0 },
+ { 0x354C, 0x05FE, WORD_LEN, 0 },
+ { 0x3544, 0x05F8, WORD_LEN, 0 },
+ { 0x355C, 0x0596, WORD_LEN, 0 },
+ { 0x3554, 0x0611, WORD_LEN, 0 },
+ { 0x34E0, 0x00F2, WORD_LEN, 0 },
+ { 0x34E8, 0x00A8, WORD_LEN, 0 },
+ { 0x34F0, 0x0F7B, WORD_LEN, 0 },
+ { 0x34F8, 0x0CD7, WORD_LEN, 0 },
+ { 0x3502, 0xFEDB, WORD_LEN, 0 },
+ { 0x350A, 0x13E4, WORD_LEN, 0 },
+ { 0x3512, 0x1F2C, WORD_LEN, 0 },
+ { 0x351A, 0x1D20, WORD_LEN, 0 },
+ { 0x3522, 0x2422, WORD_LEN, 0 },
+ { 0x352A, 0x2925, WORD_LEN, 0 },
+ { 0x3532, 0x1D04, WORD_LEN, 0 },
+ { 0x353A, 0xFBF2, WORD_LEN, 0 },
+ { 0x354E, 0x0616, WORD_LEN, 0 },
+ { 0x3546, 0x0597, WORD_LEN, 0 },
+ { 0x355E, 0x05CD, WORD_LEN, 0 },
+ { 0x3556, 0x0529, WORD_LEN, 0 },
+ { 0x34E4, 0x00B2, WORD_LEN, 0 },
+ { 0x34EC, 0x005E, WORD_LEN, 0 },
+ { 0x34F4, 0x0F43, WORD_LEN, 0 },
+ { 0x34FC, 0x0E2F, WORD_LEN, 0 },
+ { 0x3506, 0xF9FC, WORD_LEN, 0 },
+ { 0x350E, 0x0CE4, WORD_LEN, 0 },
+ { 0x3516, 0x1E1E, WORD_LEN, 0 },
+ { 0x351E, 0x1B19, WORD_LEN, 0 },
+ { 0x3526, 0x151B, WORD_LEN, 0 },
+ { 0x352E, 0x1416, WORD_LEN, 0 },
+ { 0x3536, 0x10FC, WORD_LEN, 0 },
+ { 0x353E, 0xC018, WORD_LEN, 0 },
+ { 0x3552, 0x06B4, WORD_LEN, 0 },
+ { 0x354A, 0x0506, WORD_LEN, 0 },
+ { 0x3562, 0x06AB, WORD_LEN, 0 },
+ { 0x355A, 0x063A, WORD_LEN, 0 },
+ { 0x34E2, 0x00E5, WORD_LEN, 0 },
+ { 0x34EA, 0x008B, WORD_LEN, 0 },
+ { 0x34F2, 0x0E4C, WORD_LEN, 0 },
+ { 0x34FA, 0x0CA3, WORD_LEN, 0 },
+ { 0x3504, 0x0907, WORD_LEN, 0 },
+ { 0x350C, 0x1DFD, WORD_LEN, 0 },
+ { 0x3514, 0x1E24, WORD_LEN, 0 },
+ { 0x351C, 0x2529, WORD_LEN, 0 },
+ { 0x3524, 0x1D20, WORD_LEN, 0 },
+ { 0x352C, 0x2332, WORD_LEN, 0 },
+ { 0x3534, 0x10E9, WORD_LEN, 0 },
+ { 0x353C, 0x0BCB, WORD_LEN, 0 },
+ { 0x3550, 0x04EF, WORD_LEN, 0 },
+ { 0x3548, 0x0609, WORD_LEN, 0 },
+ { 0x3560, 0x0580, WORD_LEN, 0 },
+ { 0x3558, 0x05DD, WORD_LEN, 0 },
+ { 0x3540, 0x0000, WORD_LEN, 0 },
+ { 0x3542, 0x0000, WORD_LEN, 0 }
+};
+
+static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = {
+ { 0x341E, 0x8F09, WORD_LEN, 0 },
+ { 0x341C, 0x0250, WORD_LEN, 0 },
+ { 0x341E, 0x8F09, WORD_LEN, 5 },
+ { 0x341E, 0x8F08, WORD_LEN, 0 }
+};
+
+/* Refresh Sequencer */
+static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = {
+ { 0x338C, 0x2799, WORD_LEN, 0},
+ { 0x3390, 0x6440, WORD_LEN, 5},
+ { 0x338C, 0x279B, WORD_LEN, 0},
+ { 0x3390, 0x6440, WORD_LEN, 5},
+ { 0x338C, 0xA103, WORD_LEN, 0},
+ { 0x3390, 0x0005, WORD_LEN, 5},
+ { 0x338C, 0xA103, WORD_LEN, 0},
+ { 0x3390, 0x0006, WORD_LEN, 5}
+};
+
+struct mt9d112_reg mt9d112_regs = {
+ .prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0],
+ .prev_snap_reg_settings_size = ARRAY_SIZE(preview_snapshot_mode_reg_settings_array),
+ .noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0],
+ .noise_reduction_reg_settings_size = ARRAY_SIZE(noise_reduction_reg_settings_array),
+ .plltbl = pll_setup_tbl,
+ .plltbl_size = ARRAY_SIZE(pll_setup_tbl),
+ .stbl = sequencer_tbl,
+ .stbl_size = ARRAY_SIZE(sequencer_tbl),
+ .rftbl = lens_roll_off_tbl,
+ .rftbl_size = ARRAY_SIZE(lens_roll_off_tbl)
+};
+
+
+
diff --git a/drivers/staging/dream/camera/mt9p012.h b/drivers/staging/dream/camera/mt9p012.h
new file mode 100644
index 00000000000..678a0027d42
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9p012.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+
+#ifndef MT9T012_H
+#define MT9T012_H
+
+#include <linux/types.h>
+
+struct reg_struct {
+ uint16_t vt_pix_clk_div; /* 0x0300 */
+ uint16_t vt_sys_clk_div; /* 0x0302 */
+ uint16_t pre_pll_clk_div; /* 0x0304 */
+ uint16_t pll_multiplier; /* 0x0306 */
+ uint16_t op_pix_clk_div; /* 0x0308 */
+ uint16_t op_sys_clk_div; /* 0x030A */
+ uint16_t scale_m; /* 0x0404 */
+ uint16_t row_speed; /* 0x3016 */
+ uint16_t x_addr_start; /* 0x3004 */
+ uint16_t x_addr_end; /* 0x3008 */
+ uint16_t y_addr_start; /* 0x3002 */
+ uint16_t y_addr_end; /* 0x3006 */
+ uint16_t read_mode; /* 0x3040 */
+ uint16_t x_output_size ; /* 0x034C */
+ uint16_t y_output_size; /* 0x034E */
+ uint16_t line_length_pck; /* 0x300C */
+ uint16_t frame_length_lines; /* 0x300A */
+ uint16_t coarse_int_time; /* 0x3012 */
+ uint16_t fine_int_time; /* 0x3014 */
+};
+
+
+struct mt9p012_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+
+struct mt9p012_reg {
+ struct reg_struct *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9p012_i2c_reg_conf *ttbl;
+ uint16_t ttbl_size;
+ struct mt9p012_i2c_reg_conf *lctbl;
+ uint16_t lctbl_size;
+ struct mt9p012_i2c_reg_conf *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* MT9T012_H */
diff --git a/drivers/staging/dream/camera/mt9p012_fox.c b/drivers/staging/dream/camera/mt9p012_fox.c
new file mode 100644
index 00000000000..70119d5e0ab
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9p012_fox.c
@@ -0,0 +1,1305 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9p012.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9P012_REG_MODEL_ID 0x0000
+#define MT9P012_MODEL_ID 0x2801
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x0100
+#define GROUPED_PARAMETER_UPDATE 0x0000
+#define REG_COARSE_INT_TIME 0x3012
+#define REG_VT_PIX_CLK_DIV 0x0300
+#define REG_VT_SYS_CLK_DIV 0x0302
+#define REG_PRE_PLL_CLK_DIV 0x0304
+#define REG_PLL_MULTIPLIER 0x0306
+#define REG_OP_PIX_CLK_DIV 0x0308
+#define REG_OP_SYS_CLK_DIV 0x030A
+#define REG_SCALE_M 0x0404
+#define REG_FRAME_LENGTH_LINES 0x300A
+#define REG_LINE_LENGTH_PCK 0x300C
+#define REG_X_ADDR_START 0x3004
+#define REG_Y_ADDR_START 0x3002
+#define REG_X_ADDR_END 0x3008
+#define REG_Y_ADDR_END 0x3006
+#define REG_X_OUTPUT_SIZE 0x034C
+#define REG_Y_OUTPUT_SIZE 0x034E
+#define REG_FINE_INTEGRATION_TIME 0x3014
+#define REG_ROW_SPEED 0x3016
+#define MT9P012_REG_RESET_REGISTER 0x301A
+#define MT9P012_RESET_REGISTER_PWON 0x10CC
+#define MT9P012_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+#define MT9P012_REV_7
+
+
+enum mt9p012_test_mode {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9p012_resolution {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9p012_reg_update {
+ /* Sensor egisters that need to be updated during initialization */
+ REG_INIT,
+ /* Sensor egisters that needs periodic I2C writes */
+ UPDATE_PERIODIC,
+ /* All the sensor Registers will be updated */
+ UPDATE_ALL,
+ /* Not valid update */
+ UPDATE_INVALID
+};
+
+enum mt9p012_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9P012_AF_I2C_ADDR 0x18
+
+/* AF Total steps parameters */
+#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF 32
+#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR 32
+
+#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0
+#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES 0
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_RESET_DELAY_MSECS 66
+
+/* for 20 fps preview */
+#define MT9P012_DEFAULT_CLOCK_RATE 24000000
+#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */
+
+struct mt9p012_work {
+ struct work_struct work;
+};
+static struct mt9p012_work *mt9p012_sensorw;
+static struct i2c_client *mt9p012_client;
+
+struct mt9p012_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum mt9p012_resolution prev_res;
+ enum mt9p012_resolution pict_res;
+ enum mt9p012_resolution curr_res;
+ enum mt9p012_test_mode set_test;
+};
+
+
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+DECLARE_MUTEX(mt9p012_sem);
+
+/*=============================================================
+ EXTERNAL DECLARATIONS
+==============================================================*/
+extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */
+
+
+
+/*=============================================================*/
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9p012_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr,
+ unsigned short *rdata)
+{
+ int32_t rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00)>>8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9p012_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ CDBG("mt9p012_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata,
+ int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) {
+ CDBG("mt9p012_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr,
+ unsigned short bdata)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ rc = mt9p012_i2c_txdata(saddr, buf, 2);
+
+ if (rc < 0)
+ CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n",
+ saddr, baddr, bdata);
+
+ return rc;
+}
+
+static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr,
+ unsigned short wdata)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00)>>8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00)>>8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9p012_i2c_txdata(saddr, buf, 4);
+
+ if (rc < 0)
+ CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int32_t mt9p012_i2c_write_w_table(
+ struct mt9p012_i2c_reg_conf *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+
+ for (i = 0; i < num; i++) {
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ reg_conf_tbl->waddr, reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int32_t mt9p012_test(enum mt9p012_test_mode mo)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ if (mo == TEST_OFF)
+ return 0;
+ else {
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl, mt9p012_regs.ttbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t)mo);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9p012_lens_shading_enable(uint8_t is_enable)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780,
+ ((uint16_t) is_enable) << 15);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
+
+ CDBG("%s: exiting. rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int32_t mt9p012_set_lc(void)
+{
+ int32_t rc;
+
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl, mt9p012_regs.lctbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl, mt9p012_regs.rftbl_size);
+
+ return rc;
+}
+
+static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /*Q10 */
+ uint32_t pclk_mult; /*Q10 */
+
+ if (mt9p012_ctrl->prev_res == QTR_SIZE) {
+ divider = (uint32_t)
+ (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) * 0x00000400) /
+ (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines *
+ mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck));
+
+ pclk_mult =
+ (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00000400) / (mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+ } else {
+ /* full size resolution used for preview. */
+ divider = 0x00000400; /*1.0 */
+ pclk_mult = 0x00000400; /*1.0 */
+ }
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 /
+ 0x00000400);
+}
+
+static uint16_t mt9p012_get_prev_lines_pf(void)
+{
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+ else
+ return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_prev_pixels_pl(void)
+{
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck;
+ else
+ return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_get_pict_lines_pf(void)
+{
+ return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_get_pict_pixels_pl(void)
+{
+ return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (mt9p012_ctrl->pict_res == QTR_SIZE)
+ snapshot_lines_per_frame =
+ mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+ else
+ snapshot_lines_per_frame =
+ mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+ return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9p012_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q10 format */
+ int32_t rc = 0;
+
+ mt9p012_ctrl->fps_divider = fps->fps_div;
+ mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return -EBUSY;
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_LINE_LENGTH_PCK,
+ (mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck *
+ fps->f_mult / 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ return rc;
+}
+
+static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0x01FF;
+ uint32_t line_length_ratio = 0x00000400;
+ enum mt9p012_setting setting;
+ int32_t rc = 0;
+
+ CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__);
+
+ if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ mt9p012_ctrl->my_reg_gain = gain;
+ mt9p012_ctrl->my_reg_line_count = (uint16_t)line;
+ }
+
+ if (gain > max_legal_gain) {
+ CDBG("Max legal gain Line:%d \n", __LINE__);
+ gain = max_legal_gain;
+ }
+
+ /* Verify no overflow */
+ if (mt9p012_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ line = (uint32_t)(line * mt9p012_ctrl->fps_divider /
+ 0x00000400);
+ setting = RES_PREVIEW;
+ } else {
+ line = (uint32_t)(line * mt9p012_ctrl->pict_fps_divider /
+ 0x00000400);
+ setting = RES_CAPTURE;
+ }
+
+ /* Set digital gain to 1 */
+#ifdef MT9P012_REV_7
+ gain |= 0x1000;
+#else
+ gain |= 0x0200;
+#endif
+
+ if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+ line_length_ratio = (uint32_t) (line * 0x00000400) /
+ (mt9p012_regs.reg_pat[setting].frame_length_lines - 1);
+ } else
+ line_length_ratio = 0x00000400;
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc =
+ mt9p012_i2c_write_w(
+ mt9p012_client->addr,
+ REG_GLOBAL_GAIN, gain);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_COARSE_INT_TIME,
+ line);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+
+ return rc;
+}
+
+static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+
+ CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__);
+
+ rc =
+ mt9p012_write_exp_gain(gain, line);
+ if (rc < 0) {
+ CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n",
+ __LINE__);
+ return rc;
+ }
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ 0x10CC | 0x0002);
+ if (rc < 0) {
+ CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ mdelay(5);
+
+ /* camera_timed_wait(snapshot_wait*exposure_ratio); */
+ return rc;
+}
+
+static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate,
+ enum mt9p012_setting rt)
+{
+ int32_t rc = 0;
+
+ switch (rupdate) {
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+ struct mt9p012_i2c_reg_conf ppc_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD},
+ {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size},
+
+ {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ (mt9p012_regs.reg_pat[rt].frame_length_lines *
+ mt9p012_ctrl->fps_divider / 0x00000400)},
+ {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE},
+ };
+
+ rc = mt9p012_i2c_write_w_table(&ppc_tbl[0],
+ ARRAY_SIZE(ppc_tbl));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_test(mt9p012_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWON | 0x0002);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5); /* 15? wait for sensor to transition*/
+
+ return rc;
+ }
+ break; /* UPDATE_PERIODIC */
+
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct mt9p012_i2c_reg_conf ipc_tbl1[] = {
+ {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF},
+ {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+ {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier},
+ {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+ {0x30B0, 0x0001},
+ {0x308E, 0xE060},
+ {0x3092, 0x0A52},
+ {0x3094, 0x4656},
+ {0x3096, 0x5652},
+ {0x30CA, 0x8006},
+ {0x312A, 0xDD02},
+ {0x312C, 0x00E4},
+ {0x3170, 0x299A},
+#endif
+ /* optimized settings for noise */
+ {0x3088, 0x6FF6},
+ {0x3154, 0x0282},
+ {0x3156, 0x0381},
+ {0x3162, 0x04CE},
+ {0x0204, 0x0010},
+ {0x0206, 0x0010},
+ {0x0208, 0x0010},
+ {0x020A, 0x0010},
+ {0x020C, 0x0010},
+ {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON},
+ };
+
+ struct mt9p012_i2c_reg_conf ipc_tbl2[] = {
+ {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF},
+ {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div},
+ {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier},
+ {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div},
+#ifdef MT9P012_REV_7
+ {0x30B0, 0x0001},
+ {0x308E, 0xE060},
+ {0x3092, 0x0A52},
+ {0x3094, 0x4656},
+ {0x3096, 0x5652},
+ {0x30CA, 0x8006},
+ {0x312A, 0xDD02},
+ {0x312C, 0x00E4},
+ {0x3170, 0x299A},
+#endif
+ /* optimized settings for noise */
+ {0x3088, 0x6FF6},
+ {0x3154, 0x0282},
+ {0x3156, 0x0381},
+ {0x3162, 0x04CE},
+ {0x0204, 0x0010},
+ {0x0206, 0x0010},
+ {0x0208, 0x0010},
+ {0x020A, 0x0010},
+ {0x020C, 0x0010},
+ {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON},
+ };
+
+ struct mt9p012_i2c_reg_conf ipc_tbl3[] = {
+ {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD},
+ /* Set preview or snapshot mode */
+ {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size},
+ {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ mt9p012_regs.reg_pat[rt].frame_length_lines},
+ {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE},
+ };
+
+ /* reset fps_divider */
+ mt9p012_ctrl->fps_divider = 1 * 0x0400;
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0],
+ ARRAY_SIZE(ipc_tbl1));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0],
+ ARRAY_SIZE(ipc_tbl2));
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0],
+ ARRAY_SIZE(ipc_tbl3));
+ if (rc < 0)
+ return rc;
+
+ /* load lens shading */
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_set_lc();
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);
+
+ if (rc < 0)
+ return rc;
+ }
+ break; /* case REG_INIT: */
+
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int32_t mt9p012_video_config(int mode, int res)
+{
+ int32_t rc;
+
+ switch (res) {
+ case QTR_SIZE:
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+
+ CDBG("mt9p012 sensor configuration done!\n");
+ break;
+
+ case FULL_SIZE:
+ rc =
+ mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ default:
+ return 0;
+ } /* switch */
+
+ mt9p012_ctrl->prev_res = res;
+ mt9p012_ctrl->curr_res = res;
+ mt9p012_ctrl->sensormode = mode;
+
+ rc =
+ mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain,
+ mt9p012_ctrl->my_reg_line_count);
+
+ rc =
+ mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ 0x10cc|0x0002);
+
+ return rc;
+}
+
+static int32_t mt9p012_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+ mt9p012_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t mt9p012_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res;
+
+ mt9p012_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t mt9p012_power_down(void)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER,
+ MT9P012_RESET_REGISTER_PWOFF);
+
+ mdelay(5);
+ return rc;
+}
+
+static int32_t mt9p012_move_focus(int direction, int32_t num_steps)
+{
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ uint8_t code_val_msb, code_val_lsb;
+
+ if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (num_steps == 0) {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ if (direction == MOVE_NEAR)
+ step_direction = 16; /* 10bit */
+ else if (direction == MOVE_FAR)
+ step_direction = -16; /* 10 bit */
+ else {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos)
+ mt9p012_ctrl->curr_lens_pos =
+ mt9p012_ctrl->init_curr_lens_pos;
+
+ actual_step = (int16_t)(step_direction * (int16_t)num_steps);
+ next_position = (int16_t)(mt9p012_ctrl->curr_lens_pos + actual_step);
+
+ if (next_position > 1023)
+ next_position = 1023;
+ else if (next_position < 0)
+ next_position = 0;
+
+ code_val_msb = next_position >> 4;
+ code_val_lsb = (next_position & 0x000F) << 4;
+ /* code_val_lsb |= mode_mask; */
+
+ /* Writing the digital code for current to the actuator */
+ if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb) < 0) {
+ CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+
+ /* Storing the current lens Position */
+ mt9p012_ctrl->curr_lens_pos = next_position;
+
+ return 0;
+}
+
+static int32_t mt9p012_set_default_focus(void)
+{
+ int32_t rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+
+ code_val_msb = 0x00;
+ code_val_lsb = 0x00;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb);
+
+ mt9p012_ctrl->curr_lens_pos = 0;
+ mt9p012_ctrl->init_curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+ uint16_t chipid;
+
+ rc = gpio_request(data->sensor_reset, "mt9p012");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ mdelay(20);
+
+ /* RESET the sensor image part via I2C command */
+ CDBG("mt9p012_sensor_init(): reseting sensor.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER, 0x10CC|0x0001);
+ if (rc < 0) {
+ CDBG("sensor reset failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ mdelay(MT9P012_RESET_DELAY_MSECS);
+
+ /* 3. Read sensor Model ID: */
+ rc = mt9p012_i2c_read_w(mt9p012_client->addr,
+ MT9P012_REG_MODEL_ID, &chipid);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* 4. Compare sensor ID to MT9T012VC ID: */
+ if (chipid != MT9P012_MODEL_ID) {
+ CDBG("mt9p012 wrong model_id = 0x%x\n", chipid);
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000);
+ if (rc < 0) {
+ CDBG("REV_7 write failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ /* RESET_REGISTER, enable parallel interface and disable serialiser */
+ CDBG("mt9p012_sensor_init(): enabling parallel interface.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC);
+ if (rc < 0) {
+ CDBG("enable parallel interface failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ /* To disable the 2 extra lines */
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ 0x3064, 0x0805);
+
+ if (rc < 0) {
+ CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ mdelay(MT9P012_RESET_DELAY_MSECS);
+ goto init_probe_done;
+
+init_probe_fail:
+ mt9p012_probe_init_done(data);
+init_probe_done:
+ return rc;
+}
+
+static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+
+ mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL);
+ if (!mt9p012_ctrl) {
+ CDBG("mt9p012_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ mt9p012_ctrl->fps_divider = 1 * 0x00000400;
+ mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9p012_ctrl->set_test = TEST_OFF;
+ mt9p012_ctrl->prev_res = QTR_SIZE;
+ mt9p012_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9p012_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = mt9p012_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail1;
+
+ if (mt9p012_ctrl->prev_res == QTR_SIZE)
+ rc = mt9p012_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = mt9p012_setting(REG_INIT, RES_CAPTURE);
+
+ if (rc < 0) {
+ CDBG("mt9p012_setting failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* sensor : output enable */
+ CDBG("mt9p012_sensor_open_init(): enabling output.\n");
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON);
+ if (rc < 0) {
+ CDBG("sensor output enable failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* TODO: enable AF actuator */
+#if 0
+ CDBG("enable AF actuator, gpio = %d\n",
+ mt9p012_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012");
+ if (!rc)
+ gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1);
+ else {
+ CDBG("mt9p012_ctrl gpio request failed!\n");
+ goto init_fail1;
+ }
+ mdelay(20);
+
+ rc = mt9p012_set_default_focus();
+#endif
+ if (rc >= 0)
+ goto init_done;
+
+ /* TODO:
+ * gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+ * gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); */
+init_fail1:
+ mt9p012_probe_init_done(data);
+ kfree(mt9p012_ctrl);
+init_done:
+ return rc;
+}
+
+static int mt9p012_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9p012_wait_queue);
+ return 0;
+}
+
+static int32_t mt9p012_set_sensor_mode(int mode, int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9p012_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = mt9p012_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = mt9p012_raw_snapshot_config(mode);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9p012_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ int rc = 0;
+
+ if (copy_from_user(&cdata,
+ (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ down(&mt9p012_sem);
+
+ CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+
+ if (copy_to_user((void *)argp, &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc =
+ mt9p012_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = mt9p012_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+ rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9p012_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d cdata.cfg.focus.steps=%d\n",
+ cdata.cfg.focus.dir, cdata.cfg.focus.steps);
+ rc = mt9p012_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = mt9p012_set_default_focus();
+ break;
+
+ case CFG_SET_LENS_SHADING:
+ CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+ rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ up(&mt9p012_sem);
+ return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ down(&mt9p012_sem);
+
+ mt9p012_power_down();
+
+ gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset,
+ 0);
+ gpio_free(mt9p012_ctrl->sensordata->sensor_reset);
+
+ gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+
+ kfree(mt9p012_ctrl);
+ mt9p012_ctrl = NULL;
+
+ CDBG("mt9p012_release completed\n");
+
+ up(&mt9p012_sem);
+ return rc;
+}
+
+static int mt9p012_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("mt9p012_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL);
+ if (!mt9p012_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9p012_sensorw);
+ mt9p012_init_client(client);
+ mt9p012_client = client;
+
+ mdelay(50);
+
+ CDBG("mt9p012_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("mt9p012_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id mt9p012_i2c_id[] = {
+ { "mt9p012", 0},
+ { }
+};
+
+static struct i2c_driver mt9p012_i2c_driver = {
+ .id_table = mt9p012_i2c_id,
+ .probe = mt9p012_i2c_probe,
+ .remove = __exit_p(mt9p012_i2c_remove),
+ .driver = {
+ .name = "mt9p012",
+ },
+};
+
+static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&mt9p012_i2c_driver);
+ if (rc < 0 || mt9p012_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = mt9p012_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = mt9p012_sensor_open_init;
+ s->s_release = mt9p012_sensor_release;
+ s->s_config = mt9p012_sensor_config;
+ mt9p012_probe_init_done(info);
+
+probe_done:
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ return rc;
+}
+
+static int __mt9p012_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9p012_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9p012_probe,
+ .driver = {
+ .name = "msm_camera_mt9p012",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9p012_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_init);
diff --git a/drivers/staging/dream/camera/mt9p012_reg.c b/drivers/staging/dream/camera/mt9p012_reg.c
new file mode 100644
index 00000000000..e5223d693b2
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9p012_reg.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2009 QUALCOMM Incorporated.
+ */
+
+#include "mt9p012.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct mt9p012_reg_pat[2] = {
+ { /* Preview */
+ /* vt_pix_clk_div REG=0x0300 */
+ 6, /* 5 */
+
+ /* vt_sys_clk_div REG=0x0302 */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 */
+ 2,
+
+ /* pll_multiplier REG=0x0306 */
+ 60,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8, /* 10 */
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2597,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1949,
+
+ /* read_mode REG=0x3040
+ * Preview 2x2 skipping */
+ 0x00C3,
+
+ /* x_output_size REG=0x034C */
+ 1296,
+
+ /* y_output_size REG=0x034E */
+ 972,
+
+ /* line_length_pck REG=0x300C */
+ 3784,
+
+ /* frame_length_lines REG=0x300A */
+ 1057,
+
+ /* coarse_integration_time REG=0x3012 */
+ 16,
+
+ /* fine_integration_time REG=0x3014 */
+ 1764
+ },
+ { /*Snapshot*/
+ /* vt_pix_clk_div REG=0x0300 */
+ 6,
+
+ /* vt_sys_clk_div REG=0x0302 */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 */
+ 2,
+
+ /* pll_multiplier REG=0x0306
+ * 60 for 10fps snapshot */
+ 60,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2615,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1967,
+
+ /* read_mode REG=0x3040 */
+ 0x0041,
+
+ /* x_output_size REG=0x034C */
+ 2608,
+
+ /* y_output_size REG=0x034E */
+ 1960,
+
+ /* line_length_pck REG=0x300C */
+ 3911,
+
+ /* frame_length_lines REG=0x300A //10 fps snapshot */
+ 2045,
+
+ /* coarse_integration_time REG=0x3012 */
+ 16,
+
+ /* fine_integration_time REG=0x3014 */
+ 882
+ }
+};
+
+
+struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = {
+ {0x3044, 0x0544 & 0xFBFF},
+ {0x30CA, 0x0004 | 0x0001},
+ {0x30D4, 0x9020 & 0x7FFF},
+ {0x31E0, 0x0003 & 0xFFFE},
+ {0x3180, 0x91FF & 0x7FFF},
+ {0x301A, (0x10CC | 0x8000) & 0xFFF7},
+ {0x301E, 0x0000},
+ {0x3780, 0x0000},
+};
+
+
+struct mt9p012_i2c_reg_conf mt9p012_lc_tbl[] = {
+ /* [Lens shading 85 Percent TL84] */
+ /* P_RD_P0Q0 */
+ {0x360A, 0x7FEF},
+ /* P_RD_P0Q1 */
+ {0x360C, 0x232C},
+ /* P_RD_P0Q2 */
+ {0x360E, 0x7050},
+ /* P_RD_P0Q3 */
+ {0x3610, 0xF3CC},
+ /* P_RD_P0Q4 */
+ {0x3612, 0x89D1},
+ /* P_RD_P1Q0 */
+ {0x364A, 0xBE0D},
+ /* P_RD_P1Q1 */
+ {0x364C, 0x9ACB},
+ /* P_RD_P1Q2 */
+ {0x364E, 0x2150},
+ /* P_RD_P1Q3 */
+ {0x3650, 0xB26B},
+ /* P_RD_P1Q4 */
+ {0x3652, 0x9511},
+ /* P_RD_P2Q0 */
+ {0x368A, 0x2151},
+ /* P_RD_P2Q1 */
+ {0x368C, 0x00AD},
+ /* P_RD_P2Q2 */
+ {0x368E, 0x8334},
+ /* P_RD_P2Q3 */
+ {0x3690, 0x478E},
+ /* P_RD_P2Q4 */
+ {0x3692, 0x0515},
+ /* P_RD_P3Q0 */
+ {0x36CA, 0x0710},
+ /* P_RD_P3Q1 */
+ {0x36CC, 0x452D},
+ /* P_RD_P3Q2 */
+ {0x36CE, 0xF352},
+ /* P_RD_P3Q3 */
+ {0x36D0, 0x190F},
+ /* P_RD_P3Q4 */
+ {0x36D2, 0x4413},
+ /* P_RD_P4Q0 */
+ {0x370A, 0xD112},
+ /* P_RD_P4Q1 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q2 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q3 */
+ {0x3710, 0xDC11},
+ /* P_RD_P4Q4 */
+ {0x3712, 0xD776},
+ /* P_GR_P0Q0 */
+ {0x3600, 0x1750},
+ /* P_GR_P0Q1 */
+ {0x3602, 0xF0AC},
+ /* P_GR_P0Q2 */
+ {0x3604, 0x4711},
+ /* P_GR_P0Q3 */
+ {0x3606, 0x07CE},
+ /* P_GR_P0Q4 */
+ {0x3608, 0x96B2},
+ /* P_GR_P1Q0 */
+ {0x3640, 0xA9AE},
+ /* P_GR_P1Q1 */
+ {0x3642, 0xF9AC},
+ /* P_GR_P1Q2 */
+ {0x3644, 0x39F1},
+ /* P_GR_P1Q3 */
+ {0x3646, 0x016F},
+ /* P_GR_P1Q4 */
+ {0x3648, 0x8AB2},
+ /* P_GR_P2Q0 */
+ {0x3680, 0x1752},
+ /* P_GR_P2Q1 */
+ {0x3682, 0x70F0},
+ /* P_GR_P2Q2 */
+ {0x3684, 0x83F5},
+ /* P_GR_P2Q3 */
+ {0x3686, 0x8392},
+ /* P_GR_P2Q4 */
+ {0x3688, 0x1FD6},
+ /* P_GR_P3Q0 */
+ {0x36C0, 0x1131},
+ /* P_GR_P3Q1 */
+ {0x36C2, 0x3DAF},
+ /* P_GR_P3Q2 */
+ {0x36C4, 0x89B4},
+ /* P_GR_P3Q3 */
+ {0x36C6, 0xA391},
+ /* P_GR_P3Q4 */
+ {0x36C8, 0x1334},
+ /* P_GR_P4Q0 */
+ {0x3700, 0xDC13},
+ /* P_GR_P4Q1 */
+ {0x3702, 0xD052},
+ /* P_GR_P4Q2 */
+ {0x3704, 0x5156},
+ /* P_GR_P4Q3 */
+ {0x3706, 0x1F13},
+ /* P_GR_P4Q4 */
+ {0x3708, 0x8C38},
+ /* P_BL_P0Q0 */
+ {0x3614, 0x0050},
+ /* P_BL_P0Q1 */
+ {0x3616, 0xBD4C},
+ /* P_BL_P0Q2 */
+ {0x3618, 0x41B0},
+ /* P_BL_P0Q3 */
+ {0x361A, 0x660D},
+ /* P_BL_P0Q4 */
+ {0x361C, 0xC590},
+ /* P_BL_P1Q0 */
+ {0x3654, 0x87EC},
+ /* P_BL_P1Q1 */
+ {0x3656, 0xE44C},
+ /* P_BL_P1Q2 */
+ {0x3658, 0x302E},
+ /* P_BL_P1Q3 */
+ {0x365A, 0x106E},
+ /* P_BL_P1Q4 */
+ {0x365C, 0xB58E},
+ /* P_BL_P2Q0 */
+ {0x3694, 0x0DD1},
+ /* P_BL_P2Q1 */
+ {0x3696, 0x2A50},
+ /* P_BL_P2Q2 */
+ {0x3698, 0xC793},
+ /* P_BL_P2Q3 */
+ {0x369A, 0xE8F1},
+ /* P_BL_P2Q4 */
+ {0x369C, 0x4174},
+ /* P_BL_P3Q0 */
+ {0x36D4, 0x01EF},
+ /* P_BL_P3Q1 */
+ {0x36D6, 0x06CF},
+ /* P_BL_P3Q2 */
+ {0x36D8, 0x8D91},
+ /* P_BL_P3Q3 */
+ {0x36DA, 0x91F0},
+ /* P_BL_P3Q4 */
+ {0x36DC, 0x52EF},
+ /* P_BL_P4Q0 */
+ {0x3714, 0xA6D2},
+ /* P_BL_P4Q1 */
+ {0x3716, 0xA312},
+ /* P_BL_P4Q2 */
+ {0x3718, 0x2695},
+ /* P_BL_P4Q3 */
+ {0x371A, 0x3953},
+ /* P_BL_P4Q4 */
+ {0x371C, 0x9356},
+ /* P_GB_P0Q0 */
+ {0x361E, 0x7EAF},
+ /* P_GB_P0Q1 */
+ {0x3620, 0x2A4C},
+ /* P_GB_P0Q2 */
+ {0x3622, 0x49F0},
+ {0x3624, 0xF1EC},
+ /* P_GB_P0Q4 */
+ {0x3626, 0xC670},
+ /* P_GB_P1Q0 */
+ {0x365E, 0x8E0C},
+ /* P_GB_P1Q1 */
+ {0x3660, 0xC2A9},
+ /* P_GB_P1Q2 */
+ {0x3662, 0x274F},
+ /* P_GB_P1Q3 */
+ {0x3664, 0xADAB},
+ /* P_GB_P1Q4 */
+ {0x3666, 0x8EF0},
+ /* P_GB_P2Q0 */
+ {0x369E, 0x09B1},
+ /* P_GB_P2Q1 */
+ {0x36A0, 0xAA2E},
+ /* P_GB_P2Q2 */
+ {0x36A2, 0xC3D3},
+ /* P_GB_P2Q3 */
+ {0x36A4, 0x7FAF},
+ /* P_GB_P2Q4 */
+ {0x36A6, 0x3F34},
+ /* P_GB_P3Q0 */
+ {0x36DE, 0x4C8F},
+ /* P_GB_P3Q1 */
+ {0x36E0, 0x886E},
+ /* P_GB_P3Q2 */
+ {0x36E2, 0xE831},
+ /* P_GB_P3Q3 */
+ {0x36E4, 0x1FD0},
+ /* P_GB_P3Q4 */
+ {0x36E6, 0x1192},
+ /* P_GB_P4Q0 */
+ {0x371E, 0xB952},
+ /* P_GB_P4Q1 */
+ {0x3720, 0x6DCF},
+ /* P_GB_P4Q2 */
+ {0x3722, 0x1B55},
+ /* P_GB_P4Q3 */
+ {0x3724, 0xA112},
+ /* P_GB_P4Q4 */
+ {0x3726, 0x82F6},
+ /* POLY_ORIGIN_C */
+ {0x3782, 0x0510},
+ /* POLY_ORIGIN_R */
+ {0x3784, 0x0390},
+ /* POLY_SC_ENABLE */
+ {0x3780, 0x8000},
+};
+
+/* rolloff table for illuminant A */
+struct mt9p012_i2c_reg_conf mt9p012_rolloff_tbl[] = {
+ /* P_RD_P0Q0 */
+ {0x360A, 0x7FEF},
+ /* P_RD_P0Q1 */
+ {0x360C, 0x232C},
+ /* P_RD_P0Q2 */
+ {0x360E, 0x7050},
+ /* P_RD_P0Q3 */
+ {0x3610, 0xF3CC},
+ /* P_RD_P0Q4 */
+ {0x3612, 0x89D1},
+ /* P_RD_P1Q0 */
+ {0x364A, 0xBE0D},
+ /* P_RD_P1Q1 */
+ {0x364C, 0x9ACB},
+ /* P_RD_P1Q2 */
+ {0x364E, 0x2150},
+ /* P_RD_P1Q3 */
+ {0x3650, 0xB26B},
+ /* P_RD_P1Q4 */
+ {0x3652, 0x9511},
+ /* P_RD_P2Q0 */
+ {0x368A, 0x2151},
+ /* P_RD_P2Q1 */
+ {0x368C, 0x00AD},
+ /* P_RD_P2Q2 */
+ {0x368E, 0x8334},
+ /* P_RD_P2Q3 */
+ {0x3690, 0x478E},
+ /* P_RD_P2Q4 */
+ {0x3692, 0x0515},
+ /* P_RD_P3Q0 */
+ {0x36CA, 0x0710},
+ /* P_RD_P3Q1 */
+ {0x36CC, 0x452D},
+ /* P_RD_P3Q2 */
+ {0x36CE, 0xF352},
+ /* P_RD_P3Q3 */
+ {0x36D0, 0x190F},
+ /* P_RD_P3Q4 */
+ {0x36D2, 0x4413},
+ /* P_RD_P4Q0 */
+ {0x370A, 0xD112},
+ /* P_RD_P4Q1 */
+ {0x370C, 0xF50F},
+ /* P_RD_P4Q2 */
+ {0x370E, 0x6375},
+ /* P_RD_P4Q3 */
+ {0x3710, 0xDC11},
+ /* P_RD_P4Q4 */
+ {0x3712, 0xD776},
+ /* P_GR_P0Q0 */
+ {0x3600, 0x1750},
+ /* P_GR_P0Q1 */
+ {0x3602, 0xF0AC},
+ /* P_GR_P0Q2 */
+ {0x3604, 0x4711},
+ /* P_GR_P0Q3 */
+ {0x3606, 0x07CE},
+ /* P_GR_P0Q4 */
+ {0x3608, 0x96B2},
+ /* P_GR_P1Q0 */
+ {0x3640, 0xA9AE},
+ /* P_GR_P1Q1 */
+ {0x3642, 0xF9AC},
+ /* P_GR_P1Q2 */
+ {0x3644, 0x39F1},
+ /* P_GR_P1Q3 */
+ {0x3646, 0x016F},
+ /* P_GR_P1Q4 */
+ {0x3648, 0x8AB2},
+ /* P_GR_P2Q0 */
+ {0x3680, 0x1752},
+ /* P_GR_P2Q1 */
+ {0x3682, 0x70F0},
+ /* P_GR_P2Q2 */
+ {0x3684, 0x83F5},
+ /* P_GR_P2Q3 */
+ {0x3686, 0x8392},
+ /* P_GR_P2Q4 */
+ {0x3688, 0x1FD6},
+ /* P_GR_P3Q0 */
+ {0x36C0, 0x1131},
+ /* P_GR_P3Q1 */
+ {0x36C2, 0x3DAF},
+ /* P_GR_P3Q2 */
+ {0x36C4, 0x89B4},
+ /* P_GR_P3Q3 */
+ {0x36C6, 0xA391},
+ /* P_GR_P3Q4 */
+ {0x36C8, 0x1334},
+ /* P_GR_P4Q0 */
+ {0x3700, 0xDC13},
+ /* P_GR_P4Q1 */
+ {0x3702, 0xD052},
+ /* P_GR_P4Q2 */
+ {0x3704, 0x5156},
+ /* P_GR_P4Q3 */
+ {0x3706, 0x1F13},
+ /* P_GR_P4Q4 */
+ {0x3708, 0x8C38},
+ /* P_BL_P0Q0 */
+ {0x3614, 0x0050},
+ /* P_BL_P0Q1 */
+ {0x3616, 0xBD4C},
+ /* P_BL_P0Q2 */
+ {0x3618, 0x41B0},
+ /* P_BL_P0Q3 */
+ {0x361A, 0x660D},
+ /* P_BL_P0Q4 */
+ {0x361C, 0xC590},
+ /* P_BL_P1Q0 */
+ {0x3654, 0x87EC},
+ /* P_BL_P1Q1 */
+ {0x3656, 0xE44C},
+ /* P_BL_P1Q2 */
+ {0x3658, 0x302E},
+ /* P_BL_P1Q3 */
+ {0x365A, 0x106E},
+ /* P_BL_P1Q4 */
+ {0x365C, 0xB58E},
+ /* P_BL_P2Q0 */
+ {0x3694, 0x0DD1},
+ /* P_BL_P2Q1 */
+ {0x3696, 0x2A50},
+ /* P_BL_P2Q2 */
+ {0x3698, 0xC793},
+ /* P_BL_P2Q3 */
+ {0x369A, 0xE8F1},
+ /* P_BL_P2Q4 */
+ {0x369C, 0x4174},
+ /* P_BL_P3Q0 */
+ {0x36D4, 0x01EF},
+ /* P_BL_P3Q1 */
+ {0x36D6, 0x06CF},
+ /* P_BL_P3Q2 */
+ {0x36D8, 0x8D91},
+ /* P_BL_P3Q3 */
+ {0x36DA, 0x91F0},
+ /* P_BL_P3Q4 */
+ {0x36DC, 0x52EF},
+ /* P_BL_P4Q0 */
+ {0x3714, 0xA6D2},
+ /* P_BL_P4Q1 */
+ {0x3716, 0xA312},
+ /* P_BL_P4Q2 */
+ {0x3718, 0x2695},
+ /* P_BL_P4Q3 */
+ {0x371A, 0x3953},
+ /* P_BL_P4Q4 */
+ {0x371C, 0x9356},
+ /* P_GB_P0Q0 */
+ {0x361E, 0x7EAF},
+ /* P_GB_P0Q1 */
+ {0x3620, 0x2A4C},
+ /* P_GB_P0Q2 */
+ {0x3622, 0x49F0},
+ {0x3624, 0xF1EC},
+ /* P_GB_P0Q4 */
+ {0x3626, 0xC670},
+ /* P_GB_P1Q0 */
+ {0x365E, 0x8E0C},
+ /* P_GB_P1Q1 */
+ {0x3660, 0xC2A9},
+ /* P_GB_P1Q2 */
+ {0x3662, 0x274F},
+ /* P_GB_P1Q3 */
+ {0x3664, 0xADAB},
+ /* P_GB_P1Q4 */
+ {0x3666, 0x8EF0},
+ /* P_GB_P2Q0 */
+ {0x369E, 0x09B1},
+ /* P_GB_P2Q1 */
+ {0x36A0, 0xAA2E},
+ /* P_GB_P2Q2 */
+ {0x36A2, 0xC3D3},
+ /* P_GB_P2Q3 */
+ {0x36A4, 0x7FAF},
+ /* P_GB_P2Q4 */
+ {0x36A6, 0x3F34},
+ /* P_GB_P3Q0 */
+ {0x36DE, 0x4C8F},
+ /* P_GB_P3Q1 */
+ {0x36E0, 0x886E},
+ /* P_GB_P3Q2 */
+ {0x36E2, 0xE831},
+ /* P_GB_P3Q3 */
+ {0x36E4, 0x1FD0},
+ /* P_GB_P3Q4 */
+ {0x36E6, 0x1192},
+ /* P_GB_P4Q0 */
+ {0x371E, 0xB952},
+ /* P_GB_P4Q1 */
+ {0x3720, 0x6DCF},
+ /* P_GB_P4Q2 */
+ {0x3722, 0x1B55},
+ /* P_GB_P4Q3 */
+ {0x3724, 0xA112},
+ /* P_GB_P4Q4 */
+ {0x3726, 0x82F6},
+ /* POLY_ORIGIN_C */
+ {0x3782, 0x0510},
+ /* POLY_ORIGIN_R */
+ {0x3784, 0x0390},
+ /* POLY_SC_ENABLE */
+ {0x3780, 0x8000},
+};
+
+
+struct mt9p012_reg mt9p012_regs = {
+ .reg_pat = &mt9p012_reg_pat[0],
+ .reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat),
+ .ttbl = &mt9p012_test_tbl[0],
+ .ttbl_size = ARRAY_SIZE(mt9p012_test_tbl),
+ .lctbl = &mt9p012_lc_tbl[0],
+ .lctbl_size = ARRAY_SIZE(mt9p012_lc_tbl),
+ .rftbl = &mt9p012_rolloff_tbl[0],
+ .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl)
+};
+
+
diff --git a/drivers/staging/dream/camera/mt9t013.c b/drivers/staging/dream/camera/mt9t013.c
new file mode 100644
index 00000000000..88229f2663b
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9t013.c
@@ -0,0 +1,1496 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "mt9t013.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define MT9T013_REG_MODEL_ID 0x0000
+#define MT9T013_MODEL_ID 0x2600
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD 0x0100
+#define GROUPED_PARAMETER_UPDATE 0x0000
+#define REG_COARSE_INT_TIME 0x3012
+#define REG_VT_PIX_CLK_DIV 0x0300
+#define REG_VT_SYS_CLK_DIV 0x0302
+#define REG_PRE_PLL_CLK_DIV 0x0304
+#define REG_PLL_MULTIPLIER 0x0306
+#define REG_OP_PIX_CLK_DIV 0x0308
+#define REG_OP_SYS_CLK_DIV 0x030A
+#define REG_SCALE_M 0x0404
+#define REG_FRAME_LENGTH_LINES 0x300A
+#define REG_LINE_LENGTH_PCK 0x300C
+#define REG_X_ADDR_START 0x3004
+#define REG_Y_ADDR_START 0x3002
+#define REG_X_ADDR_END 0x3008
+#define REG_Y_ADDR_END 0x3006
+#define REG_X_OUTPUT_SIZE 0x034C
+#define REG_Y_OUTPUT_SIZE 0x034E
+#define REG_FINE_INT_TIME 0x3014
+#define REG_ROW_SPEED 0x3016
+#define MT9T013_REG_RESET_REGISTER 0x301A
+#define MT9T013_RESET_REGISTER_PWON 0x10CC
+#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+
+enum mt9t013_test_mode {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9t013_resolution {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9t013_reg_update {
+ REG_INIT, /* registers that need to be updated during initialization */
+ UPDATE_PERIODIC, /* registers that needs periodic I2C writes */
+ UPDATE_ALL, /* all registers will be updated */
+ UPDATE_INVALID
+};
+
+enum mt9t013_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+/* actuator's Slave Address */
+#define MT9T013_AF_I2C_ADDR 0x18
+
+/*
+* AF Total steps parameters
+*/
+#define MT9T013_TOTAL_STEPS_NEAR_TO_FAR 30
+
+/*
+ * Time in milisecs for waiting for the sensor to reset.
+ */
+#define MT9T013_RESET_DELAY_MSECS 66
+
+/* for 30 fps preview */
+#define MT9T013_DEFAULT_CLOCK_RATE 24000000
+#define MT9T013_DEFAULT_MAX_FPS 26
+
+
+/* FIXME: Changes from here */
+struct mt9t013_work {
+ struct work_struct work;
+};
+
+static struct mt9t013_work *mt9t013_sensorw;
+static struct i2c_client *mt9t013_client;
+
+struct mt9t013_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum mt9t013_resolution prev_res;
+ enum mt9t013_resolution pict_res;
+ enum mt9t013_resolution curr_res;
+ enum mt9t013_test_mode set_test;
+
+ unsigned short imgaddr;
+};
+
+
+static struct mt9t013_ctrl *mt9t013_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue);
+DECLARE_MUTEX(mt9t013_sem);
+
+extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */
+
+static int mt9t013_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) {
+ pr_err("mt9t013_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9t013_i2c_read_w(unsigned short saddr,
+ unsigned short raddr, unsigned short *rdata)
+{
+ int32_t rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00)>>8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = mt9t013_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ pr_err("mt9t013_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int32_t mt9t013_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) {
+ pr_err("mt9t013_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9t013_i2c_write_b(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = waddr;
+ buf[1] = wdata;
+ rc = mt9t013_i2c_txdata(saddr, buf, 2);
+
+ if (rc < 0)
+ pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int32_t mt9t013_i2c_write_w(unsigned short saddr,
+ unsigned short waddr, unsigned short wdata)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00)>>8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = (wdata & 0xFF00)>>8;
+ buf[3] = (wdata & 0x00FF);
+
+ rc = mt9t013_i2c_txdata(saddr, buf, 4);
+
+ if (rc < 0)
+ pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+
+ return rc;
+}
+
+static int32_t mt9t013_i2c_write_w_table(
+ struct mt9t013_i2c_reg_conf *reg_conf_tbl, int num_of_items_in_table)
+{
+ int i;
+ int32_t rc = -EIO;
+
+ for (i = 0; i < num_of_items_in_table; i++) {
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ reg_conf_tbl->waddr, reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int32_t mt9t013_test(enum mt9t013_test_mode mo)
+{
+ int32_t rc = 0;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ if (mo == TEST_OFF)
+ return 0;
+ else {
+ rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl,
+ mt9t013_regs.ttbl_size);
+ if (rc < 0)
+ return rc;
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t)mo);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9t013_set_lc(void)
+{
+ int32_t rc;
+
+ rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, mt9t013_regs.lctbl_size);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9t013_set_default_focus(uint8_t af_step)
+{
+ int32_t rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+ code_val_msb = 0x01;
+ code_val_lsb = af_step;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1,
+ code_val_msb, code_val_lsb);
+
+ mt9t013_ctrl->curr_lens_pos = 0;
+ mt9t013_ctrl->init_curr_lens_pos = 0;
+ return rc;
+}
+
+static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /*Q10 */
+ uint32_t pclk_mult; /*Q10 */
+
+ if (mt9t013_ctrl->prev_res == QTR_SIZE) {
+ divider =
+ (uint32_t)(
+ ((mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck) *
+ 0x00000400) /
+ (mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines *
+ mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck));
+
+ pclk_mult =
+ (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00000400) /
+ (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+ } else {
+ /* full size resolution used for preview. */
+ divider = 0x00000400; /*1.0 */
+ pclk_mult = 0x00000400; /*1.0 */
+ }
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps =
+ (uint16_t) (fps * divider * pclk_mult /
+ 0x00000400 / 0x00000400);
+}
+
+static uint16_t mt9t013_get_prev_lines_pf(void)
+{
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+ else
+ return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_prev_pixels_pl(void)
+{
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck;
+ else
+ return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9t013_get_pict_lines_pf(void)
+{
+ return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9t013_get_pict_pixels_pl(void)
+{
+ return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9t013_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (mt9t013_ctrl->pict_res == QTR_SIZE) {
+ snapshot_lines_per_frame =
+ mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+ } else {
+ snapshot_lines_per_frame =
+ mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+ }
+
+ return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9t013_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q8 format */
+ int32_t rc = 0;
+
+ mt9t013_ctrl->fps_divider = fps->fps_div;
+ mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return -EBUSY;
+
+ CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n",
+ fps->fps_div,
+ (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW].
+ frame_length_lines *
+ fps->fps_div/0x00000400));
+
+ CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n",
+ fps->f_mult,
+ (uint16_t)(mt9t013_regs.reg_pat[RES_PREVIEW].
+ line_length_pck *
+ fps->f_mult / 0x00000400));
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ (uint16_t) (
+ mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck *
+ fps->f_mult / 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ const uint16_t max_legal_gain = 0x01FF;
+ uint32_t line_length_ratio = 0x00000400;
+ enum mt9t013_setting setting;
+ int32_t rc = 0;
+
+ if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ mt9t013_ctrl->my_reg_gain = gain;
+ mt9t013_ctrl->my_reg_line_count = (uint16_t) line;
+ }
+
+ if (gain > max_legal_gain)
+ gain = max_legal_gain;
+
+ /* Verify no overflow */
+ if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ line = (uint32_t) (line * mt9t013_ctrl->fps_divider /
+ 0x00000400);
+
+ setting = RES_PREVIEW;
+
+ } else {
+ line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider /
+ 0x00000400);
+
+ setting = RES_CAPTURE;
+ }
+
+ /*Set digital gain to 1 */
+ gain |= 0x0200;
+
+ if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+
+ line_length_ratio =
+ (uint32_t) (line * 0x00000400) /
+ (mt9t013_regs.reg_pat[setting].frame_length_lines - 1);
+ } else
+ line_length_ratio = 0x00000400;
+
+ /* There used to be PARAMETER_HOLD register write before and
+ * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes
+ * aec oscillation. Hence removed. */
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ (uint16_t)((uint32_t) line * 0x00000400 /
+ line_length_ratio));
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+
+ rc = mt9t013_write_exp_gain(gain, line);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ 0x10CC | 0x0002);
+
+ mdelay(5);
+
+ /* camera_timed_wait(snapshot_wait*exposure_ratio); */
+ return rc;
+}
+
+static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate,
+ enum mt9t013_setting rt)
+{
+ int32_t rc = 0;
+
+ switch (rupdate) {
+ case UPDATE_PERIODIC: {
+
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+#if 0
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc < 0)
+ return rc;
+#endif
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_PIX_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].vt_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_SYS_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].vt_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PRE_PLL_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].pre_pll_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PLL_MULTIPLIER,
+ mt9t013_regs.reg_pat[rt].pll_multiplier);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_PIX_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].op_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_SYS_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].op_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_ROW_SPEED,
+ mt9t013_regs.reg_pat[rt].row_speed);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_START,
+ mt9t013_regs.reg_pat[rt].x_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_END,
+ mt9t013_regs.reg_pat[rt].x_addr_end);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_START,
+ mt9t013_regs.reg_pat[rt].y_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_END,
+ mt9t013_regs.reg_pat[rt].y_addr_end);
+ if (rc < 0)
+ return rc;
+
+ if (machine_is_sapphire()) {
+ if (rt == 0)
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ 0x046F);
+ else
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ 0x0027);
+ } else
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ mt9t013_regs.reg_pat[rt].read_mode);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_SCALE_M,
+ mt9t013_regs.reg_pat[rt].scale_m);
+ if (rc < 0)
+ return rc;
+
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_OUTPUT_SIZE,
+ mt9t013_regs.reg_pat[rt].x_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_OUTPUT_SIZE,
+ mt9t013_regs.reg_pat[rt].y_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ mt9t013_regs.reg_pat[rt].line_length_pck);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ (mt9t013_regs.reg_pat[rt].frame_length_lines *
+ mt9t013_ctrl->fps_divider / 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ mt9t013_regs.reg_pat[rt].coarse_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FINE_INT_TIME,
+ mt9t013_regs.reg_pat[rt].fine_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_test(mt9t013_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ return rc;
+ }
+ }
+ break;
+
+ /*CAMSENSOR_REG_UPDATE_PERIODIC */
+ case REG_INIT: {
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc < 0)
+ /* MODE_SELECT, stop streaming */
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_PIX_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].vt_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_VT_SYS_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].vt_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PRE_PLL_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].pre_pll_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_PLL_MULTIPLIER,
+ mt9t013_regs.reg_pat[rt].pll_multiplier);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_PIX_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].op_pix_clk_div);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_OP_SYS_CLK_DIV,
+ mt9t013_regs.reg_pat[rt].op_sys_clk_div);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ /* additional power saving mode ok around 38.2MHz */
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3084, 0x2409);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3092, 0x0A49);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3094, 0x4949);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3096, 0x4949);
+ if (rc < 0)
+ return rc;
+
+ /* Set preview or snapshot mode */
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_ROW_SPEED,
+ mt9t013_regs.reg_pat[rt].row_speed);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_START,
+ mt9t013_regs.reg_pat[rt].x_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_ADDR_END,
+ mt9t013_regs.reg_pat[rt].x_addr_end);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_START,
+ mt9t013_regs.reg_pat[rt].y_addr_start);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_ADDR_END,
+ mt9t013_regs.reg_pat[rt].y_addr_end);
+ if (rc < 0)
+ return rc;
+
+ if (machine_is_sapphire()) {
+ if (rt == 0)
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ 0x046F);
+ else
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ 0x0027);
+ } else
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_READ_MODE,
+ mt9t013_regs.reg_pat[rt].read_mode);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_SCALE_M,
+ mt9t013_regs.reg_pat[rt].scale_m);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_X_OUTPUT_SIZE,
+ mt9t013_regs.reg_pat[rt].x_output_size);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_Y_OUTPUT_SIZE,
+ mt9t013_regs.reg_pat[rt].y_output_size);
+ if (rc < 0)
+ return 0;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_LINE_LENGTH_PCK,
+ mt9t013_regs.reg_pat[rt].line_length_pck);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ mt9t013_regs.reg_pat[rt].frame_length_lines);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_COARSE_INT_TIME,
+ mt9t013_regs.reg_pat[rt].coarse_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FINE_INT_TIME,
+ mt9t013_regs.reg_pat[rt].fine_int_time);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ /* load lens shading */
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ /* most likely needs to be written only once. */
+ rc = mt9t013_set_lc();
+ if (rc < 0)
+ return -EBUSY;
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_test(mt9t013_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc =
+ mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+ if (rc < 0)
+ /* MODE_SELECT, stop streaming */
+ return rc;
+
+ CDBG("!!! mt9t013 !!! PowerOn is done!\n");
+ mdelay(5);
+ return rc;
+ }
+ } /* case CAMSENSOR_REG_INIT: */
+ break;
+
+ /*CAMSENSOR_REG_INIT */
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int32_t mt9t013_video_config(int mode, int res)
+{
+ int32_t rc;
+
+ switch (res) {
+ case QTR_SIZE:
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+ CDBG("sensor configuration done!\n");
+ break;
+
+ case FULL_SIZE:
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return -EINVAL;
+ } /* switch */
+
+ mt9t013_ctrl->prev_res = res;
+ mt9t013_ctrl->curr_res = res;
+ mt9t013_ctrl->sensormode = mode;
+
+ return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain,
+ mt9t013_ctrl->my_reg_line_count);
+}
+
+static int32_t mt9t013_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+ mt9t013_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t mt9t013_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res;
+ mt9t013_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t mt9t013_power_down(void)
+{
+ int32_t rc = 0;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWOFF);
+ if (rc >= 0)
+ mdelay(5);
+ return rc;
+}
+
+static int32_t mt9t013_move_focus(int direction, int32_t num_steps)
+{
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ int16_t break_steps[4];
+ uint8_t code_val_msb, code_val_lsb;
+ int16_t i;
+
+ if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (num_steps == 0)
+ return -EINVAL;
+
+ if (direction == MOVE_NEAR)
+ step_direction = 4;
+ else if (direction == MOVE_FAR)
+ step_direction = -4;
+ else
+ return -EINVAL;
+
+ if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos)
+ mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos;
+
+ actual_step =
+ (int16_t) (step_direction *
+ (int16_t) num_steps);
+
+ for (i = 0; i < 4; i++)
+ break_steps[i] =
+ actual_step / 4 * (i + 1) - actual_step / 4 * i;
+
+ for (i = 0; i < 4; i++) {
+ next_position =
+ (int16_t)
+ (mt9t013_ctrl->curr_lens_pos + break_steps[i]);
+
+ if (next_position > 255)
+ next_position = 255;
+ else if (next_position < 0)
+ next_position = 0;
+
+ code_val_msb =
+ ((next_position >> 4) << 2) |
+ ((next_position << 4) >> 6);
+
+ code_val_lsb =
+ ((next_position & 0x03) << 6);
+
+ /* Writing the digital code for current to the actuator */
+ if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1,
+ code_val_msb, code_val_lsb) < 0)
+ return -EBUSY;
+
+ /* Storing the current lens Position */
+ mt9t013_ctrl->curr_lens_pos = next_position;
+
+ if (i < 3)
+ mdelay(1);
+ } /* for */
+
+ return 0;
+}
+
+static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+ uint16_t chipid;
+
+ rc = gpio_request(data->sensor_reset, "mt9t013");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ mdelay(20);
+
+ /* RESET the sensor image part via I2C command */
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER, 0x1009);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* 3. Read sensor Model ID: */
+ rc = mt9t013_i2c_read_w(mt9t013_client->addr,
+ MT9T013_REG_MODEL_ID, &chipid);
+
+ if (rc < 0)
+ goto init_probe_fail;
+
+ CDBG("mt9t013 model_id = 0x%x\n", chipid);
+
+ /* 4. Compare sensor ID to MT9T012VC ID: */
+ if (chipid != MT9T013_MODEL_ID) {
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ 0x3064, 0x0805);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ mdelay(MT9T013_RESET_DELAY_MSECS);
+
+ goto init_probe_done;
+
+ /* sensor: output enable */
+#if 0
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON);
+
+ /* if this fails, the sensor is not the MT9T013 */
+ rc = mt9t013_set_default_focus(0);
+#endif
+
+init_probe_fail:
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+init_probe_done:
+ return rc;
+}
+
+static int32_t mt9t013_poweron_af(void)
+{
+ int32_t rc = 0;
+
+ /* enable AF actuator */
+ CDBG("enable AF actuator, gpio = %d\n",
+ mt9t013_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013");
+ if (!rc) {
+ gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0);
+ mdelay(20);
+ rc = mt9t013_set_default_focus(0);
+ } else
+ pr_err("%s, gpio_request failed (%d)!\n", __func__, rc);
+ return rc;
+}
+
+static void mt9t013_poweroff_af(void)
+{
+ gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1);
+ gpio_free(mt9t013_ctrl->sensordata->vcm_pwd);
+}
+
+int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+
+ mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL);
+ if (!mt9t013_ctrl) {
+ pr_err("mt9t013_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ mt9t013_ctrl->fps_divider = 1 * 0x00000400;
+ mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9t013_ctrl->set_test = TEST_OFF;
+ mt9t013_ctrl->prev_res = QTR_SIZE;
+ mt9t013_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9t013_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = mt9t013_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ if (mt9t013_ctrl->prev_res == QTR_SIZE)
+ rc = mt9t013_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = mt9t013_setting(REG_INIT, RES_CAPTURE);
+
+ if (rc >= 0)
+ rc = mt9t013_poweron_af();
+
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+
+init_fail:
+ kfree(mt9t013_ctrl);
+init_done:
+ return rc;
+}
+
+static int mt9t013_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9t013_wait_queue);
+ return 0;
+}
+
+
+static int32_t mt9t013_set_sensor_mode(int mode, int res)
+{
+ int32_t rc = 0;
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9t013_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = mt9t013_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = mt9t013_raw_snapshot_config(mode);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* FIXME: what should we do if rc < 0? */
+ if (rc >= 0)
+ return mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ return rc;
+}
+
+int mt9t013_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ long rc = 0;
+
+ if (copy_from_user(&cdata, (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ down(&mt9t013_sem);
+
+ CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf();
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl();
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf();
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl =
+ mt9t013_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc =
+ mt9t013_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = mt9t013_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9t013_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = mt9t013_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = mt9t013_set_default_focus(cdata.cfg.focus.steps);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ up(&mt9t013_sem);
+ return rc;
+}
+
+int mt9t013_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ down(&mt9t013_sem);
+
+ mt9t013_poweroff_af();
+ mt9t013_power_down();
+
+ gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset,
+ 0);
+ gpio_free(mt9t013_ctrl->sensordata->sensor_reset);
+
+ kfree(mt9t013_ctrl);
+
+ up(&mt9t013_sem);
+ CDBG("mt9t013_release completed!\n");
+ return rc;
+}
+
+static int mt9t013_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ rc = -ENOTSUPP;
+ goto probe_failure;
+ }
+
+ mt9t013_sensorw =
+ kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL);
+
+ if (!mt9t013_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9t013_sensorw);
+ mt9t013_init_client(client);
+ mt9t013_client = client;
+ mt9t013_client->addr = mt9t013_client->addr >> 1;
+ mdelay(50);
+
+ CDBG("i2c probe ok\n");
+ return 0;
+
+probe_failure:
+ kfree(mt9t013_sensorw);
+ mt9t013_sensorw = NULL;
+ pr_err("i2c probe failure %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id mt9t013_i2c_id[] = {
+ { "mt9t013", 0},
+ { }
+};
+
+static struct i2c_driver mt9t013_i2c_driver = {
+ .id_table = mt9t013_i2c_id,
+ .probe = mt9t013_i2c_probe,
+ .remove = __exit_p(mt9t013_i2c_remove),
+ .driver = {
+ .name = "mt9t013",
+ },
+};
+
+static int mt9t013_sensor_probe(
+ const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ /* We expect this driver to match with the i2c device registered
+ * in the board file immediately. */
+ int rc = i2c_add_driver(&mt9t013_i2c_driver);
+ if (rc < 0 || mt9t013_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = mt9t013_probe_init_sensor(info);
+ if (rc < 0) {
+ i2c_del_driver(&mt9t013_i2c_driver);
+ goto probe_done;
+ }
+
+ s->s_init = mt9t013_sensor_open_init;
+ s->s_release = mt9t013_sensor_release;
+ s->s_config = mt9t013_sensor_config;
+ mt9t013_sensor_init_done(info);
+
+probe_done:
+ return rc;
+}
+
+static int __mt9t013_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9t013_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9t013_probe,
+ .driver = {
+ .name = "msm_camera_mt9t013",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9t013_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9t013_init);
diff --git a/drivers/staging/dream/camera/mt9t013.h b/drivers/staging/dream/camera/mt9t013.h
new file mode 100644
index 00000000000..9bce2036e3b
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9t013.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#ifndef MT9T013_H
+#define MT9T013_H
+
+#include <linux/types.h>
+
+struct reg_struct {
+ uint16_t vt_pix_clk_div; /* 0x0300 */
+ uint16_t vt_sys_clk_div; /* 0x0302 */
+ uint16_t pre_pll_clk_div; /* 0x0304 */
+ uint16_t pll_multiplier; /* 0x0306 */
+ uint16_t op_pix_clk_div; /* 0x0308 */
+ uint16_t op_sys_clk_div; /* 0x030A */
+ uint16_t scale_m; /* 0x0404 */
+ uint16_t row_speed; /* 0x3016 */
+ uint16_t x_addr_start; /* 0x3004 */
+ uint16_t x_addr_end; /* 0x3008 */
+ uint16_t y_addr_start; /* 0x3002 */
+ uint16_t y_addr_end; /* 0x3006 */
+ uint16_t read_mode; /* 0x3040 */
+ uint16_t x_output_size; /* 0x034C */
+ uint16_t y_output_size; /* 0x034E */
+ uint16_t line_length_pck; /* 0x300C */
+ uint16_t frame_length_lines; /* 0x300A */
+ uint16_t coarse_int_time; /* 0x3012 */
+ uint16_t fine_int_time; /* 0x3014 */
+};
+
+struct mt9t013_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct mt9t013_reg {
+ struct reg_struct *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9t013_i2c_reg_conf *ttbl;
+ uint16_t ttbl_size;
+ struct mt9t013_i2c_reg_conf *lctbl;
+ uint16_t lctbl_size;
+ struct mt9t013_i2c_reg_conf *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* #define MT9T013_H */
diff --git a/drivers/staging/dream/camera/mt9t013_reg.c b/drivers/staging/dream/camera/mt9t013_reg.c
new file mode 100644
index 00000000000..ba0a1d4b4d5
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9t013_reg.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 QUALCOMM Incorporated.
+ */
+
+#include "mt9t013.h"
+#include <linux/kernel.h>
+
+struct reg_struct const mt9t013_reg_pat[2] = {
+ { /* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */
+ /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps
+ * if this change */
+ 8,
+
+ /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps
+ * if this change */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps
+ * if this change */
+ 2,
+
+ /* pll_multiplier REG=0x0306 60 for 30fps preview, 40
+ * for 20fps preview
+ * 46 for 30fps preview, try 47/48 to increase further */
+ 46,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2053,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1541,
+
+ /* read_mode REG=0x3040 */
+ 0x046C,
+
+ /* x_output_size REG=0x034C */
+ 1024,
+
+ /* y_output_size REG=0x034E */
+ 768,
+
+ /* line_length_pck REG=0x300C */
+ 2616,
+
+ /* frame_length_lines REG=0x300A */
+ 916,
+
+ /* coarse_int_time REG=0x3012 */
+ 16,
+
+ /* fine_int_time REG=0x3014 */
+ 1461
+ },
+ { /*Snapshot */
+ /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps
+ * if this change */
+ 8,
+
+ /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps
+ * if this change */
+ 1,
+
+ /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps
+ * if this change */
+ 2,
+
+ /* pll_multiplier REG=0x0306 50 for 15fps snapshot,
+ * 40 for 10fps snapshot
+ * 46 for 30fps snapshot, try 47/48 to increase further */
+ 46,
+
+ /* op_pix_clk_div REG=0x0308 */
+ 8,
+
+ /* op_sys_clk_div REG=0x030A */
+ 1,
+
+ /* scale_m REG=0x0404 */
+ 16,
+
+ /* row_speed REG=0x3016 */
+ 0x0111,
+
+ /* x_addr_start REG=0x3004 */
+ 8,
+
+ /* x_addr_end REG=0x3008 */
+ 2071,
+
+ /* y_addr_start REG=0x3002 */
+ 8,
+
+ /* y_addr_end REG=0x3006 */
+ 1551,
+
+ /* read_mode REG=0x3040 */
+ 0x0024,
+
+ /* x_output_size REG=0x034C */
+ 2064,
+
+ /* y_output_size REG=0x034E */
+ 1544,
+
+ /* line_length_pck REG=0x300C */
+ 2952,
+
+ /* frame_length_lines REG=0x300A */
+ 1629,
+
+ /* coarse_int_time REG=0x3012 */
+ 16,
+
+ /* fine_int_time REG=0x3014 */
+ 733
+ }
+};
+
+struct mt9t013_i2c_reg_conf mt9t013_test_tbl[] = {
+ { 0x3044, 0x0544 & 0xFBFF },
+ { 0x30CA, 0x0004 | 0x0001 },
+ { 0x30D4, 0x9020 & 0x7FFF },
+ { 0x31E0, 0x0003 & 0xFFFE },
+ { 0x3180, 0x91FF & 0x7FFF },
+ { 0x301A, (0x10CC | 0x8000) & 0xFFF7 },
+ { 0x301E, 0x0000 },
+ { 0x3780, 0x0000 },
+};
+
+/* [Lens shading 85 Percent TL84] */
+struct mt9t013_i2c_reg_conf mt9t013_lc_tbl[] = {
+ { 0x360A, 0x0290 }, /* P_RD_P0Q0 */
+ { 0x360C, 0xC92D }, /* P_RD_P0Q1 */
+ { 0x360E, 0x0771 }, /* P_RD_P0Q2 */
+ { 0x3610, 0xE38C }, /* P_RD_P0Q3 */
+ { 0x3612, 0xD74F }, /* P_RD_P0Q4 */
+ { 0x364A, 0x168C }, /* P_RD_P1Q0 */
+ { 0x364C, 0xCACB }, /* P_RD_P1Q1 */
+ { 0x364E, 0x8C4C }, /* P_RD_P1Q2 */
+ { 0x3650, 0x0BEA }, /* P_RD_P1Q3 */
+ { 0x3652, 0xDC0F }, /* P_RD_P1Q4 */
+ { 0x368A, 0x70B0 }, /* P_RD_P2Q0 */
+ { 0x368C, 0x200B }, /* P_RD_P2Q1 */
+ { 0x368E, 0x30B2 }, /* P_RD_P2Q2 */
+ { 0x3690, 0xD04F }, /* P_RD_P2Q3 */
+ { 0x3692, 0xACF5 }, /* P_RD_P2Q4 */
+ { 0x36CA, 0xF7C9 }, /* P_RD_P3Q0 */
+ { 0x36CC, 0x2AED }, /* P_RD_P3Q1 */
+ { 0x36CE, 0xA652 }, /* P_RD_P3Q2 */
+ { 0x36D0, 0x8192 }, /* P_RD_P3Q3 */
+ { 0x36D2, 0x3A15 }, /* P_RD_P3Q4 */
+ { 0x370A, 0xDA30 }, /* P_RD_P4Q0 */
+ { 0x370C, 0x2E2F }, /* P_RD_P4Q1 */
+ { 0x370E, 0xBB56 }, /* P_RD_P4Q2 */
+ { 0x3710, 0x8195 }, /* P_RD_P4Q3 */
+ { 0x3712, 0x02F9 }, /* P_RD_P4Q4 */
+ { 0x3600, 0x0230 }, /* P_GR_P0Q0 */
+ { 0x3602, 0x58AD }, /* P_GR_P0Q1 */
+ { 0x3604, 0x18D1 }, /* P_GR_P0Q2 */
+ { 0x3606, 0x260D }, /* P_GR_P0Q3 */
+ { 0x3608, 0xF530 }, /* P_GR_P0Q4 */
+ { 0x3640, 0x17EB }, /* P_GR_P1Q0 */
+ { 0x3642, 0x3CAB }, /* P_GR_P1Q1 */
+ { 0x3644, 0x87CE }, /* P_GR_P1Q2 */
+ { 0x3646, 0xC02E }, /* P_GR_P1Q3 */
+ { 0x3648, 0xF48F }, /* P_GR_P1Q4 */
+ { 0x3680, 0x5350 }, /* P_GR_P2Q0 */
+ { 0x3682, 0x7EAF }, /* P_GR_P2Q1 */
+ { 0x3684, 0x4312 }, /* P_GR_P2Q2 */
+ { 0x3686, 0xC652 }, /* P_GR_P2Q3 */
+ { 0x3688, 0xBC15 }, /* P_GR_P2Q4 */
+ { 0x36C0, 0xB8AD }, /* P_GR_P3Q0 */
+ { 0x36C2, 0xBDCD }, /* P_GR_P3Q1 */
+ { 0x36C4, 0xE4B2 }, /* P_GR_P3Q2 */
+ { 0x36C6, 0xB50F }, /* P_GR_P3Q3 */
+ { 0x36C8, 0x5B95 }, /* P_GR_P3Q4 */
+ { 0x3700, 0xFC90 }, /* P_GR_P4Q0 */
+ { 0x3702, 0x8C51 }, /* P_GR_P4Q1 */
+ { 0x3704, 0xCED6 }, /* P_GR_P4Q2 */
+ { 0x3706, 0xB594 }, /* P_GR_P4Q3 */
+ { 0x3708, 0x0A39 }, /* P_GR_P4Q4 */
+ { 0x3614, 0x0230 }, /* P_BL_P0Q0 */
+ { 0x3616, 0x160D }, /* P_BL_P0Q1 */
+ { 0x3618, 0x08D1 }, /* P_BL_P0Q2 */
+ { 0x361A, 0x98AB }, /* P_BL_P0Q3 */
+ { 0x361C, 0xEA50 }, /* P_BL_P0Q4 */
+ { 0x3654, 0xB4EA }, /* P_BL_P1Q0 */
+ { 0x3656, 0xEA6C }, /* P_BL_P1Q1 */
+ { 0x3658, 0xFE08 }, /* P_BL_P1Q2 */
+ { 0x365A, 0x2C6E }, /* P_BL_P1Q3 */
+ { 0x365C, 0xEB0E }, /* P_BL_P1Q4 */
+ { 0x3694, 0x6DF0 }, /* P_BL_P2Q0 */
+ { 0x3696, 0x3ACF }, /* P_BL_P2Q1 */
+ { 0x3698, 0x3E0F }, /* P_BL_P2Q2 */
+ { 0x369A, 0xB2B1 }, /* P_BL_P2Q3 */
+ { 0x369C, 0xC374 }, /* P_BL_P2Q4 */
+ { 0x36D4, 0xF2AA }, /* P_BL_P3Q0 */
+ { 0x36D6, 0x8CCC }, /* P_BL_P3Q1 */
+ { 0x36D8, 0xDEF2 }, /* P_BL_P3Q2 */
+ { 0x36DA, 0xFA11 }, /* P_BL_P3Q3 */
+ { 0x36DC, 0x42F5 }, /* P_BL_P3Q4 */
+ { 0x3714, 0xF4F1 }, /* P_BL_P4Q0 */
+ { 0x3716, 0xF6F0 }, /* P_BL_P4Q1 */
+ { 0x3718, 0x8FD6 }, /* P_BL_P4Q2 */
+ { 0x371A, 0xEA14 }, /* P_BL_P4Q3 */
+ { 0x371C, 0x6338 }, /* P_BL_P4Q4 */
+ { 0x361E, 0x0350 }, /* P_GB_P0Q0 */
+ { 0x3620, 0x91AE }, /* P_GB_P0Q1 */
+ { 0x3622, 0x0571 }, /* P_GB_P0Q2 */
+ { 0x3624, 0x100D }, /* P_GB_P0Q3 */
+ { 0x3626, 0xCA70 }, /* P_GB_P0Q4 */
+ { 0x365E, 0xE6CB }, /* P_GB_P1Q0 */
+ { 0x3660, 0x50ED }, /* P_GB_P1Q1 */
+ { 0x3662, 0x3DAE }, /* P_GB_P1Q2 */
+ { 0x3664, 0xAA4F }, /* P_GB_P1Q3 */
+ { 0x3666, 0xDC50 }, /* P_GB_P1Q4 */
+ { 0x369E, 0x5470 }, /* P_GB_P2Q0 */
+ { 0x36A0, 0x1F6E }, /* P_GB_P2Q1 */
+ { 0x36A2, 0x6671 }, /* P_GB_P2Q2 */
+ { 0x36A4, 0xC010 }, /* P_GB_P2Q3 */
+ { 0x36A6, 0x8DF5 }, /* P_GB_P2Q4 */
+ { 0x36DE, 0x0B0C }, /* P_GB_P3Q0 */
+ { 0x36E0, 0x84CE }, /* P_GB_P3Q1 */
+ { 0x36E2, 0x8493 }, /* P_GB_P3Q2 */
+ { 0x36E4, 0xA610 }, /* P_GB_P3Q3 */
+ { 0x36E6, 0x50B5 }, /* P_GB_P3Q4 */
+ { 0x371E, 0x9651 }, /* P_GB_P4Q0 */
+ { 0x3720, 0x1EAB }, /* P_GB_P4Q1 */
+ { 0x3722, 0xAF76 }, /* P_GB_P4Q2 */
+ { 0x3724, 0xE4F4 }, /* P_GB_P4Q3 */
+ { 0x3726, 0x79F8 }, /* P_GB_P4Q4 */
+ { 0x3782, 0x0410 }, /* POLY_ORIGIN_C */
+ { 0x3784, 0x0320 }, /* POLY_ORIGIN_R */
+ { 0x3780, 0x8000 } /* POLY_SC_ENABLE */
+};
+
+struct mt9t013_reg mt9t013_regs = {
+ .reg_pat = &mt9t013_reg_pat[0],
+ .reg_pat_size = ARRAY_SIZE(mt9t013_reg_pat),
+ .ttbl = &mt9t013_test_tbl[0],
+ .ttbl_size = ARRAY_SIZE(mt9t013_test_tbl),
+ .lctbl = &mt9t013_lc_tbl[0],
+ .lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl),
+ .rftbl = &mt9t013_lc_tbl[0], /* &mt9t013_rolloff_tbl[0], */
+ .rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl)
+};
+
+
diff --git a/drivers/staging/dream/camera/s5k3e2fx.c b/drivers/staging/dream/camera/s5k3e2fx.c
new file mode 100644
index 00000000000..edba19889b0
--- /dev/null
+++ b/drivers/staging/dream/camera/s5k3e2fx.c
@@ -0,0 +1,1310 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "s5k3e2fx.h"
+
+#define S5K3E2FX_REG_MODEL_ID 0x0000
+#define S5K3E2FX_MODEL_ID 0x3E2F
+
+/* PLL Registers */
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLL_MULTIPLIER_MSB 0x0306
+#define REG_PLL_MULTIPLIER_LSB 0x0307
+#define REG_VT_PIX_CLK_DIV 0x0301
+#define REG_VT_SYS_CLK_DIV 0x0303
+#define REG_OP_PIX_CLK_DIV 0x0309
+#define REG_OP_SYS_CLK_DIV 0x030B
+
+/* Data Format Registers */
+#define REG_CCP_DATA_FORMAT_MSB 0x0112
+#define REG_CCP_DATA_FORMAT_LSB 0x0113
+
+/* Output Size */
+#define REG_X_OUTPUT_SIZE_MSB 0x034C
+#define REG_X_OUTPUT_SIZE_LSB 0x034D
+#define REG_Y_OUTPUT_SIZE_MSB 0x034E
+#define REG_Y_OUTPUT_SIZE_LSB 0x034F
+
+/* Binning */
+#define REG_X_EVEN_INC 0x0381
+#define REG_X_ODD_INC 0x0383
+#define REG_Y_EVEN_INC 0x0385
+#define REG_Y_ODD_INC 0x0387
+/*Reserved register */
+#define REG_BINNING_ENABLE 0x3014
+
+/* Frame Fotmat */
+#define REG_FRAME_LENGTH_LINES_MSB 0x0340
+#define REG_FRAME_LENGTH_LINES_LSB 0x0341
+#define REG_LINE_LENGTH_PCK_MSB 0x0342
+#define REG_LINE_LENGTH_PCK_LSB 0x0343
+
+/* MSR setting */
+/* Reserved registers */
+#define REG_SHADE_CLK_ENABLE 0x30AC
+#define REG_SEL_CCP 0x30C4
+#define REG_VPIX 0x3024
+#define REG_CLAMP_ON 0x3015
+#define REG_OFFSET 0x307E
+
+/* CDS timing settings */
+/* Reserved registers */
+#define REG_LD_START 0x3000
+#define REG_LD_END 0x3001
+#define REG_SL_START 0x3002
+#define REG_SL_END 0x3003
+#define REG_RX_START 0x3004
+#define REG_S1_START 0x3005
+#define REG_S1_END 0x3006
+#define REG_S1S_START 0x3007
+#define REG_S1S_END 0x3008
+#define REG_S3_START 0x3009
+#define REG_S3_END 0x300A
+#define REG_CMP_EN_START 0x300B
+#define REG_CLP_SL_START 0x300C
+#define REG_CLP_SL_END 0x300D
+#define REG_OFF_START 0x300E
+#define REG_RMP_EN_START 0x300F
+#define REG_TX_START 0x3010
+#define REG_TX_END 0x3011
+#define REG_STX_WIDTH 0x3012
+#define REG_TYPE1_AF_ENABLE 0x3130
+#define DRIVER_ENABLED 0x0001
+#define AUTO_START_ENABLED 0x0010
+#define REG_NEW_POSITION 0x3131
+#define REG_3152_RESERVED 0x3152
+#define REG_315A_RESERVED 0x315A
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205
+#define REG_FINE_INTEGRATION_TIME 0x0200
+#define REG_COARSE_INTEGRATION_TIME 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203
+
+/* Mode select register */
+#define S5K3E2FX_REG_MODE_SELECT 0x0100
+#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */
+#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */
+#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103
+#define S5K3E2FX_SOFTWARE_RESET 0x01
+#define REG_TEST_PATTERN_MODE 0x0601
+
+struct reg_struct {
+ uint8_t pre_pll_clk_div; /* 0x0305 */
+ uint8_t pll_multiplier_msb; /* 0x0306 */
+ uint8_t pll_multiplier_lsb; /* 0x0307 */
+ uint8_t vt_pix_clk_div; /* 0x0301 */
+ uint8_t vt_sys_clk_div; /* 0x0303 */
+ uint8_t op_pix_clk_div; /* 0x0309 */
+ uint8_t op_sys_clk_div; /* 0x030B */
+ uint8_t ccp_data_format_msb; /* 0x0112 */
+ uint8_t ccp_data_format_lsb; /* 0x0113 */
+ uint8_t x_output_size_msb; /* 0x034C */
+ uint8_t x_output_size_lsb; /* 0x034D */
+ uint8_t y_output_size_msb; /* 0x034E */
+ uint8_t y_output_size_lsb; /* 0x034F */
+ uint8_t x_even_inc; /* 0x0381 */
+ uint8_t x_odd_inc; /* 0x0383 */
+ uint8_t y_even_inc; /* 0x0385 */
+ uint8_t y_odd_inc; /* 0x0387 */
+ uint8_t binning_enable; /* 0x3014 */
+ uint8_t frame_length_lines_msb; /* 0x0340 */
+ uint8_t frame_length_lines_lsb; /* 0x0341 */
+ uint8_t line_length_pck_msb; /* 0x0342 */
+ uint8_t line_length_pck_lsb; /* 0x0343 */
+ uint8_t shade_clk_enable ; /* 0x30AC */
+ uint8_t sel_ccp; /* 0x30C4 */
+ uint8_t vpix; /* 0x3024 */
+ uint8_t clamp_on; /* 0x3015 */
+ uint8_t offset; /* 0x307E */
+ uint8_t ld_start; /* 0x3000 */
+ uint8_t ld_end; /* 0x3001 */
+ uint8_t sl_start; /* 0x3002 */
+ uint8_t sl_end; /* 0x3003 */
+ uint8_t rx_start; /* 0x3004 */
+ uint8_t s1_start; /* 0x3005 */
+ uint8_t s1_end; /* 0x3006 */
+ uint8_t s1s_start; /* 0x3007 */
+ uint8_t s1s_end; /* 0x3008 */
+ uint8_t s3_start; /* 0x3009 */
+ uint8_t s3_end; /* 0x300A */
+ uint8_t cmp_en_start; /* 0x300B */
+ uint8_t clp_sl_start; /* 0x300C */
+ uint8_t clp_sl_end; /* 0x300D */
+ uint8_t off_start; /* 0x300E */
+ uint8_t rmp_en_start; /* 0x300F */
+ uint8_t tx_start; /* 0x3010 */
+ uint8_t tx_end; /* 0x3011 */
+ uint8_t stx_width; /* 0x3012 */
+ uint8_t reg_3152_reserved; /* 0x3152 */
+ uint8_t reg_315A_reserved; /* 0x315A */
+ uint8_t analogue_gain_code_global_msb; /* 0x0204 */
+ uint8_t analogue_gain_code_global_lsb; /* 0x0205 */
+ uint8_t fine_integration_time; /* 0x0200 */
+ uint8_t coarse_integration_time; /* 0x0202 */
+ uint32_t size_h;
+ uint32_t blk_l;
+ uint32_t size_w;
+ uint32_t blk_p;
+};
+
+struct reg_struct s5k3e2fx_reg_pat[2] = {
+ { /* Preview */
+ 0x06, /* pre_pll_clk_div REG=0x0305 */
+ 0x00, /* pll_multiplier_msb REG=0x0306 */
+ 0x88, /* pll_multiplier_lsb REG=0x0307 */
+ 0x0a, /* vt_pix_clk_div REG=0x0301 */
+ 0x01, /* vt_sys_clk_div REG=0x0303 */
+ 0x0a, /* op_pix_clk_div REG=0x0309 */
+ 0x01, /* op_sys_clk_div REG=0x030B */
+ 0x0a, /* ccp_data_format_msb REG=0x0112 */
+ 0x0a, /* ccp_data_format_lsb REG=0x0113 */
+ 0x05, /* x_output_size_msb REG=0x034C */
+ 0x10, /* x_output_size_lsb REG=0x034D */
+ 0x03, /* y_output_size_msb REG=0x034E */
+ 0xcc, /* y_output_size_lsb REG=0x034F */
+
+ /* enable binning for preview */
+ 0x01, /* x_even_inc REG=0x0381 */
+ 0x01, /* x_odd_inc REG=0x0383 */
+ 0x01, /* y_even_inc REG=0x0385 */
+ 0x03, /* y_odd_inc REG=0x0387 */
+ 0x06, /* binning_enable REG=0x3014 */
+
+ 0x03, /* frame_length_lines_msb REG=0x0340 */
+ 0xde, /* frame_length_lines_lsb REG=0x0341 */
+ 0x0a, /* line_length_pck_msb REG=0x0342 */
+ 0xac, /* line_length_pck_lsb REG=0x0343 */
+ 0x81, /* shade_clk_enable REG=0x30AC */
+ 0x01, /* sel_ccp REG=0x30C4 */
+ 0x04, /* vpix REG=0x3024 */
+ 0x00, /* clamp_on REG=0x3015 */
+ 0x02, /* offset REG=0x307E */
+ 0x03, /* ld_start REG=0x3000 */
+ 0x9c, /* ld_end REG=0x3001 */
+ 0x02, /* sl_start REG=0x3002 */
+ 0x9e, /* sl_end REG=0x3003 */
+ 0x05, /* rx_start REG=0x3004 */
+ 0x0f, /* s1_start REG=0x3005 */
+ 0x24, /* s1_end REG=0x3006 */
+ 0x7c, /* s1s_start REG=0x3007 */
+ 0x9a, /* s1s_end REG=0x3008 */
+ 0x10, /* s3_start REG=0x3009 */
+ 0x14, /* s3_end REG=0x300A */
+ 0x10, /* cmp_en_start REG=0x300B */
+ 0x04, /* clp_sl_start REG=0x300C */
+ 0x26, /* clp_sl_end REG=0x300D */
+ 0x02, /* off_start REG=0x300E */
+ 0x0e, /* rmp_en_start REG=0x300F */
+ 0x30, /* tx_start REG=0x3010 */
+ 0x4e, /* tx_end REG=0x3011 */
+ 0x1E, /* stx_width REG=0x3012 */
+ 0x08, /* reg_3152_reserved REG=0x3152 */
+ 0x10, /* reg_315A_reserved REG=0x315A */
+ 0x00, /* analogue_gain_code_global_msb REG=0x0204 */
+ 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */
+ 0x02, /* fine_integration_time REG=0x0200 */
+ 0x03, /* coarse_integration_time REG=0x0202 */
+ 972,
+ 18,
+ 1296,
+ 1436
+ },
+ { /* Snapshot */
+ 0x06, /* pre_pll_clk_div REG=0x0305 */
+ 0x00, /* pll_multiplier_msb REG=0x0306 */
+ 0x88, /* pll_multiplier_lsb REG=0x0307 */
+ 0x0a, /* vt_pix_clk_div REG=0x0301 */
+ 0x01, /* vt_sys_clk_div REG=0x0303 */
+ 0x0a, /* op_pix_clk_div REG=0x0309 */
+ 0x01, /* op_sys_clk_div REG=0x030B */
+ 0x0a, /* ccp_data_format_msb REG=0x0112 */
+ 0x0a, /* ccp_data_format_lsb REG=0x0113 */
+ 0x0a, /* x_output_size_msb REG=0x034C */
+ 0x30, /* x_output_size_lsb REG=0x034D */
+ 0x07, /* y_output_size_msb REG=0x034E */
+ 0xa8, /* y_output_size_lsb REG=0x034F */
+
+ /* disable binning for snapshot */
+ 0x01, /* x_even_inc REG=0x0381 */
+ 0x01, /* x_odd_inc REG=0x0383 */
+ 0x01, /* y_even_inc REG=0x0385 */
+ 0x01, /* y_odd_inc REG=0x0387 */
+ 0x00, /* binning_enable REG=0x3014 */
+
+ 0x07, /* frame_length_lines_msb REG=0x0340 */
+ 0xb6, /* frame_length_lines_lsb REG=0x0341 */
+ 0x0a, /* line_length_pck_msb REG=0x0342 */
+ 0xac, /* line_length_pck_lsb REG=0x0343 */
+ 0x81, /* shade_clk_enable REG=0x30AC */
+ 0x01, /* sel_ccp REG=0x30C4 */
+ 0x04, /* vpix REG=0x3024 */
+ 0x00, /* clamp_on REG=0x3015 */
+ 0x02, /* offset REG=0x307E */
+ 0x03, /* ld_start REG=0x3000 */
+ 0x9c, /* ld_end REG=0x3001 */
+ 0x02, /* sl_start REG=0x3002 */
+ 0x9e, /* sl_end REG=0x3003 */
+ 0x05, /* rx_start REG=0x3004 */
+ 0x0f, /* s1_start REG=0x3005 */
+ 0x24, /* s1_end REG=0x3006 */
+ 0x7c, /* s1s_start REG=0x3007 */
+ 0x9a, /* s1s_end REG=0x3008 */
+ 0x10, /* s3_start REG=0x3009 */
+ 0x14, /* s3_end REG=0x300A */
+ 0x10, /* cmp_en_start REG=0x300B */
+ 0x04, /* clp_sl_start REG=0x300C */
+ 0x26, /* clp_sl_end REG=0x300D */
+ 0x02, /* off_start REG=0x300E */
+ 0x0e, /* rmp_en_start REG=0x300F */
+ 0x30, /* tx_start REG=0x3010 */
+ 0x4e, /* tx_end REG=0x3011 */
+ 0x1E, /* stx_width REG=0x3012 */
+ 0x08, /* reg_3152_reserved REG=0x3152 */
+ 0x10, /* reg_315A_reserved REG=0x315A */
+ 0x00, /* analogue_gain_code_global_msb REG=0x0204 */
+ 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */
+ 0x02, /* fine_integration_time REG=0x0200 */
+ 0x03, /* coarse_integration_time REG=0x0202 */
+ 1960,
+ 14,
+ 2608,
+ 124
+ }
+};
+
+struct s5k3e2fx_work {
+ struct work_struct work;
+};
+static struct s5k3e2fx_work *s5k3e2fx_sensorw;
+static struct i2c_client *s5k3e2fx_client;
+
+struct s5k3e2fx_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+
+ enum msm_s_resolution prev_res;
+ enum msm_s_resolution pict_res;
+ enum msm_s_resolution curr_res;
+ enum msm_s_test_mode set_test;
+};
+
+struct s5k3e2fx_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned char bdata;
+};
+
+static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue);
+DECLARE_MUTEX(s5k3e2fx_sem);
+
+static int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(s5k3e2fx_client->adapter, msgs, 2) < 0) {
+ CDBG("s5k3e2fx_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t s5k3e2fx_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(s5k3e2fx_client->adapter, msg, 1) < 0) {
+ CDBG("s5k3e2fx_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr,
+ unsigned char bdata)
+{
+ int32_t rc = -EIO;
+ unsigned char buf[4];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00)>>8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+
+ rc = s5k3e2fx_i2c_txdata(saddr, buf, 3);
+
+ if (rc < 0)
+ CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_i2c_write_table(
+ struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ if (rc < 0)
+ break;
+ reg_cfg_tbl++;
+ }
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr,
+ unsigned short *rdata)
+{
+ int32_t rc = 0;
+ unsigned char buf[4];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00)>>8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ CDBG("s5k3e2fx_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int s5k3e2fx_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+ uint16_t chipid = 0;
+
+ rc = gpio_request(data->sensor_reset, "s5k3e2fx");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ mdelay(20);
+
+ CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n");
+
+ rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr,
+ S5K3E2FX_REG_MODEL_ID, &chipid);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ if (chipid != S5K3E2FX_MODEL_ID) {
+ CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid);
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ goto init_probe_done;
+
+init_probe_fail:
+ s5k3e2fx_probe_init_done(data);
+init_probe_done:
+ return rc;
+}
+
+static int s5k3e2fx_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&s5k3e2fx_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id s5k3e2fx_i2c_id[] = {
+ { "s5k3e2fx", 0},
+ { }
+};
+
+static int s5k3e2fx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("s5k3e2fx_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL);
+ if (!s5k3e2fx_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, s5k3e2fx_sensorw);
+ s5k3e2fx_init_client(client);
+ s5k3e2fx_client = client;
+
+ mdelay(50);
+
+ CDBG("s5k3e2fx_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("s5k3e2fx_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static struct i2c_driver s5k3e2fx_i2c_driver = {
+ .id_table = s5k3e2fx_i2c_id,
+ .probe = s5k3e2fx_i2c_probe,
+ .remove = __exit_p(s5k3e2fx_i2c_remove),
+ .driver = {
+ .name = "s5k3e2fx",
+ },
+};
+
+static int32_t s5k3e2fx_test(enum msm_s_test_mode mo)
+{
+ int32_t rc = 0;
+
+ if (mo == S_TEST_OFF)
+ rc = 0;
+ else
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t)mo);
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate,
+ enum msm_s_setting rt)
+{
+ int32_t rc = 0;
+ uint16_t num_lperf;
+
+ switch (rupdate) {
+ case S_UPDATE_PERIODIC:
+ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+
+ struct s5k3e2fx_i2c_reg_conf tbl_1[] = {
+ {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb},
+ {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb},
+ {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb},
+ {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc},
+ {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc},
+ {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc},
+ {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc},
+ {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable},
+ };
+
+ struct s5k3e2fx_i2c_reg_conf tbl_2[] = {
+ {REG_FRAME_LENGTH_LINES_MSB, 0},
+ {REG_FRAME_LENGTH_LINES_LSB, 0},
+ {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb},
+ {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb},
+ {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable},
+ {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp},
+ {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix},
+ {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on},
+ {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset},
+ {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start},
+ {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end},
+ {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start},
+ {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end},
+ {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start},
+ {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start},
+ {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end},
+ {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start},
+ {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end},
+ {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start},
+ {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end},
+ {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start},
+ {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start},
+ {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end},
+ {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start},
+ {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start},
+ {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start},
+ {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end},
+ {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width},
+ {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved},
+ {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb},
+ {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time},
+ {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time},
+ {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM},
+ };
+
+ rc = s5k3e2fx_i2c_write_table(&tbl_1[0],
+ ARRAY_SIZE(tbl_1));
+ if (rc < 0)
+ return rc;
+
+ num_lperf =
+ (uint16_t)((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8) & 0xFF00) +
+ s5k3e2fx_reg_pat[rt].frame_length_lines_lsb;
+
+ num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400;
+
+ tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8};
+ tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)};
+
+ rc = s5k3e2fx_i2c_write_table(&tbl_2[0],
+ ARRAY_SIZE(tbl_2));
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+ }
+ break; /* UPDATE_PERIODIC */
+
+ case S_REG_INIT:
+ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) {
+
+ struct s5k3e2fx_i2c_reg_conf tbl_3[] = {
+ {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET},
+ {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY},
+ /* PLL setting */
+ {REG_PRE_PLL_CLK_DIV, s5k3e2fx_reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER_MSB, s5k3e2fx_reg_pat[rt].pll_multiplier_msb},
+ {REG_PLL_MULTIPLIER_LSB, s5k3e2fx_reg_pat[rt].pll_multiplier_lsb},
+ {REG_VT_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_sys_clk_div},
+ {REG_OP_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].op_sys_clk_div},
+ /*Data Format */
+ {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb},
+ {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb},
+ /*Output Size */
+ {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb},
+ /* Binning */
+ {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc},
+ {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc },
+ {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc},
+ {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc},
+ {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable},
+ /* Frame format */
+ {REG_FRAME_LENGTH_LINES_MSB, s5k3e2fx_reg_pat[rt].frame_length_lines_msb},
+ {REG_FRAME_LENGTH_LINES_LSB, s5k3e2fx_reg_pat[rt].frame_length_lines_lsb},
+ {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb},
+ {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb},
+ /* MSR setting */
+ {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable},
+ {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp},
+ {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix},
+ {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on},
+ {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset},
+ /* CDS timing setting */
+ {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start},
+ {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end},
+ {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start},
+ {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end},
+ {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start},
+ {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start},
+ {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end},
+ {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start},
+ {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end},
+ {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start},
+ {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end},
+ {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start},
+ {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start},
+ {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end},
+ {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start},
+ {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start},
+ {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start},
+ {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end},
+ {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width},
+ {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved},
+ {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb},
+ {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time},
+ {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time},
+ {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM},
+ };
+
+ /* reset fps_divider */
+ s5k3e2fx_ctrl->fps_divider = 1 * 0x0400;
+ rc = s5k3e2fx_i2c_write_table(&tbl_3[0],
+ ARRAY_SIZE(tbl_3));
+ if (rc < 0)
+ return rc;
+ }
+ break; /* case REG_INIT: */
+
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+
+ s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL);
+ if (!s5k3e2fx_ctrl) {
+ CDBG("s5k3e2fx_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400;
+ s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400;
+ s5k3e2fx_ctrl->set_test = S_TEST_OFF;
+ s5k3e2fx_ctrl->prev_res = S_QTR_SIZE;
+ s5k3e2fx_ctrl->pict_res = S_FULL_SIZE;
+
+ if (data)
+ s5k3e2fx_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(24000000);
+ mdelay(20);
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = s5k3e2fx_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail1;
+
+ if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE)
+ rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW);
+ else
+ rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE);
+
+ if (rc < 0) {
+ CDBG("s5k3e2fx_setting failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* initialize AF */
+ if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3146, 0x3A)) < 0)
+ goto init_fail1;
+
+ if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3130, 0x03)) < 0)
+ goto init_fail1;
+
+ goto init_done;
+
+init_fail1:
+ s5k3e2fx_probe_init_done(data);
+ kfree(s5k3e2fx_ctrl);
+init_done:
+ return rc;
+}
+
+static int32_t s5k3e2fx_power_down(void)
+{
+ int32_t rc = 0;
+ return rc;
+}
+
+static int s5k3e2fx_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ down(&s5k3e2fx_sem);
+
+ s5k3e2fx_power_down();
+
+ gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset,
+ 0);
+ gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset);
+
+ kfree(s5k3e2fx_ctrl);
+ s5k3e2fx_ctrl = NULL;
+
+ CDBG("s5k3e2fx_release completed\n");
+
+ up(&s5k3e2fx_sem);
+ return rc;
+}
+
+static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider; /* Q10 */
+
+ divider = (uint32_t)
+ ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 /
+ ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) *
+ (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p));
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t)(fps * divider / 0x00000400);
+}
+
+static uint16_t s5k3e2fx_get_prev_lines_pf(void)
+{
+ return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l);
+}
+
+static uint16_t s5k3e2fx_get_prev_pixels_pl(void)
+{
+ return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p);
+}
+
+static uint16_t s5k3e2fx_get_pict_lines_pf(void)
+{
+ return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l);
+}
+
+static uint16_t s5k3e2fx_get_pict_pixels_pl(void)
+{
+ return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p);
+}
+
+static uint32_t s5k3e2fx_get_pict_max_exp_lc(void)
+{
+ uint32_t snapshot_lines_per_frame;
+
+ if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE)
+ snapshot_lines_per_frame =
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l;
+ else
+ snapshot_lines_per_frame = 3961 * 3;
+
+ return snapshot_lines_per_frame;
+}
+
+static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps)
+{
+ /* input is new fps in Q10 format */
+ int32_t rc = 0;
+
+ s5k3e2fx_ctrl->fps_divider = fps->fps_div;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_FRAME_LENGTH_LINES_MSB,
+ (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8);
+ if (rc < 0)
+ goto set_fps_done;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_FRAME_LENGTH_LINES_LSB,
+ (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) *
+ s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00));
+
+set_fps_done:
+ return rc;
+}
+
+static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+
+ uint16_t max_legal_gain = 0x0200;
+ uint32_t ll_ratio; /* Q10 */
+ uint16_t ll_pck, fl_lines;
+ uint16_t offset = 4;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_t_msb, intg_t_lsb;
+ uint8_t ll_pck_msb, ll_pck_lsb, tmp;
+
+ struct s5k3e2fx_i2c_reg_conf tbl[2];
+
+ CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__);
+
+ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+
+ s5k3e2fx_ctrl->my_reg_gain = gain;
+ s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line;
+
+ fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+
+ ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+
+ } else {
+
+ fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l;
+
+ ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w +
+ s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p;
+ }
+
+ if (gain > max_legal_gain)
+ gain = max_legal_gain;
+
+ /* in Q10 */
+ line = (line * s5k3e2fx_ctrl->fps_divider);
+
+ if (fl_lines < (line / 0x400))
+ ll_ratio = (line / (fl_lines - offset));
+ else
+ ll_ratio = 0x400;
+
+ /* update gain registers */
+ gain_msb = (gain & 0xFF00) >> 8;
+ gain_lsb = gain & 0x00FF;
+ tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB;
+ tbl[0].bdata = gain_msb;
+ tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB;
+ tbl[1].bdata = gain_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+
+ ll_pck = ll_pck * ll_ratio;
+ ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8;
+ ll_pck_lsb = (ll_pck / 0x400) & 0x00FF;
+ tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB;
+ tbl[0].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_msb;
+ tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB;
+ tbl[1].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+
+ tmp = (ll_pck * 0x400) / ll_ratio;
+ intg_t_msb = (tmp & 0xFF00) >> 8;
+ intg_t_lsb = (tmp & 0x00FF);
+ tbl[0].waddr = REG_COARSE_INTEGRATION_TIME;
+ tbl[0].bdata = intg_t_msb;
+ tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB;
+ tbl[1].bdata = intg_t_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+
+write_gain_done:
+ return rc;
+}
+
+static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+
+ CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__);
+
+ rc =
+ s5k3e2fx_write_exp_gain(gain, line);
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_video_config(int mode, int res)
+{
+ int32_t rc;
+
+ switch (res) {
+ case S_QTR_SIZE:
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+
+ CDBG("s5k3e2fx sensor configuration done!\n");
+ break;
+
+ case S_FULL_SIZE:
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ default:
+ return 0;
+ } /* switch */
+
+ s5k3e2fx_ctrl->prev_res = res;
+ s5k3e2fx_ctrl->curr_res = res;
+ s5k3e2fx_ctrl->sensormode = mode;
+
+ rc =
+ s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain,
+ s5k3e2fx_ctrl->my_reg_line_count);
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res;
+ s5k3e2fx_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res;
+ s5k3e2fx_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_set_sensor_mode(int mode, int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = s5k3e2fx_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = s5k3e2fx_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = s5k3e2fx_raw_snapshot_config(mode);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_set_default_focus(void)
+{
+ int32_t rc = 0;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3131, 0);
+ if (rc < 0)
+ return rc;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3132, 0);
+ if (rc < 0)
+ return rc;
+
+ s5k3e2fx_ctrl->curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps)
+{
+ int32_t rc = 0;
+ int32_t i;
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_pos, pos_offset;
+ int16_t init_code = 50;
+ uint8_t next_pos_msb, next_pos_lsb;
+ int16_t s_move[5];
+ uint32_t gain; /* Q10 format */
+
+ if (direction == MOVE_NEAR)
+ step_direction = 20;
+ else if (direction == MOVE_FAR)
+ step_direction = -20;
+ else {
+ CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ actual_step = step_direction * (int16_t)num_steps;
+ pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos;
+ gain = actual_step * 0x400 / 5;
+
+ for (i = 0; i <= 4; i++) {
+ if (actual_step >= 0)
+ s_move[i] = ((((i+1)*gain+0x200) - (i*gain+0x200))/0x400);
+ else
+ s_move[i] = ((((i+1)*gain-0x200) - (i*gain-0x200))/0x400);
+ }
+
+ /* Ring Damping Code */
+ for (i = 0; i <= 4; i++) {
+ next_pos = (int16_t)(pos_offset + s_move[i]);
+
+ if (next_pos > (738 + init_code))
+ next_pos = 738 + init_code;
+ else if (next_pos < 0)
+ next_pos = 0;
+
+ CDBG("next_position in damping mode = %d\n", next_pos);
+ /* Writing the Values to the actuator */
+ if (next_pos == init_code)
+ next_pos = 0x00;
+
+ next_pos_msb = next_pos >> 8;
+ next_pos_lsb = next_pos & 0x00FF;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, next_pos_msb);
+ if (rc < 0)
+ break;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, next_pos_lsb);
+ if (rc < 0)
+ break;
+
+ pos_offset = next_pos;
+ s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code;
+ if (i < 4)
+ mdelay(3);
+ }
+
+ return rc;
+}
+
+static int s5k3e2fx_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ long rc = 0;
+
+ if (copy_from_user(&cdata,
+ (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ down(&s5k3e2fx_sem);
+
+ CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps,
+ &(cdata.cfg.gfps.pictfps));
+
+ if (copy_to_user((void *)argp, &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_L_PF:
+ cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PREV_P_PL:
+ cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_L_PF:
+ cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_P_PL:
+ cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_GET_PICT_MAX_EXP_LC:
+ cdata.cfg.pict_max_exp_lc =
+ s5k3e2fx_get_pict_max_exp_lc();
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_FPS:
+ case CFG_SET_PICT_FPS:
+ rc = s5k3e2fx_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__);
+ rc =
+ s5k3e2fx_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc =
+ s5k3e2fx_set_sensor_mode(
+ cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = s5k3e2fx_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc =
+ s5k3e2fx_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ s5k3e2fx_set_default_focus();
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ case CFG_SET_EFFECT:
+ case CFG_SET_LENS_SHADING:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ up(&s5k3e2fx_sem);
+ return rc;
+}
+
+static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+
+ rc = i2c_add_driver(&s5k3e2fx_i2c_driver);
+ if (rc < 0 || s5k3e2fx_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+
+ msm_camio_clk_rate_set(24000000);
+ mdelay(20);
+
+ rc = s5k3e2fx_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+
+ s->s_init = s5k3e2fx_sensor_open_init;
+ s->s_release = s5k3e2fx_sensor_release;
+ s->s_config = s5k3e2fx_sensor_config;
+ s5k3e2fx_probe_init_done(info);
+
+ return rc;
+
+probe_fail:
+ CDBG("SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __s5k3e2fx_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __s5k3e2fx_probe,
+ .driver = {
+ .name = "msm_camera_s5k3e2fx",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s5k3e2fx_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(s5k3e2fx_init);
+
diff --git a/drivers/staging/dream/camera/s5k3e2fx.h b/drivers/staging/dream/camera/s5k3e2fx.h
new file mode 100644
index 00000000000..69bc7508445
--- /dev/null
+++ b/drivers/staging/dream/camera/s5k3e2fx.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (C) 2008-2009 QUALCOMM Incorporated.
+ */
+
+#ifndef CAMSENSOR_S5K3E2FX
+#define CAMSENSOR_S5K3E2FX
+
+#include <mach/board.h>
+#endif /* CAMSENSOR_S5K3E2FX */
diff --git a/drivers/staging/dream/gpio_axis.c b/drivers/staging/dream/gpio_axis.c
new file mode 100644
index 00000000000..c801172aa9e
--- /dev/null
+++ b/drivers/staging/dream/gpio_axis.c
@@ -0,0 +1,180 @@
+/* drivers/input/misc/gpio_axis.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/interrupt.h>
+
+struct gpio_axis_state {
+ struct input_dev *input_dev;
+ struct gpio_event_axis_info *info;
+ uint32_t pos;
+};
+
+uint16_t gpio_axis_4bit_gray_map_table[] = {
+ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
+ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
+ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
+ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
+ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
+ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
+ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
+ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
+};
+uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_4bit_gray_map_table[in];
+}
+
+uint16_t gpio_axis_5bit_singletrack_map_table[] = {
+ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
+ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
+ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
+ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
+ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
+ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
+ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
+ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
+ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
+ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
+};
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_5bit_singletrack_map_table[in];
+}
+
+static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
+{
+ struct gpio_event_axis_info *ai = as->info;
+ int i;
+ int change;
+ uint16_t state = 0;
+ uint16_t pos;
+ uint16_t old_pos = as->pos;
+ for (i = ai->count - 1; i >= 0; i--)
+ state = (state << 1) | gpio_get_value(ai->gpio[i]);
+ pos = ai->map(ai, state);
+ if (ai->flags & GPIOEAF_PRINT_RAW)
+ pr_info("axis %d-%d raw %x, pos %d -> %d\n",
+ ai->type, ai->code, state, old_pos, pos);
+ if (report && pos != old_pos) {
+ if (ai->type == EV_REL) {
+ change = (ai->decoded_size + pos - old_pos) %
+ ai->decoded_size;
+ if (change > ai->decoded_size / 2)
+ change -= ai->decoded_size;
+ if (change == ai->decoded_size / 2) {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d unknown direction, "
+ "pos %d -> %d\n", ai->type,
+ ai->code, old_pos, pos);
+ change = 0; /* no closest direction */
+ }
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d change %d\n",
+ ai->type, ai->code, change);
+ input_report_rel(as->input_dev, ai->code, change);
+ } else {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d now %d\n",
+ ai->type, ai->code, pos);
+ input_event(as->input_dev, ai->type, ai->code, pos);
+ }
+ input_sync(as->input_dev);
+ }
+ as->pos = pos;
+}
+
+static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_axis_state *as = dev_id;
+ gpio_event_update_axis(as, 1);
+ return IRQ_HANDLED;
+}
+
+int gpio_event_axis_func(struct input_dev *input_dev,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ int irq;
+ struct gpio_event_axis_info *ai;
+ struct gpio_axis_state *as;
+
+ ai = container_of(info, struct gpio_event_axis_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ for (i = 0; i < ai->count; i++)
+ disable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ for (i = 0; i < ai->count; i++)
+ enable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ *data = as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (as == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_axis_state_failed;
+ }
+ as->input_dev = input_dev;
+ as->info = ai;
+
+ input_set_capability(input_dev, ai->type, ai->code);
+ if (ai->type == EV_ABS) {
+ input_set_abs_params(input_dev, ai->code, 0,
+ ai->decoded_size - 1, 0, 0);
+ }
+ for (i = 0; i < ai->count; i++) {
+ ret = gpio_request(ai->gpio[i], "gpio_event_axis");
+ if (ret < 0)
+ goto err_request_gpio_failed;
+ ret = gpio_direction_input(ai->gpio[i]);
+ if (ret < 0)
+ goto err_gpio_direction_input_failed;
+ ret = irq = gpio_to_irq(ai->gpio[i]);
+ if (ret < 0)
+ goto err_get_irq_num_failed;
+ ret = request_irq(irq, gpio_axis_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "gpio_event_axis", as);
+ if (ret < 0)
+ goto err_request_irq_failed;
+ }
+ gpio_event_update_axis(as, 0);
+ return 0;
+ }
+
+ ret = 0;
+ as = *data;
+ for (i = ai->count - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ai->gpio[i]), as);
+err_request_irq_failed:
+err_get_irq_num_failed:
+err_gpio_direction_input_failed:
+ gpio_free(ai->gpio[i]);
+err_request_gpio_failed:
+ ;
+ }
+ kfree(as);
+ *data = NULL;
+err_alloc_axis_state_failed:
+ return ret;
+}
diff --git a/drivers/staging/dream/gpio_event.c b/drivers/staging/dream/gpio_event.c
new file mode 100644
index 00000000000..8b64c1e579b
--- /dev/null
+++ b/drivers/staging/dream/gpio_event.c
@@ -0,0 +1,224 @@
+/* drivers/input/misc/gpio_event.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+
+struct gpio_event {
+ struct input_dev *input_dev;
+ const struct gpio_event_platform_data *info;
+ struct early_suspend early_suspend;
+ void *state[0];
+};
+
+static int gpio_input_event(
+ struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ int i;
+ int ret = 0;
+ int tmp_ret;
+ struct gpio_event_info **ii;
+ struct gpio_event *ip = input_get_drvdata(dev);
+
+ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->event) {
+ tmp_ret = (*ii)->event(ip->input_dev, *ii,
+ &ip->state[i], type, code, value);
+ if (tmp_ret)
+ ret = tmp_ret;
+ }
+ }
+ return ret;
+}
+
+static int gpio_event_call_all_func(struct gpio_event *ip, int func)
+{
+ int i;
+ int ret;
+ struct gpio_event_info **ii;
+
+ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
+ ii = ip->info->info;
+ for (i = 0; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->func == NULL) {
+ ret = -ENODEV;
+ pr_err("gpio_event_probe: Incomplete pdata, "
+ "no function\n");
+ goto err_no_func;
+ }
+ ret = (*ii)->func(ip->input_dev, *ii, &ip->state[i],
+ func);
+ if (ret) {
+ pr_err("gpio_event_probe: function failed\n");
+ goto err_func_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ i = ip->info->info_count;
+ ii = ip->info->info + i;
+ while (i > 0) {
+ i--;
+ ii--;
+ (*ii)->func(ip->input_dev, *ii, &ip->state[i], func & ~1);
+err_func_failed:
+err_no_func:
+ ;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void gpio_event_suspend(struct early_suspend *h)
+{
+ struct gpio_event *ip;
+ ip = container_of(h, struct gpio_event, early_suspend);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
+ ip->info->power(ip->info, 0);
+}
+
+void gpio_event_resume(struct early_suspend *h)
+{
+ struct gpio_event *ip;
+ ip = container_of(h, struct gpio_event, early_suspend);
+ ip->info->power(ip->info, 1);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
+}
+#endif
+
+static int __init gpio_event_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_event *ip;
+ struct input_dev *input_dev;
+ struct gpio_event_platform_data *event_info;
+
+ event_info = pdev->dev.platform_data;
+ if (event_info == NULL) {
+ pr_err("gpio_event_probe: No pdata\n");
+ return -ENODEV;
+ }
+ if (event_info->name == NULL ||
+ event_info->info == NULL ||
+ event_info->info_count == 0) {
+ pr_err("gpio_event_probe: Incomplete pdata\n");
+ return -ENODEV;
+ }
+ ip = kzalloc(sizeof(*ip) +
+ sizeof(ip->state[0]) * event_info->info_count, GFP_KERNEL);
+ if (ip == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ platform_set_drvdata(pdev, ip);
+
+ input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ ip->input_dev = input_dev;
+ ip->info = event_info;
+ if (event_info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ip->early_suspend.suspend = gpio_event_suspend;
+ ip->early_suspend.resume = gpio_event_resume;
+ register_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 1);
+ }
+
+ input_dev->name = ip->info->name;
+ input_dev->event = gpio_input_event;
+
+ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
+ if (err)
+ goto err_call_all_func_failed;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s input device\n",
+ input_dev->name);
+ goto err_input_register_device_failed;
+ }
+
+ return 0;
+
+err_input_register_device_failed:
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+err_call_all_func_failed:
+ if (event_info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 0);
+ }
+ input_free_device(input_dev);
+err_input_dev_alloc_failed:
+ kfree(ip);
+err_kp_alloc_failed:
+ return err;
+}
+
+static int gpio_event_remove(struct platform_device *pdev)
+{
+ struct gpio_event *ip = platform_get_drvdata(pdev);
+
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+ if (ip->info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 0);
+ }
+ input_unregister_device(ip->input_dev);
+ kfree(ip);
+ return 0;
+}
+
+static struct platform_driver gpio_event_driver = {
+ .probe = gpio_event_probe,
+ .remove = gpio_event_remove,
+ .driver = {
+ .name = GPIO_EVENT_DEV_NAME,
+ },
+};
+
+static int __devinit gpio_event_init(void)
+{
+ return platform_driver_register(&gpio_event_driver);
+}
+
+static void __exit gpio_event_exit(void)
+{
+ platform_driver_unregister(&gpio_event_driver);
+}
+
+module_init(gpio_event_init);
+module_exit(gpio_event_exit);
+
+MODULE_DESCRIPTION("GPIO Event Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/staging/dream/gpio_input.c b/drivers/staging/dream/gpio_input.c
new file mode 100644
index 00000000000..7e307f267a2
--- /dev/null
+++ b/drivers/staging/dream/gpio_input.c
@@ -0,0 +1,343 @@
+/* drivers/input/misc/gpio_input.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/wakelock.h>
+
+enum {
+ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
+ DEBOUNCE_PRESSED = BIT(1),
+ DEBOUNCE_NOTPRESSED = BIT(2),
+ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
+ DEBOUNCE_POLL = BIT(4), /* Stable polling state */
+
+ DEBOUNCE_UNKNOWN =
+ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
+};
+
+struct gpio_key_state {
+ struct gpio_input_state *ds;
+ uint8_t debounce;
+};
+
+struct gpio_input_state {
+ struct input_dev *input_dev;
+ const struct gpio_event_input_info *info;
+ struct hrtimer timer;
+ int use_irq;
+ int debounce_count;
+ spinlock_t irq_lock;
+ struct wake_lock wake_lock;
+ struct gpio_key_state key_state[0];
+};
+
+static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
+{
+ int i;
+ int pressed;
+ struct gpio_input_state *ds =
+ container_of(timer, struct gpio_input_state, timer);
+ unsigned gpio_flags = ds->info->flags;
+ unsigned npolarity;
+ int nkeys = ds->info->keymap_size;
+ const struct gpio_event_direct_entry *key_entry;
+ struct gpio_key_state *key_state;
+ unsigned long irqflags;
+ uint8_t debounce;
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++)
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+#endif
+ key_entry = ds->info->keymap;
+ key_state = ds->key_state;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ debounce = key_state->debounce;
+ if (debounce & DEBOUNCE_WAIT_IRQ)
+ continue;
+ if (key_state->debounce & DEBOUNCE_UNSTABLE) {
+ debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
+ enable_irq(gpio_to_irq(key_entry->gpio));
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) continue debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
+ pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
+ if (debounce & DEBOUNCE_POLL) {
+ if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
+ ds->debounce_count++;
+ key_state->debounce = DEBOUNCE_UNKNOWN;
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-"
+ "%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ continue;
+ }
+ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 1\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_PRESSED;
+ continue;
+ }
+ if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 0\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_NOTPRESSED;
+ continue;
+ }
+ /* key is stable */
+ ds->debounce_count--;
+ if (ds->use_irq)
+ key_state->debounce |= DEBOUNCE_WAIT_IRQ;
+ else
+ key_state->debounce |= DEBOUNCE_POLL;
+ if (gpio_flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
+ "changed to %d\n", ds->info->type,
+ key_entry->code, i, key_entry->gpio, pressed);
+ input_event(ds->input_dev, ds->info->type,
+ key_entry->code, pressed);
+ }
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+ }
+#endif
+
+ if (ds->debounce_count)
+ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
+ else if (!ds->use_irq)
+ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
+ else
+ wake_unlock(&ds->wake_lock);
+
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_key_state *ks = dev_id;
+ struct gpio_input_state *ds = ks->ds;
+ int keymap_index = ks - ds->key_state;
+ const struct gpio_event_direct_entry *key_entry;
+ unsigned long irqflags;
+ int pressed;
+
+ if (!ds->use_irq)
+ return IRQ_HANDLED;
+
+ key_entry = &ds->info->keymap[keymap_index];
+
+ if (ds->info->debounce_time.tv64) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
+ ks->debounce = DEBOUNCE_UNKNOWN;
+ if (ds->debounce_count++ == 0) {
+ wake_lock(&ds->wake_lock);
+ hrtimer_start(
+ &ds->timer, ds->info->debounce_time,
+ HRTIMER_MODE_REL);
+ }
+ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_event_input_irq_handler: "
+ "key %x-%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ keymap_index, key_entry->gpio);
+ } else {
+ disable_irq(irq);
+ ks->debounce = DEBOUNCE_UNSTABLE;
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ } else {
+ pressed = gpio_get_value(key_entry->gpio) ^
+ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
+ if (ds->info->flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
+ "(%d) changed to %d\n",
+ ds->info->type, key_entry->code, keymap_index,
+ key_entry->gpio, pressed);
+ input_event(ds->input_dev, ds->info->type,
+ key_entry->code, pressed);
+ }
+ return IRQ_HANDLED;
+}
+
+static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ for (i = 0; i < ds->info->keymap_size; i++) {
+ err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_event_input_irq_handler,
+ req_flags, "gpio_keys", &ds->key_state[i]);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: request_irq "
+ "failed for input %d, irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_request_irq_failed;
+ }
+ enable_irq_wake(irq);
+ }
+ return 0;
+
+ for (i = ds->info->keymap_size - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ds->info->keymap[i].gpio),
+ &ds->key_state[i]);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_input_func(struct input_dev *input_dev,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ unsigned long irqflags;
+ struct gpio_event_input_info *di;
+ struct gpio_input_state *ds = *data;
+
+ di = container_of(info, struct gpio_event_input_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ disable_irq(gpio_to_irq(di->keymap[i].gpio));
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ enable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (ktime_to_ns(di->poll_time) <= 0)
+ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
+
+ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
+ di->keymap_size, GFP_KERNEL);
+ if (ds == NULL) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate private data\n");
+ goto err_ds_alloc_failed;
+ }
+ ds->debounce_count = di->keymap_size;
+ ds->input_dev = input_dev;
+ ds->info = di;
+ wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input");
+ spin_lock_init(&ds->irq_lock);
+
+ for (i = 0; i < di->keymap_size; i++) {
+ input_set_capability(input_dev, di->type,
+ di->keymap[i].code);
+ ds->key_state[i].ds = ds;
+ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
+ }
+
+ for (i = 0; i < di->keymap_size; i++) {
+ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
+ if (ret) {
+ pr_err("gpio_event_input_func: gpio_request "
+ "failed for %d\n", di->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_input(di->keymap[i].gpio);
+ if (ret) {
+ pr_err("gpio_event_input_func: "
+ "gpio_direction_input failed for %d\n",
+ di->keymap[i].gpio);
+ goto err_gpio_configure_failed;
+ }
+ }
+
+ ret = gpio_event_input_request_irqs(ds);
+
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ ds->use_irq = ret == 0;
+
+ pr_info("GPIO Input Driver: Start gpio inputs for %s in %s "
+ "mode\n",
+ input_dev->name, ret == 0 ? "interrupt" : "polling");
+
+ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ds->timer.function = gpio_event_input_timer_func;
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ ret = 0;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ if (ds->use_irq) {
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(di->keymap[i].gpio),
+ &ds->key_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+err_gpio_configure_failed:
+ gpio_free(di->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+ wake_lock_destroy(&ds->wake_lock);
+ kfree(ds);
+err_ds_alloc_failed:
+ return ret;
+}
diff --git a/drivers/staging/dream/gpio_matrix.c b/drivers/staging/dream/gpio_matrix.c
new file mode 100644
index 00000000000..c1f47651a49
--- /dev/null
+++ b/drivers/staging/dream/gpio_matrix.c
@@ -0,0 +1,406 @@
+/* drivers/input/misc/gpio_matrix.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/wakelock.h>
+
+struct gpio_kp {
+ struct input_dev *input_dev;
+ struct gpio_event_matrix_info *keypad_info;
+ struct hrtimer timer;
+ struct wake_lock wake_lock;
+ int current_output;
+ unsigned int use_irq:1;
+ unsigned int key_state_changed:1;
+ unsigned int last_key_state_changed:1;
+ unsigned int some_keys_pressed:2;
+ unsigned long keys_pressed[0];
+};
+
+static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int key_index = out * mi->ninputs + in;
+ unsigned short keycode = mi->keymap[key_index];;
+
+ if (!test_bit(keycode, kp->input_dev->key)) {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ __clear_bit(key_index, kp->keys_pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "not cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ }
+}
+
+static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
+{
+ int rv = 0;
+ int key_index;
+
+ key_index = out * kp->keypad_info->ninputs + in;
+ while (out < kp->keypad_info->noutputs) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ rv = 1;
+ clear_phantom_key(kp, out, in);
+ }
+ key_index += kp->keypad_info->ninputs;
+ out++;
+ }
+ return rv;
+}
+
+static void remove_phantom_keys(struct gpio_kp *kp)
+{
+ int out, in, inp;
+ int key_index;
+
+ if (kp->some_keys_pressed < 3)
+ return;
+
+ for (out = 0; out < kp->keypad_info->noutputs; out++) {
+ inp = -1;
+ key_index = out * kp->keypad_info->ninputs;
+ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ if (inp == -1) {
+ inp = in;
+ continue;
+ }
+ if (inp >= 0) {
+ if (!restore_keys_for_input(kp, out + 1,
+ inp))
+ break;
+ clear_phantom_key(kp, out, inp);
+ inp = -2;
+ }
+ restore_keys_for_input(kp, out, in);
+ }
+ }
+ }
+}
+
+static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int pressed = test_bit(key_index, kp->keys_pressed);
+ unsigned short keycode = mi->keymap[key_index];
+ if (pressed != test_bit(keycode, kp->input_dev->key)) {
+ if (keycode == KEY_RESERVED) {
+ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
+ pr_info("gpiomatrix: unmapped key, %d-%d "
+ "(%d-%d) changed to %d\n",
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
+ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
+ "changed to %d\n", keycode,
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ input_report_key(kp->input_dev, keycode, pressed);
+ }
+ }
+}
+
+static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
+{
+ int out, in;
+ int key_index;
+ int gpio;
+ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
+
+ out = kp->current_output;
+ if (out == mi->noutputs) {
+ out = 0;
+ kp->last_key_state_changed = kp->key_state_changed;
+ kp->key_state_changed = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ key_index = out * mi->ninputs;
+ for (in = 0; in < mi->ninputs; in++, key_index++) {
+ gpio = mi->input_gpios[in];
+ if (gpio_get_value(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ kp->key_state_changed |= !__test_and_set_bit(
+ key_index, kp->keys_pressed);
+ } else
+ kp->key_state_changed |= __test_and_clear_bit(
+ key_index, kp->keys_pressed);
+ }
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, !polarity);
+ else
+ gpio_direction_input(gpio);
+ out++;
+ }
+ kp->current_output = out;
+ if (out < mi->noutputs) {
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, polarity);
+ else
+ gpio_direction_output(gpio, polarity);
+ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
+ if (kp->key_state_changed) {
+ hrtimer_start(&kp->timer, mi->debounce_delay,
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ kp->key_state_changed = kp->last_key_state_changed;
+ }
+ if (kp->key_state_changed) {
+ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
+ remove_phantom_keys(kp);
+ key_index = 0;
+ for (out = 0; out < mi->noutputs; out++)
+ for (in = 0; in < mi->ninputs; in++, key_index++)
+ report_key(kp, key_index, out, in);
+ }
+ if (!kp->use_irq || kp->some_keys_pressed) {
+ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < mi->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[out], polarity);
+ else
+ gpio_direction_output(mi->output_gpios[out], polarity);
+ }
+ for (in = 0; in < mi->ninputs; in++)
+ enable_irq(gpio_to_irq(mi->input_gpios[in]));
+ wake_unlock(&kp->wake_lock);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
+{
+ int i;
+ struct gpio_kp *kp = dev_id;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+
+ if (!kp->use_irq) /* ignore interrupt while registering the handler */
+ return IRQ_HANDLED;
+
+ for (i = 0; i < mi->ninputs; i++)
+ disable_irq(gpio_to_irq(mi->input_gpios[i]));
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[i],
+ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ gpio_direction_input(mi->output_gpios[i]);
+ }
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static int gpio_keypad_request_irqs(struct gpio_kp *kp)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long request_flags;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+
+ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
+ default:
+ request_flags = IRQF_TRIGGER_FALLING;
+ break;
+ case GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_RISING;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ:
+ request_flags = IRQF_TRIGGER_LOW;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_HIGH;
+ break;
+ }
+
+ for (i = 0; i < mi->ninputs; i++) {
+ err = irq = gpio_to_irq(mi->input_gpios[i]);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
+ "gpio_kp", kp);
+ if (err) {
+ pr_err("gpiomatrix: request_irq failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ goto err_request_irq_failed;
+ }
+ err = set_irq_wake(irq, 1);
+ if (err) {
+ pr_err("gpiomatrix: set_irq_wake failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ }
+ disable_irq(irq);
+ }
+ return 0;
+
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_matrix_func(struct input_dev *input_dev,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int i;
+ int err;
+ int key_count;
+ struct gpio_kp *kp;
+ struct gpio_event_matrix_info *mi;
+
+ mi = container_of(info, struct gpio_event_matrix_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
+ /* TODO: disable scanning */
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (mi->keymap == NULL ||
+ mi->input_gpios == NULL ||
+ mi->output_gpios == NULL) {
+ err = -ENODEV;
+ pr_err("gpiomatrix: Incomplete pdata\n");
+ goto err_invalid_platform_data;
+ }
+ key_count = mi->ninputs * mi->noutputs;
+
+ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
+ BITS_TO_LONGS(key_count), GFP_KERNEL);
+ if (kp == NULL) {
+ err = -ENOMEM;
+ pr_err("gpiomatrix: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ kp->input_dev = input_dev;
+ kp->keypad_info = mi;
+ set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; i < key_count; i++) {
+ if (mi->keymap[i])
+ set_bit(mi->keymap[i] & KEY_MAX,
+ input_dev->keybit);
+ }
+
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_cansleep(mi->output_gpios[i])) {
+ pr_err("gpiomatrix: unsupported output gpio %d,"
+ " can sleep\n", mi->output_gpios[i]);
+ err = -EINVAL;
+ goto err_request_output_gpio_failed;
+ }
+ err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_request_output_gpio_failed;
+ }
+ if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
+ err = gpio_direction_output(mi->output_gpios[i],
+ !(mi->flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ err = gpio_direction_input(mi->output_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_configure failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_output_gpio_configure_failed;
+ }
+ }
+ for (i = 0; i < mi->ninputs; i++) {
+ err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "input %d\n", mi->input_gpios[i]);
+ goto err_request_input_gpio_failed;
+ }
+ err = gpio_direction_input(mi->input_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_direction_input failed"
+ " for input %d\n", mi->input_gpios[i]);
+ goto err_gpio_direction_input_failed;
+ }
+ }
+ kp->current_output = mi->noutputs;
+ kp->key_state_changed = 1;
+
+ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kp->timer.function = gpio_keypad_timer_func;
+ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ err = gpio_keypad_request_irqs(kp);
+ kp->use_irq = err == 0;
+
+ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for %s "
+ "in %s mode\n", input_dev->name,
+ kp->use_irq ? "interrupt" : "polling");
+
+ if (kp->use_irq)
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+ }
+
+ err = 0;
+ kp = *data;
+
+ if (kp->use_irq)
+ for (i = mi->noutputs - 1; i >= 0; i--)
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+
+ hrtimer_cancel(&kp->timer);
+ wake_lock_destroy(&kp->wake_lock);
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_gpio_direction_input_failed:
+ gpio_free(mi->input_gpios[i]);
+err_request_input_gpio_failed:
+ ;
+ }
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_output_gpio_configure_failed:
+ gpio_free(mi->output_gpios[i]);
+err_request_output_gpio_failed:
+ ;
+ }
+ kfree(kp);
+err_kp_alloc_failed:
+err_invalid_platform_data:
+ return err;
+}
diff --git a/drivers/staging/dream/gpio_output.c b/drivers/staging/dream/gpio_output.c
new file mode 100644
index 00000000000..6f8453c97bd
--- /dev/null
+++ b/drivers/staging/dream/gpio_output.c
@@ -0,0 +1,84 @@
+/* drivers/input/misc/gpio_output.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+
+int gpio_event_output_event(
+ struct input_dev *input_dev, struct gpio_event_info *info, void **data,
+ unsigned int type, unsigned int code, int value)
+{
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+ if (type != oi->type)
+ return 0;
+ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
+ value = !value;
+ for (i = 0; i < oi->keymap_size; i++)
+ if (code == oi->keymap[i].code)
+ gpio_set_value(oi->keymap[i].gpio, value);
+ return 0;
+}
+
+int gpio_event_output_func(
+ struct input_dev *input_dev, struct gpio_event_info *info, void **data,
+ int func)
+{
+ int ret;
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
+ return 0;
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
+ for (i = 0; i < oi->keymap_size; i++)
+ input_set_capability(input_dev, oi->type,
+ oi->keymap[i].code);
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ ret = gpio_request(oi->keymap[i].gpio,
+ "gpio_event_output");
+ if (ret) {
+ pr_err("gpio_event_output_func: gpio_request "
+ "failed for %d\n", oi->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_output(oi->keymap[i].gpio,
+ output_level);
+ if (ret) {
+ pr_err("gpio_event_output_func: "
+ "gpio_direction_output failed for %d\n",
+ oi->keymap[i].gpio);
+ goto err_gpio_direction_output_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ for (i = oi->keymap_size - 1; i >= 0; i--) {
+err_gpio_direction_output_failed:
+ gpio_free(oi->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+ return ret;
+}
+
diff --git a/drivers/staging/dream/qdsp5/Makefile b/drivers/staging/dream/qdsp5/Makefile
new file mode 100644
index 00000000000..991d4a7e157
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/Makefile
@@ -0,0 +1,17 @@
+obj-y += adsp.o
+ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y)
+obj-y += adsp_info.o
+obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o
+else
+obj-y += adsp_6225.o
+endif
+
+obj-y += adsp_driver.o
+obj-y += adsp_video_verify_cmd.o
+obj-y += adsp_videoenc_verify_cmd.o
+obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o
+obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o
+obj-y += adsp_lpm_verify_cmd.o
+obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o
+obj-y += snd.o
+
diff --git a/drivers/staging/dream/qdsp5/adsp.c b/drivers/staging/dream/qdsp5/adsp.c
new file mode 100644
index 00000000000..d096456688d
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp.c
@@ -0,0 +1,1163 @@
+/* arch/arm/mach-msm/qdsp5/adsp.c
+ *
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* TODO:
+ * - move shareable rpc code outside of adsp.c
+ * - general solution for virt->phys patchup
+ * - queue IDs should be relative to modules
+ * - disallow access to non-associated queues
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+
+static struct wake_lock adsp_wake_lock;
+static inline void prevent_suspend(void)
+{
+ wake_lock(&adsp_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+ wake_unlock(&adsp_wake_lock);
+}
+
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include "adsp.h"
+
+#define INT_ADSP INT_ADSP_A9_A11
+
+static struct adsp_info adsp_info;
+static struct msm_rpc_endpoint *rpc_cb_server_client;
+static struct msm_adsp_module *adsp_modules;
+static int adsp_open_count;
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+
+static uint32_t current_image = -1;
+
+void adsp_set_image(struct adsp_info *info, uint32_t image)
+{
+ current_image = image;
+}
+
+/*
+ * Checks whether the module_id is available in the
+ * module_entries table.If module_id is available returns `0`.
+ * If module_id is not available returns `-ENXIO`.
+ */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int32_t adsp_validate_module(uint32_t module_id)
+{
+ uint32_t *ptr;
+ uint32_t module_index;
+ uint32_t num_mod_entries;
+
+ ptr = adsp_info.init_info_ptr->module_entries;
+ num_mod_entries = adsp_info.init_info_ptr->module_table_size;
+
+ for (module_index = 0; module_index < num_mod_entries; module_index++)
+ if (module_id == ptr[module_index])
+ return 0;
+
+ return -ENXIO;
+}
+#else
+static inline int32_t adsp_validate_module(uint32_t module_id) { return 0; }
+#endif
+
+uint32_t adsp_get_module(struct adsp_info *info, uint32_t task)
+{
+ BUG_ON(current_image == -1UL);
+ return info->task_to_module[current_image][task];
+}
+
+uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id)
+{
+ BUG_ON(current_image == -1UL);
+ return info->queue_offset[current_image][queue_id];
+}
+
+static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module,
+ struct msm_adsp_module *adsp_module)
+{
+ int rc;
+ struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+ struct rpc_reply_hdr *rpc_rsp;
+
+ msm_rpc_setup_req(&rpc_req.hdr,
+ RPC_ADSP_RTOS_ATOM_PROG,
+ msm_rpc_get_vers(adsp_module->rpc_client),
+ RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+ rpc_req.gotit = cpu_to_be32(1);
+ rpc_req.cmd = cpu_to_be32(cmd);
+ rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+ rpc_req.module = cpu_to_be32(module);
+ rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req));
+ if (rc < 0) {
+ pr_err("adsp: could not send RPC request: %d\n", rc);
+ return rc;
+ }
+
+ rc = msm_rpc_read(adsp_module->rpc_client,
+ (void **)&rpc_rsp, -1, (5*HZ));
+ if (rc < 0) {
+ pr_err("adsp: error receiving RPC reply: %d (%d)\n",
+ rc, -ERESTARTSYS);
+ return rc;
+ }
+
+ if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) {
+ pr_err("adsp: RPC call was denied!\n");
+ kfree(rpc_rsp);
+ return -EPERM;
+ }
+
+ if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) !=
+ RPC_ACCEPTSTAT_SUCCESS) {
+ pr_err("adsp error: RPC call was not successful (%d)\n",
+ be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat));
+ kfree(rpc_rsp);
+ return -EINVAL;
+ }
+
+ kfree(rpc_rsp);
+ return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int get_module_index(uint32_t id)
+{
+ int mod_idx;
+ for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++)
+ if (adsp_info.module[mod_idx].id == id)
+ return mod_idx;
+
+ return -ENXIO;
+}
+#endif
+
+static struct msm_adsp_module *find_adsp_module_by_id(
+ struct adsp_info *info, uint32_t id)
+{
+ if (id > info->max_module_id) {
+ return NULL;
+ } else {
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ id = get_module_index(id);
+ if (id < 0)
+ return NULL;
+#endif
+ return info->id_to_module[id];
+ }
+}
+
+static struct msm_adsp_module *find_adsp_module_by_name(
+ struct adsp_info *info, const char *name)
+{
+ unsigned n;
+ for (n = 0; n < info->module_count; n++)
+ if (!strcmp(name, adsp_modules[n].name))
+ return adsp_modules + n;
+ return NULL;
+}
+
+static int adsp_rpc_init(struct msm_adsp_module *adsp_module)
+{
+ /* remove the original connect once compatible support is complete */
+ adsp_module->rpc_client = msm_rpc_connect(
+ RPC_ADSP_RTOS_ATOM_PROG,
+ RPC_ADSP_RTOS_ATOM_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+
+ if (IS_ERR(adsp_module->rpc_client)) {
+ int rc = PTR_ERR(adsp_module->rpc_client);
+ adsp_module->rpc_client = 0;
+ pr_err("adsp: could not open rpc client: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+/*
+ * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get
+ * queue offsets and module entries (init info) as part of the event.
+ */
+static void msm_get_init_info(void)
+{
+ int rc;
+ struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+
+ adsp_info.init_info_rpc_client = msm_rpc_connect(
+ RPC_ADSP_RTOS_ATOM_PROG,
+ RPC_ADSP_RTOS_ATOM_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(adsp_info.init_info_rpc_client)) {
+ rc = PTR_ERR(adsp_info.init_info_rpc_client);
+ adsp_info.init_info_rpc_client = 0;
+ pr_err("adsp: could not open rpc client: %d\n", rc);
+ return;
+ }
+
+ msm_rpc_setup_req(&rpc_req.hdr,
+ RPC_ADSP_RTOS_ATOM_PROG,
+ msm_rpc_get_vers(adsp_info.init_info_rpc_client),
+ RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+ rpc_req.gotit = cpu_to_be32(1);
+ rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO);
+ rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+ rpc_req.module = 0;
+
+ rc = msm_rpc_write(adsp_info.init_info_rpc_client,
+ &rpc_req, sizeof(rpc_req));
+ if (rc < 0)
+ pr_err("adsp: could not send RPC request: %d\n", rc);
+}
+#endif
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **out,
+ struct msm_adsp_ops *ops, void *driver_data)
+{
+ struct msm_adsp_module *module;
+ int rc = 0;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ static uint32_t init_info_cmd_sent;
+ if (!init_info_cmd_sent) {
+ msm_get_init_info();
+ init_waitqueue_head(&adsp_info.init_info_wait);
+ rc = wait_event_timeout(adsp_info.init_info_wait,
+ adsp_info.init_info_state == ADSP_STATE_INIT_INFO,
+ 5 * HZ);
+ if (!rc) {
+ pr_info("adsp: INIT_INFO failed\n");
+ return -ETIMEDOUT;
+ }
+ init_info_cmd_sent++;
+ }
+#endif
+
+ module = find_adsp_module_by_name(&adsp_info, name);
+ if (!module)
+ return -ENODEV;
+
+ mutex_lock(&module->lock);
+ pr_info("adsp: opening module %s\n", module->name);
+ if (module->open_count++ == 0 && module->clk)
+ clk_enable(module->clk);
+
+ mutex_lock(&adsp_open_lock);
+ if (adsp_open_count++ == 0) {
+ enable_irq(INT_ADSP);
+ prevent_suspend();
+ }
+ mutex_unlock(&adsp_open_lock);
+
+ if (module->ops) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ rc = adsp_rpc_init(module);
+ if (rc)
+ goto done;
+
+ module->ops = ops;
+ module->driver_data = driver_data;
+ *out = module;
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ module->id, module);
+ if (rc) {
+ module->ops = NULL;
+ module->driver_data = NULL;
+ *out = NULL;
+ pr_err("adsp: REGISTER_APP failed\n");
+ goto done;
+ }
+
+ pr_info("adsp: module %s has been registered\n", module->name);
+
+done:
+ mutex_lock(&adsp_open_lock);
+ if (rc && --adsp_open_count == 0) {
+ disable_irq(INT_ADSP);
+ allow_suspend();
+ }
+ if (rc && --module->open_count == 0 && module->clk)
+ clk_disable(module->clk);
+ mutex_unlock(&adsp_open_lock);
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_get);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module);
+
+void msm_adsp_put(struct msm_adsp_module *module)
+{
+ unsigned long flags;
+
+ mutex_lock(&module->lock);
+ if (--module->open_count == 0 && module->clk)
+ clk_disable(module->clk);
+ if (module->ops) {
+ pr_info("adsp: closing module %s\n", module->name);
+
+ /* lock to ensure a dsp event cannot be delivered
+ * during or after removal of the ops and driver_data
+ */
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+ module->ops = NULL;
+ module->driver_data = NULL;
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+
+ if (module->state != ADSP_STATE_DISABLED) {
+ pr_info("adsp: disabling module %s\n", module->name);
+ msm_adsp_disable_locked(module);
+ }
+
+ msm_rpc_close(module->rpc_client);
+ module->rpc_client = 0;
+ if (--adsp_open_count == 0) {
+ disable_irq(INT_ADSP);
+ allow_suspend();
+ pr_info("adsp: disable interrupt\n");
+ }
+ } else {
+ pr_info("adsp: module %s is already closed\n", module->name);
+ }
+ mutex_unlock(&module->lock);
+}
+EXPORT_SYMBOL(msm_adsp_put);
+
+/* this should be common code with rpc_servers.c */
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+ uint32_t xid, uint32_t accept_status)
+{
+ int rc = 0;
+ uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+ struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+ reply->xid = cpu_to_be32(xid);
+ reply->type = cpu_to_be32(1); /* reply */
+ reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+ reply->data.acc_hdr.verf_flavor = 0;
+ reply->data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf));
+ if (rc < 0)
+ pr_err("adsp: could not write RPC response: %d\n", rc);
+ return rc;
+}
+
+int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ uint32_t ctrl_word;
+ uint32_t dsp_q_addr;
+ uint32_t dsp_addr;
+ uint32_t cmd_id = 0;
+ int cnt = 0;
+ int ret_status = 0;
+ unsigned long flags;
+ struct adsp_info *info = module->info;
+
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+ if (module->state != ADSP_STATE_ENABLED) {
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ pr_err("adsp: module %s not enabled before write\n",
+ module->name);
+ return -ENODEV;
+ }
+ if (adsp_validate_module(module->id)) {
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ pr_info("adsp: module id validation failed %s %d\n",
+ module->name, module->id);
+ return -ENXIO;
+ }
+ dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr);
+ dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M;
+
+ /* Poll until the ADSP is ready to accept a command.
+ * Wait for 100us, return error if it's not responding.
+ * If this returns an error, we need to disable ALL modules and
+ * then retry.
+ */
+ while (((ctrl_word = readl(info->write_ctrl)) &
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_READY_V) {
+ if (cnt > 100) {
+ pr_err("adsp: timeout waiting for DSP write ready\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ pr_warning("adsp: waiting for DSP write ready\n");
+ udelay(1);
+ cnt++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Clear the command bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. This notifies the DSP that
+ * we are about to send a command on this particular queue. The
+ * DSP will in response change its state.
+ */
+ writel(1, info->send_irq);
+
+ /* Poll until the adsp responds to the interrupt; this does not
+ * generate an interrupt from the adsp. This should happen within
+ * 5ms.
+ */
+ cnt = 0;
+ while ((readl(info->write_ctrl) &
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) ==
+ ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) {
+ if (cnt > 5000) {
+ pr_err("adsp: timeout waiting for adsp ack\n");
+ ret_status = -EIO;
+ goto fail;
+ }
+ udelay(1);
+ cnt++;
+ }
+
+ /* Read the ctrl word */
+ ctrl_word = readl(info->write_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) !=
+ ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) {
+ ret_status = -EAGAIN;
+ goto fail;
+ }
+
+ /* Ctrl word status bits were 00, no error in the ctrl word */
+
+ /* Get the DSP buffer address */
+ dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE;
+
+ if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint16_t *buf_ptr = (uint16_t *) cmd_buf;
+ uint16_t *dsp_addr16 = (uint16_t *)dsp_addr;
+ cmd_size /= sizeof(uint16_t);
+
+ /* Save the command ID */
+ cmd_id = (uint32_t) buf_ptr[0];
+
+ /* Copy the command to DSP memory */
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr16++ = *buf_ptr++;
+ } else {
+ uint32_t *buf_ptr = (uint32_t *) cmd_buf;
+ uint32_t *dsp_addr32 = (uint32_t *)dsp_addr;
+ cmd_size /= sizeof(uint32_t);
+
+ /* Save the command ID */
+ cmd_id = buf_ptr[0];
+
+ cmd_size++;
+ while (--cmd_size)
+ *dsp_addr32++ = *buf_ptr++;
+ }
+
+ /* Set the mutex bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+ /* Set the command bits to write done */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+ ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V;
+
+ /* Set the queue address bits */
+ ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+ ctrl_word |= dsp_q_addr;
+
+ writel(ctrl_word, info->write_ctrl);
+
+ /* Generate an interrupt to the DSP. It does not respond with
+ * an interrupt, and we do not need to wait for it to
+ * acknowledge, because it will hold the mutex lock until it's
+ * ready to receive more commands again.
+ */
+ writel(1, info->send_irq);
+
+ module->num_commands++;
+
+fail:
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ return ret_status;
+}
+EXPORT_SYMBOL(msm_adsp_write);
+
+int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+ void *cmd_buf, size_t cmd_size)
+{
+ int rc, retries = 0;
+ do {
+ rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size);
+ if (rc == -EAGAIN)
+ udelay(10);
+ } while(rc == -EAGAIN && retries++ < 100);
+ if (retries > 50)
+ pr_warning("adsp: %s command took %d attempts: rc %d\n",
+ module->name, retries, rc);
+ return rc;
+}
+
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+static void *modem_event_addr;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static void read_modem_event(void *buf, size_t len)
+{
+ uint32_t *dptr = buf;
+ struct rpc_adsp_rtos_modem_to_app_args_t *sptr;
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr;
+
+ sptr = modem_event_addr;
+ pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+ dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event);
+ dptr[1] = be32_to_cpu(pkt_ptr->module);
+ dptr[2] = be32_to_cpu(pkt_ptr->image);
+}
+#else
+static void read_modem_event(void *buf, size_t len)
+{
+ uint32_t *dptr = buf;
+ struct rpc_adsp_rtos_modem_to_app_args_t *sptr =
+ modem_event_addr;
+ dptr[0] = be32_to_cpu(sptr->event);
+ dptr[1] = be32_to_cpu(sptr->module);
+ dptr[2] = be32_to_cpu(sptr->image);
+}
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+#endif /* CONFIG_MSM_ADSP_REPORT_EVENTS */
+
+static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req)
+{
+ struct rpc_adsp_rtos_modem_to_app_args_t *args =
+ (struct rpc_adsp_rtos_modem_to_app_args_t *)req;
+ uint32_t event;
+ uint32_t proc_id;
+ uint32_t module_id;
+ uint32_t image;
+ struct msm_adsp_module *module;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ struct adsp_rtos_mp_mtoa_type *pkt_ptr =
+ &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+ event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event);
+ proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id);
+ module_id = be32_to_cpu(pkt_ptr->module);
+ image = be32_to_cpu(pkt_ptr->image);
+
+ if (be32_to_cpu(args->mtoa_pkt.desc_field) == RPC_ADSP_RTOS_INIT_INFO) {
+ struct queue_to_offset_type *qptr;
+ struct queue_to_offset_type *qtbl;
+ uint32_t *mptr;
+ uint32_t *mtbl;
+ uint32_t q_idx;
+ uint32_t num_entries;
+ uint32_t entries_per_image;
+ struct adsp_rtos_mp_mtoa_init_info_type *iptr;
+ struct adsp_rtos_mp_mtoa_init_info_type *sptr;
+ int32_t i_no, e_idx;
+
+ pr_info("adsp:INIT_INFO Event\n");
+ sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.
+ mp_mtoa_init_packet;
+
+ iptr = adsp_info.init_info_ptr;
+ iptr->image_count = be32_to_cpu(sptr->image_count);
+ iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets);
+ num_entries = iptr->num_queue_offsets;
+ qptr = &sptr->queue_offsets_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ qtbl = &iptr->queue_offsets_tbl[i_no][0];
+ for (e_idx = 0; e_idx < num_entries; e_idx++) {
+ qtbl[e_idx].offset = be32_to_cpu(qptr->offset);
+ qtbl[e_idx].queue = be32_to_cpu(qptr->queue);
+ q_idx = be32_to_cpu(qptr->queue);
+ iptr->queue_offsets[i_no][q_idx] =
+ qtbl[e_idx].offset;
+ qptr++;
+ }
+ }
+
+ num_entries = be32_to_cpu(sptr->num_task_module_entries);
+ iptr->num_task_module_entries = num_entries;
+ entries_per_image = num_entries / iptr->image_count;
+ mptr = &sptr->task_to_module_tbl[0][0];
+ for (i_no = 0; i_no < iptr->image_count; i_no++) {
+ mtbl = &iptr->task_to_module_tbl[i_no][0];
+ for (e_idx = 0; e_idx < entries_per_image; e_idx++) {
+ mtbl[e_idx] = be32_to_cpu(*mptr);
+ mptr++;
+ }
+ }
+
+ iptr->module_table_size = be32_to_cpu(sptr->module_table_size);
+ mptr = &sptr->module_entries[0];
+ for (i_no = 0; i_no < iptr->module_table_size; i_no++)
+ iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]);
+ adsp_info.init_info_state = ADSP_STATE_INIT_INFO;
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ wake_up(&adsp_info.init_info_wait);
+
+ return;
+ }
+#else
+ event = be32_to_cpu(args->event);
+ proc_id = be32_to_cpu(args->proc_id);
+ module_id = be32_to_cpu(args->module);
+ image = be32_to_cpu(args->image);
+#endif
+
+ pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n",
+ event, proc_id, module_id, image);
+
+ module = find_adsp_module_by_id(&adsp_info, module_id);
+ if (!module) {
+ pr_err("adsp: module %d is not supported!\n", module_id);
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_GARBAGE_ARGS);
+ return;
+ }
+
+ mutex_lock(&module->lock);
+ switch (event) {
+ case RPC_ADSP_RTOS_MOD_READY:
+ pr_info("adsp: module %s: READY\n", module->name);
+ module->state = ADSP_STATE_ENABLED;
+ wake_up(&module->state_wait);
+ adsp_set_image(module->info, image);
+ break;
+ case RPC_ADSP_RTOS_MOD_DISABLE:
+ pr_info("adsp: module %s: DISABLED\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_SERVICE_RESET:
+ pr_info("adsp: module %s: SERVICE_RESET\n", module->name);
+ module->state = ADSP_STATE_DISABLED;
+ wake_up(&module->state_wait);
+ break;
+ case RPC_ADSP_RTOS_CMD_SUCCESS:
+ pr_info("adsp: module %s: CMD_SUCCESS\n", module->name);
+ break;
+ case RPC_ADSP_RTOS_CMD_FAIL:
+ pr_info("adsp: module %s: CMD_FAIL\n", module->name);
+ break;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ case RPC_ADSP_RTOS_DISABLE_FAIL:
+ pr_info("adsp: module %s: DISABLE_FAIL\n", module->name);
+ break;
+#endif
+ default:
+ pr_info("adsp: unknown event %d\n", event);
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_GARBAGE_ARGS);
+ mutex_unlock(&module->lock);
+ return;
+ }
+ rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ mutex_unlock(&module->lock);
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+ modem_event_addr = (uint32_t *)req;
+ module->ops->event(module->driver_data, EVENT_MSG_ID,
+ EVENT_LEN, read_modem_event);
+#endif
+}
+
+static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req)
+{
+ switch (req->procedure) {
+ case RPC_ADSP_RTOS_MTOA_NULL_PROC:
+ rpc_send_accepted_void_reply(rpc_cb_server_client,
+ req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ break;
+ case RPC_ADSP_RTOS_MODEM_TO_APP_PROC:
+ handle_adsp_rtos_mtoa_app(req);
+ break;
+ default:
+ pr_err("adsp: unknowned proc %d\n", req->procedure);
+ rpc_send_accepted_void_reply(
+ rpc_cb_server_client, req->xid,
+ RPC_ACCEPTSTAT_PROC_UNAVAIL);
+ break;
+ }
+ return 0;
+}
+
+/* this should be common code with rpc_servers.c */
+static int adsp_rpc_thread(void *data)
+{
+ void *buffer;
+ struct rpc_request_hdr *req;
+ int rc;
+
+ do {
+ rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1);
+ if (rc < 0) {
+ pr_err("adsp: could not read rpc: %d\n", rc);
+ break;
+ }
+ req = (struct rpc_request_hdr *)buffer;
+
+ req->type = be32_to_cpu(req->type);
+ req->xid = be32_to_cpu(req->xid);
+ req->rpc_vers = be32_to_cpu(req->rpc_vers);
+ req->prog = be32_to_cpu(req->prog);
+ req->vers = be32_to_cpu(req->vers);
+ req->procedure = be32_to_cpu(req->procedure);
+
+ if (req->type != 0)
+ goto bad_rpc;
+ if (req->rpc_vers != 2)
+ goto bad_rpc;
+ if (req->prog != RPC_ADSP_RTOS_MTOA_PROG)
+ goto bad_rpc;
+ if (req->vers != RPC_ADSP_RTOS_MTOA_VERS)
+ goto bad_rpc;
+
+ handle_adsp_rtos_mtoa(req);
+ kfree(buffer);
+ continue;
+
+bad_rpc:
+ pr_err("adsp: bogus rpc from modem\n");
+ kfree(buffer);
+ } while (1);
+
+ do_exit(0);
+}
+
+static size_t read_event_size;
+static void *read_event_addr;
+
+static void read_event_16(void *buf, size_t len)
+{
+ uint16_t *dst = buf;
+ uint16_t *src = read_event_addr;
+ len /= 2;
+ if (len > read_event_size)
+ len = read_event_size;
+ while (len--)
+ *dst++ = *src++;
+}
+
+static void read_event_32(void *buf, size_t len)
+{
+ uint32_t *dst = buf;
+ uint32_t *src = read_event_addr;
+ len /= 2;
+ if (len > read_event_size)
+ len = read_event_size;
+ while (len--)
+ *dst++ = *src++;
+}
+
+static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(
+ struct adsp_info *info, void *dsp_addr)
+{
+ struct msm_adsp_module *module;
+ unsigned rtos_task_id;
+ unsigned msg_id;
+ unsigned msg_length;
+ void (*func)(void *, size_t);
+
+ if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+ uint32_t *dsp_addr32 = dsp_addr;
+ uint32_t tmp = *dsp_addr32++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M);
+ read_event_size = tmp >> 16;
+ read_event_addr = dsp_addr32;
+ msg_length = read_event_size * sizeof(uint32_t);
+ func = read_event_32;
+ } else {
+ uint16_t *dsp_addr16 = dsp_addr;
+ uint16_t tmp = *dsp_addr16++;
+ rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+ msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M;
+ read_event_size = *dsp_addr16++;
+ read_event_addr = dsp_addr16;
+ msg_length = read_event_size * sizeof(uint16_t);
+ func = read_event_16;
+ }
+
+ if (rtos_task_id > info->max_task_id) {
+ pr_err("adsp: bogus task id %d\n", rtos_task_id);
+ return 0;
+ }
+ module = find_adsp_module_by_id(info,
+ adsp_get_module(info, rtos_task_id));
+
+ if (!module) {
+ pr_err("adsp: no module for task id %d\n", rtos_task_id);
+ return 0;
+ }
+
+ module->num_events++;
+
+ if (!module->ops) {
+ pr_err("adsp: module %s is not open\n", module->name);
+ return 0;
+ }
+
+ module->ops->event(module->driver_data, msg_id, msg_length, func);
+ return 0;
+}
+
+static int adsp_get_event(struct adsp_info *info)
+{
+ uint32_t ctrl_word;
+ uint32_t ready;
+ void *dsp_addr;
+ uint32_t cmd_type;
+ int cnt;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+ /* Whenever the DSP has a message, it updates this control word
+ * and generates an interrupt. When we receive the interrupt, we
+ * read this register to find out what ADSP task the command is
+ * comming from.
+ *
+ * The ADSP should *always* be ready on the first call, but the
+ * irq handler calls us in a loop (to handle back-to-back command
+ * processing), so we give the DSP some time to return to the
+ * ready state. The DSP will not issue another IRQ for events
+ * pending between the first IRQ and the event queue being drained,
+ * unfortunately.
+ */
+
+ for (cnt = 0; cnt < 10; cnt++) {
+ ctrl_word = readl(info->read_ctrl);
+
+ if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) ==
+ ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V)
+ goto ready;
+
+ udelay(10);
+ }
+ pr_warning("adsp: not ready after 100uS\n");
+ rc = -EBUSY;
+ goto done;
+
+ready:
+ /* Here we check to see if there are pending messages. If there are
+ * none, we siply return -EAGAIN to indicate that there are no more
+ * messages pending.
+ */
+ ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M;
+ if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) &&
+ (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) {
+ rc = -EAGAIN;
+ goto done;
+ }
+
+ /* DSP says that there are messages waiting for the host to read */
+
+ /* Get the Command Type */
+ cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M;
+
+ /* Get the DSP buffer address */
+ dsp_addr = (void *)((ctrl_word &
+ ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) +
+ (uint32_t)MSM_AD5_BASE);
+
+ /* We can only handle Task-to-Host messages */
+ if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) {
+ pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type);
+ rc = -EIO;
+ goto done;
+ }
+
+ adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr);
+
+ ctrl_word = readl(info->read_ctrl);
+ ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M;
+
+ /* Write ctrl word to the DSP */
+ writel(ctrl_word, info->read_ctrl);
+
+ /* Generate an interrupt to the DSP */
+ writel(1, info->send_irq);
+
+done:
+ spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+ return rc;
+}
+
+static irqreturn_t adsp_irq_handler(int irq, void *data)
+{
+ struct adsp_info *info = &adsp_info;
+ int cnt = 0;
+ for (cnt = 0; cnt < 10; cnt++)
+ if (adsp_get_event(info) < 0)
+ break;
+ if (cnt > info->event_backlog_max)
+ info->event_backlog_max = cnt;
+ info->events_received += cnt;
+ if (cnt == 10)
+ pr_err("adsp: too many (%d) events for single irq!\n", cnt);
+ return IRQ_HANDLED;
+}
+
+int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate)
+{
+ if (module->clk && clk_rate)
+ return clk_set_rate(module->clk, clk_rate);
+
+ return -EINVAL;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n",
+ module->name, module->state, module->id);
+
+ mutex_lock(&module->lock);
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE,
+ module->id, module);
+ if (rc)
+ break;
+ module->state = ADSP_STATE_ENABLING;
+ mutex_unlock(&module->lock);
+ rc = wait_event_timeout(module->state_wait,
+ module->state != ADSP_STATE_ENABLING,
+ 1 * HZ);
+ mutex_lock(&module->lock);
+ if (module->state == ADSP_STATE_ENABLED) {
+ rc = 0;
+ } else {
+ pr_err("adsp: module '%s' enable timed out\n",
+ module->name);
+ rc = -ETIMEDOUT;
+ }
+ break;
+ case ADSP_STATE_ENABLING:
+ pr_warning("adsp: module '%s' enable in progress\n",
+ module->name);
+ break;
+ case ADSP_STATE_ENABLED:
+ pr_warning("adsp: module '%s' already enabled\n",
+ module->name);
+ break;
+ case ADSP_STATE_DISABLING:
+ pr_err("adsp: module '%s' disable in progress\n",
+ module->name);
+ rc = -EBUSY;
+ break;
+ }
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_enable);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module)
+{
+ int rc = 0;
+
+ switch (module->state) {
+ case ADSP_STATE_DISABLED:
+ pr_warning("adsp: module '%s' already disabled\n",
+ module->name);
+ break;
+ case ADSP_STATE_ENABLING:
+ case ADSP_STATE_ENABLED:
+ rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE,
+ module->id, module);
+ module->state = ADSP_STATE_DISABLED;
+ }
+ return rc;
+}
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+ int rc;
+ pr_info("msm_adsp_disable() '%s'\n", module->name);
+ mutex_lock(&module->lock);
+ rc = msm_adsp_disable_locked(module);
+ mutex_unlock(&module->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable);
+
+static int msm_adsp_probe(struct platform_device *pdev)
+{
+ unsigned count;
+ int rc, i;
+ int max_module_id;
+
+ pr_info("adsp: probe\n");
+
+ wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp");
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ adsp_info.init_info_ptr = kzalloc(
+ (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL);
+ if (!adsp_info.init_info_ptr)
+ return -ENOMEM;
+#endif
+
+ rc = adsp_init_info(&adsp_info);
+ if (rc)
+ return rc;
+ adsp_info.send_irq += (uint32_t) MSM_AD5_BASE;
+ adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE;
+ adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE;
+ count = adsp_info.module_count;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ max_module_id = count;
+#else
+ max_module_id = adsp_info.max_module_id + 1;
+#endif
+
+ adsp_modules = kzalloc(
+ sizeof(struct msm_adsp_module) * count +
+ sizeof(void *) * max_module_id, GFP_KERNEL);
+ if (!adsp_modules)
+ return -ENOMEM;
+
+ adsp_info.id_to_module = (void *) (adsp_modules + count);
+
+ spin_lock_init(&adsp_cmd_lock);
+
+ rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING,
+ "adsp", 0);
+ if (rc < 0)
+ goto fail_request_irq;
+ disable_irq(INT_ADSP);
+
+ rpc_cb_server_client = msm_rpc_open();
+ if (IS_ERR(rpc_cb_server_client)) {
+ rpc_cb_server_client = NULL;
+ rc = PTR_ERR(rpc_cb_server_client);
+ pr_err("adsp: could not create rpc server (%d)\n", rc);
+ goto fail_rpc_open;
+ }
+
+ rc = msm_rpc_register_server(rpc_cb_server_client,
+ RPC_ADSP_RTOS_MTOA_PROG,
+ RPC_ADSP_RTOS_MTOA_VERS);
+ if (rc) {
+ pr_err("adsp: could not register callback server (%d)\n", rc);
+ goto fail_rpc_register;
+ }
+
+ /* start the kernel thread to process the callbacks */
+ kthread_run(adsp_rpc_thread, NULL, "kadspd");
+
+ for (i = 0; i < count; i++) {
+ struct msm_adsp_module *mod = adsp_modules + i;
+ mutex_init(&mod->lock);
+ init_waitqueue_head(&mod->state_wait);
+ mod->info = &adsp_info;
+ mod->name = adsp_info.module[i].name;
+ mod->id = adsp_info.module[i].id;
+ if (adsp_info.module[i].clk_name)
+ mod->clk = clk_get(NULL, adsp_info.module[i].clk_name);
+ else
+ mod->clk = NULL;
+ if (mod->clk && adsp_info.module[i].clk_rate)
+ clk_set_rate(mod->clk, adsp_info.module[i].clk_rate);
+ mod->verify_cmd = adsp_info.module[i].verify_cmd;
+ mod->patch_event = adsp_info.module[i].patch_event;
+ INIT_HLIST_HEAD(&mod->pmem_regions);
+ mod->pdev.name = adsp_info.module[i].pdev_name;
+ mod->pdev.id = -1;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ adsp_info.id_to_module[i] = mod;
+#else
+ adsp_info.id_to_module[mod->id] = mod;
+#endif
+ platform_device_register(&mod->pdev);
+ }
+
+ msm_adsp_publish_cdevs(adsp_modules, count);
+
+ return 0;
+
+fail_rpc_register:
+ msm_rpc_close(rpc_cb_server_client);
+ rpc_cb_server_client = NULL;
+fail_rpc_open:
+ enable_irq(INT_ADSP);
+ free_irq(INT_ADSP, 0);
+fail_request_irq:
+ kfree(adsp_modules);
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ kfree(adsp_info.init_info_ptr);
+#endif
+ return rc;
+}
+
+static struct platform_driver msm_adsp_driver = {
+ .probe = msm_adsp_probe,
+ .driver = {
+ .name = MSM_ADSP_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init adsp_init(void)
+{
+ return platform_driver_register(&msm_adsp_driver);
+}
+
+device_initcall(adsp_init);
diff --git a/drivers/staging/dream/qdsp5/adsp.h b/drivers/staging/dream/qdsp5/adsp.h
new file mode 100644
index 00000000000..0e5c9abd3da
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp.h
@@ -0,0 +1,369 @@
+/* arch/arm/mach-msm/qdsp5/adsp.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_ADSP_H
+#define _ARCH_ARM_MACH_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_adsp.h>
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len);
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len);
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size);
+
+
+struct adsp_event;
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event);
+
+
+struct adsp_module_info {
+ const char *name;
+ const char *pdev_name;
+ uint32_t id;
+ const char *clk_name;
+ unsigned long clk_rate;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+#define ADSP_EVENT_MAX_SIZE 496
+#define EVENT_LEN 12
+#define EVENT_MSG_ID ((uint16_t)~0)
+
+struct adsp_event {
+ struct list_head list;
+ uint32_t size; /* always in bytes */
+ uint16_t msg_id;
+ uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
+ int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
+ union {
+ uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
+ uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
+ } data;
+};
+
+struct adsp_info {
+ uint32_t send_irq;
+ uint32_t read_ctrl;
+ uint32_t write_ctrl;
+
+ uint32_t max_msg16_size;
+ uint32_t max_msg32_size;
+
+ uint32_t max_task_id;
+ uint32_t max_module_id;
+ uint32_t max_queue_id;
+ uint32_t max_image_id;
+
+ /* for each image id, a map of queue id to offset */
+ uint32_t **queue_offset;
+
+ /* for each image id, a map of task id to module id */
+ uint32_t **task_to_module;
+
+ /* for each module id, map of module id to module */
+ struct msm_adsp_module **id_to_module;
+
+ uint32_t module_count;
+ struct adsp_module_info *module;
+
+ /* stats */
+ uint32_t events_received;
+ uint32_t event_backlog_max;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ /* rpc_client for init_info */
+ struct msm_rpc_endpoint *init_info_rpc_client;
+ struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr;
+ wait_queue_head_t init_info_wait;
+ unsigned init_info_state;
+#endif
+};
+
+#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a
+#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b
+#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0
+#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0
+#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2
+#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) /* must be actual vers */
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:00010000"
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x71d1094b, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0xee3a9966, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:71d1094b"
+#elif CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x20f17fd3, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0x75babbd6, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:20f17fd3"
+#else
+#error "Unknown AMSS version"
+#endif
+
+enum rpc_adsp_rtos_proc_type {
+ RPC_ADSP_RTOS_PROC_NONE = 0,
+ RPC_ADSP_RTOS_PROC_MODEM = 1,
+ RPC_ADSP_RTOS_PROC_APPS = 2,
+};
+
+enum {
+ RPC_ADSP_RTOS_CMD_REGISTER_APP,
+ RPC_ADSP_RTOS_CMD_ENABLE,
+ RPC_ADSP_RTOS_CMD_DISABLE,
+ RPC_ADSP_RTOS_CMD_KERNEL_COMMAND,
+ RPC_ADSP_RTOS_CMD_16_COMMAND,
+ RPC_ADSP_RTOS_CMD_32_COMMAND,
+ RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+ RPC_ADSP_RTOS_CMD_REMOTE_EVENT,
+ RPC_ADSP_RTOS_CMD_SET_STATE,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
+ RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
+#endif
+};
+
+enum rpc_adsp_rtos_mod_status_type {
+ RPC_ADSP_RTOS_MOD_READY,
+ RPC_ADSP_RTOS_MOD_DISABLE,
+ RPC_ADSP_RTOS_SERVICE_RESET,
+ RPC_ADSP_RTOS_CMD_FAIL,
+ RPC_ADSP_RTOS_CMD_SUCCESS,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ RPC_ADSP_RTOS_INIT_INFO,
+ RPC_ADSP_RTOS_DISABLE_FAIL,
+#endif
+};
+
+struct rpc_adsp_rtos_app_to_modem_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+ uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+ uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+};
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+enum qdsp_image_type {
+ QDSP_IMAGE_COMBO,
+ QDSP_IMAGE_GAUDIO,
+ QDSP_IMAGE_QTV_LP,
+ QDSP_IMAGE_MAX,
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+ QDSP_IMAGE_32BIT_DUMMY = 0x10000
+};
+
+struct adsp_rtos_mp_mtoa_header_type {
+ enum rpc_adsp_rtos_mod_status_type event;
+ enum rpc_adsp_rtos_proc_type proc_id;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Event Info*/
+struct adsp_rtos_mp_mtoa_type {
+ uint32_t module;
+ uint32_t image;
+ uint32_t apps_okts;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Init Info */
+#define IMG_MAX 8
+#define ENTRIES_MAX 64
+
+struct queue_to_offset_type {
+ uint32_t queue;
+ uint32_t offset;
+};
+
+struct adsp_rtos_mp_mtoa_init_info_type {
+ uint32_t image_count;
+ uint32_t num_queue_offsets;
+ struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX];
+ uint32_t num_task_module_entries;
+ uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX];
+
+ uint32_t module_table_size;
+ uint32_t module_entries[ENTRIES_MAX];
+ /*
+ * queue_offsets[] is to store only queue_offsets
+ */
+ uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX];
+};
+
+struct adsp_rtos_mp_mtoa_s_type {
+ struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
+
+ uint32_t desc_field;
+ union {
+ struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet;
+ struct adsp_rtos_mp_mtoa_type mp_mtoa_packet;
+ } adsp_rtos_mp_mtoa_data;
+};
+
+struct rpc_adsp_rtos_modem_to_app_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ struct adsp_rtos_mp_mtoa_s_type mtoa_pkt;
+};
+#else
+struct rpc_adsp_rtos_modem_to_app_args_t {
+ struct rpc_request_hdr hdr;
+ uint32_t gotit; /* if 1, the next elements are present */
+ uint32_t event; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+ uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+ uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+ uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */
+};
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+
+#define ADSP_STATE_DISABLED 0
+#define ADSP_STATE_ENABLING 1
+#define ADSP_STATE_ENABLED 2
+#define ADSP_STATE_DISABLING 3
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define ADSP_STATE_INIT_INFO 4
+#endif
+
+struct msm_adsp_module {
+ struct mutex lock;
+ const char *name;
+ unsigned id;
+ struct adsp_info *info;
+
+ struct msm_rpc_endpoint *rpc_client;
+ struct msm_adsp_ops *ops;
+ void *driver_data;
+
+ /* statistics */
+ unsigned num_commands;
+ unsigned num_events;
+
+ wait_queue_head_t state_wait;
+ unsigned state;
+
+ struct platform_device pdev;
+ struct clk *clk;
+ int open_count;
+
+ struct mutex pmem_regions_lock;
+ struct hlist_head pmem_regions;
+ int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+ size_t);
+ int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
+extern int adsp_init_info(struct adsp_info *info);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define QDSP_RTOS_NO_QUEUE 0xfffffffe
+#else
+#define QDSP_RTOS_NO_QUEUE 0xffffffff
+#endif
+
+/*
+ * Constants used to communicate with the ADSP RTOS
+ */
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU
+
+/* Combination of MUTEX and CMD bits to check if the DSP is busy */
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U
+
+/* RTOS to Host processor command mask values */
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U
+
+/* Combination of FLAG and COMMAND bits to check if MSG ready */
+#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU
+
+#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU
+#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U
+
+/* Base address of DSP and DSP hardware registers */
+#define QDSP_RAMC_OFFSET 0x400000
+
+#endif /* _ARCH_ARM_MACH_MSM_ADSP_H */
diff --git a/drivers/staging/dream/qdsp5/adsp_6210.c b/drivers/staging/dream/qdsp5/adsp_6210.c
new file mode 100644
index 00000000000..3cf4e99ed86
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_6210.c
@@ -0,0 +1,283 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6210.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3be, /* QDSP_mpuAfeQueue */
+ 0x3ee, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3c2, /* QDSP_uPAudPPCmd1Queue */
+ 0x3c6, /* QDSP_uPAudPPCmd2Queue */
+ 0x3ca, /* QDSP_uPAudPPCmd3Queue */
+ 0x3da, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x3de, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x3e2, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x3e6, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x3ea, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x3ce, /* QDSP_uPAudPreProcCmdQueue */
+ 0x3d6, /* QDSP_uPAudRecBitStreamQueue */
+ 0x3d2, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x585, /* QDSP_lpmCommandQueue */
+ 0x52d, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x541, /* QDSP_mpuModmathCmdQueue */
+ 0x555, /* QDSP_mpuVDecCmdQueue */
+ 0x559, /* QDSP_mpuVDecPktQueue */
+ 0x551, /* QDSP_mpuVEncCmdQueue */
+ 0x535, /* QDSP_rxMpuDecCmdQueue */
+ 0x539, /* QDSP_rxMpuDecPktQueue */
+ 0x53d, /* QDSP_txMpuEncQueue */
+ 0x55d, /* QDSP_uPAudPPCmd1Queue */
+ 0x561, /* QDSP_uPAudPPCmd2Queue */
+ 0x565, /* QDSP_uPAudPPCmd3Queue */
+ 0x575, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x579, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x569, /* QDSP_uPAudPreProcCmdQueue */
+ 0x571, /* QDSP_uPAudRecBitStreamQueue */
+ 0x56d, /* QDSP_uPAudRecCmdQueue */
+ 0x581, /* QDSP_uPJpegActionCmdQueue */
+ 0x57d, /* QDSP_uPJpegCfgCmdQueue */
+ 0x531, /* QDSP_uPVocProcQueue */
+ 0x545, /* QDSP_vfeCommandQueue */
+ 0x54d, /* QDSP_vfeCommandScaleQueue */
+ 0x549 /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x40c, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x410, /* QDSP_mpuVDecCmdQueue */
+ 0x414, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x41c, /* QDSP_uPAudPPCmd1Queue */
+ 0x420, /* QDSP_uPAudPPCmd2Queue */
+ 0x424, /* QDSP_uPAudPPCmd3Queue */
+ 0x430, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPreProcCmdQueue */
+ 0x42c, /* QDSP_uPAudRecBitStreamQueue */
+ 0x428, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Tables to convert tasks to modules */
+static uint32_t *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPPTASK),
+ QDSP_MODULE(AUDRECTASK),
+ QDSP_MODULE(AUDPREPROCTASK),
+ QDSP_MODULE(VFETASK),
+ QDSP_MODULE(QCAMTASK),
+ QDSP_MODULE(LPMTASK),
+ QDSP_MODULE(JPEGTASK),
+ QDSP_MODULE(VIDEOTASK),
+ QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_6220.c b/drivers/staging/dream/qdsp5/adsp_6220.c
new file mode 100644
index 00000000000..02225cd7ec8
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_6220.c
@@ -0,0 +1,284 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6220.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3f0, /* QDSP_mpuAfeQueue */
+ 0x420, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3f4, /* QDSP_uPAudPPCmd1Queue */
+ 0x3f8, /* QDSP_uPAudPPCmd2Queue */
+ 0x3fc, /* QDSP_uPAudPPCmd3Queue */
+ 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x400, /* QDSP_uPAudPreProcCmdQueue */
+ 0x408, /* QDSP_uPAudRecBitStreamQueue */
+ 0x404, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x6f2, /* QDSP_lpmCommandQueue */
+ 0x69e, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x6b2, /* QDSP_mpuModmathCmdQueue */
+ 0x6c6, /* QDSP_mpuVDecCmdQueue */
+ 0x6ca, /* QDSP_mpuVDecPktQueue */
+ 0x6c2, /* QDSP_mpuVEncCmdQueue */
+ 0x6a6, /* QDSP_rxMpuDecCmdQueue */
+ 0x6aa, /* QDSP_rxMpuDecPktQueue */
+ 0x6ae, /* QDSP_txMpuEncQueue */
+ 0x6ce, /* QDSP_uPAudPPCmd1Queue */
+ 0x6d2, /* QDSP_uPAudPPCmd2Queue */
+ 0x6d6, /* QDSP_uPAudPPCmd3Queue */
+ 0x6e6, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x6da, /* QDSP_uPAudPreProcCmdQueue */
+ 0x6e2, /* QDSP_uPAudRecBitStreamQueue */
+ 0x6de, /* QDSP_uPAudRecCmdQueue */
+ 0x6ee, /* QDSP_uPJpegActionCmdQueue */
+ 0x6ea, /* QDSP_uPJpegCfgCmdQueue */
+ 0x6a2, /* QDSP_uPVocProcQueue */
+ 0x6b6, /* QDSP_vfeCommandQueue */
+ 0x6be, /* QDSP_vfeCommandScaleQueue */
+ 0x6ba /* QDSP_vfeCommandTableQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x430, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x434, /* QDSP_mpuVDecCmdQueue */
+ 0x438, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x440, /* QDSP_uPAudPPCmd1Queue */
+ 0x444, /* QDSP_uPAudPPCmd2Queue */
+ 0x448, /* QDSP_uPAudPPCmd3Queue */
+ 0x454, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x43c, /* QDSP_uPAudPreProcCmdQueue */
+ 0x450, /* QDSP_uPAudRecBitStreamQueue */
+ 0x44c, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK),
+ QDSP_MODULE(AUDPPTASK),
+ QDSP_MODULE(AUDPREPROCTASK),
+ QDSP_MODULE(AUDRECTASK),
+ QDSP_MODULE(VFETASK),
+ QDSP_MODULE(QCAMTASK),
+ QDSP_MODULE(LPMTASK),
+ QDSP_MODULE(JPEGTASK),
+ QDSP_MODULE(VIDEOTASK),
+ QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_6225.c b/drivers/staging/dream/qdsp5/adsp_6225.c
new file mode 100644
index 00000000000..5078afbb1a8
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_6225.c
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6225.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEO_AAC_VOC,
+ QDSP_MODULE_PCM_DEC,
+ QDSP_MODULE_AUDIO_DEC_MP3,
+ QDSP_MODULE_AUDIO_DEC_AAC,
+ QDSP_MODULE_AUDIO_DEC_WMA,
+ QDSP_MODULE_HOSTPCM,
+ QDSP_MODULE_DTMF,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_SBC_ENC,
+ QDSP_MODULE_VOC_UMTS,
+ QDSP_MODULE_VOC_CDMA,
+ QDSP_MODULE_VOC_PCM,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_WAV_ENC,
+ QDSP_MODULE_AACLC_ENC,
+ QDSP_MODULE_VIDEO_AMR,
+ QDSP_MODULE_VOC_AMR,
+ QDSP_MODULE_VOC_EVRC,
+ QDSP_MODULE_VOC_13K,
+ QDSP_MODULE_VOC_FGV,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_QCAMTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MIDI,
+ QDSP_MODULE_GAUDIO,
+ QDSP_MODULE_VDEC_LP_MODE,
+ QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID 30U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_AUDPLAY2TASK,
+ QDSP_MODULE_AUDPLAY3TASK,
+ QDSP_MODULE_AUDPLAY4TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_GRAPHICSTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3f0, /* QDSP_mpuAfeQueue */
+ 0x420, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x3f4, /* QDSP_uPAudPPCmd1Queue */
+ 0x3f8, /* QDSP_uPAudPPCmd2Queue */
+ 0x3fc, /* QDSP_uPAudPPCmd3Queue */
+ 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x400, /* QDSP_uPAudPreProcCmdQueue */
+ 0x408, /* QDSP_uPAudRecBitStreamQueue */
+ 0x404, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_VOCDECTASK,
+ QDSP_MODULE_VOCENCTASK,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_VIDEOENCTASK,
+ QDSP_MODULE_VOICEPROCTASK,
+ QDSP_MODULE_VFETASK,
+ QDSP_MODULE_JPEGTASK,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_AUDPLAY1TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_LPMTASK,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MODMATHTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_DIAGTASK,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+ 0x714, /* QDSP_lpmCommandQueue */
+ 0x6bc, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ 0x6d0, /* QDSP_mpuModmathCmdQueue */
+ 0x6e8, /* QDSP_mpuVDecCmdQueue */
+ 0x6ec, /* QDSP_mpuVDecPktQueue */
+ 0x6e4, /* QDSP_mpuVEncCmdQueue */
+ 0x6c4, /* QDSP_rxMpuDecCmdQueue */
+ 0x6c8, /* QDSP_rxMpuDecPktQueue */
+ 0x6cc, /* QDSP_txMpuEncQueue */
+ 0x6f0, /* QDSP_uPAudPPCmd1Queue */
+ 0x6f4, /* QDSP_uPAudPPCmd2Queue */
+ 0x6f8, /* QDSP_uPAudPPCmd3Queue */
+ 0x708, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x6fc, /* QDSP_uPAudPreProcCmdQueue */
+ 0x704, /* QDSP_uPAudRecBitStreamQueue */
+ 0x700, /* QDSP_uPAudRecCmdQueue */
+ 0x710, /* QDSP_uPJpegActionCmdQueue */
+ 0x70c, /* QDSP_uPJpegCfgCmdQueue */
+ 0x6c0, /* QDSP_uPVocProcQueue */
+ 0x6d8, /* QDSP_vfeCommandQueue */
+ 0x6e0, /* QDSP_vfeCommandScaleQueue */
+ 0x6dc, /* QDSP_vfeCommandTableQueue */
+ 0x6d4, /* QDSP_uPDiagQueue */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+ QDSP_MODULE_KERNEL,
+ QDSP_MODULE_AFETASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_VIDEOTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDPPTASK,
+ QDSP_MODULE_AUDPLAY0TASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_AUDRECTASK,
+ QDSP_MODULE_AUDPREPROCTASK,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+ QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+ QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */
+ 0x3fe, /* QDSP_mpuAfeQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */
+ 0x402, /* QDSP_mpuVDecCmdQueue */
+ 0x406, /* QDSP_mpuVDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */
+ 0x40e, /* QDSP_uPAudPPCmd1Queue */
+ 0x412, /* QDSP_uPAudPPCmd2Queue */
+ 0x416, /* QDSP_uPAudPPCmd3Queue */
+ 0x422, /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+ 0x40a, /* QDSP_uPAudPreProcCmdQueue */
+ 0x41e, /* QDSP_uPAudRecBitStreamQueue */
+ 0x41a, /* QDSP_uPAudRecCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */
+ QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+ qdsp_combo_task_to_module_table,
+ qdsp_gaudio_task_to_module_table,
+ qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+ qdsp_combo_queue_offset_table,
+ qdsp_gaudio_queue_offset_table,
+ qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+ .clk_name = clkname, .clk_rate = clkrate, \
+ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+ adsp_vfe_patch_event),
+ QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+ QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd,
+ adsp_jpeg_patch_event),
+ QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+ adsp_video_verify_cmd, NULL),
+ QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+ adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+
+ info->max_task_id = 16;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_QUEUE_MAX;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_driver.c b/drivers/staging/dream/qdsp5/adsp_driver.c
new file mode 100644
index 00000000000..e55a0db53a9
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_driver.c
@@ -0,0 +1,641 @@
+/* arch/arm/mach-msm/qdsp5/adsp_driver.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#include "adsp.h"
+
+#include <linux/msm_adsp.h>
+#include <linux/android_pmem.h>
+
+struct adsp_pmem_region {
+ struct hlist_node list;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ struct file *file;
+};
+
+struct adsp_device {
+ struct msm_adsp_module *module;
+
+ spinlock_t event_queue_lock;
+ wait_queue_head_t event_wait;
+ struct list_head event_queue;
+ int abort;
+
+ const char *name;
+ struct device *device;
+ struct cdev cdev;
+};
+
+static struct adsp_device *inode_to_device(struct inode *inode);
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = __v >= __r->vaddr && \
+ __e <= __r->vaddr + __r->len; \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+static int adsp_pmem_check(struct msm_adsp_module *module,
+ void *vaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region_elt;
+ struct hlist_node *node;
+ struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ printk(KERN_ERR "adsp: module %s:"
+ " region (vaddr %p len %ld)"
+ " clashes with registered region"
+ " (vaddr %p paddr %p len %ld)\n",
+ module->name,
+ vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr,
+ region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int adsp_pmem_add(struct msm_adsp_module *module,
+ struct adsp_pmem_info *info)
+{
+ unsigned long paddr, kvaddr, len;
+ struct file *file;
+ struct adsp_pmem_region *region;
+ int rc = -EINVAL;
+
+ mutex_lock(&module->pmem_regions_lock);
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ INIT_HLIST_NODE(&region->list);
+ if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+ kfree(region);
+ goto end;
+ }
+
+ rc = adsp_pmem_check(module, info->vaddr, len);
+ if (rc < 0) {
+ put_pmem_file(file);
+ kfree(region);
+ goto end;
+ }
+
+ region->vaddr = info->vaddr;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->file = file;
+
+ hlist_add_head(&region->list, &module->pmem_regions);
+end:
+ mutex_unlock(&module->pmem_regions_lock);
+ return rc;
+}
+
+static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long len, struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ void *vaddr = *addr;
+ struct adsp_pmem_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * pmem buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ printk(KERN_ERR "adsp: module %s: "
+ "multiple hits for vaddr %p, len %ld\n",
+ module->name, vaddr, len);
+ hlist_for_each_entry(region_elt, node,
+ &module->pmem_regions, list) {
+ if (vaddr >= region_elt->vaddr &&
+ vaddr < region_elt->vaddr + region_elt->len &&
+ vaddr + len <= region_elt->vaddr + region_elt->len)
+ printk(KERN_ERR "\t%p, %ld --> %p\n",
+ region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+ unsigned long *kvaddr, unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
+ " lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ *kvaddr = region->kvaddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+ unsigned long len)
+{
+ struct adsp_pmem_region *region;
+ void *vaddr = *addr;
+ unsigned long *paddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
+ module->name, vaddr, len);
+ return ret;
+ }
+
+ *paddr = region->paddr + (vaddr - region->vaddr);
+ return 0;
+}
+
+static int adsp_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ /* call the per module verifier */
+ if (module->verify_cmd)
+ return module->verify_cmd(module, queue_id, cmd_data,
+ cmd_size);
+ else
+ printk(KERN_INFO "adsp: no packet verifying function "
+ "for task %s\n", module->name);
+ return 0;
+}
+
+static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
+{
+ struct adsp_command_t cmd;
+ unsigned char buf[256];
+ void *cmd_data;
+ long rc;
+
+ if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
+ return -EFAULT;
+
+ if (cmd.len > 256) {
+ cmd_data = kmalloc(cmd.len, GFP_USER);
+ if (!cmd_data)
+ return -ENOMEM;
+ } else {
+ cmd_data = buf;
+ }
+
+ if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
+ rc = -EFAULT;
+ goto end;
+ }
+
+ mutex_lock(&adev->module->pmem_regions_lock);
+ if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
+ printk(KERN_ERR "module %s: verify failed.\n",
+ adev->module->name);
+ rc = -EINVAL;
+ goto end;
+ }
+ rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
+end:
+ mutex_unlock(&adev->module->pmem_regions_lock);
+
+ if (cmd.len > 256)
+ kfree(cmd_data);
+
+ return rc;
+}
+
+static int adsp_events_pending(struct adsp_device *adev)
+{
+ unsigned long flags;
+ int yes;
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ yes = !list_empty(&adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ return yes || adev->abort;
+}
+
+static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
+ struct adsp_pmem_region **region)
+{
+ struct hlist_node *node;
+ unsigned long paddr = (unsigned long)(*addr);
+ struct adsp_pmem_region *region_elt;
+
+ hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+ if (paddr >= region_elt->paddr &&
+ paddr < region_elt->paddr + region_elt->len) {
+ *region = region_elt;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
+{
+ struct adsp_pmem_region *region;
+ unsigned long paddr = (unsigned long)(*addr);
+ unsigned long *vaddr = (unsigned long *)addr;
+ int ret;
+
+ ret = adsp_pmem_lookup_paddr(module, addr, &region);
+ if (ret) {
+ printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
+ module->name, vaddr);
+ return ret;
+ }
+
+ *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
+ return 0;
+}
+
+static int adsp_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ /* call the per-module msg verifier */
+ if (module->patch_event)
+ return module->patch_event(module, event);
+ return 0;
+}
+
+static long adsp_get_event(struct adsp_device *adev, void __user *arg)
+{
+ unsigned long flags;
+ struct adsp_event *data = NULL;
+ struct adsp_event_t evt;
+ int timeout;
+ long rc = 0;
+
+ if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
+ return -EFAULT;
+
+ timeout = (int)evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(
+ adev->event_wait, adsp_events_pending(adev),
+ msecs_to_jiffies(timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(
+ adev->event_wait, adsp_events_pending(adev));
+ }
+ if (rc < 0)
+ return rc;
+
+ if (adev->abort)
+ return -ENODEV;
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ if (!list_empty(&adev->event_queue)) {
+ data = list_first_entry(&adev->event_queue,
+ struct adsp_event, list);
+ list_del(&data->list);
+ }
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+
+ if (!data)
+ return -EAGAIN;
+
+ /* DSP messages are type 0; they may contain physical addresses */
+ if (data->type == 0)
+ adsp_patch_event(adev->module, data);
+
+ /* map adsp_event --> adsp_event_t */
+ if (evt.len < data->size) {
+ rc = -ETOOSMALL;
+ goto end;
+ }
+ if (data->msg_id != EVENT_MSG_ID) {
+ if (copy_to_user((void *)(evt.data), data->data.msg16,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ } else {
+ if (copy_to_user((void *)(evt.data), data->data.msg32,
+ data->size)) {
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+ evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
+ evt.msg_id = data->msg_id;
+ evt.flags = data->is16;
+ evt.len = data->size;
+ if (copy_to_user(arg, &evt, sizeof(evt)))
+ rc = -EFAULT;
+end:
+ kfree(data);
+ return rc;
+}
+
+static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct adsp_device *adev = filp->private_data;
+
+ switch (cmd) {
+ case ADSP_IOCTL_ENABLE:
+ return msm_adsp_enable(adev->module);
+
+ case ADSP_IOCTL_DISABLE:
+ return msm_adsp_disable(adev->module);
+
+ case ADSP_IOCTL_DISABLE_EVENT_RSP:
+ return 0;
+
+ case ADSP_IOCTL_DISABLE_ACK:
+ pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
+ break;
+
+ case ADSP_IOCTL_WRITE_COMMAND:
+ return adsp_write_cmd(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_GET_EVENT:
+ return adsp_get_event(adev, (void __user *) arg);
+
+ case ADSP_IOCTL_SET_CLKRATE: {
+#if CONFIG_MSM_AMSS_VERSION==6350
+ unsigned long clk_rate;
+ if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
+ return -EFAULT;
+ return adsp_set_clkrate(adev->module, clk_rate);
+#endif
+ }
+
+ case ADSP_IOCTL_REGISTER_PMEM: {
+ struct adsp_pmem_info info;
+ if (copy_from_user(&info, (void *) arg, sizeof(info)))
+ return -EFAULT;
+ return adsp_pmem_add(adev->module, &info);
+ }
+
+ case ADSP_IOCTL_ABORT_EVENT_READ:
+ adev->abort = 1;
+ wake_up(&adev->event_wait);
+ break;
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int adsp_release(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev = filp->private_data;
+ struct msm_adsp_module *module = adev->module;
+ struct hlist_node *node, *tmp;
+ struct adsp_pmem_region *region;
+
+ pr_info("adsp_release() '%s'\n", adev->name);
+
+ /* clear module before putting it to avoid race with open() */
+ adev->module = NULL;
+
+ mutex_lock(&module->pmem_regions_lock);
+ hlist_for_each_safe(node, tmp, &module->pmem_regions) {
+ region = hlist_entry(node, struct adsp_pmem_region, list);
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ mutex_unlock(&module->pmem_regions_lock);
+ BUG_ON(!hlist_empty(&module->pmem_regions));
+
+ msm_adsp_put(module);
+ return 0;
+}
+
+static void adsp_event(void *driver_data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct adsp_device *adev = driver_data;
+ struct adsp_event *event;
+ unsigned long flags;
+
+ if (len > ADSP_EVENT_MAX_SIZE) {
+ pr_err("adsp_event: event too large (%d bytes)\n", len);
+ return;
+ }
+
+ event = kmalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event) {
+ pr_err("adsp_event: cannot allocate buffer\n");
+ return;
+ }
+
+ if (id != EVENT_MSG_ID) {
+ event->type = 0;
+ event->is16 = 0;
+ event->msg_id = id;
+ event->size = len;
+
+ getevent(event->data.msg16, len);
+ } else {
+ event->type = 1;
+ event->is16 = 1;
+ event->msg_id = id;
+ event->size = len;
+ getevent(event->data.msg32, len);
+ }
+
+ spin_lock_irqsave(&adev->event_queue_lock, flags);
+ list_add_tail(&event->list, &adev->event_queue);
+ spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+ wake_up(&adev->event_wait);
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = adsp_event,
+};
+
+static int adsp_open(struct inode *inode, struct file *filp)
+{
+ struct adsp_device *adev;
+ int rc;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ adev = inode_to_device(inode);
+ if (!adev)
+ return -ENODEV;
+
+ pr_info("adsp_open() name = '%s'\n", adev->name);
+
+ rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
+ if (rc)
+ return rc;
+
+ pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
+ filp->private_data = adev;
+ adev->abort = 0;
+ INIT_HLIST_HEAD(&adev->module->pmem_regions);
+ mutex_init(&adev->module->pmem_regions_lock);
+
+ return 0;
+}
+
+static unsigned adsp_device_count;
+static struct adsp_device *adsp_devices;
+
+static struct adsp_device *inode_to_device(struct inode *inode)
+{
+ unsigned n = MINOR(inode->i_rdev);
+ if (n < adsp_device_count) {
+ if (adsp_devices[n].device)
+ return adsp_devices + n;
+ }
+ return NULL;
+}
+
+static dev_t adsp_devno;
+static struct class *adsp_class;
+
+static struct file_operations adsp_fops = {
+ .owner = THIS_MODULE,
+ .open = adsp_open,
+ .unlocked_ioctl = adsp_ioctl,
+ .release = adsp_release,
+};
+
+static void adsp_create(struct adsp_device *adev, const char *name,
+ struct device *parent, dev_t devt)
+{
+ struct device *dev;
+ int rc;
+
+ dev = device_create(adsp_class, parent, devt, "%s", name);
+ if (IS_ERR(dev))
+ return;
+
+ init_waitqueue_head(&adev->event_wait);
+ INIT_LIST_HEAD(&adev->event_queue);
+ spin_lock_init(&adev->event_queue_lock);
+
+ cdev_init(&adev->cdev, &adsp_fops);
+ adev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&adev->cdev, devt, 1);
+ if (rc < 0) {
+ device_destroy(adsp_class, devt);
+ } else {
+ adev->device = dev;
+ adev->name = name;
+ }
+}
+
+void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
+{
+ int rc;
+
+ adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
+ if (!adsp_devices)
+ return;
+
+ adsp_class = class_create(THIS_MODULE, "adsp");
+ if (IS_ERR(adsp_class))
+ goto fail_create_class;
+
+ rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
+ if (rc < 0)
+ goto fail_alloc_region;
+
+ adsp_device_count = n;
+ for (n = 0; n < adsp_device_count; n++) {
+ adsp_create(adsp_devices + n,
+ modules[n].name, &modules[n].pdev.dev,
+ MKDEV(MAJOR(adsp_devno), n));
+ }
+
+ return;
+
+fail_alloc_region:
+ class_unregister(adsp_class);
+fail_create_class:
+ kfree(adsp_devices);
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_info.c b/drivers/staging/dream/qdsp5/adsp_info.c
new file mode 100644
index 00000000000..b9c77d20b5c
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_info.c
@@ -0,0 +1,121 @@
+/* arch/arm/mach-msm/adsp_info.c
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "adsp.h"
+
+/* Firmware modules */
+#define QDSP_MODULE_KERNEL 0x0106dd4e
+#define QDSP_MODULE_AFETASK 0x0106dd6f
+#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70
+#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71
+#define QDSP_MODULE_AUDPPTASK 0x0106dd72
+#define QDSP_MODULE_VIDEOTASK 0x0106dd73
+#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74
+#define QDSP_MODULE_PCM_DEC 0x0106dd75
+#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76
+#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77
+#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78
+#define QDSP_MODULE_HOSTPCM 0x0106dd79
+#define QDSP_MODULE_DTMF 0x0106dd7a
+#define QDSP_MODULE_AUDRECTASK 0x0106dd7b
+#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c
+#define QDSP_MODULE_SBC_ENC 0x0106dd7d
+#define QDSP_MODULE_VOC_UMTS 0x0106dd9a
+#define QDSP_MODULE_VOC_CDMA 0x0106dd98
+#define QDSP_MODULE_VOC_PCM 0x0106dd7f
+#define QDSP_MODULE_VOCENCTASK 0x0106dd80
+#define QDSP_MODULE_VOCDECTASK 0x0106dd81
+#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82
+#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83
+#define QDSP_MODULE_VFETASK 0x0106dd84
+#define QDSP_MODULE_WAV_ENC 0x0106dd85
+#define QDSP_MODULE_AACLC_ENC 0x0106dd86
+#define QDSP_MODULE_VIDEO_AMR 0x0106dd87
+#define QDSP_MODULE_VOC_AMR 0x0106dd88
+#define QDSP_MODULE_VOC_EVRC 0x0106dd89
+#define QDSP_MODULE_VOC_13K 0x0106dd8a
+#define QDSP_MODULE_VOC_FGV 0x0106dd8b
+#define QDSP_MODULE_DIAGTASK 0x0106dd8c
+#define QDSP_MODULE_JPEGTASK 0x0106dd8d
+#define QDSP_MODULE_LPMTASK 0x0106dd8e
+#define QDSP_MODULE_QCAMTASK 0x0106dd8f
+#define QDSP_MODULE_MODMATHTASK 0x0106dd90
+#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91
+#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92
+#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93
+#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94
+#define QDSP_MODULE_MIDI 0x0106dd95
+#define QDSP_MODULE_GAUDIO 0x0106dd96
+#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97
+#define QDSP_MODULE_MAX 0x7fffffff
+
+ /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+#define QDSP_MODULE_32BIT_DUMMY 0x10000
+
+static uint32_t *qdsp_task_to_module[IMG_MAX];
+static uint32_t *qdsp_queue_offset_table[IMG_MAX];
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+ .clk_name = clkname, .clk_rate = clkrate, \
+ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+ QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+ adsp_vfe_patch_event),
+ QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+ QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+ QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd,
+ adsp_jpeg_patch_event),
+ QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+ adsp_video_verify_cmd, NULL),
+ QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+ QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+ adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+ uint32_t img_num;
+
+ info->send_irq = 0x00c00200;
+ info->read_ctrl = 0x00400038;
+ info->write_ctrl = 0x00400034;
+
+ info->max_msg16_size = 193;
+ info->max_msg32_size = 8;
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_queue_offset_table[img_num] =
+ &info->init_info_ptr->queue_offsets[img_num][0];
+
+ for (img_num = 0; img_num < IMG_MAX; img_num++)
+ qdsp_task_to_module[img_num] =
+ &info->init_info_ptr->task_to_module_tbl[img_num][0];
+ info->max_task_id = 30;
+ info->max_module_id = QDSP_MODULE_MAX - 1;
+ info->max_queue_id = QDSP_MAX_NUM_QUEUES;
+ info->max_image_id = 2;
+ info->queue_offset = qdsp_queue_offset_table;
+ info->task_to_module = qdsp_task_to_module;
+
+ info->module_count = ARRAY_SIZE(module_info);
+ info->module = module_info;
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c b/drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c
new file mode 100644
index 00000000000..4f493edb6c9
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c
@@ -0,0 +1,31 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
+ *
+ * Verification code for aDSP JPEG events.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/qdsp5/qdsp5jpegmsg.h>
+#include "adsp.h"
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) {
+ jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16;
+ return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c
new file mode 100644
index 00000000000..b33eba25569
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c
@@ -0,0 +1,182 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
+ *
+ * Verification code for aDSP JPEG packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/qdsp5/qdsp5jpegcmdi.h>
+#include "adsp.h"
+
+static uint32_t dec_fmt;
+
+static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size,
+ uint32_t *chroma_size)
+{
+ uint32_t fmt, luma_width, luma_height;
+
+ fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M;
+ luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M)
+ >> 16;
+ luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M;
+ *luma_size = luma_width * luma_height;
+ if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2)
+ *chroma_size = *luma_size/2;
+ else
+ *chroma_size = *luma_size;
+}
+
+static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data;
+ uint32_t luma_size, chroma_size;
+ int i, num_frags;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) {
+ printk(KERN_ERR "adsp: module %s: JPEG ENC CFG invalid cmd_size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ get_sizes(cmd, &luma_size, &chroma_size);
+ num_frags = (cmd->process_cfg >> 10) & 0xf;
+ num_frags = ((num_frags == 1) ? num_frags : num_frags * 2);
+ for (i = 0; i < num_frags; i += 2) {
+ if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) ||
+ adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size))
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1,
+ cmd->op_buf_0_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1,
+ cmd->op_buf_1_cfg_part2))
+ return -1;
+ return 0;
+}
+
+static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data;
+ uint32_t div;
+
+ if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) {
+ printk(KERN_ERR "adsp: module %s: JPEG DEC CFG invalid cmd_size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1,
+ cmd->ip_stream_buf_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1,
+ cmd->op_stream_buf_0_cfg_part2) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1,
+ cmd->op_stream_buf_1_cfg_part2))
+ return -1;
+ dec_fmt = cmd->op_data_format &
+ JPEG_CMD_DEC_OP_DATA_FORMAT_M;
+ div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3,
+ cmd->op_stream_buf_0_cfg_part2 / div) ||
+ adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3,
+ cmd->op_stream_buf_1_cfg_part2 / div))
+ return -1;
+ return 0;
+}
+
+static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch(cmd_id) {
+ case JPEG_CMD_ENC_CFG:
+ return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size);
+ case JPEG_CMD_DEC_CFG:
+ return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size);
+ default:
+ if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid JPEG CFG cmd_id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int verify_jpeg_action_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch (cmd_id) {
+ case JPEG_CMD_ENC_OP_CONSUMED:
+ {
+ jpeg_cmd_enc_op_consumed *cmd =
+ (jpeg_cmd_enc_op_consumed *)cmd_data;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+ printk(KERN_ERR "adsp: module %s: JPEG_CMD_ENC_OP_CONSUMED invalid size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr,
+ cmd->op_buf_size))
+ return -1;
+ }
+ break;
+ case JPEG_CMD_DEC_OP_CONSUMED:
+ {
+ uint32_t div;
+ jpeg_cmd_dec_op_consumed *cmd =
+ (jpeg_cmd_dec_op_consumed *)cmd_data;
+
+ if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+ printk(KERN_ERR "adsp: module %s: JPEG_CMD_DEC_OP_CONSUMED invalid size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+
+ div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
+ if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr,
+ cmd->luma_op_buf_size) ||
+ adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr,
+ cmd->luma_op_buf_size / div))
+ return -1;
+ }
+ break;
+ default:
+ if (cmd_id > 7) {
+ printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch(queue_id) {
+ case QDSP_uPJpegCfgCmdQueue:
+ return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size);
+ case QDSP_uPJpegActionCmdQueue:
+ return verify_jpeg_action_cmd(module, cmd_data, cmd_size);
+ default:
+ return -1;
+ }
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c
new file mode 100644
index 00000000000..1e23ef39270
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c
@@ -0,0 +1,65 @@
+/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
+ *
+ * Verificion code for aDSP LPM packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/qdsp5/qdsp5lpmcmdi.h>
+#include "adsp.h"
+
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ uint32_t cmd_id, col_height, input_row_incr, output_row_incr,
+ input_size, output_size;
+ uint32_t size_mask = 0x0fff;
+ lpm_cmd_start *cmd;
+
+ if (queue_id != QDSP_lpmCommandQueue) {
+ printk(KERN_ERR "adsp: module %s: wrong queue id %d\n",
+ module->name, queue_id);
+ return -1;
+ }
+
+ cmd = (lpm_cmd_start *)cmd_data;
+ cmd_id = cmd->cmd_id;
+
+ if (cmd_id == LPM_CMD_START) {
+ if (cmd_size != sizeof(lpm_cmd_start)) {
+ printk(KERN_ERR "adsp: module %s: wrong size %d, expect %d\n",
+ module->name, cmd_size, sizeof(lpm_cmd_start));
+ return -1;
+ }
+ col_height = cmd->ip_data_cfg_part1 & size_mask;
+ input_row_incr = cmd->ip_data_cfg_part2 & size_mask;
+ output_row_incr = cmd->op_data_cfg_part1 & size_mask;
+ input_size = col_height * input_row_incr;
+ output_size = col_height * output_row_incr;
+ if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module,
+ (void **)(&cmd->ip_data_cfg_part4),
+ input_size)) ||
+ (cmd->op_data_cfg_part3 && adsp_pmem_fixup(module,
+ (void **)(&cmd->op_data_cfg_part3),
+ output_size)))
+ return -1;
+ } else if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c b/drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c
new file mode 100644
index 00000000000..a56392b3521
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c
@@ -0,0 +1,54 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/qdsp5/qdsp5vfemsg.h>
+#include "adsp.h"
+
+static int patch_op_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16;
+ if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) ||
+ adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr))
+ return -1;
+ return 0;
+}
+
+static int patch_af_wb_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16;
+ return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf);
+}
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+ struct adsp_event *event)
+{
+ switch(event->msg_id) {
+ case VFE_MSG_OP1:
+ case VFE_MSG_OP2:
+ return patch_op_event(module, event);
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_WB_EXP:
+ return patch_af_wb_event(module, event);
+ default:
+ break;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c
new file mode 100644
index 00000000000..927d50a730f
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c
@@ -0,0 +1,239 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <mach/qdsp5/qdsp5vfecmdi.h>
+#include "adsp.h"
+
+static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr;
+static uint32_t af_size = 4228;
+static uint32_t awb_size = 8196;
+
+static inline int verify_cmd_op_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data;
+ void **addr_y = (void **)&cmd->op1_buf_y_addr;
+ void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr);
+
+ if (cmd_size != sizeof(vfe_cmd_op1_ack))
+ return -1;
+ if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) ||
+ (*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr)))
+ return -1;
+ return 0;
+}
+
+static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ int i;
+ vfe_cmd_stats_autofocus_cfg *cmd =
+ (vfe_cmd_stats_autofocus_cfg *)cmd_data;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg))
+ return -1;
+
+ for (i = 0; i < 3; i++) {
+ void **addr = (void **)(&cmd->af_stats_op_buf[i]);
+ if (*addr && adsp_pmem_fixup(module, addr, af_size))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_wb_exp_cfg *cmd =
+ (vfe_cmd_stats_wb_exp_cfg *)cmd_data;
+ int i;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg))
+ return -1;
+
+ for (i = 0; i < 3; i++) {
+ void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]);
+ if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data;
+ void **addr = (void **)&cmd->af_stats_op_buf;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_af_ack))
+ return -1;
+
+ if (*addr && adsp_pmem_fixup(module, addr, af_size))
+ return -1;
+ return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ vfe_cmd_stats_wb_exp_ack *cmd =
+ (vfe_cmd_stats_wb_exp_ack *)cmd_data;
+ void **addr = (void **)&cmd->wb_exp_stats_op_buf;
+
+ if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack))
+ return -1;
+
+ if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+ return -1;
+ return 0;
+}
+
+static int verify_vfe_command(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ switch (cmd_id) {
+ case VFE_CMD_OP1_ACK:
+ return verify_cmd_op_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_OP2_ACK:
+ return verify_cmd_op_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_AUTOFOCUS_CFG:
+ return verify_cmd_stats_autofocus_cfg(module, cmd_data,
+ cmd_size);
+ case VFE_CMD_STATS_WB_EXP_CFG:
+ return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_AF_ACK:
+ return verify_cmd_stats_af_ack(module, cmd_data, cmd_size);
+ case VFE_CMD_STATS_WB_EXP_ACK:
+ return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size);
+ default:
+ if (cmd_id > 29) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE command id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int verify_vfe_command_scale(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ // FIXME: check the size
+ if (cmd_id > 1) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE SCALE command id %d\n", module->name, cmd_id);
+ return -1;
+ }
+ return 0;
+}
+
+
+static uint32_t get_size(uint32_t hw)
+{
+ uint32_t height, width;
+ uint32_t height_mask = 0x3ffc;
+ uint32_t width_mask = 0x3ffc000;
+
+ height = (hw & height_mask) >> 2;
+ width = (hw & width_mask) >> 14 ;
+ return height * width;
+}
+
+static int verify_vfe_command_table(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+ int i;
+
+ switch (cmd_id) {
+ case VFE_CMD_AXI_IP_CFG:
+ {
+ vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data;
+ uint32_t size;
+ if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_IP_CFG) command size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+ size = get_size(cmd->ip_cfg_part2);
+
+ for (i = 0; i < 8; i++) {
+ void **addr = (void **)
+ &cmd->ip_buf_addr[i];
+ if (*addr && adsp_pmem_fixup(module, addr, size))
+ return -1;
+ }
+ }
+ case VFE_CMD_AXI_OP_CFG:
+ {
+ vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data;
+ void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr;
+
+ if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_OP_CFG) command size %d\n",
+ module->name, cmd_size);
+ return -1;
+ }
+ size1_y = get_size(cmd->op1_y_cfg_part2);
+ size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2);
+ size2_y = get_size(cmd->op2_y_cfg_part2);
+ size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2);
+ for (i = 0; i < 8; i++) {
+ addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]);
+ addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]);
+ addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]);
+ addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]);
+/*
+ printk("module %s: [%d] %p %p %p %p\n",
+ module->name, i,
+ *addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr);
+*/
+ if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) ||
+ (*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) ||
+ (*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) ||
+ (*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr)))
+ return -1;
+ }
+ }
+ default:
+ if (cmd_id > 4) {
+ printk(KERN_ERR "adsp: module %s: invalid VFE TABLE command id %d\n",
+ module->name, cmd_id);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_vfeCommandQueue:
+ return verify_vfe_command(module, cmd_data, cmd_size);
+ case QDSP_vfeCommandScaleQueue:
+ return verify_vfe_command_scale(module, cmd_data, cmd_size);
+ case QDSP_vfeCommandTableQueue:
+ return verify_vfe_command_table(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_ERR "adsp: module %s: unknown queue id %d\n",
+ module->name, queue_id);
+ return -1;
+ }
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c
new file mode 100644
index 00000000000..53aff77cfd9
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c
@@ -0,0 +1,163 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VDEC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+ ##args); } \
+ while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+
+#include <mach/qdsp5/qdsp5vdeccmdi.h>
+#include "adsp.h"
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+ unsigned short low)
+{
+ return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+ unsigned short *low)
+{
+ *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+ *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+ unsigned short *low,
+ unsigned short size_high,
+ unsigned short size_low,
+ struct msm_adsp_module *module,
+ unsigned long *addr, unsigned long *size)
+{
+ void *phys_addr;
+ unsigned long phys_size;
+ unsigned long kvaddr;
+
+ phys_addr = high_low_short_to_ptr(*high, *low);
+ phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+ DLOG("virt %x %x\n", phys_addr, phys_size);
+ if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+ DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+ *high, *low, size_high, size_low, phys_addr, phys_size);
+ return -1;
+ }
+ ptr_to_high_low_short(phys_addr, high, low);
+ DLOG("phys %x %x\n", phys_addr, phys_size);
+ if (addr)
+ *addr = kvaddr;
+ if (size)
+ *size = phys_size;
+ return 0;
+}
+
+static int verify_vdec_pkt_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+ viddec_cmd_subframe_pkt *pkt;
+ unsigned long subframe_pkt_addr;
+ unsigned long subframe_pkt_size;
+ viddec_cmd_frame_header_packet *frame_header_pkt;
+ int i, num_addr, skip;
+ unsigned short *frame_buffer_high, *frame_buffer_low;
+ unsigned long frame_buffer_size;
+ unsigned short frame_buffer_size_high, frame_buffer_size_low;
+
+ DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+ if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
+ printk(KERN_INFO "adsp_video: unknown video packet %u\n",
+ cmd_id);
+ return 0;
+ }
+ if (cmd_size < sizeof(viddec_cmd_subframe_pkt))
+ return -1;
+
+ pkt = (viddec_cmd_subframe_pkt *)cmd_data;
+
+ if (pmem_fixup_high_low(&(pkt->subframe_packet_high),
+ &(pkt->subframe_packet_low),
+ pkt->subframe_packet_size_high,
+ pkt->subframe_packet_size_low,
+ module,
+ &subframe_pkt_addr,
+ &subframe_pkt_size))
+ return -1;
+
+ /* deref those ptrs and check if they are a frame header packet */
+ frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr;
+
+ switch (frame_header_pkt->packet_id) {
+ case 0xB201: /* h.264 */
+ num_addr = skip = 8;
+ break;
+ case 0x4D01: /* mpeg-4 and h.263 */
+ num_addr = 3;
+ skip = 0;
+ break;
+ default:
+ return 0;
+ }
+
+ frame_buffer_high = &frame_header_pkt->frame_buffer_0_high;
+ frame_buffer_low = &frame_header_pkt->frame_buffer_0_low;
+ frame_buffer_size = (frame_header_pkt->x_dimension *
+ frame_header_pkt->y_dimension * 3) / 2;
+ ptr_to_high_low_short((void *)frame_buffer_size,
+ &frame_buffer_size_high,
+ &frame_buffer_size_low);
+ for (i = 0; i < num_addr; i++) {
+ if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+ frame_buffer_size_high,
+ frame_buffer_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ frame_buffer_high += 2;
+ frame_buffer_low += 2;
+ }
+ /* Patch the output buffer. */
+ frame_buffer_high += 2*skip;
+ frame_buffer_low += 2*skip;
+ if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+ frame_buffer_size_high,
+ frame_buffer_size_low, module, NULL, NULL))
+ return -1;
+ return 0;
+}
+
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_mpuVDecPktQueue:
+ DLOG("\n");
+ return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_INFO "unknown video queue %u\n", queue_id);
+ return 0;
+ }
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c
new file mode 100644
index 00000000000..ee374495052
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c
@@ -0,0 +1,235 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VENC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+ ##args); } \
+ while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+#include <mach/qdsp5/qdsp5venccmdi.h>
+#include "adsp.h"
+
+
+static unsigned short x_dimension, y_dimension;
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+ unsigned short low)
+{
+ return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+ unsigned short *low)
+{
+ *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+ *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+ unsigned short *low,
+ unsigned short size_high,
+ unsigned short size_low,
+ struct msm_adsp_module *module,
+ unsigned long *addr, unsigned long *size)
+{
+ void *phys_addr;
+ unsigned long phys_size;
+ unsigned long kvaddr;
+
+ phys_addr = high_low_short_to_ptr(*high, *low);
+ phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+ DLOG("virt %x %x\n", phys_addr, phys_size);
+ if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+ DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+ *high, *low, size_high, size_low, phys_addr, phys_size);
+ return -1;
+ }
+ ptr_to_high_low_short(phys_addr, high, low);
+ DLOG("phys %x %x\n", phys_addr, phys_size);
+ if (addr)
+ *addr = kvaddr;
+ if (size)
+ *size = phys_size;
+ return 0;
+}
+
+static int verify_venc_cmd(struct msm_adsp_module *module,
+ void *cmd_data, size_t cmd_size)
+{
+ unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+ unsigned long frame_buf_size, luma_buf_size, chroma_buf_size;
+ unsigned short frame_buf_size_high, frame_buf_size_low;
+ unsigned short luma_buf_size_high, luma_buf_size_low;
+ unsigned short chroma_buf_size_high, chroma_buf_size_low;
+ videnc_cmd_cfg *config_cmd;
+ videnc_cmd_frame_start *frame_cmd;
+ videnc_cmd_dis *dis_cmd;
+
+ DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+ switch (cmd_id) {
+ case VIDENC_CMD_ACTIVE:
+ if (cmd_size < sizeof(videnc_cmd_active))
+ return -1;
+ break;
+ case VIDENC_CMD_IDLE:
+ if (cmd_size < sizeof(videnc_cmd_idle))
+ return -1;
+ x_dimension = y_dimension = 0;
+ break;
+ case VIDENC_CMD_STATUS_QUERY:
+ if (cmd_size < sizeof(videnc_cmd_status_query))
+ return -1;
+ break;
+ case VIDENC_CMD_RC_CFG:
+ if (cmd_size < sizeof(videnc_cmd_rc_cfg))
+ return -1;
+ break;
+ case VIDENC_CMD_INTRA_REFRESH:
+ if (cmd_size < sizeof(videnc_cmd_intra_refresh))
+ return -1;
+ break;
+ case VIDENC_CMD_DIGITAL_ZOOM:
+ if (cmd_size < sizeof(videnc_cmd_digital_zoom))
+ return -1;
+ break;
+ case VIDENC_CMD_DIS_CFG:
+ if (cmd_size < sizeof(videnc_cmd_dis_cfg))
+ return -1;
+ break;
+ case VIDENC_CMD_CFG:
+ if (cmd_size < sizeof(videnc_cmd_cfg))
+ return -1;
+ config_cmd = (videnc_cmd_cfg *)cmd_data;
+ x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8;
+ x_dimension = x_dimension*16;
+ y_dimension = (config_cmd->venc_frame_dim) & 0xFF;
+ y_dimension = y_dimension * 16;
+ break;
+ case VIDENC_CMD_FRAME_START:
+ if (cmd_size < sizeof(videnc_cmd_frame_start))
+ return -1;
+ frame_cmd = (videnc_cmd_frame_start *)cmd_data;
+ luma_buf_size = x_dimension * y_dimension;
+ chroma_buf_size = luma_buf_size>>1;
+ frame_buf_size = luma_buf_size + chroma_buf_size;
+ ptr_to_high_low_short((void *)luma_buf_size,
+ &luma_buf_size_high,
+ &luma_buf_size_low);
+ ptr_to_high_low_short((void *)chroma_buf_size,
+ &chroma_buf_size_high,
+ &chroma_buf_size_low);
+ ptr_to_high_low_short((void *)frame_buf_size,
+ &frame_buf_size_high,
+ &frame_buf_size_low);
+ /* Address of raw Y data. */
+ if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high,
+ &frame_cmd->input_luma_addr_low,
+ luma_buf_size_high,
+ luma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Address of raw CbCr data */
+ if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high,
+ &frame_cmd->input_chroma_addr_low,
+ chroma_buf_size_high,
+ chroma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Reference VOP */
+ if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high,
+ &frame_cmd->ref_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Encoded Packet Address */
+ if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high,
+ &frame_cmd->enc_pkt_buf_ptr_low,
+ frame_cmd->enc_pkt_buf_size_high,
+ frame_cmd->enc_pkt_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Unfiltered VOP Buffer Address */
+ if (pmem_fixup_high_low(
+ &frame_cmd->unfilt_recon_vop_buf_ptr_high,
+ &frame_cmd->unfilt_recon_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ /* Filtered VOP Buffer Address */
+ if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high,
+ &frame_cmd->filt_recon_vop_buf_ptr_low,
+ frame_buf_size_high,
+ frame_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ break;
+ case VIDENC_CMD_DIS:
+ if (cmd_size < sizeof(videnc_cmd_dis))
+ return -1;
+ dis_cmd = (videnc_cmd_dis *)cmd_data;
+ luma_buf_size = x_dimension * y_dimension;
+ ptr_to_high_low_short((void *)luma_buf_size,
+ &luma_buf_size_high,
+ &luma_buf_size_low);
+ /* Prev VFE Luma Output Address */
+ if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high,
+ &dis_cmd->vfe_out_prev_luma_addr_low,
+ luma_buf_size_high,
+ luma_buf_size_low,
+ module,
+ NULL, NULL))
+ return -1;
+ break;
+ default:
+ printk(KERN_INFO "adsp_video:unknown encoder video command %u\n",
+ cmd_id);
+ return 0;
+ }
+
+ return 0;
+}
+
+
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+ unsigned int queue_id, void *cmd_data,
+ size_t cmd_size)
+{
+ switch (queue_id) {
+ case QDSP_mpuVEncCmdQueue:
+ DLOG("\n");
+ return verify_venc_cmd(module, cmd_data, cmd_size);
+ default:
+ printk(KERN_INFO "unknown video queue %u\n", queue_id);
+ return 0;
+ }
+}
+
diff --git a/drivers/staging/dream/qdsp5/audio_aac.c b/drivers/staging/dream/qdsp5/audio_aac.c
new file mode 100644
index 00000000000..ad2390f32a4
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_aac.c
@@ -0,0 +1,1052 @@
+/* arch/arm/mach-msm/qdsp5/audio_aac.c
+ *
+ * aac audio decoder device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009 QUALCOMM USA, INC.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audmgr.h"
+
+#include <mach/msm_adsp.h>
+#include <mach/msm_audio_aac.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 32768
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AAC 5
+
+#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ struct msm_audio_aac_config aac_config;
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ dprintk("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audio_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ dprintk("audio_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audio_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audio_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ dprintk("%s: FLUSH_ACK\n", __func__);
+ audio->wflush = 0;
+ audio->rflush = 0;
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_aac = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ audpp_cmd_cfg_adec_params_aac cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+ cmd.format = audio->aac_config.format;
+ cmd.audio_object = audio->aac_config.audio_object;
+ cmd.ep_config = audio->aac_config.ep_config;
+ cmd.aac_section_data_resilience_flag =
+ audio->aac_config.aac_section_data_resilience_flag;
+ cmd.aac_scalefactor_data_resilience_flag =
+ audio->aac_config.aac_scalefactor_data_resilience_flag;
+ cmd.aac_spectral_data_resilience_flag =
+ audio->aac_config.aac_spectral_data_resilience_flag;
+ cmd.sbr_on_flag = audio->aac_config.sbr_on_flag;
+ cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag;
+ cmd.channel_configuration = audio->aac_config.channel_configuration;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 1024); /* AAC frame size */
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audplay_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+/* printk("frame %d busy\n", audio->out_tail); */
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ audio->out_needed = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+ audio->buf_refresh = 0;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static int audaac_validate_usr_config(struct msm_audio_aac_config *config)
+{
+ int ret_val = -1;
+
+ if (config->format != AUDIO_AAC_FORMAT_ADTS &&
+ config->format != AUDIO_AAC_FORMAT_RAW &&
+ config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW &&
+ config->format != AUDIO_AAC_FORMAT_LOAS)
+ goto done;
+
+ if (config->audio_object != AUDIO_AAC_OBJECT_LC &&
+ config->audio_object != AUDIO_AAC_OBJECT_LTP &&
+ config->audio_object != AUDIO_AAC_OBJECT_ERLC)
+ goto done;
+
+ if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) {
+ if (config->ep_config > 3)
+ goto done;
+ if (config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_OFF &&
+ config->aac_scalefactor_data_resilience_flag !=
+ AUDIO_AAC_SCA_DATA_RES_ON)
+ goto done;
+ if (config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_OFF &&
+ config->aac_section_data_resilience_flag !=
+ AUDIO_AAC_SEC_DATA_RES_ON)
+ goto done;
+ if (config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_OFF &&
+ config->aac_spectral_data_resilience_flag !=
+ AUDIO_AAC_SPEC_DATA_RES_ON)
+ goto done;
+ } else {
+ config->aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ config->aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ config->aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+ }
+
+ if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF &&
+ config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON)
+ goto done;
+
+ if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF &&
+ config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON)
+ goto done;
+
+ if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR)
+ goto done;
+
+ if (config->channel_configuration > 2)
+ goto done;
+
+ ret_val = 0;
+ done:
+ return ret_val;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audio_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ dprintk("%s: AUDIO_FLUSH\n", __func__);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ if (audio->running)
+ audpp_flush(audio->dec_id);
+ else {
+ audio->rflush = 0;
+ audio->wflush = 0;
+ }
+ break;
+
+ case AUDIO_SET_CONFIG:{
+ struct msm_audio_config config;
+
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (config.channel_count == 1) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count =
+ AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode ==
+ AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG:{
+ if (copy_to_user((void *)arg, &audio->aac_config,
+ sizeof(audio->aac_config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG:{
+ struct msm_audio_aac_config usr_config;
+
+ if (copy_from_user
+ (&usr_config, (void *)arg,
+ sizeof(usr_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (audaac_validate_usr_config(&usr_config) == 0) {
+ audio->aac_config = usr_config;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("ioctl: allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audio_aac: buf alloc fail\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audio_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver
+ does not know frame size, read count must be greater
+ or equal to size of PCM samples */
+ dprintk("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ dprintk("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audio_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ dprintk("audio_read: kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audio_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned dsize;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->reserved) {
+ dprintk("%s: append reserved byte %x\n",
+ __func__, audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > (frame->size - 1)) ?
+ frame->size - 1 : count;
+ cpy_ptr++;
+ dsize = 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > frame->size) ? frame->size : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ dprintk("%s: odd length buf reserve last byte %x\n",
+ __func__, audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audio_release()\n");
+
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ audio->reserved = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_aac_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_aac_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_aac, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ goto done;
+ }
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS;
+ audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC;
+ audio->aac_config.ep_config = 0;
+ audio->aac_config.aac_section_data_resilience_flag =
+ AUDIO_AAC_SEC_DATA_RES_OFF;
+ audio->aac_config.aac_scalefactor_data_resilience_flag =
+ AUDIO_AAC_SCA_DATA_RES_OFF;
+ audio->aac_config.aac_spectral_data_resilience_flag =
+ AUDIO_AAC_SPEC_DATA_RES_OFF;
+ audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON;
+ audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON;
+ audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR;
+ audio->aac_config.channel_configuration = 2;
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_aac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_aac_audio.lock);
+ mutex_init(&the_aac_audio.write_lock);
+ mutex_init(&the_aac_audio.read_lock);
+ spin_lock_init(&the_aac_audio.dsp_lock);
+ init_waitqueue_head(&the_aac_audio.write_wait);
+ init_waitqueue_head(&the_aac_audio.read_wait);
+ the_aac_audio.read_data = NULL;
+ return misc_register(&audio_aac_misc);
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_amrnb.c b/drivers/staging/dream/qdsp5/audio_amrnb.c
new file mode 100644
index 00000000000..cd818a526f8
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_amrnb.c
@@ -0,0 +1,873 @@
+/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+ *
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1024 /* Hold minimum 700ms voice data */
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_AMRNB 10
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+struct audpp_cmd_cfg_adec_params_amrnb {
+ audpp_cmd_cfg_adec_params_common common;
+ unsigned short stereo_cfg;
+} __attribute__((packed)) ;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audamrnb_send_data(struct audio *audio, unsigned needed);
+static void audamrnb_config_hostpcm(struct audio *audio);
+static void audamrnb_buffer_refresh(struct audio *audio);
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audamrnb_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_disable(struct audio *audio)
+{
+ dprintk("audamrnb_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrnb_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audamrnb_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audamrnb_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audamrnb_buffer_refresh(audio);
+ } else {
+ dprintk("audamrnb_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audamrnb_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audamrnb_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audamrnb_config_hostpcm(audio);
+ audamrnb_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audamrnb_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audamrnb_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audamrnb_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audamrnb_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ default:
+ pr_err("audamrnb_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrnb = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_amrnb cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audamrnb_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ);
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audamrnb_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audamrnb_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audamrnb_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+/* printk("frame %d busy\n", audio->out_tail); */
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audamrnb_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audamrnb_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audamrnb_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audamrnb_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audamrnb_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audamrnb_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audamrnb_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audamrnb_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+
+ case AUDIO_SET_CONFIG:{
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("audamrnb_ioctl: allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audamrnb_ioctl: no mem for pcm buf\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audamrnb_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ dprintk("audamrnb_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audamrnb_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audamrnb_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ }
+ }
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audamrnb_read: kick start pcm feedback again\n");
+ audamrnb_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audamrnb_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audamrnb_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ dprintk("audamrnb_write() \n");
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ dprintk("audamrnb_write() buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audamrnb_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audamrnb_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audamrnb_release()\n");
+
+ mutex_lock(&audio->lock);
+ audamrnb_disable(audio);
+ audamrnb_flush(audio);
+ audamrnb_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_amrnb_audio;
+
+static int audamrnb_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_amrnb_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_amrnb, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ audmgr_disable(&audio->audmgr);
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ goto done;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audamrnb_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_amrnb_fops = {
+ .owner = THIS_MODULE,
+ .open = audamrnb_open,
+ .release = audamrnb_release,
+ .read = audamrnb_read,
+ .write = audamrnb_write,
+ .unlocked_ioctl = audamrnb_ioctl,
+};
+
+struct miscdevice audio_amrnb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb",
+ .fops = &audio_amrnb_fops,
+};
+
+static int __init audamrnb_init(void)
+{
+ mutex_init(&the_amrnb_audio.lock);
+ mutex_init(&the_amrnb_audio.write_lock);
+ mutex_init(&the_amrnb_audio.read_lock);
+ spin_lock_init(&the_amrnb_audio.dsp_lock);
+ init_waitqueue_head(&the_amrnb_audio.write_wait);
+ init_waitqueue_head(&the_amrnb_audio.read_wait);
+ the_amrnb_audio.read_data = NULL;
+ return misc_register(&audio_amrnb_misc);
+}
+
+static void __exit audamrnb_exit(void)
+{
+ misc_deregister(&audio_amrnb_misc);
+}
+
+module_init(audamrnb_init);
+module_exit(audamrnb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-NB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/drivers/staging/dream/qdsp5/audio_evrc.c b/drivers/staging/dream/qdsp5/audio_evrc.c
new file mode 100644
index 00000000000..4b43e183f9e
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_evrc.c
@@ -0,0 +1,845 @@
+/* arch/arm/mach-msm/audio_evrc.c
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code also borrows from audio_aac.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+ printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Hold 30 packets of 24 bytes each*/
+#define BUFSZ 720
+#define DMASZ (BUFSZ * 2)
+
+#define AUDDEC_DEC_EVRC 12
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT 5
+/* DSP only accepts 5 buffers at most
+ * but support 2 buffers currently
+ */
+#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1;
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+static struct audio the_evrc_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audevrc_send_data(struct audio *audio, unsigned needed);
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audevrc_config_hostpcm(struct audio *audio);
+static void audevrc_buffer_refresh(struct audio *audio);
+
+/* must be called with audio->lock held */
+static int audevrc_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_EVRC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_disable(struct audio *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+
+static void audevrc_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr
+ == payload[2 + index * 2]) {
+ dprintk("audevrc_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audevrc_update_pcm_buf_entry: expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audevrc_buffer_refresh(audio);
+ } else {
+ dprintk("audevrc_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audevrc_send_data(audio, 1);
+ break;
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ dprintk("audevrc_update_pcm_buf_entry:======> \n");
+ audevrc_update_pcm_buf_entry(audio, msg);
+ break;
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audevrc_config_hostpcm(audio);
+ audevrc_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audevrc_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audevrc_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audevrc_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audevrc_dsp_event: ROUTING_ACK\n");
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ default:
+ pr_err("audevrc_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_evrc = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_evrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = sizeof(cmd);
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audevrc_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audevrc_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audevrc_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audevrc_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audevrc_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audevrc_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audevrc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audevrc_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audevrc_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audevrc_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_SET_CONFIG:{
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ }
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config, sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk("audevrc_ioctl: allocate PCM buf %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err
+ ("audevrc_ioctl: no mem for pcm buf\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+ if (!audio->pcm_feedback) {
+ return 0;
+ /* PCM feedback is not enabled. Nothing to read */
+ }
+ mutex_lock(&audio->read_lock);
+ dprintk("audevrc_read() \n");
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped));
+ dprintk("audevrc_read() wait terminated \n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ * not know frame size, read count must be greater or
+ * equal to size of PCM samples
+ */
+ dprintk("audevrc_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audevrc_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audevrc_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+
+ }
+ }
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audevrc_read: kick start pcm feedback again\n");
+ audevrc_buffer_refresh(audio);
+ }
+ mutex_unlock(&audio->read_lock);
+ if (buf > start)
+ rc = buf - start;
+ dprintk("audevrc_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audevrc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ mutex_lock(&audio->write_lock);
+ dprintk("audevrc_write() \n");
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audevrc_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audevrc_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audevrc_release()\n");
+
+ mutex_lock(&audio->lock);
+ audevrc_disable(audio);
+ audevrc_flush(audio);
+ audevrc_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_evrc_audio;
+
+static int audevrc_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_evrc_audio;
+ int rc;
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ return -EBUSY;
+ }
+
+ /* Acquire Lock */
+ mutex_lock(&audio->lock);
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto dma_fail;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto audmgr_fail;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_evrc, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ goto adsp_fail;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x3FFF;
+
+ audevrc_flush(audio);
+
+ audio->opened = 1;
+ file->private_data = audio;
+
+ mutex_unlock(&audio->lock);
+ return rc;
+
+adsp_fail:
+ audmgr_close(&audio->audmgr);
+audmgr_fail:
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+dma_fail:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_evrc_fops = {
+ .owner = THIS_MODULE,
+ .open = audevrc_open,
+ .release = audevrc_release,
+ .read = audevrc_read,
+ .write = audevrc_write,
+ .unlocked_ioctl = audevrc_ioctl,
+};
+
+struct miscdevice audio_evrc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc",
+ .fops = &audio_evrc_fops,
+};
+
+static int __init audevrc_init(void)
+{
+ mutex_init(&the_evrc_audio.lock);
+ mutex_init(&the_evrc_audio.write_lock);
+ mutex_init(&the_evrc_audio.read_lock);
+ spin_lock_init(&the_evrc_audio.dsp_lock);
+ init_waitqueue_head(&the_evrc_audio.write_wait);
+ init_waitqueue_head(&the_evrc_audio.read_wait);
+ the_evrc_audio.read_data = NULL;
+ return misc_register(&audio_evrc_misc);
+}
+
+static void __exit audevrc_exit(void)
+{
+ misc_deregister(&audio_evrc_misc);
+}
+
+module_init(audevrc_init);
+module_exit(audevrc_exit);
+
+MODULE_DESCRIPTION("MSM EVRC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/drivers/staging/dream/qdsp5/audio_in.c b/drivers/staging/dream/qdsp5/audio_in.c
new file mode 100644
index 00000000000..3d950a24589
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_in.c
@@ -0,0 +1,967 @@
+/* arch/arm/mach-msm/qdsp5/audio_in.c
+ *
+ * pcm audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (8)
+#define FRAME_SIZE (2052 * 2)
+#define MONO_DATA_SIZE (2048)
+#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
+#define DMASZ (FRAME_SIZE * FRAME_NUM)
+
+#define AGC_PARAM_SIZE (20)
+#define NS_PARAM_SIZE (6)
+#define IIR_PARAM_SIZE (48)
+#define DEBUG (0)
+
+#define AGC_ENABLE 0x0001
+#define NS_ENABLE 0x0002
+#define IIR_ENABLE 0x0004
+
+struct tx_agc_config {
+ uint16_t agc_params[AGC_PARAM_SIZE];
+};
+
+struct ns_config {
+ uint16_t ns_params[NS_PARAM_SIZE];
+};
+
+struct tx_iir_filter {
+ uint16_t num_bands;
+ uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct audpre_cmd_iir_config_type {
+ uint16_t cmd_id;
+ uint16_t active_flag;
+ uint16_t num_bands;
+ uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct buffer {
+ void *data;
+ uint32_t size;
+ uint32_t read;
+ uint32_t addr;
+};
+
+struct audio_in {
+ struct buffer in[FRAME_NUM];
+
+ spinlock_t dsp_lock;
+
+ atomic_t in_bytes;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ wait_queue_head_t wait;
+
+ struct msm_adsp_module *audpre;
+ struct msm_adsp_module *audrec;
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t type; /* 0 for PCM ,1 for AAC */
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+
+ unsigned short samp_rate_index;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+
+ /* audpre settings */
+ int agc_enable;
+ struct tx_agc_config agc;
+
+ int ns_enable;
+ struct ns_config ns;
+
+ int iir_enable;
+ struct tx_iir_filter iir;
+};
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable);
+static int audio_in_encoder_config(struct audio_in *audio);
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+static void audio_flush(struct audio_in *audio);
+static int audio_dsp_set_agc(struct audio_in *audio);
+static int audio_dsp_set_ns(struct audio_in *audio);
+static int audio_dsp_set_tx_iir(struct audio_in *audio);
+
+static unsigned convert_dsp_samp_index(unsigned index)
+{
+ switch (index) {
+ case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000;
+ case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100;
+ case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000;
+ case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000;
+ case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050;
+ case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000;
+ case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000;
+ case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025;
+ case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000;
+ default: return AUDREC_CMD_SAMP_RATE_INDX_11025;
+ }
+}
+
+static unsigned convert_samp_rate(unsigned hz)
+{
+ switch (hz) {
+ case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000;
+ case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100;
+ case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000;
+ case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000;
+ case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050;
+ case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000;
+ case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000;
+ case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025;
+ case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000;
+ default: return RPC_AUD_DEF_SAMPLE_RATE_11025;
+ }
+}
+
+static unsigned convert_samp_index(unsigned index)
+{
+ switch (index) {
+ case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000;
+ case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100;
+ case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000;
+ case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000;
+ case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050;
+ case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000;
+ case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000;
+ case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025;
+ case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000;
+ default: return 11025;
+ }
+}
+
+/* must be called with audio->lock held */
+static int audio_in_enable(struct audio_in *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ if (audio->enabled)
+ return 0;
+
+ cfg.tx_rate = audio->samp_rate;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ else
+ cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audpre)) {
+ pr_err("audrec: msm_adsp_enable(audpre) failed\n");
+ return -ENODEV;
+ }
+ if (msm_adsp_enable(audio->audrec)) {
+ pr_err("audrec: msm_adsp_enable(audrec) failed\n");
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ audio_in_dsp_enable(audio, 1);
+
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_in_disable(struct audio_in *audio)
+{
+ if (audio->enabled) {
+ audio->enabled = 0;
+
+ audio_in_dsp_enable(audio, 0);
+
+ wake_up(&audio->wait);
+
+ msm_adsp_disable(audio->audrec);
+ msm_adsp_disable(audio->audpre);
+ audmgr_disable(&audio->audmgr);
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audpre_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ uint16_t msg[2];
+ getevent(msg, sizeof(msg));
+
+ switch (id) {
+ case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
+ pr_info("audpre: type %d, status_flag %d\n", msg[0], msg[1]);
+ break;
+ case AUDPREPROC_MSG_ERROR_MSG_ID:
+ pr_info("audpre: err_index %d\n", msg[0]);
+ break;
+ default:
+ pr_err("audpre: unknown event %d\n", id);
+ }
+}
+
+struct audio_frame {
+ uint16_t count_low;
+ uint16_t count_high;
+ uint16_t bytes;
+ uint16_t unknown;
+ unsigned char samples[];
+} __attribute__((packed));
+
+static void audio_in_get_dsp_frames(struct audio_in *audio)
+{
+ struct audio_frame *frame;
+ uint32_t index;
+ unsigned long flags;
+
+ index = audio->in_head;
+
+ /* XXX check for bogus frame size? */
+
+ frame = (void *) (((char *)audio->in[index].data) - sizeof(*frame));
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->in[index].size = frame->bytes;
+
+ audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (audio->in_head == audio->in_tail)
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ audio->in_count++;
+
+ audio_dsp_read_buffer(audio, audio->dsp_cnt++);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+ wake_up(&audio->wait);
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audio_in *audio = data;
+ uint16_t msg[3];
+ getevent(msg, sizeof(msg));
+
+ switch (id) {
+ case AUDREC_MSG_CMD_CFG_DONE_MSG:
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) {
+ pr_info("audpre: CFG ENABLED\n");
+ audio_dsp_set_agc(audio);
+ audio_dsp_set_ns(audio);
+ audio_dsp_set_tx_iir(audio);
+ audio_in_encoder_config(audio);
+ } else {
+ pr_info("audrec: CFG SLEEP\n");
+ audio->running = 0;
+ }
+ } else {
+ pr_info("audrec: CMD_CFG_DONE %x\n", msg[0]);
+ }
+ break;
+ case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+ pr_info("audrec: PARAM CFG DONE\n");
+ audio->running = 1;
+ break;
+ }
+ case AUDREC_MSG_FATAL_ERR_MSG:
+ pr_err("audrec: ERROR %x\n", msg[0]);
+ break;
+ case AUDREC_MSG_PACKET_READY_MSG:
+/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */
+ audio_in_get_dsp_frames(audio);
+ break;
+ default:
+ pr_err("audrec: unknown event %d\n", id);
+ }
+}
+
+struct msm_adsp_ops audpre_adsp_ops = {
+ .event = audpre_dsp_event,
+};
+
+struct msm_adsp_ops audrec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+
+#define audio_send_queue_pre(audio, cmd, len) \
+ msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len)
+#define audio_send_queue_recbs(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, \
+ QDSP_uPAudRecCmdQueue, cmd, len)
+
+static int audio_dsp_set_agc(struct audio_in *audio)
+{
+ audpreproc_cmd_cfg_agc_params cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+
+ if (audio->agc_enable) {
+ /* cmd.tx_agc_param_mask = 0xFE00 from sample code */
+ cmd.tx_agc_param_mask =
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) |
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+ cmd.tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+ memcpy(&cmd.static_gain, &audio->agc.agc_params[0],
+ sizeof(uint16_t) * 6);
+ /* cmd.param_mask = 0xFFF0 from sample code */
+ cmd.param_mask =
+ (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) |
+ (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK);
+ memcpy(&cmd.aig_attackk, &audio->agc.agc_params[6],
+ sizeof(uint16_t) * 14);
+
+ } else {
+ cmd.tx_agc_param_mask =
+ (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+ cmd.tx_agc_enable_flag =
+ AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("tx_agc_param_mask = 0x%04x\n", cmd.tx_agc_param_mask);
+ pr_info("tx_agc_enable_flag = 0x%04x\n", cmd.tx_agc_enable_flag);
+ pr_info("static_gain = 0x%04x\n", cmd.static_gain);
+ pr_info("adaptive_gain_flag = 0x%04x\n", cmd.adaptive_gain_flag);
+ pr_info("expander_th = 0x%04x\n", cmd.expander_th);
+ pr_info("expander_slope = 0x%04x\n", cmd.expander_slope);
+ pr_info("compressor_th = 0x%04x\n", cmd.compressor_th);
+ pr_info("compressor_slope = 0x%04x\n", cmd.compressor_slope);
+ pr_info("param_mask = 0x%04x\n", cmd.param_mask);
+ pr_info("aig_attackk = 0x%04x\n", cmd.aig_attackk);
+ pr_info("aig_leak_down = 0x%04x\n", cmd.aig_leak_down);
+ pr_info("aig_leak_up = 0x%04x\n", cmd.aig_leak_up);
+ pr_info("aig_max = 0x%04x\n", cmd.aig_max);
+ pr_info("aig_min = 0x%04x\n", cmd.aig_min);
+ pr_info("aig_releasek = 0x%04x\n", cmd.aig_releasek);
+ pr_info("aig_leakrate_fast = 0x%04x\n", cmd.aig_leakrate_fast);
+ pr_info("aig_leakrate_slow = 0x%04x\n", cmd.aig_leakrate_slow);
+ pr_info("attackk_msw = 0x%04x\n", cmd.attackk_msw);
+ pr_info("attackk_lsw = 0x%04x\n", cmd.attackk_lsw);
+ pr_info("delay = 0x%04x\n", cmd.delay);
+ pr_info("releasek_msw = 0x%04x\n", cmd.releasek_msw);
+ pr_info("releasek_lsw = 0x%04x\n", cmd.releasek_lsw);
+ pr_info("rms_tav = 0x%04x\n", cmd.rms_tav);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_ns(struct audio_in *audio)
+{
+ audpreproc_cmd_cfg_ns_params cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+
+ if (audio->ns_enable) {
+ /* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */
+ cmd.ec_mode_new =
+ AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
+ AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
+ AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
+ memcpy(&cmd.dens_gamma_n, &audio->ns.ns_params,
+ sizeof(audio->ns.ns_params));
+ } else {
+ cmd.ec_mode_new =
+ AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS |
+ AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("ec_mode_new = 0x%04x\n", cmd.ec_mode_new);
+ pr_info("dens_gamma_n = 0x%04x\n", cmd.dens_gamma_n);
+ pr_info("dens_nfe_block_size = 0x%04x\n", cmd.dens_nfe_block_size);
+ pr_info("dens_limit_ns = 0x%04x\n", cmd.dens_limit_ns);
+ pr_info("dens_limit_ns_d = 0x%04x\n", cmd.dens_limit_ns_d);
+ pr_info("wb_gamma_e = 0x%04x\n", cmd.wb_gamma_e);
+ pr_info("wb_gamma_n = 0x%04x\n", cmd.wb_gamma_n);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_tx_iir(struct audio_in *audio)
+{
+ struct audpre_cmd_iir_config_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+ if (audio->iir_enable) {
+ cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA;
+ cmd.num_bands = audio->iir.num_bands;
+ memcpy(&cmd.iir_params, &audio->iir.iir_params,
+ sizeof(audio->iir.iir_params));
+ } else {
+ cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
+ }
+#if DEBUG
+ pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+ pr_info("active_flag = 0x%04x\n", cmd.active_flag);
+#endif
+ return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable)
+{
+ audrec_cmd_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_CFG;
+ cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
+ cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type);
+ cmd.type_1 = 0;
+
+ return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_encoder_config(struct audio_in *audio)
+{
+ audrec_cmd_arec0param_cfg cmd;
+ uint16_t *data = (void *) audio->data;
+ unsigned n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
+ cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16;
+ cmd.ptr_to_extpkt_buffer_lsw = audio->phys;
+ cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
+ cmd.samp_rate_index = audio->samp_rate_index;
+ cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */
+
+ /* FIXME have no idea why cmd.rec_quality is fixed
+ * as 0x1C00 from sample code
+ */
+ cmd.rec_quality = 0x1C00;
+
+ /* prepare buffer pointers:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ * AAC
+ * Mono/Stere: 768 + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ audio->in[n].data = data + 4;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ data += (4 + (audio->channel_mode ? 2048 : 1024));
+ else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ data += (4 + 768);
+ }
+
+ return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+ audrec_cmd_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
+ /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
+ cmd.type = AUDREC_CMD_TYPE_0;
+ cmd.curr_rec_count_msw = read_cnt >> 16;
+ cmd.curr_rec_count_lsw = read_cnt;
+
+ return audio_send_queue_recbs(audio, &cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_enable_agc(struct audio_in *audio, int enable)
+{
+ if (audio->agc_enable != enable) {
+ audio->agc_enable = enable;
+ if (audio->running)
+ audio_dsp_set_agc(audio);
+ }
+}
+
+static void audio_enable_ns(struct audio_in *audio, int enable)
+{
+ if (audio->ns_enable != enable) {
+ audio->ns_enable = enable;
+ if (audio->running)
+ audio_dsp_set_ns(audio);
+ }
+}
+
+static void audio_enable_tx_iir(struct audio_in *audio, int enable)
+{
+ if (audio->iir_enable != enable) {
+ audio->iir_enable = enable;
+ if (audio->running)
+ audio_dsp_set_tx_iir(audio);
+ }
+}
+
+static void audio_flush(struct audio_in *audio)
+{
+ int i;
+
+ audio->dsp_cnt = 0;
+ audio->in_head = 0;
+ audio->in_tail = 0;
+ audio->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ audio->in[i].size = 0;
+ audio->in[i].read = 0;
+ }
+}
+
+static long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_in_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_in_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->read_lock);
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.channel_count == 1) {
+ cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO;
+ } else if (cfg.channel_count == 2) {
+ cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ if (cfg.type == 0) {
+ cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+ } else if (cfg.type == 1) {
+ cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->samp_rate = convert_samp_rate(cfg.sample_rate);
+ audio->samp_rate_index =
+ convert_dsp_samp_index(cfg.sample_rate);
+ audio->channel_mode = cfg.channel_count;
+ audio->buffer_size =
+ audio->channel_mode ? STEREO_DATA_SIZE
+ : MONO_DATA_SIZE;
+ audio->type = cfg.type;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ cfg.buffer_size = audio->buffer_size;
+ cfg.buffer_count = FRAME_NUM;
+ cfg.sample_rate = convert_samp_index(audio->samp_rate);
+ if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO)
+ cfg.channel_count = 1;
+ else
+ cfg.channel_count = 2;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+ cfg.type = 0;
+ else
+ cfg.type = 1;
+ cfg.unused[0] = 0;
+ cfg.unused[1] = 0;
+ cfg.unused[2] = 0;
+ if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio_in *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->wait, (audio->in_count > 0) || audio->stopped);
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ index = audio->in_tail;
+ data = (uint8_t *) audio->in[index].data;
+ size = audio->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (index != audio->in_tail) {
+ /* overrun -- data is invalid and we need to retry */
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ continue;
+ }
+ audio->in[index].size = 0;
+ audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+ audio->in_count--;
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ count -= size;
+ buf += size;
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ break;
+ } else {
+ pr_err("audio_in: short read\n");
+ break;
+ }
+ if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+ break; /* AAC only read one frame */
+ }
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ return buf - start;
+
+ return rc;
+}
+
+static ssize_t audio_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static int audio_in_release(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ audio_in_disable(audio);
+ audio_flush(audio);
+ msm_adsp_put(audio->audrec);
+ msm_adsp_put(audio->audpre);
+ audio->audrec = NULL;
+ audio->audpre = NULL;
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio_in the_audio_in;
+
+static int audio_in_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_in;
+ int rc;
+
+ mutex_lock(&audio->lock);
+ if (audio->opened) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025;
+ audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025;
+ audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
+ audio->buffer_size = MONO_DATA_SIZE;
+ audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+ rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+ &audpre_adsp_ops, audio);
+ if (rc)
+ goto done;
+ rc = msm_adsp_get("AUDRECTASK", &audio->audrec,
+ &audrec_adsp_ops, audio);
+ if (rc)
+ goto done;
+
+ audio->dsp_cnt = 0;
+ audio->stopped = 0;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio_in *audio = file->private_data;
+ int rc = 0, enable;
+ uint16_t enable_mask;
+#if DEBUG
+ int i;
+#endif
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPRE: {
+ if (copy_from_user(&enable_mask, (void *) arg,
+ sizeof(enable_mask)))
+ goto out_fault;
+
+ enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
+ audio_enable_agc(audio, enable);
+ enable = (enable_mask & NS_ENABLE) ? 1 : 0;
+ audio_enable_ns(audio, enable);
+ enable = (enable_mask & IIR_ENABLE) ? 1 : 0;
+ audio_enable_tx_iir(audio, enable);
+ break;
+ }
+ case AUDIO_SET_AGC: {
+ if (copy_from_user(&audio->agc, (void *) arg,
+ sizeof(audio->agc)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set agc\n");
+ for (i = 0; i < AGC_PARAM_SIZE; i++) \
+ pr_info("agc_params[%d] = 0x%04x\n", i,
+ audio->agc.agc_params[i]);
+#endif
+ break;
+ }
+ case AUDIO_SET_NS: {
+ if (copy_from_user(&audio->ns, (void *) arg,
+ sizeof(audio->ns)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set ns\n");
+ for (i = 0; i < NS_PARAM_SIZE; i++) \
+ pr_info("ns_params[%d] = 0x%04x\n",
+ i, audio->ns.ns_params[i]);
+#endif
+ break;
+ }
+ case AUDIO_SET_TX_IIR: {
+ if (copy_from_user(&audio->iir, (void *) arg,
+ sizeof(audio->iir)))
+ goto out_fault;
+#if DEBUG
+ pr_info("set iir\n");
+ pr_info("iir.num_bands = 0x%04x\n", audio->iir.num_bands);
+ for (i = 0; i < IIR_PARAM_SIZE; i++) \
+ pr_info("iir_params[%d] = 0x%04x\n",
+ i, audio->iir.iir_params[i]);
+#endif
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+
+ goto out;
+
+out_fault:
+ rc = -EFAULT;
+out:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static int audpre_open(struct inode *inode, struct file *file)
+{
+ struct audio_in *audio = &the_audio_in;
+ file->private_data = audio;
+ return 0;
+}
+
+static struct file_operations audio_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+static struct file_operations audpre_fops = {
+ .owner = THIS_MODULE,
+ .open = audpre_open,
+ .unlocked_ioctl = audpre_ioctl,
+};
+
+struct miscdevice audio_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_in",
+ .fops = &audio_fops,
+};
+
+struct miscdevice audpre_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audpre",
+ .fops = &audpre_fops,
+};
+
+static int __init audio_in_init(void)
+{
+ int rc;
+ the_audio_in.data = dma_alloc_coherent(NULL, DMASZ,
+ &the_audio_in.phys, GFP_KERNEL);
+ if (!the_audio_in.data) {
+ printk(KERN_ERR "%s: Unable to allocate DMA buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&the_audio_in.lock);
+ mutex_init(&the_audio_in.read_lock);
+ spin_lock_init(&the_audio_in.dsp_lock);
+ init_waitqueue_head(&the_audio_in.wait);
+ rc = misc_register(&audio_in_misc);
+ if (!rc) {
+ rc = misc_register(&audpre_misc);
+ if (rc < 0)
+ misc_deregister(&audio_in_misc);
+ }
+ return rc;
+}
+
+device_initcall(audio_in_init);
diff --git a/drivers/staging/dream/qdsp5/audio_mp3.c b/drivers/staging/dream/qdsp5/audio_mp3.c
new file mode 100644
index 00000000000..b95574f699f
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_mp3.c
@@ -0,0 +1,971 @@
+/* arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768
+#define BUFSZ_MIN 4096
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */
+#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most
+ but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ unsigned out_dma_sz;
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* ---- End of Host PCM section */
+
+ struct msm_adsp_module *audplay;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int pcm_feedback;
+ int buf_refresh;
+
+ int reserved; /* A byte is being reserved */
+ char rsv_byte; /* Handle odd length user data */
+
+ unsigned volume;
+
+ uint16_t dec_id;
+ uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ pr_info("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_MP3;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ pr_info("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ if (audio->rflush) {
+ audio->buf_refresh = 1;
+ return;
+ }
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ pr_info("audio_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+
+ } else {
+ pr_err
+ ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+ , audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audplay_buffer_refresh(audio);
+ } else {
+ pr_info("audio_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+ wake_up(&audio->read_wait);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audplay_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audio_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ break;
+ }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ pr_info("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ pr_info("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ pr_info("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ pr_info("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audplay_config_hostpcm(audio);
+ audplay_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ break;
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ pr_info("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+
+ case AUDPP_MSG_FLUSH_ACK:
+ dprintk("%s: FLUSH_ACK\n", __func__);
+ audio->wflush = 0;
+ audio->rflush = 0;
+ if (audio->pcm_feedback)
+ audplay_buffer_refresh(audio);
+ break;
+
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+
+struct msm_adsp_ops audplay_adsp_ops = {
+ .event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V |
+ AUDDEC_DEC_MP3;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ audpp_cmd_cfg_adec_params_mp3 cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ pr_info("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len/2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+ (audio->in[audio->fill_next].size % 576); /* Mp3 frame size */
+ refresh_cmd.buf_read_count = 0;
+ pr_info("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ pr_info("audplay_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = 1;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (audio->wflush) {
+ audio->out_needed = 1;
+ goto done;
+ }
+
+ if (needed && !audio->wflush) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->reserved = 0;
+ atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+ /* Make sure read/write thread are free from
+ * sleep and knowing that system is not able
+ * to process io request at the moment
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audio_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ pr_info("audio_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ audio_ioport_reset(audio);
+ audio->stopped = 0;
+ break;
+ case AUDIO_FLUSH:
+ dprintk("%s: AUDIO_FLUSH\n", __func__);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ audio_ioport_reset(audio);
+ audio->rflush = 0;
+ audio->wflush = 0;
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ audplay_buffer_refresh(audio);
+ }
+ break;
+
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = (audio->out_dma_sz >> 1);
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void *) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+ if (copy_from_user
+ (&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ pr_info("ioctl: allocate PCM buffer %d\n",
+ config.buffer_count *
+ config.buffer_size);
+ audio->read_data =
+ dma_alloc_coherent(NULL,
+ config.buffer_size *
+ config.buffer_count,
+ &audio->read_phys,
+ GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err("audio_mp3: malloc pcm \
+ buf failed\n");
+ rc = -1;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count;
+ index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback disabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ pr_info("audio_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].
+ used > 0) || (audio->stopped)
+ || (audio->rflush));
+
+ if (rc < 0)
+ break;
+
+ if (audio->stopped || audio->rflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since
+ * driver does not know frame size, read count
+ * must be greater or equal
+ * to size of PCM samples
+ */
+ pr_info("audio_read: no partial frame done reading\n");
+ break;
+ } else {
+ pr_info("audio_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user
+ (buf, audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audio_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ if (audio->in[audio->read_next].used == 0)
+ break; /* No data ready at this moment
+ * Exit while loop to prevent
+ * output thread sleep too long
+ */
+ }
+ }
+
+ /* don't feed output buffer to HW decoder during flushing
+ * buffer refresh command will be sent once flush completes
+ * send buf refresh command here can confuse HW decoder
+ */
+ if (audio->buf_refresh && !audio->rflush) {
+ audio->buf_refresh = 0;
+ pr_info("audio_read: kick start pcm feedback again\n");
+ audplay_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ pr_info("audio_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned dsize;
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ cpy_ptr = frame->data;
+ dsize = 0;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped)
+ || (audio->wflush));
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (audio->reserved) {
+ dprintk("%s: append reserved byte %x\n",
+ __func__, audio->rsv_byte);
+ *cpy_ptr = audio->rsv_byte;
+ xfer = (count > (frame->size - 1)) ?
+ frame->size - 1 : count;
+ cpy_ptr++;
+ dsize = 1;
+ audio->reserved = 0;
+ } else
+ xfer = (count > frame->size) ? frame->size : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ dsize += xfer;
+ if (dsize & 1) {
+ audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+ dprintk("%s: odd length buf reserve last byte %x\n",
+ __func__, audio->rsv_byte);
+ audio->reserved = 1;
+ dsize--;
+ }
+ count -= xfer;
+ buf += xfer;
+
+ if (dsize > 0) {
+ audio->out_head ^= 1;
+ frame->used = dsize;
+ audplay_send_data(audio, 0);
+ }
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audio_release()\n");
+
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ audio->reserved = 0;
+ dma_free_coherent(NULL, audio->out_dma_sz, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data != NULL) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static struct audio the_mp3_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_mp3_audio;
+ int rc;
+ unsigned pmem_sz;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ pmem_sz = DMASZ_MAX;
+
+ while (pmem_sz >= DMASZ_MIN) {
+ audio->data = dma_alloc_coherent(NULL, pmem_sz,
+ &audio->phys, GFP_KERNEL);
+ if (audio->data)
+ break;
+ else if (pmem_sz == DMASZ_MIN) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ } else
+ pmem_sz >>= 1;
+ }
+
+ dprintk("%s: allocated %d bytes DMA buffer\n", __func__, pmem_sz);
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc) {
+ dma_free_coherent(NULL, pmem_sz,
+ audio->data, audio->phys);
+ goto done;
+ }
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops,
+ audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ dma_free_coherent(NULL, pmem_sz,
+ audio->data, audio->phys);
+ audmgr_close(&audio->audmgr);
+ goto done;
+ }
+
+ audio->out_dma_sz = pmem_sz;
+ pmem_sz >>= 1; /* Shift by 1 to get size of ping pong buffer */
+
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = pmem_sz;
+
+ audio->out[1].data = audio->data + pmem_sz;
+ audio->out[1].addr = audio->phys + pmem_sz;
+ audio->out[1].size = pmem_sz;
+
+ audio->volume = 0x2000; /* equal to Q13 number 1.0 Unit Gain */
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &audio_mp3_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_mp3_audio.lock);
+ mutex_init(&the_mp3_audio.write_lock);
+ mutex_init(&the_mp3_audio.read_lock);
+ spin_lock_init(&the_mp3_audio.dsp_lock);
+ init_waitqueue_head(&the_mp3_audio.write_wait);
+ init_waitqueue_head(&the_mp3_audio.read_wait);
+ the_mp3_audio.read_data = NULL;
+ return misc_register(&audio_mp3_misc);
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_out.c b/drivers/staging/dream/qdsp5/audio_out.c
new file mode 100644
index 00000000000..d1adcf65f2b
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_out.c
@@ -0,0 +1,851 @@
+/* arch/arm/mach-msm/qdsp5/audio_out.c
+ *
+ * pcm audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+#include <mach/htc_pwrsink.h>
+
+#include "evlog.h"
+
+#define LOG_AUDIO_EVENTS 1
+#define LOG_AUDIO_FAULTS 0
+
+enum {
+ EV_NULL,
+ EV_OPEN,
+ EV_WRITE,
+ EV_RETURN,
+ EV_IOCTL,
+ EV_WRITE_WAIT,
+ EV_WAIT_EVENT,
+ EV_FILL_BUFFER,
+ EV_SEND_BUFFER,
+ EV_DSP_EVENT,
+ EV_ENABLE,
+};
+
+#if (LOG_AUDIO_EVENTS != 1)
+static inline void LOG(unsigned id, unsigned arg) {}
+#else
+static const char *pcm_log_strings[] = {
+ "NULL",
+ "OPEN",
+ "WRITE",
+ "RETURN",
+ "IOCTL",
+ "WRITE_WAIT",
+ "WAIT_EVENT",
+ "FILL_BUFFER",
+ "SEND_BUFFER",
+ "DSP_EVENT",
+ "ENABLE",
+};
+
+DECLARE_LOG(pcm_log, 64, pcm_log_strings);
+
+static int __init _pcm_log_init(void)
+{
+ return ev_log_init(&pcm_log);
+}
+module_init(_pcm_log_init);
+
+#define LOG(id,arg) ev_log_write(&pcm_log, id, arg)
+#endif
+
+
+
+
+
+#define BUFSZ (960 * 5)
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_EQ_FLAG_DIS 0x0000
+#define AUDPP_CMD_EQ_FLAG_ENA -1
+#define AUDPP_CMD_IIR_FLAG_DIS 0x0000
+#define AUDPP_CMD_IIR_FLAG_ENA -1
+
+#define AUDPP_CMD_IIR_TUNING_FILTER 1
+#define AUDPP_CMD_EQUALIZER 2
+#define AUDPP_CMD_ADRC 3
+
+#define ADRC_ENABLE 0x0001
+#define EQ_ENABLE 0x0002
+#define IIR_ENABLE 0x0004
+
+struct adrc_filter {
+ uint16_t compression_th;
+ uint16_t compression_slope;
+ uint16_t rms_time;
+ uint16_t attack_const_lsw;
+ uint16_t attack_const_msw;
+ uint16_t release_const_lsw;
+ uint16_t release_const_msw;
+ uint16_t adrc_system_delay;
+};
+
+struct eqalizer {
+ uint16_t num_bands;
+ uint16_t eq_params[132];
+};
+
+struct rx_iir_filter {
+ uint16_t num_bands;
+ uint16_t iir_params[48];
+};
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ uint16_t eq_flag;
+ uint16_t num_bands;
+ uint16_t eq_params[132];
+} audpp_cmd_cfg_object_params_eq;
+
+typedef struct {
+ audpp_cmd_cfg_object_params_common common;
+ uint16_t active_flag;
+ uint16_t num_bands;
+ uint16_t iir_params[48];
+} audpp_cmd_cfg_object_params_rx_iir;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[2];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ atomic_t out_bytes;
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t wait;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ unsigned volume;
+
+ struct wake_lock wakelock;
+ struct wake_lock idlelock;
+
+ int adrc_enable;
+ struct adrc_filter adrc;
+
+ int eq_enable;
+ struct eqalizer eq;
+
+ int rx_iir_enable;
+ struct rx_iir_filter iir;
+};
+
+static void audio_prevent_sleep(struct audio *audio)
+{
+ printk(KERN_INFO "++++++++++++++++++++++++++++++\n");
+ wake_lock(&audio->wakelock);
+ wake_lock(&audio->idlelock);
+}
+
+static void audio_allow_sleep(struct audio *audio)
+{
+ wake_unlock(&audio->wakelock);
+ wake_unlock(&audio->idlelock);
+ printk(KERN_INFO "------------------------------\n");
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes);
+static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len);
+static int audio_dsp_set_adrc(struct audio *audio);
+static int audio_dsp_set_eq(struct audio *audio);
+static int audio_dsp_set_rx_iir(struct audio *audio);
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ pr_info("audio_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ /* refuse to start if we're not ready */
+ if (!audio->out[0].used || !audio->out[1].used)
+ return -EIO;
+
+ /* we start buffers 0 and 1, so buffer 0 will be the
+ * next one the dsp will want
+ */
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM;
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ audio_prevent_sleep(audio);
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0) {
+ audio_allow_sleep(audio);
+ return rc;
+ }
+
+ if (audpp_enable(-1, audio_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ audmgr_disable(&audio->audmgr);
+ audio_allow_sleep(audio);
+ return -ENODEV;
+ }
+
+ audio->enabled = 1;
+ htc_pwrsink_set(PWRSINK_AUDIO, 100);
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+ pr_info("audio_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ audio_dsp_out_enable(audio, 0);
+
+ audpp_disable(-1, audio);
+
+ wake_up(&audio->wait);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ audio_allow_sleep(audio);
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+ struct buffer *frame;
+ unsigned long flags;
+
+ LOG(EV_DSP_EVENT, id);
+ switch (id) {
+ case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+ unsigned id = msg[2];
+ unsigned idx = msg[3] - 1;
+
+ /* pr_info("audio_dsp_event: HOST_PCM id %d idx %d\n", id, idx); */
+ if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+ pr_err("bogus id\n");
+ break;
+ }
+ if (idx > 1) {
+ pr_err("bogus buffer idx\n");
+ break;
+ }
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (audio->running) {
+ atomic_add(audio->out[idx].used, &audio->out_bytes);
+ audio->out[idx].used = 0;
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ audio_dsp_send_buffer(
+ audio, audio->out_tail, frame->used);
+ audio->out_tail ^= 1;
+ } else {
+ audio->out_needed++;
+ }
+ wake_up(&audio->wait);
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ break;
+ }
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_info("audio_dsp_event: PCMDMAMISSED %d\n", msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ LOG(EV_ENABLE, 1);
+ pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(5, audio->volume, 0);
+ audio_dsp_set_adrc(audio);
+ audio_dsp_set_eq(audio);
+ audio_dsp_set_rx_iir(audio);
+ audio_dsp_out_enable(audio, 1);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ LOG(EV_ENABLE, 0);
+ pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+ audio->running = 0;
+ } else {
+ pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ default:
+ pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+ }
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes)
+{
+ audpp_cmd_pcm_intf cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+ if (yes) {
+ cmd.write_buf1LSW = audio->out[0].addr;
+ cmd.write_buf1MSW = audio->out[0].addr >> 16;
+ cmd.write_buf1_len = audio->out[0].size;
+ cmd.write_buf2LSW = audio->out[1].addr;
+ cmd.write_buf2MSW = audio->out[1].addr >> 16;
+ cmd.write_buf2_len = audio->out[1].size;
+ cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
+ cmd.weight_decoder_to_rx = audio->out_weight;
+ cmd.weight_arm_to_rx = 1;
+ cmd.partition_number_arm_to_dsp = 0;
+ cmd.sample_rate = audio->out_sample_rate;
+ cmd.channel_mode = audio->out_channel_mode;
+ }
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len)
+{
+ audpp_cmd_pcm_intf_send_buffer cmd;
+
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+ cmd.dsp_to_arm_buf_id = 0;
+ cmd.arm_to_dsp_buf_id = idx + 1;
+ cmd.arm_to_dsp_buf_len = len;
+
+ LOG(EV_SEND_BUFFER, idx);
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_adrc(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_adrc cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_ADRC;
+
+ if (audio->adrc_enable) {
+ cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+ cmd.compression_th = audio->adrc.compression_th;
+ cmd.compression_slope = audio->adrc.compression_slope;
+ cmd.rms_time = audio->adrc.rms_time;
+ cmd.attack_const_lsw = audio->adrc.attack_const_lsw;
+ cmd.attack_const_msw = audio->adrc.attack_const_msw;
+ cmd.release_const_lsw = audio->adrc.release_const_lsw;
+ cmd.release_const_msw = audio->adrc.release_const_msw;
+ cmd.adrc_system_delay = audio->adrc.adrc_system_delay;
+ } else {
+ cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+ }
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_eq(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_eq cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_EQUALIZER;
+
+ if (audio->eq_enable) {
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA;
+ cmd.num_bands = audio->eq.num_bands;
+ memcpy(&cmd.eq_params, audio->eq.eq_params,
+ sizeof(audio->eq.eq_params));
+ } else {
+ cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS;
+ }
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_rx_iir(struct audio *audio)
+{
+ audpp_cmd_cfg_object_params_rx_iir cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+ if (audio->rx_iir_enable) {
+ cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+ cmd.num_bands = audio->iir.num_bands;
+ memcpy(&cmd.iir_params, audio->iir.iir_params,
+ sizeof(audio->iir.iir_params));
+ } else {
+ cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+ }
+
+ return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static int audio_enable_adrc(struct audio *audio, int enable)
+{
+ if (audio->adrc_enable != enable) {
+ audio->adrc_enable = enable;
+ if (audio->running)
+ audio_dsp_set_adrc(audio);
+ }
+ return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+ if (audio->eq_enable != enable) {
+ audio->eq_enable = enable;
+ if (audio->running)
+ audio_dsp_set_eq(audio);
+ }
+ return 0;
+}
+
+static int audio_enable_rx_iir(struct audio *audio, int enable)
+{
+ if (audio->rx_iir_enable != enable) {
+ audio->rx_iir_enable = enable;
+ if (audio->running)
+ audio_dsp_set_rx_iir(audio);
+ }
+ return 0;
+}
+
+static void audio_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = atomic_read(&audio->out_bytes);
+ if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(6, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ LOG(EV_IOCTL, cmd);
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audio_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audio_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->wait);
+ mutex_lock(&audio->write_lock);
+ audio_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (config.channel_count == 1) {
+ config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+ } else if (config.channel_count == 2) {
+ config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ audio->out_sample_rate = config.sample_rate;
+ audio->out_channel_mode = config.channel_count;
+ rc = 0;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = 2;
+ config.sample_rate = audio->out_sample_rate;
+ if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+ config.channel_count = 1;
+ } else {
+ config.channel_count = 2;
+ }
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void*) arg, &config, sizeof(config))) {
+ rc = -EFAULT;
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+{
+ return -EINVAL;
+}
+
+static inline int rt_policy(int policy)
+{
+ if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR))
+ return 1;
+ return 0;
+}
+
+static inline int task_has_rt_policy(struct task_struct *p)
+{
+ return rt_policy(p->policy);
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct sched_param s = { .sched_priority = 1 };
+ struct audio *audio = file->private_data;
+ unsigned long flags;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int old_prio = current->rt_priority;
+ int old_policy = current->policy;
+ int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE);
+ int rc = 0;
+
+ LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24));
+
+ /* just for this write, set us real-time */
+ if (!task_has_rt_policy(current)) {
+ struct cred *new = prepare_creds();
+ cap_raise(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ sched_setscheduler(current, SCHED_RR, &s);
+ }
+
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+
+ LOG(EV_WAIT_EVENT, 0);
+ rc = wait_event_interruptible(audio->wait,
+ (frame->used == 0) || (audio->stopped));
+ LOG(EV_WAIT_EVENT, 1);
+
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = count > frame->size ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ LOG(EV_FILL_BUFFER, audio->out_head ^ 1);
+ frame = audio->out + audio->out_tail;
+ if (frame->used && audio->out_needed) {
+ audio_dsp_send_buffer(audio, audio->out_tail, frame->used);
+ audio->out_tail ^= 1;
+ audio->out_needed--;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+
+ mutex_unlock(&audio->write_lock);
+
+ /* restore scheduling policy and priority */
+ if (!rt_policy(old_policy)) {
+ struct sched_param v = { .sched_priority = old_prio };
+ sched_setscheduler(current, old_policy, &v);
+ if (likely(!cap_nice)) {
+ struct cred *new = prepare_creds();
+ cap_lower(new->cap_effective, CAP_SYS_NICE);
+ commit_creds(new);
+ sched_setscheduler(current, SCHED_RR, &s);
+ }
+ }
+
+ LOG(EV_RETURN,(buf > start) ? (buf - start) : rc);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ LOG(EV_OPEN, 0);
+ mutex_lock(&audio->lock);
+ audio_disable(audio);
+ audio_flush(audio);
+ audio->opened = 0;
+ mutex_unlock(&audio->lock);
+ htc_pwrsink_set(PWRSINK_AUDIO, 0);
+ return 0;
+}
+
+static struct audio the_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ if (!audio->data) {
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto done;
+
+ audio->out_buffer_size = BUFSZ;
+ audio->out_sample_rate = 44100;
+ audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ audio->out_weight = 100;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000;
+
+ audio_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+ LOG(EV_OPEN, 1);
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0, enable;
+ uint16_t enable_mask;
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_ENABLE_AUDPP:
+ if (copy_from_user(&enable_mask, (void *) arg, sizeof(enable_mask)))
+ goto out_fault;
+
+ enable = (enable_mask & ADRC_ENABLE)? 1 : 0;
+ audio_enable_adrc(audio, enable);
+ enable = (enable_mask & EQ_ENABLE)? 1 : 0;
+ audio_enable_eq(audio, enable);
+ enable = (enable_mask & IIR_ENABLE)? 1 : 0;
+ audio_enable_rx_iir(audio, enable);
+ break;
+
+ case AUDIO_SET_ADRC:
+ if (copy_from_user(&audio->adrc, (void*) arg, sizeof(audio->adrc)))
+ goto out_fault;
+ break;
+
+ case AUDIO_SET_EQ:
+ if (copy_from_user(&audio->eq, (void*) arg, sizeof(audio->eq)))
+ goto out_fault;
+ break;
+
+ case AUDIO_SET_RX_IIR:
+ if (copy_from_user(&audio->iir, (void*) arg, sizeof(audio->iir)))
+ goto out_fault;
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ goto out;
+
+ out_fault:
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static int audpp_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_audio;
+
+ file->private_data = audio;
+ return 0;
+}
+
+static struct file_operations audio_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_release,
+ .read = audio_read,
+ .write = audio_write,
+ .unlocked_ioctl = audio_ioctl,
+};
+
+static struct file_operations audpp_fops = {
+ .owner = THIS_MODULE,
+ .open = audpp_open,
+ .unlocked_ioctl = audpp_ioctl,
+};
+
+struct miscdevice audio_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_out",
+ .fops = &audio_fops,
+};
+
+struct miscdevice audpp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_pcm_ctl",
+ .fops = &audpp_fops,
+};
+
+static int __init audio_init(void)
+{
+ mutex_init(&the_audio.lock);
+ mutex_init(&the_audio.write_lock);
+ spin_lock_init(&the_audio.dsp_lock);
+ init_waitqueue_head(&the_audio.wait);
+ wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm");
+ wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+ return (misc_register(&audio_misc) || misc_register(&audpp_misc));
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_qcelp.c b/drivers/staging/dream/qdsp5/audio_qcelp.c
new file mode 100644
index 00000000000..f0f50e36805
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audio_qcelp.c
@@ -0,0 +1,856 @@
+/* arch/arm/mach-msm/qdsp5/audio_qcelp.c
+ *
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code is based in part on audio_mp3.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "audmgr.h"
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1080 /* QCELP 13K Hold 600ms packet data = 36 * 30 */
+#define BUF_COUNT 2
+#define DMASZ (BUFSZ * BUF_COUNT)
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT 5
+
+#define AUDDEC_DEC_QCELP 9
+
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used; /* Input usage actual DSP produced PCM size */
+ unsigned addr;
+};
+
+struct audio {
+ struct buffer out[BUF_COUNT];
+
+ spinlock_t dsp_lock;
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+ struct mutex lock;
+ struct mutex write_lock;
+ wait_queue_head_t write_wait;
+
+ /* Host PCM section - START */
+ struct buffer in[PCM_BUF_MAX_COUNT];
+ struct mutex read_lock;
+ wait_queue_head_t read_wait; /* Wait queue for read */
+ char *read_data; /* pointer to reader buffer */
+ dma_addr_t read_phys; /* physical address of reader buffer */
+ uint8_t read_next; /* index to input buffers to be read next */
+ uint8_t fill_next; /* index to buffer that DSP should be filling */
+ uint8_t pcm_buf_count; /* number of pcm buffer allocated */
+ /* Host PCM section - END */
+
+ struct msm_adsp_module *audplay;
+
+ struct audmgr audmgr;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ uint8_t opened:1;
+ uint8_t enabled:1;
+ uint8_t running:1;
+ uint8_t stopped:1; /* set when stopped, cleared on flush */
+ uint8_t pcm_feedback:1; /* set when non-tunnel mode */
+ uint8_t buf_refresh:1;
+
+ unsigned volume;
+
+ uint16_t dec_id;
+};
+
+static struct audio the_qcelp_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audqcelp_send_data(struct audio *audio, unsigned needed);
+static void audqcelp_config_hostpcm(struct audio *audio);
+static void audqcelp_buffer_refresh(struct audio *audio);
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ dprintk("audqcelp_enable()\n");
+
+ if (audio->enabled)
+ return 0;
+
+ audio->out_tail = 0;
+ audio->out_needed = 0;
+
+ cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+ cfg.codec = RPC_AUD_DEF_CODEC_13K;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&audio->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(audio->audplay)) {
+ pr_err("audio: msm_adsp_enable(audplay) failed\n");
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+
+ if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+ pr_err("audio: audpp_enable() failed\n");
+ msm_adsp_disable(audio->audplay);
+ audmgr_disable(&audio->audmgr);
+ return -ENODEV;
+ }
+ audio->enabled = 1;
+ return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_disable(struct audio *audio)
+{
+ dprintk("audqcelp_disable()\n");
+ if (audio->enabled) {
+ audio->enabled = 0;
+ auddec_dsp_config(audio, 0);
+ wake_up(&audio->write_wait);
+ wake_up(&audio->read_wait);
+ msm_adsp_disable(audio->audplay);
+ audpp_disable(audio->dec_id, audio);
+ audmgr_disable(&audio->audmgr);
+ audio->out_needed = 0;
+ }
+ return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audqcelp_update_pcm_buf_entry(struct audio *audio,
+ uint32_t *payload)
+{
+ uint8_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ for (index = 0; index < payload[1]; index++) {
+ if (audio->in[audio->fill_next].addr ==
+ payload[2 + index * 2]) {
+ dprintk("audqcelp_update_pcm_buf_entry: in[%d] ready\n",
+ audio->fill_next);
+ audio->in[audio->fill_next].used =
+ payload[3 + index * 2];
+ if ((++audio->fill_next) == audio->pcm_buf_count)
+ audio->fill_next = 0;
+ } else {
+ pr_err(
+ "audqcelp_update_pcm_buf_entry: expected=%x ret=%x\n",
+ audio->in[audio->fill_next].addr,
+ payload[1 + index * 2]);
+ break;
+ }
+ }
+ if (audio->in[audio->fill_next].used == 0) {
+ audqcelp_buffer_refresh(audio);
+ } else {
+ dprintk("audqcelp_update_pcm_buf_entry: read cannot keep up\n");
+ audio->buf_refresh = 1;
+ }
+
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct audio *audio = data;
+ uint32_t msg[28];
+ getevent(msg, sizeof(msg));
+
+ dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+ switch (id) {
+ case AUDPLAY_MSG_DEC_NEEDS_DATA:
+ audqcelp_send_data(audio, 1);
+ break;
+
+ case AUDPLAY_MSG_BUFFER_UPDATE:
+ audqcelp_update_pcm_buf_entry(audio, msg);
+ break;
+
+ default:
+ pr_err("unexpected message from decoder \n");
+ }
+}
+
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+ struct audio *audio = private;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned status = msg[1];
+
+ switch (status) {
+ case AUDPP_DEC_STATUS_SLEEP:
+ dprintk("decoder status: sleep \n");
+ break;
+
+ case AUDPP_DEC_STATUS_INIT:
+ dprintk("decoder status: init \n");
+ audpp_cmd_cfg_routing_mode(audio);
+ break;
+
+ case AUDPP_DEC_STATUS_CFG:
+ dprintk("decoder status: cfg \n");
+ break;
+ case AUDPP_DEC_STATUS_PLAY:
+ dprintk("decoder status: play \n");
+ if (audio->pcm_feedback) {
+ audqcelp_config_hostpcm(audio);
+ audqcelp_buffer_refresh(audio);
+ }
+ break;
+ default:
+ pr_err("unknown decoder status \n");
+ }
+ break;
+ }
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ dprintk("audqcelp_dsp_event: CFG_MSG ENABLE\n");
+ auddec_dsp_config(audio, 1);
+ audio->out_needed = 0;
+ audio->running = 1;
+ audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+ 0);
+ audpp_avsync(audio->dec_id, 22050);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ dprintk("audqcelp_dsp_event: CFG_MSG DISABLE\n");
+ audpp_avsync(audio->dec_id, 0);
+ audio->running = 0;
+ } else {
+ pr_err("audqcelp_dsp_event: CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ dprintk("audqcelp_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+ audpp_cmd_cfg_adec_params(audio);
+ break;
+ default:
+ pr_err("audqcelp_dsp_event: UNKNOWN (%d)\n", id);
+ }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_qcelp = {
+ .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+ msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+ cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+ audpp_cmd_cfg_dec_type cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+ if (enable)
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+ AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+ else
+ cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+ struct audpp_cmd_cfg_adec_params_v13k cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+ cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+ cmd.common.dec_id = audio->dec_id;
+ cmd.common.input_sampling_frequency = 8000;
+ cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+ audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+ struct audpp_cmd_routing_mode cmd;
+ dprintk("audpp_cmd_cfg_routing_mode()\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+ cmd.object_number = audio->dec_id;
+ if (audio->pcm_feedback)
+ cmd.routing_mode = ROUTING_MODE_FTRT;
+ else
+ cmd.routing_mode = ROUTING_MODE_RT;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+ unsigned idx, unsigned len)
+{
+ audplay_cmd_bitstream_data_avail cmd;
+
+ cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+ cmd.decoder_id = audio->dec_id;
+ cmd.buf_ptr = audio->out[idx].addr;
+ cmd.buf_size = len / 2;
+ cmd.partition_number = 0;
+ return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audqcelp_buffer_refresh(struct audio *audio)
+{
+ struct audplay_cmd_buffer_refresh refresh_cmd;
+
+ refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+ refresh_cmd.num_buffers = 1;
+ refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+ refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+ refresh_cmd.buf_read_count = 0;
+ dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+ refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+
+ (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audqcelp_config_hostpcm(struct audio *audio)
+{
+ struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+ dprintk("audqcelp_config_hostpcm()\n");
+ cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+ cfg_cmd.max_buffers = audio->pcm_buf_count;
+ cfg_cmd.byte_swap = 0;
+ cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+ cfg_cmd.feedback_frequency = 1;
+ cfg_cmd.partition_number = 0;
+
+ (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+static void audqcelp_send_data(struct audio *audio, unsigned needed)
+{
+ struct buffer *frame;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (!audio->running)
+ goto done;
+
+ if (needed) {
+ /* We were called from the callback because the DSP
+ * requested more data. Note that the DSP does want
+ * more data, and if a buffer was in-flight, mark it
+ * as available (since the DSP must now be done with
+ * it).
+ */
+ audio->out_needed = 1;
+ frame = audio->out + audio->out_tail;
+ if (frame->used == 0xffffffff) {
+ dprintk("frame %d free\n", audio->out_tail);
+ frame->used = 0;
+ audio->out_tail ^= 1;
+ wake_up(&audio->write_wait);
+ }
+ }
+
+ if (audio->out_needed) {
+ /* If the DSP currently wants data and we have a
+ * buffer available, we will send it and reset
+ * the needed flag. We'll mark the buffer as in-flight
+ * so that it won't be recycled until the next buffer
+ * is requested
+ */
+
+ frame = audio->out + audio->out_tail;
+ if (frame->used) {
+ BUG_ON(frame->used == 0xffffffff);
+ dprintk("frame %d busy\n", audio->out_tail);
+ audplay_dsp_send_data_avail(audio, audio->out_tail,
+ frame->used);
+ frame->used = 0xffffffff;
+ audio->out_needed = 0;
+ }
+ }
+ done:
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audqcelp_flush(struct audio *audio)
+{
+ audio->out[0].used = 0;
+ audio->out[1].used = 0;
+ audio->out_head = 0;
+ audio->out_tail = 0;
+ audio->stopped = 0;
+}
+
+static void audqcelp_flush_pcm_buf(struct audio *audio)
+{
+ uint8_t index;
+
+ for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+ audio->in[index].used = 0;
+
+ audio->read_next = 0;
+ audio->fill_next = 0;
+}
+
+static long audqcelp_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct audio *audio = file->private_data;
+ int rc = 0;
+
+ dprintk("audqcelp_ioctl() cmd = %d\n", cmd);
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+ stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == AUDIO_SET_VOLUME) {
+ unsigned long flags;
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio->volume = arg;
+ if (audio->running)
+ audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return 0;
+ }
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_START:
+ rc = audqcelp_enable(audio);
+ break;
+ case AUDIO_STOP:
+ rc = audqcelp_disable(audio);
+ audio->stopped = 1;
+ break;
+ case AUDIO_FLUSH:
+ if (audio->stopped) {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the write_lock.
+ * While audio->stopped write threads will always
+ * exit immediately.
+ */
+ wake_up(&audio->write_wait);
+ mutex_lock(&audio->write_lock);
+ audqcelp_flush(audio);
+ mutex_unlock(&audio->write_lock);
+ wake_up(&audio->read_wait);
+ mutex_lock(&audio->read_lock);
+ audqcelp_flush_pcm_buf(audio);
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+ break;
+ case AUDIO_SET_CONFIG:
+ dprintk("AUDIO_SET_CONFIG not applicable \n");
+ break;
+ case AUDIO_GET_CONFIG:{
+ struct msm_audio_config config;
+ config.buffer_size = BUFSZ;
+ config.buffer_count = BUF_COUNT;
+ config.sample_rate = 8000;
+ config.channel_count = 1;
+ config.unused[0] = 0;
+ config.unused[1] = 0;
+ config.unused[2] = 0;
+ config.unused[3] = 0;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ break;
+ }
+ case AUDIO_GET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ config.pcm_feedback = 0;
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+ config.buffer_size = PCM_BUFSZ_MIN;
+ if (copy_to_user((void *)arg, &config,
+ sizeof(config)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+ break;
+ }
+ case AUDIO_SET_PCM_CONFIG:{
+ struct msm_audio_pcm_config config;
+
+ if (copy_from_user(&config, (void *)arg,
+ sizeof(config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_MAX_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ /* Check if pcm feedback is required */
+ if ((config.pcm_feedback) && (!audio->read_data)) {
+ dprintk(
+ "audqcelp_ioctl: allocate PCM buf %d\n",
+ config.buffer_count * config.buffer_size);
+ audio->read_data = dma_alloc_coherent(NULL,
+ config.buffer_size * config.buffer_count,
+ &audio->read_phys, GFP_KERNEL);
+ if (!audio->read_data) {
+ pr_err(
+ "audqcelp_ioctl: no mem for pcm buf\n"
+ );
+ rc = -ENOMEM;
+ } else {
+ uint8_t index;
+ uint32_t offset = 0;
+
+ audio->pcm_feedback = 1;
+ audio->buf_refresh = 0;
+ audio->pcm_buf_count =
+ config.buffer_count;
+ audio->read_next = 0;
+ audio->fill_next = 0;
+
+ for (index = 0;
+ index < config.buffer_count; index++) {
+ audio->in[index].data =
+ audio->read_data + offset;
+ audio->in[index].addr =
+ audio->read_phys + offset;
+ audio->in[index].size =
+ config.buffer_size;
+ audio->in[index].used = 0;
+ offset += config.buffer_size;
+ }
+ rc = 0;
+ }
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+ case AUDIO_PAUSE:
+ dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+ rc = audpp_pause(audio->dec_id, (int) arg);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ int rc = 0;
+
+ if (!audio->pcm_feedback)
+ return 0; /* PCM feedback is not enabled. Nothing to read */
+
+ mutex_lock(&audio->read_lock);
+ dprintk("audqcelp_read() %d \n", count);
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->read_wait,
+ (audio->in[audio->read_next].used > 0) ||
+ (audio->stopped));
+ if (rc < 0)
+ break;
+
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ if (count < audio->in[audio->read_next].used) {
+ /* Read must happen in frame boundary. Since driver does
+ not know frame size, read count must be greater or equal
+ to size of PCM samples */
+ dprintk("audqcelp_read:read stop - partial frame\n");
+ break;
+ } else {
+ dprintk("audqcelp_read: read from in[%d]\n",
+ audio->read_next);
+ if (copy_to_user(buf,
+ audio->in[audio->read_next].data,
+ audio->in[audio->read_next].used)) {
+ pr_err("audqcelp_read: invalid addr %x \n",
+ (unsigned int)buf);
+ rc = -EFAULT;
+ break;
+ }
+ count -= audio->in[audio->read_next].used;
+ buf += audio->in[audio->read_next].used;
+ audio->in[audio->read_next].used = 0;
+ if ((++audio->read_next) == audio->pcm_buf_count)
+ audio->read_next = 0;
+ }
+ }
+
+ if (audio->buf_refresh) {
+ audio->buf_refresh = 0;
+ dprintk("audqcelp_read: kick start pcm feedback again\n");
+ audqcelp_buffer_refresh(audio);
+ }
+
+ mutex_unlock(&audio->read_lock);
+
+ if (buf > start)
+ rc = buf - start;
+
+ dprintk("audqcelp_read: read %d bytes\n", rc);
+ return rc;
+}
+
+static ssize_t audqcelp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct audio *audio = file->private_data;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ if (count & 1)
+ return -EINVAL;
+ dprintk("audqcelp_write() \n");
+ mutex_lock(&audio->write_lock);
+ while (count > 0) {
+ frame = audio->out + audio->out_head;
+ rc = wait_event_interruptible(audio->write_wait,
+ (frame->used == 0)
+ || (audio->stopped));
+ dprintk("audqcelp_write() buffer available\n");
+ if (rc < 0)
+ break;
+ if (audio->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = (count > frame->size) ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ frame->used = xfer;
+ audio->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ audqcelp_send_data(audio, 0);
+
+ }
+ mutex_unlock(&audio->write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int audqcelp_release(struct inode *inode, struct file *file)
+{
+ struct audio *audio = file->private_data;
+
+ dprintk("audqcelp_release()\n");
+
+ mutex_lock(&audio->lock);
+ audqcelp_disable(audio);
+ audqcelp_flush(audio);
+ audqcelp_flush_pcm_buf(audio);
+ msm_adsp_put(audio->audplay);
+ audio->audplay = NULL;
+ audio->opened = 0;
+ if (audio->data)
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ audio->data = NULL;
+ if (audio->read_data) {
+ dma_free_coherent(NULL,
+ audio->in[0].size * audio->pcm_buf_count,
+ audio->read_data, audio->read_phys);
+ audio->read_data = NULL;
+ }
+ audio->pcm_feedback = 0;
+ mutex_unlock(&audio->lock);
+ return 0;
+}
+
+static int audqcelp_open(struct inode *inode, struct file *file)
+{
+ struct audio *audio = &the_qcelp_audio;
+ int rc;
+
+ mutex_lock(&audio->lock);
+
+ if (audio->opened) {
+ pr_err("audio: busy\n");
+ rc = -EBUSY;
+ goto done;
+ }
+
+ audio->data = dma_alloc_coherent(NULL, DMASZ,
+ &audio->phys, GFP_KERNEL);
+ if (!audio->data) {
+ pr_err("audio: could not allocate DMA buffers\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ rc = audmgr_open(&audio->audmgr);
+ if (rc)
+ goto err;
+
+ rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+ &audplay_adsp_ops_qcelp, audio);
+ if (rc) {
+ pr_err("audio: failed to get audplay0 dsp module\n");
+ audmgr_close(&audio->audmgr);
+ goto err;
+ }
+
+ audio->dec_id = 0;
+
+ audio->out[0].data = audio->data + 0;
+ audio->out[0].addr = audio->phys + 0;
+ audio->out[0].size = BUFSZ;
+
+ audio->out[1].data = audio->data + BUFSZ;
+ audio->out[1].addr = audio->phys + BUFSZ;
+ audio->out[1].size = BUFSZ;
+
+ audio->volume = 0x2000; /* Q13 1.0 */
+
+ audqcelp_flush(audio);
+
+ file->private_data = audio;
+ audio->opened = 1;
+ rc = 0;
+done:
+ mutex_unlock(&audio->lock);
+ return rc;
+err:
+ dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+static struct file_operations audio_qcelp_fops = {
+ .owner = THIS_MODULE,
+ .open = audqcelp_open,
+ .release = audqcelp_release,
+ .read = audqcelp_read,
+ .write = audqcelp_write,
+ .unlocked_ioctl = audqcelp_ioctl,
+};
+
+struct miscdevice audio_qcelp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp",
+ .fops = &audio_qcelp_fops,
+};
+
+static int __init audqcelp_init(void)
+{
+ mutex_init(&the_qcelp_audio.lock);
+ mutex_init(&the_qcelp_audio.write_lock);
+ mutex_init(&the_qcelp_audio.read_lock);
+ spin_lock_init(&the_qcelp_audio.dsp_lock);
+ init_waitqueue_head(&the_qcelp_audio.write_wait);
+ init_waitqueue_head(&the_qcelp_audio.read_wait);
+ the_qcelp_audio.read_data = NULL;
+ return misc_register(&audio_qcelp_misc);
+}
+
+static void __exit audqcelp_exit(void)
+{
+ misc_deregister(&audio_qcelp_misc);
+}
+
+module_init(audqcelp_init);
+module_exit(audqcelp_exit);
+
+MODULE_DESCRIPTION("MSM QCELP 13K driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM");
diff --git a/drivers/staging/dream/qdsp5/audmgr.c b/drivers/staging/dream/qdsp5/audmgr.c
new file mode 100644
index 00000000000..1ad8b82c257
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audmgr.c
@@ -0,0 +1,313 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.c
+ *
+ * interface to "audmgr" service on the baseband cpu
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include <asm/atomic.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#define STATE_CLOSED 0
+#define STATE_DISABLED 1
+#define STATE_ENABLING 2
+#define STATE_ENABLED 3
+#define STATE_DISABLING 4
+#define STATE_ERROR 5
+
+static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid)
+{
+ uint32_t rep[6];
+
+ rep[0] = cpu_to_be32(xid);
+ rep[1] = cpu_to_be32(1);
+ rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ rep[4] = 0;
+ rep[5] = 0;
+
+ msm_rpc_write(ept, rep, sizeof(rep));
+}
+
+static void process_audmgr_callback(struct audmgr *am,
+ struct rpc_audmgr_cb_func_ptr *args,
+ int len)
+{
+ if (len < (sizeof(uint32_t) * 3))
+ return;
+ if (be32_to_cpu(args->set_to_one) != 1)
+ return;
+
+ switch (be32_to_cpu(args->status)) {
+ case RPC_AUDMGR_STATUS_READY:
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ am->handle = be32_to_cpu(args->u.handle);
+ pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle);
+ break;
+ case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
+ uint32_t volume;
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ volume = be32_to_cpu(args->u.volume);
+ pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume);
+ am->state = STATE_ENABLED;
+ wake_up(&am->wait);
+ break;
+ }
+ case RPC_AUDMGR_STATUS_PENDING:
+ pr_err("audmgr: PENDING?\n");
+ break;
+ case RPC_AUDMGR_STATUS_SUSPEND:
+ pr_err("audmgr: SUSPEND?\n");
+ break;
+ case RPC_AUDMGR_STATUS_FAILURE:
+ pr_err("audmgr: FAILURE\n");
+ break;
+ case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
+ pr_err("audmgr: VOLUME_CHANGE?\n");
+ break;
+ case RPC_AUDMGR_STATUS_DISABLED:
+ pr_err("audmgr: DISABLED\n");
+ am->state = STATE_DISABLED;
+ wake_up(&am->wait);
+ break;
+ case RPC_AUDMGR_STATUS_ERROR:
+ pr_err("audmgr: ERROR?\n");
+ am->state = STATE_ERROR;
+ wake_up(&am->wait);
+ break;
+ default:
+ break;
+ }
+}
+
+static void process_rpc_request(uint32_t proc, uint32_t xid,
+ void *data, int len, void *private)
+{
+ struct audmgr *am = private;
+ uint32_t *x = data;
+
+ if (0) {
+ int n = len / 4;
+ pr_info("rpc_call proc %d:", proc);
+ while (n--)
+ printk(" %08x", be32_to_cpu(*x++));
+ printk("\n");
+ }
+
+ if (proc == AUDMGR_CB_FUNC_PTR)
+ process_audmgr_callback(am, data, len);
+ else
+ pr_err("audmgr: unknown rpc proc %d\n", proc);
+ rpc_ack(am->ept, xid);
+}
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_VERSION 2
+
+#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
+#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
+#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
+#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
+
+static int audmgr_rpc_thread(void *data)
+{
+ struct audmgr *am = data;
+ struct rpc_request_hdr *hdr = NULL;
+ uint32_t type;
+ int len;
+
+ pr_info("audmgr_rpc_thread() start\n");
+
+ while (!kthread_should_stop()) {
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1);
+ if (len < 0) {
+ pr_err("audmgr: rpc read failed (%d)\n", len);
+ break;
+ }
+ if (len < RPC_COMMON_HDR_SZ)
+ continue;
+
+ type = be32_to_cpu(hdr->type);
+ if (type == RPC_TYPE_REPLY) {
+ struct rpc_reply_hdr *rep = (void *) hdr;
+ uint32_t status;
+ if (len < RPC_REPLY_HDR_SZ)
+ continue;
+ status = be32_to_cpu(rep->reply_stat);
+ if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
+ status = be32_to_cpu(rep->data.acc_hdr.accept_stat);
+ pr_info("audmgr: rpc_reply status %d\n", status);
+ } else {
+ pr_info("audmgr: rpc_reply denied!\n");
+ }
+ /* process reply */
+ continue;
+ }
+
+ if (len < RPC_REQUEST_HDR_SZ)
+ continue;
+
+ process_rpc_request(be32_to_cpu(hdr->procedure),
+ be32_to_cpu(hdr->xid),
+ (void *) (hdr + 1),
+ len - sizeof(*hdr),
+ data);
+ }
+ pr_info("audmgr_rpc_thread() exit\n");
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ am->task = NULL;
+ wake_up(&am->wait);
+ return 0;
+}
+
+struct audmgr_enable_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_audmgr_enable_client_args args;
+};
+
+struct audmgr_disable_msg {
+ struct rpc_request_hdr hdr;
+ uint32_t handle;
+};
+
+int audmgr_open(struct audmgr *am)
+{
+ int rc;
+
+ if (am->state != STATE_CLOSED)
+ return 0;
+
+ am->ept = msm_rpc_connect(AUDMGR_PROG,
+ AUDMGR_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+
+ init_waitqueue_head(&am->wait);
+
+ if (IS_ERR(am->ept)) {
+ rc = PTR_ERR(am->ept);
+ am->ept = NULL;
+ pr_err("audmgr: failed to connect to audmgr svc\n");
+ return rc;
+ }
+
+ am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc");
+ if (IS_ERR(am->task)) {
+ rc = PTR_ERR(am->task);
+ am->task = NULL;
+ msm_rpc_close(am->ept);
+ am->ept = NULL;
+ return rc;
+ }
+
+ am->state = STATE_DISABLED;
+ return 0;
+}
+EXPORT_SYMBOL(audmgr_open);
+
+int audmgr_close(struct audmgr *am)
+{
+ return -EBUSY;
+}
+EXPORT_SYMBOL(audmgr_close);
+
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg)
+{
+ struct audmgr_enable_msg msg;
+ int rc;
+
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ if (am->state == STATE_DISABLING)
+ pr_err("audmgr: state is DISABLING in enable?\n");
+ am->state = STATE_ENABLING;
+
+ msg.args.set_to_one = cpu_to_be32(1);
+ msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate);
+ msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate);
+ msg.args.def_method = cpu_to_be32(cfg->def_method);
+ msg.args.codec_type = cpu_to_be32(cfg->codec);
+ msg.args.snd_method = cpu_to_be32(cfg->snd_method);
+ msg.args.cb_func = cpu_to_be32(0x11111111);
+ msg.args.client_data = cpu_to_be32(0x11223344);
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_ENABLE_CLIENT);
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while enabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_enable);
+
+int audmgr_disable(struct audmgr *am)
+{
+ struct audmgr_disable_msg msg;
+ int rc;
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_DISABLE_CLIENT);
+ msg.handle = cpu_to_be32(am->handle);
+
+ am->state = STATE_DISABLING;
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while disabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_disable);
diff --git a/drivers/staging/dream/qdsp5/audmgr.h b/drivers/staging/dream/qdsp5/audmgr.h
new file mode 100644
index 00000000000..c07c36b3a0a
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audmgr.h
@@ -0,0 +1,215 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_H
+
+#if CONFIG_MSM_AMSS_VERSION==6350
+#include "audmgr_new.h"
+#else
+
+enum rpc_aud_def_sample_rate_type {
+ RPC_AUD_DEF_SAMPLE_RATE_NONE,
+ RPC_AUD_DEF_SAMPLE_RATE_8000,
+ RPC_AUD_DEF_SAMPLE_RATE_11025,
+ RPC_AUD_DEF_SAMPLE_RATE_12000,
+ RPC_AUD_DEF_SAMPLE_RATE_16000,
+ RPC_AUD_DEF_SAMPLE_RATE_22050,
+ RPC_AUD_DEF_SAMPLE_RATE_24000,
+ RPC_AUD_DEF_SAMPLE_RATE_32000,
+ RPC_AUD_DEF_SAMPLE_RATE_44100,
+ RPC_AUD_DEF_SAMPLE_RATE_48000,
+ RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+ RPC_AUD_DEF_METHOD_NONE,
+ RPC_AUD_DEF_METHOD_KEY_BEEP,
+ RPC_AUD_DEF_METHOD_PLAYBACK,
+ RPC_AUD_DEF_METHOD_VOICE,
+ RPC_AUD_DEF_METHOD_RECORD,
+ RPC_AUD_DEF_METHOD_HOST_PCM,
+ RPC_AUD_DEF_METHOD_MIDI_OUT,
+ RPC_AUD_DEF_METHOD_RECORD_SBC,
+ RPC_AUD_DEF_METHOD_DTMF_RINGER,
+ RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+ RPC_AUD_DEF_CODEC_NONE,
+ RPC_AUD_DEF_CODEC_DTMF,
+ RPC_AUD_DEF_CODEC_MIDI,
+ RPC_AUD_DEF_CODEC_MP3,
+ RPC_AUD_DEF_CODEC_PCM,
+ RPC_AUD_DEF_CODEC_AAC,
+ RPC_AUD_DEF_CODEC_WMA,
+ RPC_AUD_DEF_CODEC_RA,
+ RPC_AUD_DEF_CODEC_ADPCM,
+ RPC_AUD_DEF_CODEC_GAUDIO,
+ RPC_AUD_DEF_CODEC_VOC_EVRC,
+ RPC_AUD_DEF_CODEC_VOC_13K,
+ RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+ RPC_AUD_DEF_CODEC_VOC_AMR,
+ RPC_AUD_DEF_CODEC_VOC_EFR,
+ RPC_AUD_DEF_CODEC_VOC_FR,
+ RPC_AUD_DEF_CODEC_VOC_HR,
+ RPC_AUD_DEF_CODEC_VOC,
+ RPC_AUD_DEF_CODEC_SBC,
+ RPC_AUD_DEF_CODEC_VOC_PCM,
+ RPC_AUD_DEF_CODEC_AMR_WB,
+ RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+ RPC_AUD_DEF_CODEC_MAX,
+};
+
+enum rpc_snd_method_type {
+ RPC_SND_METHOD_VOICE = 0,
+ RPC_SND_METHOD_KEY_BEEP,
+ RPC_SND_METHOD_MESSAGE,
+ RPC_SND_METHOD_RING,
+ RPC_SND_METHOD_MIDI,
+ RPC_SND_METHOD_AUX,
+ RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+ RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_1,
+ RPC_VOC_CODEC_STEREO_HEADSET,
+ RPC_VOC_CODEC_ON_CHIP_AUX,
+ RPC_VOC_CODEC_BT_OFF_BOARD,
+ RPC_VOC_CODEC_BT_A2DP,
+ RPC_VOC_CODEC_OFF_BOARD,
+ RPC_VOC_CODEC_SDAC,
+ RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TTY_ON_CHIP_1,
+ RPC_VOC_CODEC_TTY_OFF_BOARD,
+ RPC_VOC_CODEC_TTY_VCO,
+ RPC_VOC_CODEC_TTY_HCO,
+ RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+ RPC_VOC_CODEC_MAX,
+ RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+ RPC_AUDMGR_STATUS_READY,
+ RPC_AUDMGR_STATUS_CODEC_CONFIG,
+ RPC_AUDMGR_STATUS_PENDING,
+ RPC_AUDMGR_STATUS_SUSPEND,
+ RPC_AUDMGR_STATUS_FAILURE,
+ RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+ RPC_AUDMGR_STATUS_DISABLED,
+ RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+ uint32_t set_to_one;
+ uint32_t tx_sample_rate;
+ uint32_t rx_sample_rate;
+ uint32_t def_method;
+ uint32_t codec_type;
+ uint32_t snd_method;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT 2
+#define AUDMGR_DISABLE_CLIENT 3
+#define AUDMGR_SUSPEND_EVENT_RSP 4
+#define AUDMGR_REGISTER_OPERATION_LISTENER 5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
+#define AUDMGR_REGISTER_CODEC_LISTENER 7
+#define AUDMGR_GET_RX_SAMPLE_RATE 8
+#define AUDMGR_GET_TX_SAMPLE_RATE 9
+#define AUDMGR_SET_DEVICE_MODE 10
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_PROG_VERS "rs30000013:46255756"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0x46255756
+#else
+#define AUDMGR_PROG_VERS "rs30000013:e94e8f0c"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0xe94e8f0c
+#endif
+
+struct rpc_audmgr_cb_func_ptr {
+ uint32_t cb_id;
+ uint32_t set_to_one;
+ uint32_t status;
+ union {
+ uint32_t handle;
+ uint32_t volume;
+
+ } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR 1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x5fa922a9
+#else
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x21570ba7
+#endif
+
+struct audmgr {
+ wait_queue_head_t wait;
+ uint32_t handle;
+ struct msm_rpc_endpoint *ept;
+ struct task_struct *task;
+ int state;
+};
+
+struct audmgr_config {
+ uint32_t tx_rate;
+ uint32_t rx_rate;
+ uint32_t def_method;
+ uint32_t codec;
+ uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_pause(unsigned id, int pause);
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
+#endif
diff --git a/drivers/staging/dream/qdsp5/audmgr_new.h b/drivers/staging/dream/qdsp5/audmgr_new.h
new file mode 100644
index 00000000000..49670fe48b9
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audmgr_new.h
@@ -0,0 +1,213 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+
+enum rpc_aud_def_sample_rate_type {
+ RPC_AUD_DEF_SAMPLE_RATE_NONE,
+ RPC_AUD_DEF_SAMPLE_RATE_8000,
+ RPC_AUD_DEF_SAMPLE_RATE_11025,
+ RPC_AUD_DEF_SAMPLE_RATE_12000,
+ RPC_AUD_DEF_SAMPLE_RATE_16000,
+ RPC_AUD_DEF_SAMPLE_RATE_22050,
+ RPC_AUD_DEF_SAMPLE_RATE_24000,
+ RPC_AUD_DEF_SAMPLE_RATE_32000,
+ RPC_AUD_DEF_SAMPLE_RATE_44100,
+ RPC_AUD_DEF_SAMPLE_RATE_48000,
+ RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+ RPC_AUD_DEF_METHOD_NONE,
+ RPC_AUD_DEF_METHOD_KEY_BEEP,
+ RPC_AUD_DEF_METHOD_PLAYBACK,
+ RPC_AUD_DEF_METHOD_VOICE,
+ RPC_AUD_DEF_METHOD_RECORD,
+ RPC_AUD_DEF_METHOD_HOST_PCM,
+ RPC_AUD_DEF_METHOD_MIDI_OUT,
+ RPC_AUD_DEF_METHOD_RECORD_SBC,
+ RPC_AUD_DEF_METHOD_DTMF_RINGER,
+ RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+ RPC_AUD_DEF_CODEC_NONE,
+ RPC_AUD_DEF_CODEC_DTMF,
+ RPC_AUD_DEF_CODEC_MIDI,
+ RPC_AUD_DEF_CODEC_MP3,
+ RPC_AUD_DEF_CODEC_PCM,
+ RPC_AUD_DEF_CODEC_AAC,
+ RPC_AUD_DEF_CODEC_WMA,
+ RPC_AUD_DEF_CODEC_RA,
+ RPC_AUD_DEF_CODEC_ADPCM,
+ RPC_AUD_DEF_CODEC_GAUDIO,
+ RPC_AUD_DEF_CODEC_VOC_EVRC,
+ RPC_AUD_DEF_CODEC_VOC_13K,
+ RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+ RPC_AUD_DEF_CODEC_VOC_AMR,
+ RPC_AUD_DEF_CODEC_VOC_EFR,
+ RPC_AUD_DEF_CODEC_VOC_FR,
+ RPC_AUD_DEF_CODEC_VOC_HR,
+ RPC_AUD_DEF_CODEC_VOC_CDMA,
+ RPC_AUD_DEF_CODEC_VOC_CDMA_WB,
+ RPC_AUD_DEF_CODEC_VOC_UMTS,
+ RPC_AUD_DEF_CODEC_VOC_UMTS_WB,
+ RPC_AUD_DEF_CODEC_SBC,
+ RPC_AUD_DEF_CODEC_VOC_PCM,
+ RPC_AUD_DEF_CODEC_AMR_WB,
+ RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+ RPC_AUD_DEF_CODEC_AAC_BSAC,
+ RPC_AUD_DEF_CODEC_MAX,
+ RPC_AUD_DEF_CODEC_AMR_NB,
+ RPC_AUD_DEF_CODEC_13K,
+ RPC_AUD_DEF_CODEC_EVRC,
+ RPC_AUD_DEF_CODEC_MAX_002,
+};
+
+enum rpc_snd_method_type {
+ RPC_SND_METHOD_VOICE = 0,
+ RPC_SND_METHOD_KEY_BEEP,
+ RPC_SND_METHOD_MESSAGE,
+ RPC_SND_METHOD_RING,
+ RPC_SND_METHOD_MIDI,
+ RPC_SND_METHOD_AUX,
+ RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+ RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+ RPC_VOC_CODEC_ON_CHIP_1,
+ RPC_VOC_CODEC_STEREO_HEADSET,
+ RPC_VOC_CODEC_ON_CHIP_AUX,
+ RPC_VOC_CODEC_BT_OFF_BOARD,
+ RPC_VOC_CODEC_BT_A2DP,
+ RPC_VOC_CODEC_OFF_BOARD,
+ RPC_VOC_CODEC_SDAC,
+ RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+ RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+ RPC_VOC_CODEC_TTY_ON_CHIP_1,
+ RPC_VOC_CODEC_TTY_OFF_BOARD,
+ RPC_VOC_CODEC_TTY_VCO,
+ RPC_VOC_CODEC_TTY_HCO,
+ RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+ RPC_VOC_CODEC_MAX,
+ RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+ RPC_AUDMGR_STATUS_READY,
+ RPC_AUDMGR_STATUS_CODEC_CONFIG,
+ RPC_AUDMGR_STATUS_PENDING,
+ RPC_AUDMGR_STATUS_SUSPEND,
+ RPC_AUDMGR_STATUS_FAILURE,
+ RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+ RPC_AUDMGR_STATUS_DISABLED,
+ RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+ uint32_t set_to_one;
+ uint32_t tx_sample_rate;
+ uint32_t rx_sample_rate;
+ uint32_t def_method;
+ uint32_t codec_type;
+ uint32_t snd_method;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT 2
+#define AUDMGR_DISABLE_CLIENT 3
+#define AUDMGR_SUSPEND_EVENT_RSP 4
+#define AUDMGR_REGISTER_OPERATION_LISTENER 5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6
+#define AUDMGR_REGISTER_CODEC_LISTENER 7
+#define AUDMGR_GET_RX_SAMPLE_RATE 8
+#define AUDMGR_GET_TX_SAMPLE_RATE 9
+#define AUDMGR_SET_DEVICE_MODE 10
+
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS MSM_RPC_VERS(1,0)
+
+struct rpc_audmgr_cb_func_ptr {
+ uint32_t cb_id;
+ uint32_t status; /* Audmgr status */
+ uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */
+ uint32_t disc;
+ /* disc = AUDMGR_STATUS_READY => data=handle
+ disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle
+ disc = AUDMGR_STATUS_DISABLED => data =status_disabled
+ disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */
+ union {
+ uint32_t handle;
+ uint32_t volume;
+ uint32_t status_disabled;
+ uint32_t volume_change;
+ } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR 1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR 3
+
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0xf8e3e2d9
+
+struct audmgr {
+ wait_queue_head_t wait;
+ uint32_t handle;
+ struct msm_rpc_endpoint *ept;
+ struct task_struct *task;
+ int state;
+};
+
+struct audmgr_config {
+ uint32_t tx_rate;
+ uint32_t rx_rate;
+ uint32_t def_method;
+ uint32_t codec;
+ uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+int audpp_pause(unsigned id, int pause);
+int audpp_flush(unsigned id);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
diff --git a/drivers/staging/dream/qdsp5/audpp.c b/drivers/staging/dream/qdsp5/audpp.c
new file mode 100644
index 00000000000..d06556eda31
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audpp.c
@@ -0,0 +1,429 @@
+
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#include "evlog.h"
+
+
+enum {
+ EV_NULL,
+ EV_ENABLE,
+ EV_DISABLE,
+ EV_EVENT,
+ EV_DATA,
+};
+
+static const char *dsp_log_strings[] = {
+ "NULL",
+ "ENABLE",
+ "DISABLE",
+ "EVENT",
+ "DATA",
+};
+
+DECLARE_LOG(dsp_log, 64, dsp_log_strings);
+
+static int __init _dsp_log_init(void)
+{
+ return ev_log_init(&dsp_log);
+}
+module_init(_dsp_log_init);
+#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
+
+static DEFINE_MUTEX(audpp_lock);
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+#define AUDPP_AVSYNC_INFO_SIZE 7
+
+struct audpp_state {
+ struct msm_adsp_module *mod;
+ audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
+ void *private[AUDPP_CLNT_MAX_COUNT];
+ struct mutex *lock;
+ unsigned open_count;
+ unsigned enabled;
+
+ /* which channels are actually enabled */
+ unsigned avsync_mask;
+
+ /* flags, 48 bits sample/bytes counter per channel */
+ uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
+};
+
+struct audpp_state the_audpp_state = {
+ .lock = &audpp_lock,
+};
+
+int audpp_send_queue1(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd1Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue1);
+
+int audpp_send_queue2(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd2Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue2);
+
+int audpp_send_queue3(void *cmd, unsigned len)
+{
+ return msm_adsp_write(the_audpp_state.mod,
+ QDSP_uPAudPPCmd3Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue3);
+
+static int audpp_dsp_config(int enable)
+{
+ audpp_cmd_cfg cmd;
+
+ cmd.cmd_id = AUDPP_CMD_CFG;
+ cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
+
+ return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
+ uint16_t *msg)
+{
+ unsigned n;
+ for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
+ if (audpp->func[n])
+ audpp->func[n] (audpp->private[n], id, msg);
+ }
+}
+
+static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
+ unsigned id, uint16_t *msg)
+{
+ if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
+ audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
+}
+
+static void audpp_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent)(void *ptr, size_t len))
+{
+ struct audpp_state *audpp = data;
+ uint16_t msg[8];
+
+ if (id == AUDPP_MSG_AVSYNC_MSG) {
+ getevent(audpp->avsync, sizeof(audpp->avsync));
+
+ /* mask off any channels we're not watching to avoid
+ * cases where we might get one last update after
+ * disabling avsync and end up in an odd state when
+ * we next read...
+ */
+ audpp->avsync[0] &= audpp->avsync_mask;
+ return;
+ }
+
+ getevent(msg, sizeof(msg));
+
+ LOG(EV_EVENT, (id << 16) | msg[0]);
+ LOG(EV_DATA, (msg[1] << 16) | msg[2]);
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:{
+ unsigned cid = msg[0];
+ pr_info("audpp: status %d %d %d\n", cid, msg[1],
+ msg[2]);
+ if ((cid < 5) && audpp->func[cid])
+ audpp->func[cid] (audpp->private[cid], id, msg);
+ break;
+ }
+ case AUDPP_MSG_HOST_PCM_INTF_MSG:
+ if (audpp->func[5])
+ audpp->func[5] (audpp->private[5], id, msg);
+ break;
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_err("audpp: DMA missed obj=%x\n", msg[0]);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ pr_info("audpp: ENABLE\n");
+ audpp->enabled = 1;
+ audpp_broadcast(audpp, id, msg);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ pr_info("audpp: DISABLE\n");
+ audpp->enabled = 0;
+ audpp_broadcast(audpp, id, msg);
+ } else {
+ pr_err("audpp: invalid config msg %d\n", msg[0]);
+ }
+ break;
+ case AUDPP_MSG_ROUTING_ACK:
+ audpp_broadcast(audpp, id, msg);
+ break;
+ case AUDPP_MSG_FLUSH_ACK:
+ audpp_notify_clnt(audpp, msg[0], id, msg);
+ break;
+ default:
+ pr_info("audpp: unhandled msg id %x\n", id);
+ }
+}
+
+static struct msm_adsp_ops adsp_ops = {
+ .event = audpp_dsp_event,
+};
+
+static void audpp_fake_event(struct audpp_state *audpp, int id,
+ unsigned event, unsigned arg)
+{
+ uint16_t msg[1];
+ msg[0] = arg;
+ audpp->func[id] (audpp->private[id], event, msg);
+}
+
+int audpp_enable(int id, audpp_event_func func, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ int res = 0;
+
+ if (id < -1 || id > 4)
+ return -EINVAL;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ if (audpp->func[id]) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ audpp->func[id] = func;
+ audpp->private[id] = private;
+
+ LOG(EV_ENABLE, 1);
+ if (audpp->open_count++ == 0) {
+ pr_info("audpp: enable\n");
+ res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+ if (res < 0) {
+ pr_err("audpp: cannot open AUDPPTASK\n");
+ audpp->open_count = 0;
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ goto out;
+ }
+ LOG(EV_ENABLE, 2);
+ msm_adsp_enable(audpp->mod);
+ audpp_dsp_config(1);
+ } else {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (audpp->enabled)
+ audpp_fake_event(audpp, id,
+ AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
+ local_irq_restore(flags);
+ }
+
+ res = 0;
+out:
+ mutex_unlock(audpp->lock);
+ return res;
+}
+EXPORT_SYMBOL(audpp_enable);
+
+void audpp_disable(int id, void *private)
+{
+ struct audpp_state *audpp = &the_audpp_state;
+ unsigned long flags;
+
+ if (id < -1 || id > 4)
+ return;
+
+ if (id == -1)
+ id = 5;
+
+ mutex_lock(audpp->lock);
+ LOG(EV_DISABLE, 1);
+ if (!audpp->func[id])
+ goto out;
+ if (audpp->private[id] != private)
+ goto out;
+
+ local_irq_save(flags);
+ audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
+ audpp->func[id] = NULL;
+ audpp->private[id] = NULL;
+ local_irq_restore(flags);
+
+ if (--audpp->open_count == 0) {
+ pr_info("audpp: disable\n");
+ LOG(EV_DISABLE, 2);
+ audpp_dsp_config(0);
+ msm_adsp_disable(audpp->mod);
+ msm_adsp_put(audpp->mod);
+ audpp->mod = NULL;
+ }
+out:
+ mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+void audpp_avsync(int id, unsigned rate)
+{
+ unsigned long flags;
+ audpp_cmd_avsync cmd;
+
+ if (BAD_ID(id))
+ return;
+
+ local_irq_save(flags);
+ if (rate)
+ the_audpp_state.avsync_mask |= (1 << id);
+ else
+ the_audpp_state.avsync_mask &= (~(1 << id));
+ the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
+ local_irq_restore(flags);
+
+ cmd.cmd_id = AUDPP_CMD_AVSYNC;
+ cmd.object_number = id;
+ cmd.interrupt_interval_lsw = rate;
+ cmd.interrupt_interval_msw = rate >> 16;
+ audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_avsync);
+
+unsigned audpp_avsync_sample_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_sample_count);
+
+unsigned audpp_avsync_byte_count(int id)
+{
+ uint16_t *avsync = the_audpp_state.avsync;
+ unsigned val;
+ unsigned long flags;
+ unsigned mask;
+
+ if (BAD_ID(id))
+ return 0;
+
+ mask = 1 << id;
+ id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
+ local_irq_save(flags);
+ if (avsync[0] & mask)
+ val = (avsync[id] << 16) | avsync[id + 1];
+ else
+ val = 0;
+ local_irq_restore(flags);
+
+ return val;
+}
+EXPORT_SYMBOL(audpp_avsync_byte_count);
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_VOLUME_PAN 0
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
+{
+ /* cmd, obj_cfg[7], cmd_type, volume, pan */
+ uint16_t cmd[11];
+
+ if (id > 6)
+ return -EINVAL;
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+ cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+ cmd[8] = AUDPP_CMD_VOLUME_PAN;
+ cmd[9] = volume;
+ cmd[10] = pan;
+
+ return audpp_send_queue3(cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_set_volume_and_pan);
+
+int audpp_pause(unsigned id, int pause)
+{
+ /* pause 1 = pause 0 = resume */
+ u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(pause_cmd, 0, sizeof(pause_cmd));
+
+ pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ if (pause == 1)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+ else if (pause == 0)
+ pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
+ else
+ return -EINVAL;
+
+ return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
+}
+EXPORT_SYMBOL(audpp_pause);
+
+int audpp_flush(unsigned id)
+{
+ u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+ if (id >= CH_COUNT)
+ return -EINVAL;
+
+ memset(flush_cmd, 0, sizeof(flush_cmd));
+
+ flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
+ flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
+
+ return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
+}
+EXPORT_SYMBOL(audpp_flush);
diff --git a/drivers/staging/dream/qdsp5/evlog.h b/drivers/staging/dream/qdsp5/evlog.h
new file mode 100644
index 00000000000..922ce670a32
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/evlog.h
@@ -0,0 +1,133 @@
+/* arch/arm/mach-msm/qdsp5/evlog.h
+ *
+ * simple event log debugging facility
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/debugfs.h>
+
+#define EV_LOG_ENTRY_NAME(n) n##_entry
+
+#define DECLARE_LOG(_name, _size, _str) \
+static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \
+static struct ev_log _name = { \
+ .name = #_name, \
+ .strings = _str, \
+ .num_strings = ARRAY_SIZE(_str), \
+ .entry = EV_LOG_ENTRY_NAME(_name), \
+ .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \
+}
+
+struct ev_entry {
+ ktime_t when;
+ uint32_t id;
+ uint32_t arg;
+};
+
+struct ev_log {
+ struct ev_entry *entry;
+ unsigned max;
+ unsigned next;
+ unsigned fault;
+ const char **strings;
+ unsigned num_strings;
+ const char *name;
+};
+
+static char ev_buf[4096];
+
+static ssize_t ev_log_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ev_log *log = file->private_data;
+ struct ev_entry *entry;
+ unsigned long flags;
+ int size = 0;
+ unsigned n, id, max;
+ ktime_t now, t;
+
+ max = log->max;
+ now = ktime_get();
+ local_irq_save(flags);
+ n = (log->next - 1) & (max - 1);
+ entry = log->entry;
+ while (n != log->next) {
+ t = ktime_sub(now, entry[n].when);
+ id = entry[n].id;
+ if (id) {
+ const char *str;
+ if (id < log->num_strings)
+ str = log->strings[id];
+ else
+ str = "UNKNOWN";
+ size += scnprintf(ev_buf + size, 4096 - size,
+ "%8d.%03d %08x %s\n",
+ t.tv.sec, t.tv.nsec / 1000000,
+ entry[n].arg, str);
+ }
+ n = (n - 1) & (max - 1);
+ }
+ log->fault = 0;
+ local_irq_restore(flags);
+ return simple_read_from_buffer(buf, count, ppos, ev_buf, size);
+}
+
+static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg)
+{
+ struct ev_entry *entry;
+ unsigned long flags;
+ local_irq_save(flags);
+
+ if (log->fault) {
+ if (log->fault == 1)
+ goto done;
+ log->fault--;
+ }
+
+ entry = log->entry + log->next;
+ entry->when = ktime_get();
+ entry->id = id;
+ entry->arg = arg;
+ log->next = (log->next + 1) & (log->max - 1);
+done:
+ local_irq_restore(flags);
+}
+
+static void ev_log_freeze(struct ev_log *log, unsigned count)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ log->fault = count;
+ local_irq_restore(flags);
+}
+
+static int ev_log_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations ev_log_ops = {
+ .read = ev_log_read,
+ .open = ev_log_open,
+};
+
+static int ev_log_init(struct ev_log *log)
+{
+ debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops);
+ return 0;
+}
+
diff --git a/drivers/staging/dream/qdsp5/snd.c b/drivers/staging/dream/qdsp5/snd.c
new file mode 100644
index 00000000000..037d7ffb7e6
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/snd.c
@@ -0,0 +1,279 @@
+/* arch/arm/mach-msm/qdsp5/snd.c
+ *
+ * interface to "snd" service on the baseband cpu
+ *
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_rpcrouter.h>
+
+struct snd_ctxt {
+ struct mutex lock;
+ int opened;
+ struct msm_rpc_endpoint *ept;
+ struct msm_snd_endpoints *snd_epts;
+};
+
+static struct snd_ctxt the_snd;
+
+#define RPC_SND_PROG 0x30000002
+#define RPC_SND_CB_PROG 0x31000002
+#if CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_SND_VERS 0x94756085 /* 2490720389 */
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || \
+ (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */
+#elif CONFIG_MSM_AMSS_VERSION == 6350
+#define RPC_SND_VERS MSM_RPC_VERS(1,0)
+#endif
+
+#define SND_SET_DEVICE_PROC 2
+#define SND_SET_VOLUME_PROC 3
+
+struct rpc_snd_set_device_args {
+ uint32_t device;
+ uint32_t ear_mute;
+ uint32_t mic_mute;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+struct rpc_snd_set_volume_args {
+ uint32_t device;
+ uint32_t method;
+ uint32_t volume;
+
+ uint32_t cb_func;
+ uint32_t client_data;
+};
+
+struct snd_set_device_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_snd_set_device_args args;
+};
+
+struct snd_set_volume_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_snd_set_volume_args args;
+};
+
+struct snd_endpoint *get_snd_endpoints(int *size);
+
+static inline int check_mute(int mute)
+{
+ return (mute == SND_MUTE_MUTED ||
+ mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL;
+}
+
+static int get_endpoint(struct snd_ctxt *snd, unsigned long arg)
+{
+ int rc = 0, index;
+ struct msm_snd_endpoint ept;
+
+ if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) {
+ pr_err("snd_ioctl get endpoint: invalid read pointer.\n");
+ return -EFAULT;
+ }
+
+ index = ept.id;
+ if (index < 0 || index >= snd->snd_epts->num) {
+ pr_err("snd_ioctl get endpoint: invalid index!\n");
+ return -EINVAL;
+ }
+
+ ept.id = snd->snd_epts->endpoints[index].id;
+ strncpy(ept.name,
+ snd->snd_epts->endpoints[index].name,
+ sizeof(ept.name));
+
+ if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) {
+ pr_err("snd_ioctl get endpoint: invalid write pointer.\n");
+ rc = -EFAULT;
+ }
+
+ return rc;
+}
+
+static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct snd_set_device_msg dmsg;
+ struct snd_set_volume_msg vmsg;
+ struct msm_snd_device_config dev;
+ struct msm_snd_volume_config vol;
+ struct snd_ctxt *snd = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&snd->lock);
+ switch (cmd) {
+ case SND_SET_DEVICE:
+ if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
+ pr_err("snd_ioctl set device: invalid pointer.\n");
+ rc = -EFAULT;
+ break;
+ }
+
+ dmsg.args.device = cpu_to_be32(dev.device);
+ dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
+ dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
+ if (check_mute(dev.ear_mute) < 0 ||
+ check_mute(dev.mic_mute) < 0) {
+ pr_err("snd_ioctl set device: invalid mute status.\n");
+ rc = -EINVAL;
+ break;
+ }
+ dmsg.args.cb_func = -1;
+ dmsg.args.client_data = 0;
+
+ pr_info("snd_set_device %d %d %d\n", dev.device,
+ dev.ear_mute, dev.mic_mute);
+
+ rc = msm_rpc_call(snd->ept,
+ SND_SET_DEVICE_PROC,
+ &dmsg, sizeof(dmsg), 5 * HZ);
+ break;
+
+ case SND_SET_VOLUME:
+ if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) {
+ pr_err("snd_ioctl set volume: invalid pointer.\n");
+ rc = -EFAULT;
+ break;
+ }
+
+ vmsg.args.device = cpu_to_be32(vol.device);
+ vmsg.args.method = cpu_to_be32(vol.method);
+ if (vol.method != SND_METHOD_VOICE) {
+ pr_err("snd_ioctl set volume: invalid method.\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ vmsg.args.volume = cpu_to_be32(vol.volume);
+ vmsg.args.cb_func = -1;
+ vmsg.args.client_data = 0;
+
+ pr_info("snd_set_volume %d %d %d\n", vol.device,
+ vol.method, vol.volume);
+
+ rc = msm_rpc_call(snd->ept,
+ SND_SET_VOLUME_PROC,
+ &vmsg, sizeof(vmsg), 5 * HZ);
+ break;
+
+ case SND_GET_NUM_ENDPOINTS:
+ if (copy_to_user((void __user *)arg,
+ &snd->snd_epts->num, sizeof(unsigned))) {
+ pr_err("snd_ioctl get endpoint: invalid pointer.\n");
+ rc = -EFAULT;
+ }
+ break;
+
+ case SND_GET_ENDPOINT:
+ rc = get_endpoint(snd, arg);
+ break;
+
+ default:
+ pr_err("snd_ioctl unknown command.\n");
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&snd->lock);
+
+ return rc;
+}
+
+static int snd_release(struct inode *inode, struct file *file)
+{
+ struct snd_ctxt *snd = file->private_data;
+
+ mutex_lock(&snd->lock);
+ snd->opened = 0;
+ mutex_unlock(&snd->lock);
+ return 0;
+}
+
+static int snd_open(struct inode *inode, struct file *file)
+{
+ struct snd_ctxt *snd = &the_snd;
+ int rc = 0;
+
+ mutex_lock(&snd->lock);
+ if (snd->opened == 0) {
+ if (snd->ept == NULL) {
+ snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ if (IS_ERR(snd->ept)) {
+ rc = PTR_ERR(snd->ept);
+ snd->ept = NULL;
+ pr_err("snd: failed to connect snd svc\n");
+ goto err;
+ }
+ }
+ file->private_data = snd;
+ snd->opened = 1;
+ } else {
+ pr_err("snd already opened.\n");
+ rc = -EBUSY;
+ }
+
+err:
+ mutex_unlock(&snd->lock);
+ return rc;
+}
+
+static struct file_operations snd_fops = {
+ .owner = THIS_MODULE,
+ .open = snd_open,
+ .release = snd_release,
+ .unlocked_ioctl = snd_ioctl,
+};
+
+struct miscdevice snd_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_snd",
+ .fops = &snd_fops,
+};
+
+static int snd_probe(struct platform_device *pdev)
+{
+ struct snd_ctxt *snd = &the_snd;
+ mutex_init(&snd->lock);
+ snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
+ return misc_register(&snd_misc);
+}
+
+static struct platform_driver snd_plat_driver = {
+ .probe = snd_probe,
+ .driver = {
+ .name = "msm_snd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init snd_init(void)
+{
+ return platform_driver_register(&snd_plat_driver);
+}
+
+module_init(snd_init);
diff --git a/drivers/staging/dream/smd/Kconfig b/drivers/staging/dream/smd/Kconfig
new file mode 100644
index 00000000000..17b8bdc7b9b
--- /dev/null
+++ b/drivers/staging/dream/smd/Kconfig
@@ -0,0 +1,26 @@
+config MSM_SMD
+ depends on ARCH_MSM
+ default y
+ bool "MSM Shared Memory Driver (SMD)"
+ help
+ Support for the shared memory interface between the apps
+ processor and the baseband processor. Provides access to
+ the "shared heap", as well as virtual serial channels
+ used to communicate with various services on the baseband
+ processor.
+
+config MSM_ONCRPCROUTER
+ depends on MSM_SMD
+ default y
+ bool "MSM ONCRPC router support"
+ help
+ Support for the MSM ONCRPC router for communication between
+ the ARM9 and ARM11
+
+config MSM_RPCSERVERS
+ depends on MSM_ONCRPCROUTER
+ default y
+ bool "Kernel side RPC server bundle"
+ help
+ none
+
diff --git a/drivers/staging/dream/smd/Makefile b/drivers/staging/dream/smd/Makefile
new file mode 100644
index 00000000000..892c7414bbe
--- /dev/null
+++ b/drivers/staging/dream/smd/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_MSM_SMD) += smd.o smd_tty.o smd_qmi.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
+obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o
+obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o
diff --git a/drivers/staging/dream/smd/rpc_server_dog_keepalive.c b/drivers/staging/dream/smd/rpc_server_dog_keepalive.c
new file mode 100644
index 00000000000..b23fccfa87e
--- /dev/null
+++ b/drivers/staging/dream/smd/rpc_server_dog_keepalive.c
@@ -0,0 +1,68 @@
+/* arch/arm/mach-msm/rpc_server_dog_keepalive.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* dog_keepalive server definitions */
+
+#define DOG_KEEPALIVE_PROG 0x30000015
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define DOG_KEEPALIVE_VERS 0
+#define RPC_DOG_KEEPALIVE_BEACON 1
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define DOG_KEEPALIVE_VERS 0x731fa727
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define DOG_KEEPALIVE_VERS 0x00010000
+#define RPC_DOG_KEEPALIVE_BEACON 2
+#else
+#error "Unsupported AMSS version"
+#endif
+#define RPC_DOG_KEEPALIVE_NULL 0
+
+
+/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_DOG_KEEPALIVE_NULL:
+ return 0;
+ case RPC_DOG_KEEPALIVE_BEACON:
+ printk(KERN_INFO "DOG KEEPALIVE PING\n");
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server rpc_server = {
+ .prog = DOG_KEEPALIVE_PROG,
+ .vers = DOG_KEEPALIVE_VERS,
+ .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+ /* Dual server registration to support backwards compatibility vers */
+ return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/drivers/staging/dream/smd/rpc_server_time_remote.c b/drivers/staging/dream/smd/rpc_server_time_remote.c
new file mode 100644
index 00000000000..2f90fc88c38
--- /dev/null
+++ b/drivers/staging/dream/smd/rpc_server_time_remote.c
@@ -0,0 +1,77 @@
+/* arch/arm/mach-msm/rpc_server_time_remote.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <mach/msm_rpcrouter.h>
+
+/* time_remote_mtoa server definitions. */
+
+#define TIME_REMOTE_MTOA_PROG 0x3000005d
+#if CONFIG_MSM_AMSS_VERSION==6210
+#define TIME_REMOTE_MTOA_VERS 0
+#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
+#define TIME_REMOTE_MTOA_VERS 0x9202a8e4
+#elif CONFIG_MSM_AMSS_VERSION==6350
+#define TIME_REMOTE_MTOA_VERS 0x00010000
+#else
+#error "Unknown AMSS version"
+#endif
+#define RPC_TIME_REMOTE_MTOA_NULL 0
+#define RPC_TIME_TOD_SET_APPS_BASES 2
+
+struct rpc_time_tod_set_apps_bases_args {
+ uint32_t tick;
+ uint64_t stamp;
+};
+
+static int handle_rpc_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_TIME_REMOTE_MTOA_NULL:
+ return 0;
+
+ case RPC_TIME_TOD_SET_APPS_BASES: {
+ struct rpc_time_tod_set_apps_bases_args *args;
+ args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1);
+ args->tick = be32_to_cpu(args->tick);
+ args->stamp = be64_to_cpu(args->stamp);
+ printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n"
+ "\ttick = %d\n"
+ "\tstamp = %lld\n",
+ args->tick, args->stamp);
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server rpc_server = {
+ .prog = TIME_REMOTE_MTOA_PROG,
+ .vers = TIME_REMOTE_MTOA_VERS,
+ .rpc_call = handle_rpc_call,
+};
+
+static int __init rpc_server_init(void)
+{
+ /* Dual server registration to support backwards compatibility vers */
+ return msm_rpc_create_server(&rpc_server);
+}
+
+
+module_init(rpc_server_init);
diff --git a/drivers/staging/dream/smd/smd.c b/drivers/staging/dream/smd/smd.c
new file mode 100644
index 00000000000..8f35be7193f
--- /dev/null
+++ b/drivers/staging/dream/smd/smd.c
@@ -0,0 +1,1330 @@
+/* arch/arm/mach-msm/smd.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_iomap.h>
+#include <mach/system.h>
+
+#include "smd_private.h"
+#include "../../../../arch/arm/mach-msm/proc_comm.h"
+
+void (*msm_hw_reset_hook)(void);
+
+#define MODULE_NAME "msm_smd"
+
+enum {
+ MSM_SMD_DEBUG = 1U << 0,
+ MSM_SMSM_DEBUG = 1U << 0,
+};
+
+static int msm_smd_debug_mask;
+
+module_param_named(debug_mask, msm_smd_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+void *smem_find(unsigned id, unsigned size);
+static void smd_diag(void);
+
+static unsigned last_heap_free = 0xffffffff;
+
+#define MSM_A2M_INT(n) (MSM_CSR_BASE + 0x400 + (n) * 4)
+
+static inline void notify_other_smsm(void)
+{
+ writel(1, MSM_A2M_INT(5));
+}
+
+static inline void notify_other_smd(void)
+{
+ writel(1, MSM_A2M_INT(0));
+}
+
+static void smd_diag(void)
+{
+ char *x;
+
+ x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG);
+ if (x != 0) {
+ x[SZ_DIAG_ERR_MSG - 1] = 0;
+ pr_info("smem: DIAG '%s'\n", x);
+ }
+}
+
+/* call when SMSM_RESET flag is set in the A9's smsm_state */
+static void handle_modem_crash(void)
+{
+ pr_err("ARM9 has CRASHED\n");
+ smd_diag();
+
+ /* hard reboot if possible */
+ if (msm_hw_reset_hook)
+ msm_hw_reset_hook();
+
+ /* in this case the modem or watchdog should reboot us */
+ for (;;)
+ ;
+}
+
+extern int (*msm_check_for_modem_crash)(void);
+
+static int check_for_modem_crash(void)
+{
+ struct smsm_shared *smsm;
+
+ smsm = smem_find(ID_SHARED_STATE, 2 * sizeof(struct smsm_shared));
+
+ /* if the modem's not ready yet, we have to hope for the best */
+ if (!smsm)
+ return 0;
+
+ if (smsm[1].state & SMSM_RESET) {
+ handle_modem_crash();
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+#define SMD_SS_CLOSED 0x00000000
+#define SMD_SS_OPENING 0x00000001
+#define SMD_SS_OPENED 0x00000002
+#define SMD_SS_FLUSHING 0x00000003
+#define SMD_SS_CLOSING 0x00000004
+#define SMD_SS_RESET 0x00000005
+#define SMD_SS_RESET_OPENING 0x00000006
+
+#define SMD_BUF_SIZE 8192
+#define SMD_CHANNELS 64
+
+#define SMD_HEADER_SIZE 20
+
+
+/* the spinlock is used to synchronize between the
+** irq handler and code that mutates the channel
+** list or fiddles with channel state
+*/
+static DEFINE_SPINLOCK(smd_lock);
+static DEFINE_SPINLOCK(smem_lock);
+
+/* the mutex is used during open() and close()
+** operations to avoid races while creating or
+** destroying smd_channel structures
+*/
+static DEFINE_MUTEX(smd_creation_mutex);
+
+static int smd_initialized;
+
+struct smd_alloc_elm {
+ char name[20];
+ uint32_t cid;
+ uint32_t ctype;
+ uint32_t ref_count;
+};
+
+struct smd_half_channel {
+ unsigned state;
+ unsigned char fDSR;
+ unsigned char fCTS;
+ unsigned char fCD;
+ unsigned char fRI;
+ unsigned char fHEAD;
+ unsigned char fTAIL;
+ unsigned char fSTATE;
+ unsigned char fUNUSED;
+ unsigned tail;
+ unsigned head;
+ unsigned char data[SMD_BUF_SIZE];
+};
+
+struct smd_shared {
+ struct smd_half_channel ch0;
+ struct smd_half_channel ch1;
+};
+
+struct smd_channel {
+ volatile struct smd_half_channel *send;
+ volatile struct smd_half_channel *recv;
+ struct list_head ch_list;
+
+ unsigned current_packet;
+ unsigned n;
+ void *priv;
+ void (*notify)(void *priv, unsigned flags);
+
+ int (*read)(smd_channel_t *ch, void *data, int len);
+ int (*write)(smd_channel_t *ch, const void *data, int len);
+ int (*read_avail)(smd_channel_t *ch);
+ int (*write_avail)(smd_channel_t *ch);
+
+ void (*update_state)(smd_channel_t *ch);
+ unsigned last_state;
+
+ char name[32];
+ struct platform_device pdev;
+};
+
+static LIST_HEAD(smd_ch_closed_list);
+static LIST_HEAD(smd_ch_list);
+
+static unsigned char smd_ch_allocated[64];
+static struct work_struct probe_work;
+
+static void smd_alloc_channel(const char *name, uint32_t cid, uint32_t type);
+
+static void smd_channel_probe_worker(struct work_struct *work)
+{
+ struct smd_alloc_elm *shared;
+ unsigned n;
+
+ shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
+
+ for (n = 0; n < 64; n++) {
+ if (smd_ch_allocated[n])
+ continue;
+ if (!shared[n].ref_count)
+ continue;
+ if (!shared[n].name[0])
+ continue;
+ smd_alloc_channel(shared[n].name,
+ shared[n].cid,
+ shared[n].ctype);
+ smd_ch_allocated[n] = 1;
+ }
+}
+
+static char *chstate(unsigned n)
+{
+ switch (n) {
+ case SMD_SS_CLOSED:
+ return "CLOSED";
+ case SMD_SS_OPENING:
+ return "OPENING";
+ case SMD_SS_OPENED:
+ return "OPENED";
+ case SMD_SS_FLUSHING:
+ return "FLUSHING";
+ case SMD_SS_CLOSING:
+ return "CLOSING";
+ case SMD_SS_RESET:
+ return "RESET";
+ case SMD_SS_RESET_OPENING:
+ return "ROPENING";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/* how many bytes are available for reading */
+static int smd_stream_read_avail(struct smd_channel *ch)
+{
+ return (ch->recv->head - ch->recv->tail) & (SMD_BUF_SIZE - 1);
+}
+
+/* how many bytes we are free to write */
+static int smd_stream_write_avail(struct smd_channel *ch)
+{
+ return (SMD_BUF_SIZE - 1) -
+ ((ch->send->head - ch->send->tail) & (SMD_BUF_SIZE - 1));
+}
+
+static int smd_packet_read_avail(struct smd_channel *ch)
+{
+ if (ch->current_packet) {
+ int n = smd_stream_read_avail(ch);
+ if (n > ch->current_packet)
+ n = ch->current_packet;
+ return n;
+ } else {
+ return 0;
+ }
+}
+
+static int smd_packet_write_avail(struct smd_channel *ch)
+{
+ int n = smd_stream_write_avail(ch);
+ return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
+}
+
+static int ch_is_open(struct smd_channel *ch)
+{
+ return (ch->recv->state == SMD_SS_OPENED) &&
+ (ch->send->state == SMD_SS_OPENED);
+}
+
+/* provide a pointer and length to readable data in the fifo */
+static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr)
+{
+ unsigned head = ch->recv->head;
+ unsigned tail = ch->recv->tail;
+ *ptr = (void *) (ch->recv->data + tail);
+
+ if (tail <= head)
+ return head - tail;
+ else
+ return SMD_BUF_SIZE - tail;
+}
+
+/* advance the fifo read pointer after data from ch_read_buffer is consumed */
+static void ch_read_done(struct smd_channel *ch, unsigned count)
+{
+ BUG_ON(count > smd_stream_read_avail(ch));
+ ch->recv->tail = (ch->recv->tail + count) & (SMD_BUF_SIZE - 1);
+ ch->recv->fTAIL = 1;
+}
+
+/* basic read interface to ch_read_{buffer,done} used
+** by smd_*_read() and update_packet_state()
+** will read-and-discard if the _data pointer is null
+*/
+static int ch_read(struct smd_channel *ch, void *_data, int len)
+{
+ void *ptr;
+ unsigned n;
+ unsigned char *data = _data;
+ int orig_len = len;
+
+ while (len > 0) {
+ n = ch_read_buffer(ch, &ptr);
+ if (n == 0)
+ break;
+
+ if (n > len)
+ n = len;
+ if (_data)
+ memcpy(data, ptr, n);
+
+ data += n;
+ len -= n;
+ ch_read_done(ch, n);
+ }
+
+ return orig_len - len;
+}
+
+static void update_stream_state(struct smd_channel *ch)
+{
+ /* streams have no special state requiring updating */
+}
+
+static void update_packet_state(struct smd_channel *ch)
+{
+ unsigned hdr[5];
+ int r;
+
+ /* can't do anything if we're in the middle of a packet */
+ if (ch->current_packet != 0)
+ return;
+
+ /* don't bother unless we can get the full header */
+ if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE)
+ return;
+
+ r = ch_read(ch, hdr, SMD_HEADER_SIZE);
+ BUG_ON(r != SMD_HEADER_SIZE);
+
+ ch->current_packet = hdr[0];
+}
+
+/* provide a pointer and length to next free space in the fifo */
+static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr)
+{
+ unsigned head = ch->send->head;
+ unsigned tail = ch->send->tail;
+ *ptr = (void *) (ch->send->data + head);
+
+ if (head < tail) {
+ return tail - head - 1;
+ } else {
+ if (tail == 0)
+ return SMD_BUF_SIZE - head - 1;
+ else
+ return SMD_BUF_SIZE - head;
+ }
+}
+
+/* advace the fifo write pointer after freespace
+ * from ch_write_buffer is filled
+ */
+static void ch_write_done(struct smd_channel *ch, unsigned count)
+{
+ BUG_ON(count > smd_stream_write_avail(ch));
+ ch->send->head = (ch->send->head + count) & (SMD_BUF_SIZE - 1);
+ ch->send->fHEAD = 1;
+}
+
+static void hc_set_state(volatile struct smd_half_channel *hc, unsigned n)
+{
+ if (n == SMD_SS_OPENED) {
+ hc->fDSR = 1;
+ hc->fCTS = 1;
+ hc->fCD = 1;
+ } else {
+ hc->fDSR = 0;
+ hc->fCTS = 0;
+ hc->fCD = 0;
+ }
+ hc->state = n;
+ hc->fSTATE = 1;
+ notify_other_smd();
+}
+
+static void do_smd_probe(void)
+{
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ if (shared->heap_info.free_offset != last_heap_free) {
+ last_heap_free = shared->heap_info.free_offset;
+ schedule_work(&probe_work);
+ }
+}
+
+static void smd_state_change(struct smd_channel *ch,
+ unsigned last, unsigned next)
+{
+ ch->last_state = next;
+
+ pr_info("SMD: ch %d %s -> %s\n", ch->n,
+ chstate(last), chstate(next));
+
+ switch (next) {
+ case SMD_SS_OPENING:
+ ch->recv->tail = 0;
+ case SMD_SS_OPENED:
+ if (ch->send->state != SMD_SS_OPENED)
+ hc_set_state(ch->send, SMD_SS_OPENED);
+ ch->notify(ch->priv, SMD_EVENT_OPEN);
+ break;
+ case SMD_SS_FLUSHING:
+ case SMD_SS_RESET:
+ /* we should force them to close? */
+ default:
+ ch->notify(ch->priv, SMD_EVENT_CLOSE);
+ }
+}
+
+static irqreturn_t smd_irq_handler(int irq, void *data)
+{
+ unsigned long flags;
+ struct smd_channel *ch;
+ int do_notify = 0;
+ unsigned ch_flags;
+ unsigned tmp;
+
+ spin_lock_irqsave(&smd_lock, flags);
+ list_for_each_entry(ch, &smd_ch_list, ch_list) {
+ ch_flags = 0;
+ if (ch_is_open(ch)) {
+ if (ch->recv->fHEAD) {
+ ch->recv->fHEAD = 0;
+ ch_flags |= 1;
+ do_notify |= 1;
+ }
+ if (ch->recv->fTAIL) {
+ ch->recv->fTAIL = 0;
+ ch_flags |= 2;
+ do_notify |= 1;
+ }
+ if (ch->recv->fSTATE) {
+ ch->recv->fSTATE = 0;
+ ch_flags |= 4;
+ do_notify |= 1;
+ }
+ }
+ tmp = ch->recv->state;
+ if (tmp != ch->last_state)
+ smd_state_change(ch, ch->last_state, tmp);
+ if (ch_flags) {
+ ch->update_state(ch);
+ ch->notify(ch->priv, SMD_EVENT_DATA);
+ }
+ }
+ if (do_notify)
+ notify_other_smd();
+ spin_unlock_irqrestore(&smd_lock, flags);
+ do_smd_probe();
+ return IRQ_HANDLED;
+}
+
+static void smd_fake_irq_handler(unsigned long arg)
+{
+ smd_irq_handler(0, NULL);
+}
+
+static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0);
+
+void smd_sleep_exit(void)
+{
+ unsigned long flags;
+ struct smd_channel *ch;
+ unsigned tmp;
+ int need_int = 0;
+
+ spin_lock_irqsave(&smd_lock, flags);
+ list_for_each_entry(ch, &smd_ch_list, ch_list) {
+ if (ch_is_open(ch)) {
+ if (ch->recv->fHEAD) {
+ if (msm_smd_debug_mask & MSM_SMD_DEBUG)
+ pr_info("smd_sleep_exit ch %d fHEAD "
+ "%x %x %x\n",
+ ch->n, ch->recv->fHEAD,
+ ch->recv->head, ch->recv->tail);
+ need_int = 1;
+ break;
+ }
+ if (ch->recv->fTAIL) {
+ if (msm_smd_debug_mask & MSM_SMD_DEBUG)
+ pr_info("smd_sleep_exit ch %d fTAIL "
+ "%x %x %x\n",
+ ch->n, ch->recv->fTAIL,
+ ch->send->head, ch->send->tail);
+ need_int = 1;
+ break;
+ }
+ if (ch->recv->fSTATE) {
+ if (msm_smd_debug_mask & MSM_SMD_DEBUG)
+ pr_info("smd_sleep_exit ch %d fSTATE %x"
+ "\n", ch->n, ch->recv->fSTATE);
+ need_int = 1;
+ break;
+ }
+ tmp = ch->recv->state;
+ if (tmp != ch->last_state) {
+ if (msm_smd_debug_mask & MSM_SMD_DEBUG)
+ pr_info("smd_sleep_exit ch %d "
+ "state %x != %x\n",
+ ch->n, tmp, ch->last_state);
+ need_int = 1;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&smd_lock, flags);
+ do_smd_probe();
+ if (need_int) {
+ if (msm_smd_debug_mask & MSM_SMD_DEBUG)
+ pr_info("smd_sleep_exit need interrupt\n");
+ tasklet_schedule(&smd_fake_irq_tasklet);
+ }
+}
+
+
+void smd_kick(smd_channel_t *ch)
+{
+ unsigned long flags;
+ unsigned tmp;
+
+ spin_lock_irqsave(&smd_lock, flags);
+ ch->update_state(ch);
+ tmp = ch->recv->state;
+ if (tmp != ch->last_state) {
+ ch->last_state = tmp;
+ if (tmp == SMD_SS_OPENED)
+ ch->notify(ch->priv, SMD_EVENT_OPEN);
+ else
+ ch->notify(ch->priv, SMD_EVENT_CLOSE);
+ }
+ ch->notify(ch->priv, SMD_EVENT_DATA);
+ notify_other_smd();
+ spin_unlock_irqrestore(&smd_lock, flags);
+}
+
+static int smd_is_packet(int chn)
+{
+ if ((chn > 4) || (chn == 1))
+ return 1;
+ else
+ return 0;
+}
+
+static int smd_stream_write(smd_channel_t *ch, const void *_data, int len)
+{
+ void *ptr;
+ const unsigned char *buf = _data;
+ unsigned xfer;
+ int orig_len = len;
+
+ if (len < 0)
+ return -EINVAL;
+
+ while ((xfer = ch_write_buffer(ch, &ptr)) != 0) {
+ if (!ch_is_open(ch))
+ break;
+ if (xfer > len)
+ xfer = len;
+ memcpy(ptr, buf, xfer);
+ ch_write_done(ch, xfer);
+ len -= xfer;
+ buf += xfer;
+ if (len == 0)
+ break;
+ }
+
+ notify_other_smd();
+
+ return orig_len - len;
+}
+
+static int smd_packet_write(smd_channel_t *ch, const void *_data, int len)
+{
+ unsigned hdr[5];
+
+ if (len < 0)
+ return -EINVAL;
+
+ if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE))
+ return -ENOMEM;
+
+ hdr[0] = len;
+ hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
+
+ smd_stream_write(ch, hdr, sizeof(hdr));
+ smd_stream_write(ch, _data, len);
+
+ return len;
+}
+
+static int smd_stream_read(smd_channel_t *ch, void *data, int len)
+{
+ int r;
+
+ if (len < 0)
+ return -EINVAL;
+
+ r = ch_read(ch, data, len);
+ if (r > 0)
+ notify_other_smd();
+
+ return r;
+}
+
+static int smd_packet_read(smd_channel_t *ch, void *data, int len)
+{
+ unsigned long flags;
+ int r;
+
+ if (len < 0)
+ return -EINVAL;
+
+ if (len > ch->current_packet)
+ len = ch->current_packet;
+
+ r = ch_read(ch, data, len);
+ if (r > 0)
+ notify_other_smd();
+
+ spin_lock_irqsave(&smd_lock, flags);
+ ch->current_packet -= r;
+ update_packet_state(ch);
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+ return r;
+}
+
+static void smd_alloc_channel(const char *name, uint32_t cid, uint32_t type)
+{
+ struct smd_channel *ch;
+ struct smd_shared *shared;
+
+ shared = smem_alloc(ID_SMD_CHANNELS + cid, sizeof(*shared));
+ if (!shared) {
+ pr_err("smd_alloc_channel() cid %d does not exist\n", cid);
+ return;
+ }
+
+ ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
+ if (ch == 0) {
+ pr_err("smd_alloc_channel() out of memory\n");
+ return;
+ }
+
+ ch->send = &shared->ch0;
+ ch->recv = &shared->ch1;
+ ch->n = cid;
+
+ if (smd_is_packet(cid)) {
+ ch->read = smd_packet_read;
+ ch->write = smd_packet_write;
+ ch->read_avail = smd_packet_read_avail;
+ ch->write_avail = smd_packet_write_avail;
+ ch->update_state = update_packet_state;
+ } else {
+ ch->read = smd_stream_read;
+ ch->write = smd_stream_write;
+ ch->read_avail = smd_stream_read_avail;
+ ch->write_avail = smd_stream_write_avail;
+ ch->update_state = update_stream_state;
+ }
+
+ memcpy(ch->name, "SMD_", 4);
+ memcpy(ch->name + 4, name, 20);
+ ch->name[23] = 0;
+ ch->pdev.name = ch->name;
+ ch->pdev.id = -1;
+
+ pr_info("smd_alloc_channel() '%s' cid=%d, shared=%p\n",
+ ch->name, ch->n, shared);
+
+ mutex_lock(&smd_creation_mutex);
+ list_add(&ch->ch_list, &smd_ch_closed_list);
+ mutex_unlock(&smd_creation_mutex);
+
+ platform_device_register(&ch->pdev);
+}
+
+static void do_nothing_notify(void *priv, unsigned flags)
+{
+}
+
+struct smd_channel *smd_get_channel(const char *name)
+{
+ struct smd_channel *ch;
+
+ mutex_lock(&smd_creation_mutex);
+ list_for_each_entry(ch, &smd_ch_closed_list, ch_list) {
+ if (!strcmp(name, ch->name)) {
+ list_del(&ch->ch_list);
+ mutex_unlock(&smd_creation_mutex);
+ return ch;
+ }
+ }
+ mutex_unlock(&smd_creation_mutex);
+
+ return NULL;
+}
+
+int smd_open(const char *name, smd_channel_t **_ch,
+ void *priv, void (*notify)(void *, unsigned))
+{
+ struct smd_channel *ch;
+ unsigned long flags;
+
+ if (smd_initialized == 0) {
+ pr_info("smd_open() before smd_init()\n");
+ return -ENODEV;
+ }
+
+ ch = smd_get_channel(name);
+ if (!ch)
+ return -ENODEV;
+
+ if (notify == 0)
+ notify = do_nothing_notify;
+
+ ch->notify = notify;
+ ch->current_packet = 0;
+ ch->last_state = SMD_SS_CLOSED;
+ ch->priv = priv;
+
+ *_ch = ch;
+
+ spin_lock_irqsave(&smd_lock, flags);
+ list_add(&ch->ch_list, &smd_ch_list);
+
+ /* If the remote side is CLOSING, we need to get it to
+ * move to OPENING (which we'll do by moving from CLOSED to
+ * OPENING) and then get it to move from OPENING to
+ * OPENED (by doing the same state change ourselves).
+ *
+ * Otherwise, it should be OPENING and we can move directly
+ * to OPENED so that it will follow.
+ */
+ if (ch->recv->state == SMD_SS_CLOSING) {
+ ch->send->head = 0;
+ hc_set_state(ch->send, SMD_SS_OPENING);
+ } else {
+ hc_set_state(ch->send, SMD_SS_OPENED);
+ }
+ spin_unlock_irqrestore(&smd_lock, flags);
+ smd_kick(ch);
+
+ return 0;
+}
+
+int smd_close(smd_channel_t *ch)
+{
+ unsigned long flags;
+
+ pr_info("smd_close(%p)\n", ch);
+
+ if (ch == 0)
+ return -1;
+
+ spin_lock_irqsave(&smd_lock, flags);
+ ch->notify = do_nothing_notify;
+ list_del(&ch->ch_list);
+ hc_set_state(ch->send, SMD_SS_CLOSED);
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+ mutex_lock(&smd_creation_mutex);
+ list_add(&ch->ch_list, &smd_ch_closed_list);
+ mutex_unlock(&smd_creation_mutex);
+
+ return 0;
+}
+
+int smd_read(smd_channel_t *ch, void *data, int len)
+{
+ return ch->read(ch, data, len);
+}
+
+int smd_write(smd_channel_t *ch, const void *data, int len)
+{
+ return ch->write(ch, data, len);
+}
+
+int smd_read_avail(smd_channel_t *ch)
+{
+ return ch->read_avail(ch);
+}
+
+int smd_write_avail(smd_channel_t *ch)
+{
+ return ch->write_avail(ch);
+}
+
+int smd_wait_until_readable(smd_channel_t *ch, int bytes)
+{
+ return -1;
+}
+
+int smd_wait_until_writable(smd_channel_t *ch, int bytes)
+{
+ return -1;
+}
+
+int smd_cur_packet_size(smd_channel_t *ch)
+{
+ return ch->current_packet;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+void *smem_alloc(unsigned id, unsigned size)
+{
+ return smem_find(id, size);
+}
+
+static void *_smem_find(unsigned id, unsigned *size)
+{
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ struct smem_heap_entry *toc = shared->heap_toc;
+
+ if (id >= SMEM_NUM_ITEMS)
+ return 0;
+
+ if (toc[id].allocated) {
+ *size = toc[id].size;
+ return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset);
+ }
+
+ return 0;
+}
+
+void *smem_find(unsigned id, unsigned size_in)
+{
+ unsigned size;
+ void *ptr;
+
+ ptr = _smem_find(id, &size);
+ if (!ptr)
+ return 0;
+
+ size_in = ALIGN(size_in, 8);
+ if (size_in != size) {
+ pr_err("smem_find(%d, %d): wrong size %d\n",
+ id, size_in, size);
+ return 0;
+ }
+
+ return ptr;
+}
+
+static irqreturn_t smsm_irq_handler(int irq, void *data)
+{
+ unsigned long flags;
+ struct smsm_shared *smsm;
+
+ spin_lock_irqsave(&smem_lock, flags);
+ smsm = smem_alloc(ID_SHARED_STATE,
+ 2 * sizeof(struct smsm_shared));
+
+ if (smsm == 0) {
+ pr_info("<SM NO STATE>\n");
+ } else {
+ unsigned apps = smsm[0].state;
+ unsigned modm = smsm[1].state;
+
+ if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
+ pr_info("<SM %08x %08x>\n", apps, modm);
+ if (modm & SMSM_RESET) {
+ handle_modem_crash();
+ } else {
+ apps |= SMSM_INIT;
+ if (modm & SMSM_SMDINIT)
+ apps |= SMSM_SMDINIT;
+ if (modm & SMSM_RPCINIT)
+ apps |= SMSM_RPCINIT;
+ }
+
+ if (smsm[0].state != apps) {
+ if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
+ pr_info("<SM %08x NOTIFY>\n", apps);
+ smsm[0].state = apps;
+ do_smd_probe();
+ notify_other_smsm();
+ }
+ }
+ spin_unlock_irqrestore(&smem_lock, flags);
+ return IRQ_HANDLED;
+}
+
+int smsm_change_state(uint32_t clear_mask, uint32_t set_mask)
+{
+ unsigned long flags;
+ struct smsm_shared *smsm;
+
+ spin_lock_irqsave(&smem_lock, flags);
+
+ smsm = smem_alloc(ID_SHARED_STATE,
+ 2 * sizeof(struct smsm_shared));
+
+ if (smsm) {
+ if (smsm[1].state & SMSM_RESET)
+ handle_modem_crash();
+ smsm[0].state = (smsm[0].state & ~clear_mask) | set_mask;
+ if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
+ pr_info("smsm_change_state %x\n",
+ smsm[0].state);
+ notify_other_smsm();
+ }
+
+ spin_unlock_irqrestore(&smem_lock, flags);
+
+ if (smsm == NULL) {
+ pr_err("smsm_change_state <SM NO STATE>\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+uint32_t smsm_get_state(void)
+{
+ unsigned long flags;
+ struct smsm_shared *smsm;
+ uint32_t rv;
+
+ spin_lock_irqsave(&smem_lock, flags);
+
+ smsm = smem_alloc(ID_SHARED_STATE,
+ 2 * sizeof(struct smsm_shared));
+
+ if (smsm)
+ rv = smsm[1].state;
+ else
+ rv = 0;
+
+ if (rv & SMSM_RESET)
+ handle_modem_crash();
+
+ spin_unlock_irqrestore(&smem_lock, flags);
+
+ if (smsm == NULL)
+ pr_err("smsm_get_state <SM NO STATE>\n");
+ return rv;
+}
+
+int smsm_set_sleep_duration(uint32_t delay)
+{
+ uint32_t *ptr;
+
+ ptr = smem_alloc(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr));
+ if (ptr == NULL) {
+ pr_err("smsm_set_sleep_duration <SM NO SLEEP_DELAY>\n");
+ return -EIO;
+ }
+ if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
+ pr_info("smsm_set_sleep_duration %d -> %d\n",
+ *ptr, delay);
+ *ptr = delay;
+ return 0;
+}
+
+int smsm_set_interrupt_info(struct smsm_interrupt_info *info)
+{
+ struct smsm_interrupt_info *ptr;
+
+ ptr = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*ptr));
+ if (ptr == NULL) {
+ pr_err("smsm_set_sleep_duration <SM NO INT_INFO>\n");
+ return -EIO;
+ }
+ if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
+ pr_info("smsm_set_interrupt_info %x %x -> %x %x\n",
+ ptr->aArm_en_mask, ptr->aArm_interrupts_pending,
+ info->aArm_en_mask, info->aArm_interrupts_pending);
+ *ptr = *info;
+ return 0;
+}
+
+#define MAX_NUM_SLEEP_CLIENTS 64
+#define MAX_SLEEP_NAME_LEN 8
+
+#define NUM_GPIO_INT_REGISTERS 6
+#define GPIO_SMEM_NUM_GROUPS 2
+#define GPIO_SMEM_MAX_PC_INTERRUPTS 8
+
+struct tramp_gpio_save {
+ unsigned int enable;
+ unsigned int detect;
+ unsigned int polarity;
+};
+
+struct tramp_gpio_smem {
+ uint16_t num_fired[GPIO_SMEM_NUM_GROUPS];
+ uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS];
+ uint32_t enabled[NUM_GPIO_INT_REGISTERS];
+ uint32_t detection[NUM_GPIO_INT_REGISTERS];
+ uint32_t polarity[NUM_GPIO_INT_REGISTERS];
+};
+
+
+void smsm_print_sleep_info(void)
+{
+ unsigned long flags;
+ uint32_t *ptr;
+ struct tramp_gpio_smem *gpio;
+ struct smsm_interrupt_info *int_info;
+
+
+ spin_lock_irqsave(&smem_lock, flags);
+
+ ptr = smem_alloc(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr));
+ if (ptr)
+ pr_info("SMEM_SMSM_SLEEP_DELAY: %x\n", *ptr);
+
+ ptr = smem_alloc(SMEM_SMSM_LIMIT_SLEEP, sizeof(*ptr));
+ if (ptr)
+ pr_info("SMEM_SMSM_LIMIT_SLEEP: %x\n", *ptr);
+
+ ptr = smem_alloc(SMEM_SLEEP_POWER_COLLAPSE_DISABLED, sizeof(*ptr));
+ if (ptr)
+ pr_info("SMEM_SLEEP_POWER_COLLAPSE_DISABLED: %x\n", *ptr);
+
+ int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info));
+ if (int_info)
+ pr_info("SMEM_SMSM_INT_INFO %x %x %x\n",
+ int_info->aArm_en_mask,
+ int_info->aArm_interrupts_pending,
+ int_info->aArm_wakeup_reason);
+
+ gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*gpio));
+ if (gpio) {
+ int i;
+ for (i = 0; i < NUM_GPIO_INT_REGISTERS; i++)
+ pr_info("SMEM_GPIO_INT: %d: e %x d %x p %x\n",
+ i, gpio->enabled[i], gpio->detection[i],
+ gpio->polarity[i]);
+
+ for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++)
+ pr_info("SMEM_GPIO_INT: %d: f %d: %d %d...\n",
+ i, gpio->num_fired[i], gpio->fired[i][0],
+ gpio->fired[i][1]);
+ }
+
+ spin_unlock_irqrestore(&smem_lock, flags);
+}
+
+int smd_core_init(void)
+{
+ int r;
+ pr_info("smd_core_init()\n");
+
+ r = request_irq(INT_A9_M2A_0, smd_irq_handler,
+ IRQF_TRIGGER_RISING, "smd_dev", 0);
+ if (r < 0)
+ return r;
+ r = enable_irq_wake(INT_A9_M2A_0);
+ if (r < 0)
+ pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_0\n");
+
+ r = request_irq(INT_A9_M2A_5, smsm_irq_handler,
+ IRQF_TRIGGER_RISING, "smsm_dev", 0);
+ if (r < 0) {
+ free_irq(INT_A9_M2A_0, 0);
+ return r;
+ }
+ r = enable_irq_wake(INT_A9_M2A_5);
+ if (r < 0)
+ pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_5\n");
+
+ /* we may have missed a signal while booting -- fake
+ * an interrupt to make sure we process any existing
+ * state
+ */
+ smsm_irq_handler(0, 0);
+
+ pr_info("smd_core_init() done\n");
+
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int dump_ch(char *buf, int max, int n,
+ struct smd_half_channel *s,
+ struct smd_half_channel *r)
+{
+ return scnprintf(
+ buf, max,
+ "ch%02d:"
+ " %8s(%04d/%04d) %c%c%c%c%c%c%c <->"
+ " %8s(%04d/%04d) %c%c%c%c%c%c%c\n", n,
+ chstate(s->state), s->tail, s->head,
+ s->fDSR ? 'D' : 'd',
+ s->fCTS ? 'C' : 'c',
+ s->fCD ? 'C' : 'c',
+ s->fRI ? 'I' : 'i',
+ s->fHEAD ? 'W' : 'w',
+ s->fTAIL ? 'R' : 'r',
+ s->fSTATE ? 'S' : 's',
+ chstate(r->state), r->tail, r->head,
+ r->fDSR ? 'D' : 'd',
+ r->fCTS ? 'R' : 'r',
+ r->fCD ? 'C' : 'c',
+ r->fRI ? 'I' : 'i',
+ r->fHEAD ? 'W' : 'w',
+ r->fTAIL ? 'R' : 'r',
+ r->fSTATE ? 'S' : 's'
+ );
+}
+
+static int debug_read_stat(char *buf, int max)
+{
+ struct smsm_shared *smsm;
+ char *msg;
+ int i = 0;
+
+ smsm = smem_find(ID_SHARED_STATE,
+ 2 * sizeof(struct smsm_shared));
+
+ msg = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG);
+
+ if (smsm) {
+ if (smsm[1].state & SMSM_RESET)
+ i += scnprintf(buf + i, max - i,
+ "smsm: ARM9 HAS CRASHED\n");
+ i += scnprintf(buf + i, max - i, "smsm: a9: %08x a11: %08x\n",
+ smsm[0].state, smsm[1].state);
+ } else {
+ i += scnprintf(buf + i, max - i, "smsm: cannot find\n");
+ }
+ if (msg) {
+ msg[SZ_DIAG_ERR_MSG - 1] = 0;
+ i += scnprintf(buf + i, max - i, "diag: '%s'\n", msg);
+ }
+ return i;
+}
+
+static int debug_read_mem(char *buf, int max)
+{
+ unsigned n;
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ struct smem_heap_entry *toc = shared->heap_toc;
+ int i = 0;
+
+ i += scnprintf(buf + i, max - i,
+ "heap: init=%d free=%d remain=%d\n",
+ shared->heap_info.initialized,
+ shared->heap_info.free_offset,
+ shared->heap_info.heap_remaining);
+
+ for (n = 0; n < SMEM_NUM_ITEMS; n++) {
+ if (toc[n].allocated == 0)
+ continue;
+ i += scnprintf(buf + i, max - i,
+ "%04d: offsed %08x size %08x\n",
+ n, toc[n].offset, toc[n].size);
+ }
+ return i;
+}
+
+static int debug_read_ch(char *buf, int max)
+{
+ struct smd_shared *shared;
+ int n, i = 0;
+
+ for (n = 0; n < SMD_CHANNELS; n++) {
+ shared = smem_find(ID_SMD_CHANNELS + n,
+ sizeof(struct smd_shared));
+ if (shared == 0)
+ continue;
+ i += dump_ch(buf + i, max - i, n, &shared->ch0, &shared->ch1);
+ }
+
+ return i;
+}
+
+static int debug_read_version(char *buf, int max)
+{
+ struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
+ unsigned version = shared->version[VERSION_MODEM];
+ return sprintf(buf, "%d.%d\n", version >> 16, version & 0xffff);
+}
+
+static int debug_read_build_id(char *buf, int max)
+{
+ unsigned size;
+ void *data;
+
+ data = _smem_find(SMEM_HW_SW_BUILD_ID, &size);
+ if (!data)
+ return 0;
+
+ if (size >= max)
+ size = max;
+ memcpy(buf, data, size);
+
+ return size;
+}
+
+static int debug_read_alloc_tbl(char *buf, int max)
+{
+ struct smd_alloc_elm *shared;
+ int n, i = 0;
+
+ shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
+
+ for (n = 0; n < 64; n++) {
+ if (shared[n].ref_count == 0)
+ continue;
+ i += scnprintf(buf + i, max - i,
+ "%03d: %20s cid=%02d ctype=%d ref_count=%d\n",
+ n, shared[n].name, shared[n].cid,
+ shared[n].ctype, shared[n].ref_count);
+ }
+
+ return i;
+}
+
+static int debug_boom(char *buf, int max)
+{
+ unsigned ms = 5000;
+ msm_proc_comm(PCOM_RESET_MODEM, &ms, 0);
+ return 0;
+}
+
+#define DEBUG_BUFMAX 4096
+static char debug_buffer[DEBUG_BUFMAX];
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int (*fill)(char *buf, int max) = file->private_data;
+ int bsize = fill(debug_buffer, DEBUG_BUFMAX);
+ return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations debug_ops = {
+ .read = debug_read,
+ .open = debug_open,
+};
+
+static void debug_create(const char *name, mode_t mode,
+ struct dentry *dent,
+ int (*fill)(char *buf, int max))
+{
+ debugfs_create_file(name, mode, dent, fill, &debug_ops);
+}
+
+static void smd_debugfs_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("smd", 0);
+ if (IS_ERR(dent))
+ return;
+
+ debug_create("ch", 0444, dent, debug_read_ch);
+ debug_create("stat", 0444, dent, debug_read_stat);
+ debug_create("mem", 0444, dent, debug_read_mem);
+ debug_create("version", 0444, dent, debug_read_version);
+ debug_create("tbl", 0444, dent, debug_read_alloc_tbl);
+ debug_create("build", 0444, dent, debug_read_build_id);
+ debug_create("boom", 0444, dent, debug_boom);
+}
+#else
+static void smd_debugfs_init(void) {}
+#endif
+
+static int __init msm_smd_probe(struct platform_device *pdev)
+{
+ pr_info("smd_init()\n");
+
+ INIT_WORK(&probe_work, smd_channel_probe_worker);
+
+ if (smd_core_init()) {
+ pr_err("smd_core_init() failed\n");
+ return -1;
+ }
+
+ do_smd_probe();
+
+ msm_check_for_modem_crash = check_for_modem_crash;
+
+ smd_debugfs_init();
+ smd_initialized = 1;
+
+ return 0;
+}
+
+static struct platform_driver msm_smd_driver = {
+ .probe = msm_smd_probe,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_smd_init(void)
+{
+ return platform_driver_register(&msm_smd_driver);
+}
+
+module_init(msm_smd_init);
+
+MODULE_DESCRIPTION("MSM Shared Memory Core");
+MODULE_AUTHOR("Brian Swetland <swetland@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/dream/smd/smd_private.h b/drivers/staging/dream/smd/smd_private.h
new file mode 100644
index 00000000000..c0eb3de1be5
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_private.h
@@ -0,0 +1,171 @@
+/* arch/arm/mach-msm/smd_private.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007 QUALCOMM Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_
+
+struct smem_heap_info
+{
+ unsigned initialized;
+ unsigned free_offset;
+ unsigned heap_remaining;
+ unsigned reserved;
+};
+
+struct smem_heap_entry
+{
+ unsigned allocated;
+ unsigned offset;
+ unsigned size;
+ unsigned reserved;
+};
+
+struct smem_proc_comm
+{
+ unsigned command;
+ unsigned status;
+ unsigned data1;
+ unsigned data2;
+};
+
+#define PC_APPS 0
+#define PC_MODEM 1
+
+#define VERSION_QDSP6 4
+#define VERSION_APPS_SBL 6
+#define VERSION_MODEM_SBL 7
+#define VERSION_APPS 8
+#define VERSION_MODEM 9
+
+struct smem_shared
+{
+ struct smem_proc_comm proc_comm[4];
+ unsigned version[32];
+ struct smem_heap_info heap_info;
+ struct smem_heap_entry heap_toc[128];
+};
+
+struct smsm_shared
+{
+ unsigned host;
+ unsigned state;
+};
+
+struct smsm_interrupt_info
+{
+ uint32_t aArm_en_mask;
+ uint32_t aArm_interrupts_pending;
+ uint32_t aArm_wakeup_reason;
+};
+
+#define SZ_DIAG_ERR_MSG 0xC8
+#define ID_DIAG_ERR_MSG SMEM_DIAG_ERR_MESSAGE
+#define ID_SMD_CHANNELS SMEM_SMD_BASE_ID
+#define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE
+#define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL
+
+#define SMSM_INIT 0x000001
+#define SMSM_SMDINIT 0x000008
+#define SMSM_RPCINIT 0x000020
+#define SMSM_RESET 0x000040
+#define SMSM_RSA 0x0080
+#define SMSM_RUN 0x000100
+#define SMSM_PWRC 0x0200
+#define SMSM_TIMEWAIT 0x0400
+#define SMSM_TIMEINIT 0x0800
+#define SMSM_PWRC_EARLY_EXIT 0x1000
+#define SMSM_WFPI 0x2000
+#define SMSM_SLEEP 0x4000
+#define SMSM_SLEEPEXIT 0x8000
+#define SMSM_OEMSBL_RELEASE 0x10000
+#define SMSM_PWRC_SUSPEND 0x200000
+
+#define SMSM_WKUP_REASON_RPC 0x00000001
+#define SMSM_WKUP_REASON_INT 0x00000002
+#define SMSM_WKUP_REASON_GPIO 0x00000004
+#define SMSM_WKUP_REASON_TIMER 0x00000008
+#define SMSM_WKUP_REASON_ALARM 0x00000010
+#define SMSM_WKUP_REASON_RESET 0x00000020
+
+void *smem_alloc(unsigned id, unsigned size);
+int smsm_change_state(uint32_t clear_mask, uint32_t set_mask);
+uint32_t smsm_get_state(void);
+int smsm_set_sleep_duration(uint32_t delay);
+int smsm_set_interrupt_info(struct smsm_interrupt_info *info);
+void smsm_print_sleep_info(void);
+
+#define SMEM_NUM_SMD_CHANNELS 64
+
+typedef enum
+{
+ /* fixed items */
+ SMEM_PROC_COMM = 0,
+ SMEM_HEAP_INFO,
+ SMEM_ALLOCATION_TABLE,
+ SMEM_VERSION_INFO,
+ SMEM_HW_RESET_DETECT,
+ SMEM_AARM_WARM_BOOT,
+ SMEM_DIAG_ERR_MESSAGE,
+ SMEM_SPINLOCK_ARRAY,
+ SMEM_MEMORY_BARRIER_LOCATION,
+
+ /* dynamic items */
+ SMEM_AARM_PARTITION_TABLE,
+ SMEM_AARM_BAD_BLOCK_TABLE,
+ SMEM_RESERVE_BAD_BLOCKS,
+ SMEM_WM_UUID,
+ SMEM_CHANNEL_ALLOC_TBL,
+ SMEM_SMD_BASE_ID,
+ SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_CHANNELS,
+ SMEM_SMEM_LOG_EVENTS,
+ SMEM_SMEM_STATIC_LOG_IDX,
+ SMEM_SMEM_STATIC_LOG_EVENTS,
+ SMEM_SMEM_SLOW_CLOCK_SYNC,
+ SMEM_SMEM_SLOW_CLOCK_VALUE,
+ SMEM_BIO_LED_BUF,
+ SMEM_SMSM_SHARED_STATE,
+ SMEM_SMSM_INT_INFO,
+ SMEM_SMSM_SLEEP_DELAY,
+ SMEM_SMSM_LIMIT_SLEEP,
+ SMEM_SLEEP_POWER_COLLAPSE_DISABLED,
+ SMEM_KEYPAD_KEYS_PRESSED,
+ SMEM_KEYPAD_STATE_UPDATED,
+ SMEM_KEYPAD_STATE_IDX,
+ SMEM_GPIO_INT,
+ SMEM_MDDI_LCD_IDX,
+ SMEM_MDDI_HOST_DRIVER_STATE,
+ SMEM_MDDI_LCD_DISP_STATE,
+ SMEM_LCD_CUR_PANEL,
+ SMEM_MARM_BOOT_SEGMENT_INFO,
+ SMEM_AARM_BOOT_SEGMENT_INFO,
+ SMEM_SLEEP_STATIC,
+ SMEM_SCORPION_FREQUENCY,
+ SMEM_SMD_PROFILES,
+ SMEM_TSSC_BUSY,
+ SMEM_HS_SUSPEND_FILTER_INFO,
+ SMEM_BATT_INFO,
+ SMEM_APPS_BOOT_MODE,
+ SMEM_VERSION_FIRST,
+ SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24,
+ SMEM_OSS_RRCASN1_BUF1,
+ SMEM_OSS_RRCASN1_BUF2,
+ SMEM_ID_VENDOR0,
+ SMEM_ID_VENDOR1,
+ SMEM_ID_VENDOR2,
+ SMEM_HW_SW_BUILD_ID,
+ SMEM_NUM_ITEMS,
+} smem_mem_type;
+
+#endif
diff --git a/drivers/staging/dream/smd/smd_qmi.c b/drivers/staging/dream/smd/smd_qmi.c
new file mode 100644
index 00000000000..d4e7d880462
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_qmi.c
@@ -0,0 +1,860 @@
+/* arch/arm/mach-msm/smd_qmi.c
+ *
+ * QMI Control Driver -- Manages network data connections.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#include <asm/uaccess.h>
+#include <mach/msm_smd.h>
+
+#define QMI_CTL 0x00
+#define QMI_WDS 0x01
+#define QMI_DMS 0x02
+#define QMI_NAS 0x03
+
+#define QMI_RESULT_SUCCESS 0x0000
+#define QMI_RESULT_FAILURE 0x0001
+
+struct qmi_msg {
+ unsigned char service;
+ unsigned char client_id;
+ unsigned short txn_id;
+ unsigned short type;
+ unsigned short size;
+ unsigned char *tlv;
+};
+
+#define qmi_ctl_client_id 0
+
+#define STATE_OFFLINE 0
+#define STATE_QUERYING 1
+#define STATE_ONLINE 2
+
+struct qmi_ctxt {
+ struct miscdevice misc;
+
+ struct mutex lock;
+
+ unsigned char ctl_txn_id;
+ unsigned char wds_client_id;
+ unsigned short wds_txn_id;
+
+ unsigned wds_busy;
+ unsigned wds_handle;
+ unsigned state_dirty;
+ unsigned state;
+
+ unsigned char addr[4];
+ unsigned char mask[4];
+ unsigned char gateway[4];
+ unsigned char dns1[4];
+ unsigned char dns2[4];
+
+ smd_channel_t *ch;
+ const char *ch_name;
+ struct wake_lock wake_lock;
+
+ struct work_struct open_work;
+ struct work_struct read_work;
+};
+
+static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n);
+
+static void qmi_read_work(struct work_struct *ws);
+static void qmi_open_work(struct work_struct *work);
+
+void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n)
+{
+ mutex_init(&ctxt->lock);
+ INIT_WORK(&ctxt->read_work, qmi_read_work);
+ INIT_WORK(&ctxt->open_work, qmi_open_work);
+ wake_lock_init(&ctxt->wake_lock, WAKE_LOCK_SUSPEND, ctxt->misc.name);
+ ctxt->ctl_txn_id = 1;
+ ctxt->wds_txn_id = 1;
+ ctxt->wds_busy = 1;
+ ctxt->state = STATE_OFFLINE;
+
+}
+
+static struct workqueue_struct *qmi_wq;
+
+static int verbose = 0;
+
+/* anyone waiting for a state change waits here */
+static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue);
+
+
+static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix)
+{
+ unsigned sz, n;
+ unsigned char *x;
+
+ if (!verbose)
+ return;
+
+ printk(KERN_INFO
+ "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n",
+ prefix, msg->service, msg->client_id,
+ msg->txn_id, msg->type, msg->size);
+
+ x = msg->tlv;
+ sz = msg->size;
+
+ while (sz >= 3) {
+ sz -= 3;
+
+ n = x[1] | (x[2] << 8);
+ if (n > sz)
+ break;
+
+ printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ",
+ prefix, x[0], n);
+ x += 3;
+ sz -= n;
+ while (n-- > 0)
+ printk("%02x ", *x++);
+ printk("}\n");
+ }
+}
+
+int qmi_add_tlv(struct qmi_msg *msg,
+ unsigned type, unsigned size, const void *data)
+{
+ unsigned char *x = msg->tlv + msg->size;
+
+ x[0] = type;
+ x[1] = size;
+ x[2] = size >> 8;
+
+ memcpy(x + 3, data, size);
+
+ msg->size += (size + 3);
+
+ return 0;
+}
+
+/* Extract a tagged item from a qmi message buffer,
+** taking care not to overrun the buffer.
+*/
+static int qmi_get_tlv(struct qmi_msg *msg,
+ unsigned type, unsigned size, void *data)
+{
+ unsigned char *x = msg->tlv;
+ unsigned len = msg->size;
+ unsigned n;
+
+ while (len >= 3) {
+ len -= 3;
+
+ /* size of this item */
+ n = x[1] | (x[2] << 8);
+ if (n > len)
+ break;
+
+ if (x[0] == type) {
+ if (n != size)
+ return -1;
+ memcpy(data, x + 3, size);
+ return 0;
+ }
+
+ x += (n + 3);
+ len -= n;
+ }
+
+ return -1;
+}
+
+static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error)
+{
+ unsigned short status[2];
+ if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) {
+ *error = 0;
+ return QMI_RESULT_FAILURE;
+ } else {
+ *error = status[1];
+ return status[0];
+ }
+}
+
+/* 0x01 <qmux-header> <payload> */
+#define QMUX_HEADER 13
+
+/* should be >= HEADER + FOOTER */
+#define QMUX_OVERHEAD 16
+
+static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned char *data;
+ unsigned hlen;
+ unsigned len;
+ int r;
+
+ qmi_dump_msg(msg, "send");
+
+ if (msg->service == QMI_CTL) {
+ hlen = QMUX_HEADER - 1;
+ } else {
+ hlen = QMUX_HEADER;
+ }
+
+ /* QMUX length is total header + total payload - IFC selector */
+ len = hlen + msg->size - 1;
+ if (len > 0xffff)
+ return -1;
+
+ data = msg->tlv - hlen;
+
+ /* prepend encap and qmux header */
+ *data++ = 0x01; /* ifc selector */
+
+ /* qmux header */
+ *data++ = len;
+ *data++ = len >> 8;
+ *data++ = 0x00; /* flags: client */
+ *data++ = msg->service;
+ *data++ = msg->client_id;
+
+ /* qmi header */
+ *data++ = 0x00; /* flags: send */
+ *data++ = msg->txn_id;
+ if (msg->service != QMI_CTL)
+ *data++ = msg->txn_id >> 8;
+
+ *data++ = msg->type;
+ *data++ = msg->type >> 8;
+ *data++ = msg->size;
+ *data++ = msg->size >> 8;
+
+ /* len + 1 takes the interface selector into account */
+ r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1);
+
+ if (r != len) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned err;
+ if (msg->type == 0x0022) {
+ unsigned char n[2];
+ if (qmi_get_status(msg, &err))
+ return;
+ if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
+ return;
+ if (n[0] == QMI_WDS) {
+ printk(KERN_INFO
+ "qmi: ctl: wds use client_id 0x%02x\n", n[1]);
+ ctxt->wds_client_id = n[1];
+ ctxt->wds_busy = 0;
+ }
+ }
+}
+
+static int qmi_network_get_profile(struct qmi_ctxt *ctxt);
+
+static void swapaddr(unsigned char *src, unsigned char *dst)
+{
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+}
+
+static unsigned char zero[4];
+static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
+{
+ unsigned char tmp[4];
+ unsigned r;
+
+ r = qmi_get_tlv(msg, 0x1e, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->addr);
+ r = qmi_get_tlv(msg, 0x21, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->mask);
+ r = qmi_get_tlv(msg, 0x20, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->gateway);
+ r = qmi_get_tlv(msg, 0x15, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->dns1);
+ r = qmi_get_tlv(msg, 0x16, 4, tmp);
+ swapaddr(r ? zero : tmp, ctxt->dns2);
+}
+
+static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ unsigned err;
+ switch (msg->type) {
+ case 0x0021:
+ if (qmi_get_status(msg, &err)) {
+ printk(KERN_ERR
+ "qmi: wds: network stop failed (%04x)\n", err);
+ } else {
+ printk(KERN_INFO
+ "qmi: wds: network stopped\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ }
+ break;
+ case 0x0020:
+ if (qmi_get_status(msg, &err)) {
+ printk(KERN_ERR
+ "qmi: wds: network start failed (%04x)\n", err);
+ } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) {
+ printk(KERN_INFO
+ "qmi: wds no handle?\n");
+ } else {
+ printk(KERN_INFO
+ "qmi: wds: got handle 0x%08x\n",
+ ctxt->wds_handle);
+ }
+ break;
+ case 0x002D:
+ printk("qmi: got network profile\n");
+ if (ctxt->state == STATE_QUERYING) {
+ qmi_read_runtime_profile(ctxt, msg);
+ ctxt->state = STATE_ONLINE;
+ ctxt->state_dirty = 1;
+ }
+ break;
+ default:
+ printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type);
+ }
+ ctxt->wds_busy = 0;
+}
+
+static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ if (msg->type == 0x0022) {
+ unsigned char n[2];
+ if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
+ return;
+ switch (n[0]) {
+ case 1:
+ printk(KERN_INFO "qmi: wds: DISCONNECTED\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ break;
+ case 2:
+ printk(KERN_INFO "qmi: wds: CONNECTED\n");
+ ctxt->state = STATE_QUERYING;
+ ctxt->state_dirty = 1;
+ qmi_network_get_profile(ctxt);
+ break;
+ case 3:
+ printk(KERN_INFO "qmi: wds: SUSPENDED\n");
+ ctxt->state = STATE_OFFLINE;
+ ctxt->state_dirty = 1;
+ }
+ } else {
+ printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type);
+ }
+}
+
+static void qmi_process_wds_msg(struct qmi_ctxt *ctxt,
+ struct qmi_msg *msg)
+{
+ printk("wds: %04x @ %02x\n", msg->type, msg->client_id);
+ if (msg->client_id == ctxt->wds_client_id) {
+ qmi_process_unicast_wds_msg(ctxt, msg);
+ } else if (msg->client_id == 0xff) {
+ qmi_process_broadcast_wds_msg(ctxt, msg);
+ } else {
+ printk(KERN_ERR
+ "qmi_process_wds_msg client id 0x%02x unknown\n",
+ msg->client_id);
+ }
+}
+
+static void qmi_process_qmux(struct qmi_ctxt *ctxt,
+ unsigned char *buf, unsigned sz)
+{
+ struct qmi_msg msg;
+
+ /* require a full header */
+ if (sz < 5)
+ return;
+
+ /* require a size that matches the buffer size */
+ if (sz != (buf[0] | (buf[1] << 8)))
+ return;
+
+ /* only messages from a service (bit7=1) are allowed */
+ if (buf[2] != 0x80)
+ return;
+
+ msg.service = buf[3];
+ msg.client_id = buf[4];
+
+ /* annoyingly, CTL messages have a shorter TID */
+ if (buf[3] == 0) {
+ if (sz < 7)
+ return;
+ msg.txn_id = buf[6];
+ buf += 7;
+ sz -= 7;
+ } else {
+ if (sz < 8)
+ return;
+ msg.txn_id = buf[6] | (buf[7] << 8);
+ buf += 8;
+ sz -= 8;
+ }
+
+ /* no type and size!? */
+ if (sz < 4)
+ return;
+ sz -= 4;
+
+ msg.type = buf[0] | (buf[1] << 8);
+ msg.size = buf[2] | (buf[3] << 8);
+ msg.tlv = buf + 4;
+
+ if (sz != msg.size)
+ return;
+
+ qmi_dump_msg(&msg, "recv");
+
+ mutex_lock(&ctxt->lock);
+ switch (msg.service) {
+ case QMI_CTL:
+ qmi_process_ctl_msg(ctxt, &msg);
+ break;
+ case QMI_WDS:
+ qmi_process_wds_msg(ctxt, &msg);
+ break;
+ default:
+ printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n",
+ msg.service);
+ break;
+ }
+ mutex_unlock(&ctxt->lock);
+
+ wake_up(&qmi_wait_queue);
+}
+
+#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD)
+
+static void qmi_read_work(struct work_struct *ws)
+{
+ struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work);
+ struct smd_channel *ch = ctxt->ch;
+ unsigned char buf[QMI_MAX_PACKET];
+ int sz;
+
+ for (;;) {
+ sz = smd_cur_packet_size(ch);
+ if (sz == 0)
+ break;
+ if (sz < smd_read_avail(ch))
+ break;
+ if (sz > QMI_MAX_PACKET) {
+ smd_read(ch, 0, sz);
+ continue;
+ }
+ if (smd_read(ch, buf, sz) != sz) {
+ printk(KERN_ERR "qmi: not enough data?!\n");
+ continue;
+ }
+
+ /* interface selector must be 1 */
+ if (buf[0] != 0x01)
+ continue;
+
+ qmi_process_qmux(ctxt, buf + 1, sz - 1);
+ }
+}
+
+static int qmi_request_wds_cid(struct qmi_ctxt *ctxt);
+
+static void qmi_open_work(struct work_struct *ws)
+{
+ struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work);
+ mutex_lock(&ctxt->lock);
+ qmi_request_wds_cid(ctxt);
+ mutex_unlock(&ctxt->lock);
+}
+
+static void qmi_notify(void *priv, unsigned event)
+{
+ struct qmi_ctxt *ctxt = priv;
+
+ switch (event) {
+ case SMD_EVENT_DATA: {
+ int sz;
+ sz = smd_cur_packet_size(ctxt->ch);
+ if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) {
+ wake_lock_timeout(&ctxt->wake_lock, HZ / 2);
+ queue_work(qmi_wq, &ctxt->read_work);
+ }
+ break;
+ }
+ case SMD_EVENT_OPEN:
+ printk(KERN_INFO "qmi: smd opened\n");
+ queue_work(qmi_wq, &ctxt->open_work);
+ break;
+ case SMD_EVENT_CLOSE:
+ printk(KERN_INFO "qmi: smd closed\n");
+ break;
+ }
+}
+
+static int qmi_request_wds_cid(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[64 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+ unsigned char n;
+
+ msg.service = QMI_CTL;
+ msg.client_id = qmi_ctl_client_id;
+ msg.txn_id = ctxt->ctl_txn_id;
+ msg.type = 0x0022;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->ctl_txn_id += 2;
+
+ n = QMI_WDS;
+ qmi_add_tlv(&msg, 0x01, 0x01, &n);
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_get_profile(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[96 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x002D;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn)
+{
+ unsigned char data[96 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+ char *auth_type;
+ char *user;
+ char *pass;
+
+ for (user = apn; *user; user++) {
+ if (*user == ' ') {
+ *user++ = 0;
+ break;
+ }
+ }
+ for (pass = user; *pass; pass++) {
+ if (*pass == ' ') {
+ *pass++ = 0;
+ break;
+ }
+ }
+
+ for (auth_type = pass; *auth_type; auth_type++) {
+ if (*auth_type == ' ') {
+ *auth_type++ = 0;
+ break;
+ }
+ }
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x0020;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ qmi_add_tlv(&msg, 0x14, strlen(apn), apn);
+ if (*auth_type)
+ qmi_add_tlv(&msg, 0x16, strlen(auth_type), auth_type);
+ if (*user) {
+ if (!*auth_type) {
+ unsigned char x;
+ x = 3;
+ qmi_add_tlv(&msg, 0x16, 1, &x);
+ }
+ qmi_add_tlv(&msg, 0x17, strlen(user), user);
+ if (*pass)
+ qmi_add_tlv(&msg, 0x18, strlen(pass), pass);
+ }
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_network_down(struct qmi_ctxt *ctxt)
+{
+ unsigned char data[16 + QMUX_OVERHEAD];
+ struct qmi_msg msg;
+
+ msg.service = QMI_WDS;
+ msg.client_id = ctxt->wds_client_id;
+ msg.txn_id = ctxt->wds_txn_id;
+ msg.type = 0x0021;
+ msg.size = 0;
+ msg.tlv = data + QMUX_HEADER;
+
+ ctxt->wds_txn_id += 2;
+
+ qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle);
+
+ return qmi_send(ctxt, &msg);
+}
+
+static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max)
+{
+ int i;
+ char *statename;
+
+ if (ctxt->state == STATE_ONLINE) {
+ statename = "up";
+ } else if (ctxt->state == STATE_OFFLINE) {
+ statename = "down";
+ } else {
+ statename = "busy";
+ }
+
+ i = scnprintf(buf, max, "STATE=%s\n", statename);
+ i += scnprintf(buf + i, max - i, "CID=%d\n",ctxt->wds_client_id);
+
+ if (ctxt->state != STATE_ONLINE){
+ return i;
+ }
+
+ i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n",
+ ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]);
+ i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n",
+ ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]);
+ i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n",
+ ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2],
+ ctxt->gateway[3]);
+ i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n",
+ ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]);
+ i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n",
+ ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]);
+
+ return i;
+}
+
+static ssize_t qmi_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct qmi_ctxt *ctxt = fp->private_data;
+ char msg[256];
+ int len;
+ int r;
+
+ mutex_lock(&ctxt->lock);
+ for (;;) {
+ if (ctxt->state_dirty) {
+ ctxt->state_dirty = 0;
+ len = qmi_print_state(ctxt, msg, 256);
+ break;
+ }
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty);
+ if (r < 0)
+ return r;
+ mutex_lock(&ctxt->lock);
+ }
+ mutex_unlock(&ctxt->lock);
+
+ if (len > count)
+ len = count;
+
+ if (copy_to_user(buf, msg, len))
+ return -EFAULT;
+
+ return len;
+}
+
+
+static ssize_t qmi_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct qmi_ctxt *ctxt = fp->private_data;
+ unsigned char cmd[64];
+ int len;
+ int r;
+
+ if (count < 1)
+ return 0;
+
+ len = count > 63 ? 63 : count;
+
+ if (copy_from_user(cmd, buf, len))
+ return -EFAULT;
+
+ cmd[len] = 0;
+
+ /* lazy */
+ if (cmd[len-1] == '\n') {
+ cmd[len-1] = 0;
+ len--;
+ }
+
+ if (!strncmp(cmd, "verbose", 7)) {
+ verbose = 1;
+ } else if (!strncmp(cmd, "terse", 5)) {
+ verbose = 0;
+ } else if (!strncmp(cmd, "poll", 4)) {
+ ctxt->state_dirty = 1;
+ wake_up(&qmi_wait_queue);
+ } else if (!strncmp(cmd, "down", 4)) {
+retry_down:
+ mutex_lock(&ctxt->lock);
+ if (ctxt->wds_busy) {
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
+ if (r < 0)
+ return r;
+ goto retry_down;
+ }
+ ctxt->wds_busy = 1;
+ qmi_network_down(ctxt);
+ mutex_unlock(&ctxt->lock);
+ } else if (!strncmp(cmd, "up:", 3)) {
+retry_up:
+ mutex_lock(&ctxt->lock);
+ if (ctxt->wds_busy) {
+ mutex_unlock(&ctxt->lock);
+ r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
+ if (r < 0)
+ return r;
+ goto retry_up;
+ }
+ ctxt->wds_busy = 1;
+ qmi_network_up(ctxt, cmd+3);
+ mutex_unlock(&ctxt->lock);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static int qmi_open(struct inode *ip, struct file *fp)
+{
+ struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev));
+ int r = 0;
+
+ if (!ctxt) {
+ printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev));
+ return -ENODEV;
+ }
+
+ fp->private_data = ctxt;
+
+ mutex_lock(&ctxt->lock);
+ if (ctxt->ch == 0)
+ r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify);
+ if (r == 0)
+ wake_up(&qmi_wait_queue);
+ mutex_unlock(&ctxt->lock);
+
+ return r;
+}
+
+static int qmi_release(struct inode *ip, struct file *fp)
+{
+ return 0;
+}
+
+static struct file_operations qmi_fops = {
+ .owner = THIS_MODULE,
+ .read = qmi_read,
+ .write = qmi_write,
+ .open = qmi_open,
+ .release = qmi_release,
+};
+
+static struct qmi_ctxt qmi_device0 = {
+ .ch_name = "SMD_DATA5_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi0",
+ .fops = &qmi_fops,
+ }
+};
+static struct qmi_ctxt qmi_device1 = {
+ .ch_name = "SMD_DATA6_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi1",
+ .fops = &qmi_fops,
+ }
+};
+static struct qmi_ctxt qmi_device2 = {
+ .ch_name = "SMD_DATA7_CNTL",
+ .misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qmi2",
+ .fops = &qmi_fops,
+ }
+};
+
+static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n)
+{
+ if (n == qmi_device0.misc.minor)
+ return &qmi_device0;
+ if (n == qmi_device1.misc.minor)
+ return &qmi_device1;
+ if (n == qmi_device2.misc.minor)
+ return &qmi_device2;
+ return 0;
+}
+
+static int __init qmi_init(void)
+{
+ int ret;
+
+ qmi_wq = create_singlethread_workqueue("qmi");
+ if (qmi_wq == 0)
+ return -ENOMEM;
+
+ qmi_ctxt_init(&qmi_device0, 0);
+ qmi_ctxt_init(&qmi_device1, 1);
+ qmi_ctxt_init(&qmi_device2, 2);
+
+ ret = misc_register(&qmi_device0.misc);
+ if (ret == 0)
+ ret = misc_register(&qmi_device1.misc);
+ if (ret == 0)
+ ret = misc_register(&qmi_device2.misc);
+ return ret;
+}
+
+module_init(qmi_init);
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.c b/drivers/staging/dream/smd/smd_rpcrouter.c
new file mode 100644
index 00000000000..d4a4a887e42
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_rpcrouter.c
@@ -0,0 +1,1274 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* TODO: handle cases where smd_write() will tempfail due to full fifo */
+/* TODO: thread priority? schedule a work to bump it? */
+/* TODO: maybe make server_list_lock a mutex */
+/* TODO: pool fragments to avoid kmalloc/kfree churn */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/wakelock.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/byteorder.h>
+
+#include <mach/msm_smd.h>
+#include "smd_rpcrouter.h"
+
+#define TRACE_R2R_MSG 0
+#define TRACE_R2R_RAW 0
+#define TRACE_RPC_MSG 0
+#define TRACE_NOTIFY_MSG 0
+
+#define MSM_RPCROUTER_DEBUG 0
+#define MSM_RPCROUTER_DEBUG_PKT 0
+#define MSM_RPCROUTER_R2R_DEBUG 0
+#define DUMP_ALL_RECEIVED_HEADERS 0
+
+#define DIAG(x...) printk("[RR] ERROR " x)
+
+#if MSM_RPCROUTER_DEBUG
+#define D(x...) printk(x)
+#else
+#define D(x...) do {} while (0)
+#endif
+
+#if TRACE_R2R_MSG
+#define RR(x...) printk("[RR] "x)
+#else
+#define RR(x...) do {} while (0)
+#endif
+
+#if TRACE_RPC_MSG
+#define IO(x...) printk("[RPC] "x)
+#else
+#define IO(x...) do {} while (0)
+#endif
+
+#if TRACE_NOTIFY_MSG
+#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x)
+#else
+#define NTFY(x...) do {} while (0)
+#endif
+
+static LIST_HEAD(local_endpoints);
+static LIST_HEAD(remote_endpoints);
+
+static LIST_HEAD(server_list);
+
+static smd_channel_t *smd_channel;
+static int initialized;
+static wait_queue_head_t newserver_wait;
+static wait_queue_head_t smd_wait;
+
+static DEFINE_SPINLOCK(local_endpoints_lock);
+static DEFINE_SPINLOCK(remote_endpoints_lock);
+static DEFINE_SPINLOCK(server_list_lock);
+static DEFINE_SPINLOCK(smd_lock);
+
+static struct workqueue_struct *rpcrouter_workqueue;
+static struct wake_lock rpcrouter_wake_lock;
+static int rpcrouter_need_len;
+
+static atomic_t next_xid = ATOMIC_INIT(1);
+static uint8_t next_pacmarkid;
+
+static void do_read_data(struct work_struct *work);
+static void do_create_pdevs(struct work_struct *work);
+static void do_create_rpcrouter_pdev(struct work_struct *work);
+
+static DECLARE_WORK(work_read_data, do_read_data);
+static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
+static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
+
+#define RR_STATE_IDLE 0
+#define RR_STATE_HEADER 1
+#define RR_STATE_BODY 2
+#define RR_STATE_ERROR 3
+
+struct rr_context {
+ struct rr_packet *pkt;
+ uint8_t *ptr;
+ uint32_t state; /* current assembly state */
+ uint32_t count; /* bytes needed in this state */
+};
+
+static struct rr_context the_rr_context;
+
+static struct platform_device rpcrouter_pdev = {
+ .name = "oncrpc_router",
+ .id = -1,
+};
+
+
+static int rpcrouter_send_control_msg(union rr_control_msg *msg)
+{
+ struct rr_header hdr;
+ unsigned long flags;
+ int need;
+
+ if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) {
+ printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, "
+ "router not initialized\n");
+ return -EINVAL;
+ }
+
+ hdr.version = RPCROUTER_VERSION;
+ hdr.type = msg->cmd;
+ hdr.src_pid = RPCROUTER_PID_LOCAL;
+ hdr.src_cid = RPCROUTER_ROUTER_ADDRESS;
+ hdr.confirm_rx = 0;
+ hdr.size = sizeof(*msg);
+ hdr.dst_pid = 0;
+ hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
+
+ /* TODO: what if channel is full? */
+
+ need = sizeof(hdr) + hdr.size;
+ spin_lock_irqsave(&smd_lock, flags);
+ while (smd_write_avail(smd_channel) < need) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+ smd_write(smd_channel, &hdr, sizeof(hdr));
+ smd_write(smd_channel, msg, hdr.size);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ return 0;
+}
+
+static struct rr_server *rpcrouter_create_server(uint32_t pid,
+ uint32_t cid,
+ uint32_t prog,
+ uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ int rc;
+
+ server = kmalloc(sizeof(struct rr_server), GFP_KERNEL);
+ if (!server)
+ return ERR_PTR(-ENOMEM);
+
+ memset(server, 0, sizeof(struct rr_server));
+ server->pid = pid;
+ server->cid = cid;
+ server->prog = prog;
+ server->vers = ver;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_add_tail(&server->list, &server_list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ if (pid == RPCROUTER_PID_REMOTE) {
+ rc = msm_rpcrouter_create_server_cdev(server);
+ if (rc < 0)
+ goto out_fail;
+ }
+ return server;
+out_fail:
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ kfree(server);
+ return ERR_PTR(rc);
+}
+
+static void rpcrouter_destroy_server(struct rr_server *server)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ kfree(server);
+}
+
+static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->prog == prog
+ && server->vers == ver) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->device_number == dev) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+
+ ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL);
+ if (!ept)
+ return NULL;
+ memset(ept, 0, sizeof(struct msm_rpc_endpoint));
+
+ /* mark no reply outstanding */
+ ept->reply_pid = 0xffffffff;
+
+ ept->cid = (uint32_t) ept;
+ ept->pid = RPCROUTER_PID_LOCAL;
+ ept->dev = dev;
+
+ if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) {
+ struct rr_server *srv;
+ /*
+ * This is a userspace client which opened
+ * a program/ver devicenode. Bind the client
+ * to that destination
+ */
+ srv = rpcrouter_lookup_server_by_dev(dev);
+ /* TODO: bug? really? */
+ BUG_ON(!srv);
+
+ ept->dst_pid = srv->pid;
+ ept->dst_cid = srv->cid;
+ ept->dst_prog = cpu_to_be32(srv->prog);
+ ept->dst_vers = cpu_to_be32(srv->vers);
+
+ D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers);
+ } else {
+ /* mark not connected */
+ ept->dst_pid = 0xffffffff;
+ D("Creating a master local ept %p\n", ept);
+ }
+
+ init_waitqueue_head(&ept->wait_q);
+ INIT_LIST_HEAD(&ept->read_q);
+ spin_lock_init(&ept->read_q_lock);
+ wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
+ INIT_LIST_HEAD(&ept->incomplete);
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_add_tail(&ept->list, &local_endpoints);
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+}
+
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept)
+{
+ int rc;
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.pid = ept->pid;
+ msg.cli.cid = ept->cid;
+
+ RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid);
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ wake_lock_destroy(&ept->read_q_wake_lock);
+ list_del(&ept->list);
+ kfree(ept);
+ return 0;
+}
+
+static int rpcrouter_create_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *new_c;
+ unsigned long flags;
+
+ new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL);
+ if (!new_c)
+ return -ENOMEM;
+ memset(new_c, 0, sizeof(struct rr_remote_endpoint));
+
+ new_c->cid = cid;
+ new_c->pid = RPCROUTER_PID_REMOTE;
+ init_waitqueue_head(&new_c->quota_wait);
+ spin_lock_init(&new_c->quota_lock);
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_add_tail(&new_c->list, &remote_endpoints);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return 0;
+}
+
+static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_for_each_entry(ept, &local_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return NULL;
+}
+
+static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_for_each_entry(ept, &remote_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return NULL;
+}
+
+static int process_control_msg(union rr_control_msg *msg, int len)
+{
+ union rr_control_msg ctl;
+ struct rr_server *server;
+ struct rr_remote_endpoint *r_ept;
+ int rc = 0;
+ unsigned long flags;
+
+ if (len != sizeof(*msg)) {
+ printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n",
+ len, sizeof(*msg));
+ return -EINVAL;
+ }
+
+ switch (msg->cmd) {
+ case RPCROUTER_CTRL_CMD_HELLO:
+ RR("o HELLO\n");
+
+ RR("x HELLO\n");
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
+ rpcrouter_send_control_msg(&ctl);
+
+ initialized = 1;
+
+ /* Send list of servers one at a time */
+ ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+
+ /* TODO: long time to hold a spinlock... */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ ctl.srv.pid = server->pid;
+ ctl.srv.cid = server->cid;
+ ctl.srv.prog = server->prog;
+ ctl.srv.vers = server->vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ server->pid, server->cid,
+ server->prog, server->vers);
+
+ rpcrouter_send_control_msg(&ctl);
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev);
+ break;
+
+ case RPCROUTER_CTRL_CMD_RESUME_TX:
+ RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (!r_ept) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to resume client\n");
+ break;
+ }
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ r_ept->tx_quota_cntr = 0;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ wake_up(&r_ept->quota_wait);
+ break;
+
+ case RPCROUTER_CTRL_CMD_NEW_SERVER:
+ RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers);
+
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+
+ if (!server) {
+ server = rpcrouter_create_server(
+ msg->srv.pid, msg->srv.cid,
+ msg->srv.prog, msg->srv.vers);
+ if (!server)
+ return -ENOMEM;
+ /*
+ * XXX: Verify that its okay to add the
+ * client to our remote client list
+ * if we get a NEW_SERVER notification
+ */
+ if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) {
+ rc = rpcrouter_create_remote_endpoint(
+ msg->srv.cid);
+ if (rc < 0)
+ printk(KERN_ERR
+ "rpcrouter:Client create"
+ "error (%d)\n", rc);
+ }
+ schedule_work(&work_create_pdevs);
+ wake_up(&newserver_wait);
+ } else {
+ if ((server->pid == msg->srv.pid) &&
+ (server->cid == msg->srv.cid)) {
+ printk(KERN_ERR "rpcrouter: Duplicate svr\n");
+ } else {
+ server->pid = msg->srv.pid;
+ server->cid = msg->srv.cid;
+ }
+ }
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
+ RR("o REMOVE_SERVER prog=%08x:%d\n",
+ msg->srv.prog, msg->srv.vers);
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+ if (server)
+ rpcrouter_destroy_server(server);
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
+ RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+ if (msg->cli.pid != RPCROUTER_PID_REMOTE) {
+ printk(KERN_ERR
+ "rpcrouter: Denying remote removal of "
+ "local client\n");
+ break;
+ }
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (r_ept) {
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_del(&r_ept->list);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ kfree(r_ept);
+ }
+
+ /* Notify local clients of this event */
+ printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n");
+ rc = -ENOSYS;
+
+ break;
+ default:
+ RR("o UNKNOWN(%08x)\n", msg->cmd);
+ rc = -ENOSYS;
+ }
+
+ return rc;
+}
+
+static void do_create_rpcrouter_pdev(struct work_struct *work)
+{
+ platform_device_register(&rpcrouter_pdev);
+}
+
+static void do_create_pdevs(struct work_struct *work)
+{
+ unsigned long flags;
+ struct rr_server *server;
+
+ /* TODO: race if destroyed while being registered */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->pid == RPCROUTER_PID_REMOTE) {
+ if (server->pdev_name[0] == 0) {
+ spin_unlock_irqrestore(&server_list_lock,
+ flags);
+ msm_rpcrouter_create_server_pdev(server);
+ schedule_work(&work_create_pdevs);
+ return;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+}
+
+static void rpcrouter_smdnotify(void *_dev, unsigned event)
+{
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ if (smd_read_avail(smd_channel) >= rpcrouter_need_len)
+ wake_lock(&rpcrouter_wake_lock);
+ wake_up(&smd_wait);
+}
+
+static void *rr_malloc(unsigned sz)
+{
+ void *ptr = kmalloc(sz, GFP_KERNEL);
+ if (ptr)
+ return ptr;
+
+ printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz);
+ do {
+ ptr = kmalloc(sz, GFP_KERNEL);
+ } while (!ptr);
+
+ return ptr;
+}
+
+/* TODO: deal with channel teardown / restore */
+static int rr_read(void *data, int len)
+{
+ int rc;
+ unsigned long flags;
+// printk("rr_read() %d\n", len);
+ for(;;) {
+ spin_lock_irqsave(&smd_lock, flags);
+ if (smd_read_avail(smd_channel) >= len) {
+ rc = smd_read(smd_channel, data, len);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ if (rc == len)
+ return 0;
+ else
+ return -EIO;
+ }
+ rpcrouter_need_len = len;
+ wake_unlock(&rpcrouter_wake_lock);
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+// printk("rr_read: waiting (%d)\n", len);
+ wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
+ }
+ return 0;
+}
+
+static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX];
+
+static void do_read_data(struct work_struct *work)
+{
+ struct rr_header hdr;
+ struct rr_packet *pkt;
+ struct rr_fragment *frag;
+ struct msm_rpc_endpoint *ept;
+ uint32_t pm, mid;
+ unsigned long flags;
+
+ if (rr_read(&hdr, sizeof(hdr)))
+ goto fail_io;
+
+#if TRACE_R2R_RAW
+ RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
+ hdr.version, hdr.type, hdr.src_pid, hdr.src_cid,
+ hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
+#endif
+
+ if (hdr.version != RPCROUTER_VERSION) {
+ DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION);
+ goto fail_data;
+ }
+ if (hdr.size > RPCROUTER_MSGSIZE_MAX) {
+ DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX);
+ goto fail_data;
+ }
+
+ if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) {
+ if (rr_read(r2r_buf, hdr.size))
+ goto fail_io;
+ process_control_msg((void*) r2r_buf, hdr.size);
+ goto done;
+ }
+
+ if (hdr.size < sizeof(pm)) {
+ DIAG("runt packet (no pacmark)\n");
+ goto fail_data;
+ }
+ if (rr_read(&pm, sizeof(pm)))
+ goto fail_io;
+
+ hdr.size -= sizeof(pm);
+
+ frag = rr_malloc(hdr.size + sizeof(*frag));
+ frag->next = NULL;
+ frag->length = hdr.size;
+ if (rr_read(frag->data, hdr.size))
+ goto fail_io;
+
+ ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid);
+ if (!ept) {
+ DIAG("no local ept for cid %08x\n", hdr.dst_cid);
+ kfree(frag);
+ goto done;
+ }
+
+ /* See if there is already a partial packet that matches our mid
+ * and if so, append this fragment to that packet.
+ */
+ mid = PACMARK_MID(pm);
+ list_for_each_entry(pkt, &ept->incomplete, list) {
+ if (pkt->mid == mid) {
+ pkt->last->next = frag;
+ pkt->last = frag;
+ pkt->length += frag->length;
+ if (PACMARK_LAST(pm)) {
+ list_del(&pkt->list);
+ goto packet_complete;
+ }
+ goto done;
+ }
+ }
+ /* This mid is new -- create a packet for it, and put it on
+ * the incomplete list if this fragment is not a last fragment,
+ * otherwise put it on the read queue.
+ */
+ pkt = rr_malloc(sizeof(struct rr_packet));
+ pkt->first = frag;
+ pkt->last = frag;
+ memcpy(&pkt->hdr, &hdr, sizeof(hdr));
+ pkt->mid = mid;
+ pkt->length = frag->length;
+ if (!PACMARK_LAST(pm)) {
+ list_add_tail(&pkt->list, &ept->incomplete);
+ goto done;
+ }
+
+packet_complete:
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ wake_lock(&ept->read_q_wake_lock);
+ list_add_tail(&pkt->list, &ept->read_q);
+ wake_up(&ept->wait_q);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+done:
+
+ if (hdr.confirm_rx) {
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX;
+ msg.cli.pid = hdr.dst_pid;
+ msg.cli.cid = hdr.dst_cid;
+
+ RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid);
+ rpcrouter_send_control_msg(&msg);
+ }
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return;
+
+fail_io:
+fail_data:
+ printk(KERN_ERR "rpc_router has died\n");
+ wake_unlock(&rpcrouter_wake_lock);
+}
+
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog,
+ uint32_t vers, uint32_t proc)
+{
+ memset(hdr, 0, sizeof(struct rpc_request_hdr));
+ hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ hdr->rpc_vers = cpu_to_be32(2);
+ hdr->prog = cpu_to_be32(prog);
+ hdr->vers = cpu_to_be32(vers);
+ hdr->procedure = cpu_to_be32(proc);
+}
+
+struct msm_rpc_endpoint *msm_rpc_open(void)
+{
+ struct msm_rpc_endpoint *ept;
+
+ ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0));
+ if (ept == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ return ept;
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+EXPORT_SYMBOL(msm_rpc_close);
+
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
+{
+ struct rr_header hdr;
+ uint32_t pacmark;
+ struct rpc_request_hdr *rq = buffer;
+ struct rr_remote_endpoint *r_ept;
+ unsigned long flags;
+ int needed;
+ DEFINE_WAIT(__wait);
+
+ /* TODO: fragmentation for large outbound packets */
+ if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count)
+ return -EINVAL;
+
+ /* snoop the RPC packet and enforce permissions */
+
+ /* has to have at least the xid and type fields */
+ if (count < (sizeof(uint32_t) * 2)) {
+ printk(KERN_ERR "rr_write: rejecting runt packet\n");
+ return -EINVAL;
+ }
+
+ if (rq->type == 0) {
+ /* RPC CALL */
+ if (count < (sizeof(uint32_t) * 6)) {
+ printk(KERN_ERR
+ "rr_write: rejecting runt call packet\n");
+ return -EINVAL;
+ }
+ if (ept->dst_pid == 0xffffffff) {
+ printk(KERN_ERR "rr_write: not connected\n");
+ return -ENOTCONN;
+ }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ if ((ept->dst_prog != rq->prog) ||
+ !msm_rpc_is_compatible_version(
+ be32_to_cpu(ept->dst_vers),
+ be32_to_cpu(rq->vers))) {
+#else
+ if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) {
+#endif
+ printk(KERN_ERR
+ "rr_write: cannot write to %08x:%d "
+ "(bound to %08x:%d)\n",
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(ept->dst_prog),
+ be32_to_cpu(ept->dst_vers));
+ return -EINVAL;
+ }
+ hdr.dst_pid = ept->dst_pid;
+ hdr.dst_cid = ept->dst_cid;
+ IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n",
+ ept,
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ ept->dst_pid, ept->dst_cid, count,
+ be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure));
+ } else {
+ /* RPC REPLY */
+ /* TODO: locking */
+ if (ept->reply_pid == 0xffffffff) {
+ printk(KERN_ERR
+ "rr_write: rejecting unexpected reply\n");
+ return -EINVAL;
+ }
+ if (ept->reply_xid != rq->xid) {
+ printk(KERN_ERR
+ "rr_write: rejecting packet w/ bad xid\n");
+ return -EINVAL;
+ }
+
+ hdr.dst_pid = ept->reply_pid;
+ hdr.dst_cid = ept->reply_cid;
+
+ /* consume this reply */
+ ept->reply_pid = 0xffffffff;
+
+ IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n",
+ ept,
+ be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
+ }
+
+ r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid);
+
+ if (!r_ept) {
+ printk(KERN_ERR
+ "msm_rpc_write(): No route to ept "
+ "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
+ return -EHOSTUNREACH;
+ }
+
+ /* Create routing header */
+ hdr.type = RPCROUTER_CTRL_CMD_DATA;
+ hdr.version = RPCROUTER_VERSION;
+ hdr.src_pid = ept->pid;
+ hdr.src_cid = ept->cid;
+ hdr.confirm_rx = 0;
+ hdr.size = count + sizeof(uint32_t);
+
+ for (;;) {
+ prepare_to_wait(&r_ept->quota_wait, &__wait,
+ TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA)
+ break;
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))
+ break;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ schedule();
+ }
+ finish_wait(&r_ept->quota_wait, &__wait);
+
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ return -ERESTARTSYS;
+ }
+ r_ept->tx_quota_cntr++;
+ if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA)
+ hdr.confirm_rx = 1;
+
+ /* bump pacmark while interrupts disabled to avoid race
+ * probably should be atomic op instead
+ */
+ pacmark = PACMARK(count, ++next_pacmarkid, 0, 1);
+
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+ spin_lock_irqsave(&smd_lock, flags);
+
+ needed = sizeof(hdr) + hdr.size;
+ while (smd_write_avail(smd_channel) < needed) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+
+ /* TODO: deal with full fifo */
+ smd_write(smd_channel, &hdr, sizeof(hdr));
+ smd_write(smd_channel, &pacmark, sizeof(pacmark));
+ smd_write(smd_channel, buffer, count);
+
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+ return count;
+}
+EXPORT_SYMBOL(msm_rpc_write);
+
+/*
+ * NOTE: It is the responsibility of the caller to kfree buffer
+ */
+int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer,
+ unsigned user_len, long timeout)
+{
+ struct rr_fragment *frag, *next;
+ char *buf;
+ int rc;
+
+ rc = __msm_rpc_read(ept, &frag, user_len, timeout);
+ if (rc <= 0)
+ return rc;
+
+ /* single-fragment messages conveniently can be
+ * returned as-is (the buffer is at the front)
+ */
+ if (frag->next == 0) {
+ *buffer = (void*) frag;
+ return rc;
+ }
+
+ /* multi-fragment messages, we have to do it the
+ * hard way, which is rather disgusting right now
+ */
+ buf = rr_malloc(rc);
+ *buffer = buf;
+
+ while (frag != NULL) {
+ memcpy(buf, frag->data, frag->length);
+ next = frag->next;
+ buf += frag->length;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ long timeout)
+{
+ return msm_rpc_call_reply(ept, proc,
+ _request, request_size,
+ NULL, 0, timeout);
+}
+EXPORT_SYMBOL(msm_rpc_call);
+
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ void *_reply, int reply_size,
+ long timeout)
+{
+ struct rpc_request_hdr *req = _request;
+ struct rpc_reply_hdr *reply;
+ int rc;
+
+ if (request_size < sizeof(*req))
+ return -ETOOSMALL;
+
+ if (ept->dst_pid == 0xffffffff)
+ return -ENOTCONN;
+
+ /* We can't use msm_rpc_setup_req() here, because dst_prog and
+ * dst_vers here are already in BE.
+ */
+ memset(req, 0, sizeof(*req));
+ req->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ req->rpc_vers = cpu_to_be32(2);
+ req->prog = ept->dst_prog;
+ req->vers = ept->dst_vers;
+ req->procedure = cpu_to_be32(proc);
+
+ rc = msm_rpc_write(ept, req, request_size);
+ if (rc < 0)
+ return rc;
+
+ for (;;) {
+ rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
+ if (rc < 0)
+ return rc;
+ if (rc < (3 * sizeof(uint32_t))) {
+ rc = -EIO;
+ break;
+ }
+ /* we should not get CALL packets -- ignore them */
+ if (reply->type == 0) {
+ kfree(reply);
+ continue;
+ }
+ /* If an earlier call timed out, we could get the (no
+ * longer wanted) reply for it. Ignore replies that
+ * we don't expect.
+ */
+ if (reply->xid != req->xid) {
+ kfree(reply);
+ continue;
+ }
+ if (reply->reply_stat != 0) {
+ rc = -EPERM;
+ break;
+ }
+ if (reply->data.acc_hdr.accept_stat != 0) {
+ rc = -EINVAL;
+ break;
+ }
+ if (_reply == NULL) {
+ rc = 0;
+ break;
+ }
+ if (rc > reply_size) {
+ rc = -ENOMEM;
+ } else {
+ memcpy(_reply, reply, rc);
+ }
+ break;
+ }
+ kfree(reply);
+ return rc;
+}
+EXPORT_SYMBOL(msm_rpc_call_reply);
+
+
+static inline int ept_packet_available(struct msm_rpc_endpoint *ept)
+{
+ unsigned long flags;
+ int ret;
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ ret = !list_empty(&ept->read_q);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return ret;
+}
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag_ret,
+ unsigned len, long timeout)
+{
+ struct rr_packet *pkt;
+ struct rpc_request_hdr *rq;
+ DEFINE_WAIT(__wait);
+ unsigned long flags;
+ int rc;
+
+ IO("READ on ept %p\n", ept);
+
+ if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
+ if (timeout < 0) {
+ wait_event(ept->wait_q, ept_packet_available(ept));
+ } else {
+ rc = wait_event_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ } else {
+ if (timeout < 0) {
+ rc = wait_event_interruptible(
+ ept->wait_q, ept_packet_available(ept));
+ if (rc < 0)
+ return rc;
+ } else {
+ rc = wait_event_interruptible_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ }
+
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ if (list_empty(&ept->read_q)) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -EAGAIN;
+ }
+ pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
+ if (pkt->length > len) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -ETOOSMALL;
+ }
+ list_del(&pkt->list);
+ if (list_empty(&ept->read_q))
+ wake_unlock(&ept->read_q_wake_lock);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+ rc = pkt->length;
+
+ *frag_ret = pkt->first;
+ rq = (void*) pkt->first->data;
+ if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) {
+ IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n",
+ ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(rq->procedure),
+ be32_to_cpu(rq->xid));
+ /* RPC CALL */
+ if (ept->reply_pid != 0xffffffff) {
+ printk(KERN_WARNING
+ "rr_read: lost previous reply xid...\n");
+ }
+ /* TODO: locking? */
+ ept->reply_pid = pkt->hdr.src_pid;
+ ept->reply_cid = pkt->hdr.src_cid;
+ ept->reply_xid = rq->xid;
+ }
+#if TRACE_RPC_MSG
+ else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1))
+ IO("READ on ept %p is a REPLY\n", ept);
+ else IO("READ on ept %p (%d bytes)\n", ept, rc);
+#endif
+
+ kfree(pkt);
+ return rc;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+int msm_rpc_is_compatible_version(uint32_t server_version,
+ uint32_t client_version)
+{
+ if ((server_version & RPC_VERSION_MODE_MASK) !=
+ (client_version & RPC_VERSION_MODE_MASK))
+ return 0;
+
+ if (server_version & RPC_VERSION_MODE_MASK)
+ return server_version == client_version;
+
+ return ((server_version & RPC_VERSION_MAJOR_MASK) ==
+ (client_version & RPC_VERSION_MAJOR_MASK)) &&
+ ((server_version & RPC_VERSION_MINOR_MASK) >=
+ (client_version & RPC_VERSION_MINOR_MASK));
+}
+EXPORT_SYMBOL(msm_rpc_is_compatible_version);
+
+static int msm_rpc_get_compatible_server(uint32_t prog,
+ uint32_t ver,
+ uint32_t *found_vers)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ if (found_vers == NULL)
+ return 0;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if ((server->prog == prog) &&
+ msm_rpc_is_compatible_version(server->vers, ver)) {
+ *found_vers = server->vers;
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return -1;
+}
+#endif
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_server *server;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ if (!(vers & RPC_VERSION_MODE_MASK)) {
+ uint32_t found_vers;
+ if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0)
+ return ERR_PTR(-EHOSTUNREACH);
+ if (found_vers != vers) {
+ D("RPC using new version %08x:{%08x --> %08x}\n",
+ prog, vers, found_vers);
+ vers = found_vers;
+ }
+ }
+#endif
+
+ server = rpcrouter_lookup_server(prog, vers);
+ if (!server)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ ept = msm_rpc_open();
+ if (IS_ERR(ept))
+ return ept;
+
+ ept->flags = flags;
+ ept->dst_pid = server->pid;
+ ept->dst_cid = server->cid;
+ ept->dst_prog = cpu_to_be32(prog);
+ ept->dst_vers = cpu_to_be32(vers);
+
+ return ept;
+}
+EXPORT_SYMBOL(msm_rpc_connect);
+
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept)
+{
+ return be32_to_cpu(ept->dst_vers);
+}
+EXPORT_SYMBOL(msm_rpc_get_vers);
+
+/* TODO: permission check? */
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ int rc;
+ union rr_control_msg msg;
+ struct rr_server *server;
+
+ server = rpcrouter_create_server(ept->pid, ept->cid,
+ prog, vers);
+ if (!server)
+ return -ENODEV;
+
+ msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+ msg.srv.pid = ept->pid;
+ msg.srv.cid = ept->cid;
+ msg.srv.prog = prog;
+ msg.srv.vers = vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ ept->pid, ept->cid, prog, vers);
+
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/* TODO: permission check -- disallow unreg of somebody else's server */
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ struct rr_server *server;
+ server = rpcrouter_lookup_server(prog, vers);
+
+ if (!server)
+ return -ENOENT;
+ rpcrouter_destroy_server(server);
+ return 0;
+}
+
+static int msm_rpcrouter_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ /* Initialize what we need to start processing */
+ INIT_LIST_HEAD(&local_endpoints);
+ INIT_LIST_HEAD(&remote_endpoints);
+
+ init_waitqueue_head(&newserver_wait);
+ init_waitqueue_head(&smd_wait);
+ wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL");
+
+ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
+ if (!rpcrouter_workqueue)
+ return -ENOMEM;
+
+ rc = msm_rpcrouter_init_devices();
+ if (rc < 0)
+ goto fail_destroy_workqueue;
+
+ /* Open up SMD channel 2 */
+ initialized = 0;
+ rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
+ if (rc < 0)
+ goto fail_remove_devices;
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return 0;
+
+ fail_remove_devices:
+ msm_rpcrouter_exit_devices();
+ fail_destroy_workqueue:
+ destroy_workqueue(rpcrouter_workqueue);
+ return rc;
+}
+
+static struct platform_driver msm_smd_channel2_driver = {
+ .probe = msm_rpcrouter_probe,
+ .driver = {
+ .name = "SMD_RPCCALL",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rpcrouter_init(void)
+{
+ return platform_driver_register(&msm_smd_channel2_driver);
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.h b/drivers/staging/dream/smd/smd_rpcrouter.h
new file mode 100644
index 00000000000..a7416a2ec58
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_rpcrouter.h
@@ -0,0 +1,195 @@
+/** arch/arm/mach-msm/smd_rpcrouter.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <mach/msm_smd.h>
+#include <mach/msm_rpcrouter.h>
+
+/* definitions for the R2R wire protcol */
+
+#define RPCROUTER_VERSION 1
+#define RPCROUTER_PROCESSORS_MAX 4
+#define RPCROUTER_MSGSIZE_MAX 512
+
+#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff
+#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe
+
+#define RPCROUTER_PID_LOCAL 1
+#define RPCROUTER_PID_REMOTE 0
+
+#define RPCROUTER_CTRL_CMD_DATA 1
+#define RPCROUTER_CTRL_CMD_HELLO 2
+#define RPCROUTER_CTRL_CMD_BYE 3
+#define RPCROUTER_CTRL_CMD_NEW_SERVER 4
+#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5
+#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6
+#define RPCROUTER_CTRL_CMD_RESUME_TX 7
+#define RPCROUTER_CTRL_CMD_EXIT 8
+
+#define RPCROUTER_DEFAULT_RX_QUOTA 5
+
+union rr_control_msg {
+ uint32_t cmd;
+ struct {
+ uint32_t cmd;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t pid;
+ uint32_t cid;
+ } srv;
+ struct {
+ uint32_t cmd;
+ uint32_t pid;
+ uint32_t cid;
+ } cli;
+};
+
+struct rr_header {
+ uint32_t version;
+ uint32_t type;
+ uint32_t src_pid;
+ uint32_t src_cid;
+ uint32_t confirm_rx;
+ uint32_t size;
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+};
+
+/* internals */
+
+#define RPCROUTER_MAX_REMOTE_SERVERS 100
+
+struct rr_fragment {
+ unsigned char data[RPCROUTER_MSGSIZE_MAX];
+ uint32_t length;
+ struct rr_fragment *next;
+};
+
+struct rr_packet {
+ struct list_head list;
+ struct rr_fragment *first;
+ struct rr_fragment *last;
+ struct rr_header hdr;
+ uint32_t mid;
+ uint32_t length;
+};
+
+#define PACMARK_LAST(n) ((n) & 0x80000000)
+#define PACMARK_MID(n) (((n) >> 16) & 0xFF)
+#define PACMARK_LEN(n) ((n) & 0xFFFF)
+
+static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first,
+ uint32_t last)
+{
+ return (len & 0xFFFF) |
+ ((mid & 0xFF) << 16) |
+ ((!!first) << 30) |
+ ((!!last) << 31);
+}
+
+struct rr_server {
+ struct list_head list;
+
+ uint32_t pid;
+ uint32_t cid;
+ uint32_t prog;
+ uint32_t vers;
+
+ dev_t device_number;
+ struct cdev cdev;
+ struct device *device;
+ struct rpcsvr_platform_device p_device;
+ char pdev_name[32];
+};
+
+struct rr_remote_endpoint {
+ uint32_t pid;
+ uint32_t cid;
+
+ int tx_quota_cntr;
+ spinlock_t quota_lock;
+ wait_queue_head_t quota_wait;
+
+ struct list_head list;
+};
+
+struct msm_rpc_endpoint {
+ struct list_head list;
+
+ /* incomplete packets waiting for assembly */
+ struct list_head incomplete;
+
+ /* complete packets waiting to be read */
+ struct list_head read_q;
+ spinlock_t read_q_lock;
+ struct wake_lock read_q_wake_lock;
+ wait_queue_head_t wait_q;
+ unsigned flags;
+
+ /* endpoint address */
+ uint32_t pid;
+ uint32_t cid;
+
+ /* bound remote address
+ * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail
+ * RPC_CALLs must be to the prog/vers below or they will fail
+ */
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+ uint32_t dst_prog; /* be32 */
+ uint32_t dst_vers; /* be32 */
+
+ /* reply remote address
+ * if reply_pid == 0xffffffff, none available
+ * RPC_REPLY writes may only go to the pid/cid/xid of the
+ * last RPC_CALL we received.
+ */
+ uint32_t reply_pid;
+ uint32_t reply_cid;
+ uint32_t reply_xid; /* be32 */
+ uint32_t next_pm; /* Pacmark sequence */
+
+ /* device node if this endpoint is accessed via userspace */
+ dev_t dev;
+};
+
+/* shared between smd_rpcrouter*.c */
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag,
+ unsigned len, long timeout);
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server);
+int msm_rpcrouter_create_server_pdev(struct rr_server *server);
+
+int msm_rpcrouter_init_devices(void);
+void msm_rpcrouter_exit_devices(void);
+
+extern dev_t msm_rpcrouter_devno;
+extern struct class *msm_rpcrouter_class;
+#endif
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_device.c b/drivers/staging/dream/smd/smd_rpcrouter_device.c
new file mode 100644
index 00000000000..cd3910bcc4e
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_rpcrouter_device.c
@@ -0,0 +1,376 @@
+/* arch/arm/mach-msm/smd_rpcrouter_device.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/msm_rpcrouter.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "smd_rpcrouter.h"
+
+#define SAFETY_MEM_SIZE 65536
+
+/* Next minor # available for a remote server */
+static int next_minor = 1;
+
+struct class *msm_rpcrouter_class;
+dev_t msm_rpcrouter_devno;
+
+static struct cdev rpcrouter_cdev;
+static struct device *rpcrouter_device;
+
+static int rpcrouter_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+ struct msm_rpc_endpoint *ept;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
+ if (!ept)
+ return -ENOMEM;
+
+ filp->private_data = ept;
+ return 0;
+}
+
+static int rpcrouter_release(struct inode *inode, struct file *filp)
+{
+ struct msm_rpc_endpoint *ept;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+
+static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_fragment *frag, *next;
+ int rc;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ rc = __msm_rpc_read(ept, &frag, count, -1);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ while (frag != NULL) {
+ if (copy_to_user(buf, frag->data, frag->length)) {
+ printk(KERN_ERR
+ "rpcrouter: could not copy all read data to user!\n");
+ rc = -EFAULT;
+ }
+ buf += frag->length;
+ next = frag->next;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ int rc = 0;
+ void *k_buffer;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* A check for safety, this seems non-standard */
+ if (count > SAFETY_MEM_SIZE)
+ return -EINVAL;
+
+ k_buffer = kmalloc(count, GFP_KERNEL);
+ if (!k_buffer)
+ return -ENOMEM;
+
+ if (copy_from_user(k_buffer, buf, count)) {
+ rc = -EFAULT;
+ goto write_out_free;
+ }
+
+ rc = msm_rpc_write(ept, k_buffer, count);
+ if (rc < 0)
+ goto write_out_free;
+
+ rc = count;
+write_out_free:
+ kfree(k_buffer);
+ return rc;
+}
+
+static unsigned int rpcrouter_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned mask = 0;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* If there's data already in the read queue, return POLLIN.
+ * Else, wait for the requested amount of time, and check again.
+ */
+
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+
+ if (!mask) {
+ poll_wait(filp, &ept->wait_q, wait);
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+
+static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rpcrouter_ioctl_server_args server_args;
+ int rc = 0;
+ uint32_t n;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+ switch (cmd) {
+
+ case RPC_ROUTER_IOCTL_GET_VERSION:
+ n = RPC_ROUTER_VERSION_V1;
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MTU:
+ /* the pacmark word reduces the actual payload
+ * possible per message
+ */
+ n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_REGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+ msm_rpc_register_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+
+ msm_rpc_unregister_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
+ n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
+ rc = put_user(n, (unsigned int *)arg);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static struct file_operations rpcrouter_server_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+static struct file_operations rpcrouter_router_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server)
+{
+ int rc;
+ uint32_t dev_vers;
+
+ if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
+ printk(KERN_ERR
+ "rpcrouter: Minor numbers exhausted - Increase "
+ "RPCROUTER_MAX_REMOTE_SERVERS\n");
+ return -ENOBUFS;
+ }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ /* Servers with bit 31 set are remote msm servers with hashkey version.
+ * Servers with bit 31 not set are remote msm servers with
+ * backwards compatible version type in which case the minor number
+ * (lower 16 bits) is set to zero.
+ *
+ */
+ if ((server->vers & RPC_VERSION_MODE_MASK))
+ dev_vers = server->vers;
+ else
+ dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
+#else
+ dev_vers = server->vers;
+#endif
+
+ server->device_number =
+ MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);
+
+ server->device =
+ device_create(msm_rpcrouter_class, rpcrouter_device,
+ server->device_number, NULL, "%.8x:%.8x",
+ server->prog, dev_vers);
+ if (IS_ERR(server->device)) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to create device (%ld)\n",
+ PTR_ERR(server->device));
+ return PTR_ERR(server->device);;
+ }
+
+ cdev_init(&server->cdev, &rpcrouter_server_fops);
+ server->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&server->cdev, server->device_number, 1);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to add chrdev (%d)\n", rc);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ return rc;
+ }
+ return 0;
+}
+
+/* for backward compatible version type (31st bit cleared)
+ * clearing minor number (lower 16 bits) in device name
+ * is neccessary for driver binding
+ */
+int msm_rpcrouter_create_server_pdev(struct rr_server *server)
+{
+ sprintf(server->pdev_name, "rs%.8x:%.8x",
+ server->prog,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ (server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
+ (server->vers & RPC_VERSION_MAJOR_MASK));
+#else
+ server->vers);
+#endif
+
+ server->p_device.base.id = -1;
+ server->p_device.base.name = server->pdev_name;
+
+ server->p_device.prog = server->prog;
+ server->p_device.vers = server->vers;
+
+ platform_device_register(&server->p_device.base);
+ return 0;
+}
+
+int msm_rpcrouter_init_devices(void)
+{
+ int rc;
+ int major;
+
+ /* Create the device nodes */
+ msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
+ if (IS_ERR(msm_rpcrouter_class)) {
+ rc = -ENOMEM;
+ printk(KERN_ERR
+ "rpcrouter: failed to create oncrpc class\n");
+ goto fail;
+ }
+
+ rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1,
+ "oncrpc");
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
+ goto fail_destroy_class;
+ }
+
+ major = MAJOR(msm_rpcrouter_devno);
+ rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
+ msm_rpcrouter_devno, NULL, "%.8x:%d",
+ 0, 0);
+ if (IS_ERR(rpcrouter_device)) {
+ rc = -ENOMEM;
+ goto fail_unregister_cdev_region;
+ }
+
+ cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
+ rpcrouter_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
+ if (rc < 0)
+ goto fail_destroy_device;
+
+ return 0;
+
+fail_destroy_device:
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+fail_unregister_cdev_region:
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+fail_destroy_class:
+ class_destroy(msm_rpcrouter_class);
+fail:
+ return rc;
+}
+
+void msm_rpcrouter_exit_devices(void)
+{
+ cdev_del(&rpcrouter_cdev);
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+ class_destroy(msm_rpcrouter_class);
+}
+
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_servers.c b/drivers/staging/dream/smd/smd_rpcrouter_servers.c
new file mode 100644
index 00000000000..2597bbbc6f5
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_rpcrouter_servers.c
@@ -0,0 +1,229 @@
+/* arch/arm/mach-msm/rpc_servers.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_rpcrouter.h>
+#include <linux/uaccess.h>
+
+#include <mach/msm_rpcrouter.h>
+#include "smd_rpcrouter.h"
+
+static struct msm_rpc_endpoint *endpoint;
+
+#define FLAG_REGISTERED 0x0001
+
+static LIST_HEAD(rpc_server_list);
+static DEFINE_MUTEX(rpc_server_list_lock);
+static int rpc_servers_active;
+static struct wake_lock rpc_servers_wake_lock;
+
+static void rpc_server_register(struct msm_rpc_server *server)
+{
+ int rc;
+ rc = msm_rpc_register_server(endpoint, server->prog, server->vers);
+ if (rc < 0)
+ printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n",
+ server, server->prog, server->vers);
+}
+
+static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if ((server->prog == prog) &&
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ msm_rpc_is_compatible_version(server->vers, vers)) {
+#else
+ server->vers == vers) {
+#endif
+ mutex_unlock(&rpc_server_list_lock);
+ return server;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+ return NULL;
+}
+
+static void rpc_server_register_all(void)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if (!(server->flags & FLAG_REGISTERED)) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+}
+
+int msm_rpc_create_server(struct msm_rpc_server *server)
+{
+ /* make sure we're in a sane state first */
+ server->flags = 0;
+ INIT_LIST_HEAD(&server->list);
+
+ mutex_lock(&rpc_server_list_lock);
+ list_add(&server->list, &rpc_server_list);
+ if (rpc_servers_active) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ mutex_unlock(&rpc_server_list_lock);
+
+ return 0;
+}
+
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+ uint32_t xid, uint32_t accept_status)
+{
+ int rc = 0;
+ uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+ struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+ reply->xid = cpu_to_be32(xid);
+ reply->type = cpu_to_be32(1); /* reply */
+ reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+ reply->data.acc_hdr.verf_flavor = 0;
+ reply->data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf));
+ if (rc < 0)
+ printk(KERN_ERR
+ "%s: could not write response: %d\n",
+ __FUNCTION__, rc);
+
+ return rc;
+}
+
+static int rpc_servers_thread(void *data)
+{
+ void *buffer;
+ struct rpc_request_hdr *req;
+ struct msm_rpc_server *server;
+ int rc;
+
+ for (;;) {
+ wake_unlock(&rpc_servers_wake_lock);
+ rc = wait_event_interruptible(endpoint->wait_q,
+ !list_empty(&endpoint->read_q));
+ wake_lock(&rpc_servers_wake_lock);
+ rc = msm_rpc_read(endpoint, &buffer, -1, -1);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: could not read: %d\n",
+ __FUNCTION__, rc);
+ break;
+ }
+ req = (struct rpc_request_hdr *)buffer;
+
+ req->type = be32_to_cpu(req->type);
+ req->xid = be32_to_cpu(req->xid);
+ req->rpc_vers = be32_to_cpu(req->rpc_vers);
+ req->prog = be32_to_cpu(req->prog);
+ req->vers = be32_to_cpu(req->vers);
+ req->procedure = be32_to_cpu(req->procedure);
+
+ server = rpc_server_find(req->prog, req->vers);
+
+ if (req->rpc_vers != 2)
+ continue;
+ if (req->type != 0)
+ continue;
+ if (!server) {
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ continue;
+ }
+
+ rc = server->rpc_call(server, req, rc);
+
+ switch (rc) {
+ case 0:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ break;
+ default:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ break;
+ }
+
+ kfree(buffer);
+ }
+
+ do_exit(0);
+}
+
+static int rpcservers_probe(struct platform_device *pdev)
+{
+ struct task_struct *server_thread;
+
+ endpoint = msm_rpc_open();
+ if (IS_ERR(endpoint))
+ return PTR_ERR(endpoint);
+
+ /* we're online -- register any servers installed beforehand */
+ rpc_servers_active = 1;
+ rpc_server_register_all();
+
+ /* start the kernel thread */
+ server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd");
+ if (IS_ERR(server_thread))
+ return PTR_ERR(server_thread);
+
+ return 0;
+}
+
+static struct platform_driver rpcservers_driver = {
+ .probe = rpcservers_probe,
+ .driver = {
+ .name = "oncrpc_router",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rpc_servers_init(void)
+{
+ wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server");
+ return platform_driver_register(&rpcservers_driver);
+}
+
+module_init(rpc_servers_init);
+
+MODULE_DESCRIPTION("MSM RPC Servers");
+MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/dream/smd/smd_tty.c b/drivers/staging/dream/smd/smd_tty.c
new file mode 100644
index 00000000000..2edd9d1ec2d
--- /dev/null
+++ b/drivers/staging/dream/smd/smd_tty.c
@@ -0,0 +1,213 @@
+/* arch/arm/mach-msm/smd_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <mach/msm_smd.h>
+
+#define MAX_SMD_TTYS 32
+
+static DEFINE_MUTEX(smd_tty_lock);
+
+struct smd_tty_info {
+ smd_channel_t *ch;
+ struct tty_struct *tty;
+ struct wake_lock wake_lock;
+ int open_count;
+};
+
+static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
+
+
+static void smd_tty_notify(void *priv, unsigned event)
+{
+ unsigned char *ptr;
+ int avail;
+ struct smd_tty_info *info = priv;
+ struct tty_struct *tty = info->tty;
+
+ if (!tty)
+ return;
+
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ for (;;) {
+ if (test_bit(TTY_THROTTLED, &tty->flags)) break;
+ avail = smd_read_avail(info->ch);
+ if (avail == 0) break;
+
+ avail = tty_prepare_flip_string(tty, &ptr, avail);
+
+ if (smd_read(info->ch, ptr, avail) != avail) {
+ /* shouldn't be possible since we're in interrupt
+ ** context here and nobody else could 'steal' our
+ ** characters.
+ */
+ printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
+ }
+
+ wake_lock_timeout(&info->wake_lock, HZ / 2);
+ tty_flip_buffer_push(tty);
+ }
+
+ /* XXX only when writable and necessary */
+ tty_wakeup(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+ int res = 0;
+ int n = tty->index;
+ struct smd_tty_info *info;
+ const char *name;
+
+ if (n == 0) {
+ name = "SMD_DS";
+ } else if (n == 27) {
+ name = "SMD_GPSNMEA";
+ } else {
+ return -ENODEV;
+ }
+
+ info = smd_tty + n;
+
+ mutex_lock(&smd_tty_lock);
+ wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, name);
+ tty->driver_data = info;
+
+ if (info->open_count++ == 0) {
+ info->tty = tty;
+ if (info->ch) {
+ smd_kick(info->ch);
+ } else {
+ res = smd_open(name, &info->ch, info, smd_tty_notify);
+ }
+ }
+ mutex_unlock(&smd_tty_lock);
+
+ return res;
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = tty->driver_data;
+
+ if (info == 0)
+ return;
+
+ mutex_lock(&smd_tty_lock);
+ if (--info->open_count == 0) {
+ info->tty = 0;
+ tty->driver_data = 0;
+ wake_lock_destroy(&info->wake_lock);
+ if (info->ch) {
+ smd_close(info->ch);
+ info->ch = 0;
+ }
+ }
+ mutex_unlock(&smd_tty_lock);
+}
+
+static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ int avail;
+
+ /* if we're writing to a packet channel we will
+ ** never be able to write more data than there
+ ** is currently space for
+ */
+ avail = smd_write_avail(info->ch);
+ if (len > avail)
+ len = avail;
+
+ return smd_write(info->ch, buf, len);
+}
+
+static int smd_tty_write_room(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ return smd_write_avail(info->ch);
+}
+
+static int smd_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ return smd_read_avail(info->ch);
+}
+
+static void smd_tty_unthrottle(struct tty_struct *tty)
+{
+ struct smd_tty_info *info = tty->driver_data;
+ smd_kick(info->ch);
+}
+
+static struct tty_operations smd_tty_ops = {
+ .open = smd_tty_open,
+ .close = smd_tty_close,
+ .write = smd_tty_write,
+ .write_room = smd_tty_write_room,
+ .chars_in_buffer = smd_tty_chars_in_buffer,
+ .unthrottle = smd_tty_unthrottle,
+};
+
+static struct tty_driver *smd_tty_driver;
+
+static int __init smd_tty_init(void)
+{
+ int ret;
+
+ smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
+ if (smd_tty_driver == 0)
+ return -ENOMEM;
+
+ smd_tty_driver->owner = THIS_MODULE;
+ smd_tty_driver->driver_name = "smd_tty_driver";
+ smd_tty_driver->name = "smd";
+ smd_tty_driver->major = 0;
+ smd_tty_driver->minor_start = 0;
+ smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ smd_tty_driver->init_termios = tty_std_termios;
+ smd_tty_driver->init_termios.c_iflag = 0;
+ smd_tty_driver->init_termios.c_oflag = 0;
+ smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ smd_tty_driver->init_termios.c_lflag = 0;
+ smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(smd_tty_driver, &smd_tty_ops);
+
+ ret = tty_register_driver(smd_tty_driver);
+ if (ret) return ret;
+
+ /* this should be dynamic */
+ tty_register_device(smd_tty_driver, 0, 0);
+ tty_register_device(smd_tty_driver, 27, 0);
+
+ return 0;
+}
+
+module_init(smd_tty_init);
diff --git a/drivers/staging/dream/synaptics_i2c_rmi.c b/drivers/staging/dream/synaptics_i2c_rmi.c
new file mode 100644
index 00000000000..ae696d3bc8e
--- /dev/null
+++ b/drivers/staging/dream/synaptics_i2c_rmi.c
@@ -0,0 +1,658 @@
+/*
+ * Support for synaptics touchscreen.
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Arve Hjønnevåg <arve@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * http://www.synaptics.com/sites/default/files/511_000099_01F.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include "synaptics_i2c_rmi.h"
+
+static struct workqueue_struct *synaptics_wq;
+
+struct synaptics_ts_data {
+ u16 addr;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int use_irq;
+ struct hrtimer timer;
+ struct work_struct work;
+ u16 max[2];
+ int snap_state[2][2];
+ int snap_down_on[2];
+ int snap_down_off[2];
+ int snap_up_on[2];
+ int snap_up_off[2];
+ int snap_down[2];
+ int snap_up[2];
+ u32 flags;
+ int (*power)(int on);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static int i2c_set(struct synaptics_ts_data *ts, u8 reg, u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ts->client, reg, val);
+ if (ret < 0)
+ pr_err("i2c_smbus_write_byte_data failed (%s)\n", msg);
+ return ret;
+}
+
+static int i2c_read(struct synaptics_ts_data *ts, u8 reg, char *msg)
+{
+ int ret = i2c_smbus_read_byte_data(ts->client, reg);
+ if (ret < 0)
+ pr_err("i2c_smbus_read_byte_data failed (%s)\n", msg);
+ return ret;
+}
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h);
+static void synaptics_ts_late_resume(struct early_suspend *h);
+#endif
+
+static int synaptics_init_panel(struct synaptics_ts_data *ts)
+{
+ int ret;
+
+ ret = i2c_set(ts, 0xff, 0x10, "set page select");
+ if (ret == 0)
+ ret = i2c_set(ts, 0x41, 0x04, "set No Clip Z");
+
+ ret = i2c_set(ts, 0xff, 0x04, "fallback page select");
+ ret = i2c_set(ts, 0xf0, 0x81, "select 80 reports per second");
+ return ret;
+}
+
+static void decode_report(struct synaptics_ts_data *ts, u8 *buf)
+{
+/*
+ * This sensor sends two 6-byte absolute finger reports, an optional
+ * 2-byte relative report followed by a status byte. This function
+ * reads the two finger reports and transforms the coordinates
+ * according the platform data so they can be aligned with the lcd
+ * behind the touchscreen. Typically we flip the y-axis since the
+ * sensor uses the bottom left corner as the origin, but if the sensor
+ * is mounted upside down the platform data will request that the
+ * x-axis should be flipped instead. The snap to inactive edge border
+ * are used to allow tapping the edges of the screen on the G1. The
+ * active area of the touchscreen is smaller than the lcd. When the
+ * finger gets close the edge of the screen we snap it to the
+ * edge. This allows ui elements at the edge of the screen to be hit,
+ * and it prevents hitting ui elements that are not at the edge of the
+ * screen when the finger is touching the edge.
+ */
+ int pos[2][2];
+ int f, a;
+ int base = 2;
+ int z = buf[1];
+ int w = buf[0] >> 4;
+ int finger = buf[0] & 7;
+ int finger2_pressed;
+
+ for (f = 0; f < 2; f++) {
+ u32 flip_flag = SYNAPTICS_FLIP_X;
+ for (a = 0; a < 2; a++) {
+ int p = buf[base + 1];
+ p |= (u16)(buf[base] & 0x1f) << 8;
+ if (ts->flags & flip_flag)
+ p = ts->max[a] - p;
+ if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) {
+ if (ts->snap_state[f][a]) {
+ if (p <= ts->snap_down_off[a])
+ p = ts->snap_down[a];
+ else if (p >= ts->snap_up_off[a])
+ p = ts->snap_up[a];
+ else
+ ts->snap_state[f][a] = 0;
+ } else {
+ if (p <= ts->snap_down_on[a]) {
+ p = ts->snap_down[a];
+ ts->snap_state[f][a] = 1;
+ } else if (p >= ts->snap_up_on[a]) {
+ p = ts->snap_up[a];
+ ts->snap_state[f][a] = 1;
+ }
+ }
+ }
+ pos[f][a] = p;
+ base += 2;
+ flip_flag <<= 1;
+ }
+ base += 2;
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(pos[f][0], pos[f][1]);
+ }
+ if (z) {
+ input_report_abs(ts->input_dev, ABS_X, pos[0][0]);
+ input_report_abs(ts->input_dev, ABS_Y, pos[0][1]);
+ }
+ input_report_abs(ts->input_dev, ABS_PRESSURE, z);
+ input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w);
+ input_report_key(ts->input_dev, BTN_TOUCH, finger);
+ finger2_pressed = finger > 1 && finger != 7;
+ input_report_key(ts->input_dev, BTN_2, finger2_pressed);
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]);
+ input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]);
+ }
+ input_sync(ts->input_dev);
+}
+
+static void synaptics_ts_work_func(struct work_struct *work)
+{
+ int i;
+ int ret;
+ int bad_data = 0;
+ struct i2c_msg msg[2];
+ u8 start_reg = 0;
+ u8 buf[15];
+ struct synaptics_ts_data *ts =
+ container_of(work, struct synaptics_ts_data, work);
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(buf);
+ msg[1].buf = buf;
+
+ for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) {
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ pr_err("ts_work: i2c_transfer failed\n");
+ bad_data = 1;
+ continue;
+ }
+ if ((buf[14] & 0xc0) != 0x40) {
+ pr_warning("synaptics_ts_work_func:"
+ " bad read %x %x %x %x %x %x %x %x %x"
+ " %x %x %x %x %x %x, ret %d\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], ret);
+ if (bad_data)
+ synaptics_init_panel(ts);
+ bad_data = 1;
+ continue;
+ }
+ bad_data = 0;
+ if ((buf[14] & 1) == 0)
+ break;
+
+ decode_report(ts, buf);
+ }
+ if (ts->use_irq)
+ enable_irq(ts->client->irq);
+}
+
+static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer)
+{
+ struct synaptics_ts_data *ts =
+ container_of(timer, struct synaptics_ts_data, timer);
+
+ queue_work(synaptics_wq, &ts->work);
+
+ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id)
+{
+ struct synaptics_ts_data *ts = dev_id;
+
+ disable_irq_nosync(ts->client->irq);
+ queue_work(synaptics_wq, &ts->work);
+ return IRQ_HANDLED;
+}
+
+static int detect(struct synaptics_ts_data *ts, u32 *panel_version)
+{
+ int ret;
+ int retry = 10;
+
+ ret = i2c_set(ts, 0xf4, 0x01, "reset device");
+
+ while (retry-- > 0) {
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe4);
+ if (ret >= 0)
+ break;
+ msleep(100);
+ }
+ if (ret < 0) {
+ pr_err("i2c_smbus_read_byte_data failed\n");
+ return ret;
+ }
+
+ *panel_version = ret << 8;
+ ret = i2c_read(ts, 0xe5, "product minor");
+ if (ret < 0)
+ return ret;
+ *panel_version |= ret;
+
+ ret = i2c_read(ts, 0xe3, "property");
+ if (ret < 0)
+ return ret;
+
+ pr_info("synaptics: version %x, product property %x\n",
+ *panel_version, ret);
+ return 0;
+}
+
+static void compute_areas(struct synaptics_ts_data *ts,
+ struct synaptics_i2c_rmi_platform_data *pdata,
+ u16 max_x, u16 max_y)
+{
+ int inactive_area_left;
+ int inactive_area_right;
+ int inactive_area_top;
+ int inactive_area_bottom;
+ int snap_left_on;
+ int snap_left_off;
+ int snap_right_on;
+ int snap_right_off;
+ int snap_top_on;
+ int snap_top_off;
+ int snap_bottom_on;
+ int snap_bottom_off;
+ int fuzz_x;
+ int fuzz_y;
+ int fuzz_p;
+ int fuzz_w;
+ int swapped = !!(ts->flags & SYNAPTICS_SWAP_XY);
+
+ inactive_area_left = pdata->inactive_left;
+ inactive_area_right = pdata->inactive_right;
+ inactive_area_top = pdata->inactive_top;
+ inactive_area_bottom = pdata->inactive_bottom;
+ snap_left_on = pdata->snap_left_on;
+ snap_left_off = pdata->snap_left_off;
+ snap_right_on = pdata->snap_right_on;
+ snap_right_off = pdata->snap_right_off;
+ snap_top_on = pdata->snap_top_on;
+ snap_top_off = pdata->snap_top_off;
+ snap_bottom_on = pdata->snap_bottom_on;
+ snap_bottom_off = pdata->snap_bottom_off;
+ fuzz_x = pdata->fuzz_x;
+ fuzz_y = pdata->fuzz_y;
+ fuzz_p = pdata->fuzz_p;
+ fuzz_w = pdata->fuzz_w;
+
+ inactive_area_left = inactive_area_left * max_x / 0x10000;
+ inactive_area_right = inactive_area_right * max_x / 0x10000;
+ inactive_area_top = inactive_area_top * max_y / 0x10000;
+ inactive_area_bottom = inactive_area_bottom * max_y / 0x10000;
+ snap_left_on = snap_left_on * max_x / 0x10000;
+ snap_left_off = snap_left_off * max_x / 0x10000;
+ snap_right_on = snap_right_on * max_x / 0x10000;
+ snap_right_off = snap_right_off * max_x / 0x10000;
+ snap_top_on = snap_top_on * max_y / 0x10000;
+ snap_top_off = snap_top_off * max_y / 0x10000;
+ snap_bottom_on = snap_bottom_on * max_y / 0x10000;
+ snap_bottom_off = snap_bottom_off * max_y / 0x10000;
+ fuzz_x = fuzz_x * max_x / 0x10000;
+ fuzz_y = fuzz_y * max_y / 0x10000;
+
+
+ ts->snap_down[swapped] = -inactive_area_left;
+ ts->snap_up[swapped] = max_x + inactive_area_right;
+ ts->snap_down[!swapped] = -inactive_area_top;
+ ts->snap_up[!swapped] = max_y + inactive_area_bottom;
+ ts->snap_down_on[swapped] = snap_left_on;
+ ts->snap_down_off[swapped] = snap_left_off;
+ ts->snap_up_on[swapped] = max_x - snap_right_on;
+ ts->snap_up_off[swapped] = max_x - snap_right_off;
+ ts->snap_down_on[!swapped] = snap_top_on;
+ ts->snap_down_off[!swapped] = snap_top_off;
+ ts->snap_up_on[!swapped] = max_y - snap_bottom_on;
+ ts->snap_up_off[!swapped] = max_y - snap_bottom_off;
+ pr_info("synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y);
+ pr_info("synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n",
+ inactive_area_left, inactive_area_right,
+ inactive_area_top, inactive_area_bottom);
+ pr_info("synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n",
+ snap_left_on, snap_left_off, snap_right_on, snap_right_off,
+ snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off);
+
+ input_set_abs_params(ts->input_dev, ABS_X,
+ -inactive_area_left, max_x + inactive_area_right,
+ fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y,
+ -inactive_area_top, max_y + inactive_area_bottom,
+ fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0);
+ input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left,
+ max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top,
+ max_y + inactive_area_bottom, fuzz_y, 0);
+}
+
+static struct synaptics_i2c_rmi_platform_data fake_pdata;
+
+static int __devinit synaptics_ts_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct synaptics_ts_data *ts;
+ u8 buf0[4];
+ u8 buf1[8];
+ struct i2c_msg msg[2];
+ int ret = 0;
+ struct synaptics_i2c_rmi_platform_data *pdata;
+ u32 panel_version = 0;
+ u16 max_x, max_y;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("synaptics_ts_probe: need I2C_FUNC_I2C\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ pr_err("synaptics_ts_probe: need I2C_FUNC_SMBUS_WORD_DATA\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+ INIT_WORK(&ts->work, synaptics_ts_work_func);
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+ pdata = client->dev.platform_data;
+ if (pdata)
+ ts->power = pdata->power;
+ else
+ pdata = &fake_pdata;
+
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0) {
+ pr_err("synaptics_ts_probe power on failed\n");
+ goto err_power_failed;
+ }
+ }
+
+ ret = detect(ts, &panel_version);
+ if (ret)
+ goto err_detect_failed;
+
+ while (pdata->version > panel_version)
+ pdata++;
+ ts->flags = pdata->flags;
+
+ ret = i2c_read(ts, 0xf0, "device control");
+ if (ret < 0)
+ goto err_detect_failed;
+ pr_info("synaptics: device control %x\n", ret);
+
+ ret = i2c_read(ts, 0xf1, "interrupt enable");
+ if (ret < 0)
+ goto err_detect_failed;
+ pr_info("synaptics_ts_probe: interrupt enable %x\n", ret);
+
+ ret = i2c_set(ts, 0xf1, 0, "disable interrupt");
+ if (ret < 0)
+ goto err_detect_failed;
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = buf0;
+ buf0[0] = 0xe0;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 8;
+ msg[1].buf = buf1;
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ pr_err("i2c_transfer failed\n");
+ goto err_detect_failed;
+ }
+ pr_info("synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n",
+ buf1[0], buf1[1], buf1[2], buf1[3],
+ buf1[4], buf1[5], buf1[6], buf1[7]);
+
+ ret = i2c_set(ts, 0xff, 0x10, "page select = 0x10");
+ if (ret < 0)
+ goto err_detect_failed;
+
+ ret = i2c_smbus_read_word_data(ts->client, 0x04);
+ if (ret < 0) {
+ pr_err("i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ ret = i2c_smbus_read_word_data(ts->client, 0x06);
+ if (ret < 0) {
+ pr_err("i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(max_x, max_y);
+
+ /* will also switch back to page 0x04 */
+ ret = synaptics_init_panel(ts);
+ if (ret < 0) {
+ pr_err("synaptics_init_panel failed\n");
+ goto err_detect_failed;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ pr_err("synaptics: Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ ts->input_dev->name = "synaptics-rmi-touchscreen";
+ ts->input_dev->phys = "msm/input0";
+ ts->input_dev->id.bustype = BUS_I2C;
+
+ __set_bit(EV_SYN, ts->input_dev->evbit);
+ __set_bit(EV_KEY, ts->input_dev->evbit);
+ __set_bit(BTN_TOUCH, ts->input_dev->keybit);
+ __set_bit(BTN_2, ts->input_dev->keybit);
+ __set_bit(EV_ABS, ts->input_dev->evbit);
+
+ compute_areas(ts, pdata, max_x, max_y);
+
+
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ pr_err("synaptics: Unable to register %s input device\n",
+ ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+ if (client->irq) {
+ ret = request_irq(client->irq, synaptics_ts_irq_handler,
+ 0, client->name, ts);
+ if (ret == 0) {
+ ret = i2c_set(ts, 0xf1, 0x01, "enable abs int");
+ if (ret)
+ free_irq(client->irq, ts);
+ }
+ if (ret == 0)
+ ts->use_irq = 1;
+ else
+ dev_err(&client->dev, "request_irq failed\n");
+ }
+ if (!ts->use_irq) {
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = synaptics_ts_timer_func;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = synaptics_ts_early_suspend;
+ ts->early_suspend.resume = synaptics_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ pr_info("synaptics: Start touchscreen %s in %s mode\n",
+ ts->input_dev->name, ts->use_irq ? "interrupt" : "polling");
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+err_detect_failed:
+err_power_failed:
+ kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+ return ret;
+}
+
+static int synaptics_ts_remove(struct i2c_client *client)
+{
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->use_irq)
+ disable_irq(client->irq);
+ else
+ hrtimer_cancel(&ts->timer);
+ ret = cancel_work_sync(&ts->work);
+ if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */
+ enable_irq(client->irq);
+ i2c_set(ts, 0xf1, 0, "disable interrupt");
+ i2c_set(ts, 0xf0, 0x86, "deep sleep");
+
+ if (ts->power) {
+ ret = ts->power(0);
+ if (ret < 0)
+ pr_err("synaptics_ts_suspend power off failed\n");
+ }
+ return 0;
+}
+
+static int synaptics_ts_resume(struct i2c_client *client)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0)
+ pr_err("synaptics_ts_resume power on failed\n");
+ }
+
+ synaptics_init_panel(ts);
+
+ if (ts->use_irq) {
+ enable_irq(client->irq);
+ i2c_set(ts, 0xf1, 0x01, "enable abs int");
+ } else
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void synaptics_ts_late_resume(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_resume(ts->client);
+}
+#endif
+#else
+#define synaptics_ts_suspend NULL
+#define synaptics_ts_resume NULL
+#endif
+
+
+
+static const struct i2c_device_id synaptics_ts_id[] = {
+ { SYNAPTICS_I2C_RMI_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver synaptics_ts_driver = {
+ .probe = synaptics_ts_probe,
+ .remove = synaptics_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = synaptics_ts_suspend,
+ .resume = synaptics_ts_resume,
+#endif
+ .id_table = synaptics_ts_id,
+ .driver = {
+ .name = SYNAPTICS_I2C_RMI_NAME,
+ },
+};
+
+static int __devinit synaptics_ts_init(void)
+{
+ synaptics_wq = create_singlethread_workqueue("synaptics_wq");
+ if (!synaptics_wq)
+ return -ENOMEM;
+ return i2c_add_driver(&synaptics_ts_driver);
+}
+
+static void __exit synaptics_ts_exit(void)
+{
+ i2c_del_driver(&synaptics_ts_driver);
+ if (synaptics_wq)
+ destroy_workqueue(synaptics_wq);
+}
+
+module_init(synaptics_ts_init);
+module_exit(synaptics_ts_exit);
+
+MODULE_DESCRIPTION("Synaptics Touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arve Hjønnevåg <arve@android.com>");
diff --git a/drivers/staging/dream/synaptics_i2c_rmi.h b/drivers/staging/dream/synaptics_i2c_rmi.h
new file mode 100644
index 00000000000..ca51b2fc564
--- /dev/null
+++ b/drivers/staging/dream/synaptics_i2c_rmi.h
@@ -0,0 +1,53 @@
+/*
+ * include/linux/synaptics_i2c_rmi.h - platform data structure for f75375s sensor
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SYNAPTICS_I2C_RMI_H
+#define _LINUX_SYNAPTICS_I2C_RMI_H
+
+#define SYNAPTICS_I2C_RMI_NAME "synaptics-rmi-ts"
+
+enum {
+ SYNAPTICS_FLIP_X = 1UL << 0,
+ SYNAPTICS_FLIP_Y = 1UL << 1,
+ SYNAPTICS_SWAP_XY = 1UL << 2,
+ SYNAPTICS_SNAP_TO_INACTIVE_EDGE = 1UL << 3,
+};
+
+struct synaptics_i2c_rmi_platform_data {
+ uint32_t version; /* Use this entry for panels with */
+ /* (major << 8 | minor) version or above. */
+ /* If non-zero another array entry follows */
+ int (*power)(int on); /* Only valid in first array entry */
+ uint32_t flags;
+ uint32_t inactive_left; /* 0x10000 = screen width */
+ uint32_t inactive_right; /* 0x10000 = screen width */
+ uint32_t inactive_top; /* 0x10000 = screen height */
+ uint32_t inactive_bottom; /* 0x10000 = screen height */
+ uint32_t snap_left_on; /* 0x10000 = screen width */
+ uint32_t snap_left_off; /* 0x10000 = screen width */
+ uint32_t snap_right_on; /* 0x10000 = screen width */
+ uint32_t snap_right_off; /* 0x10000 = screen width */
+ uint32_t snap_top_on; /* 0x10000 = screen height */
+ uint32_t snap_top_off; /* 0x10000 = screen height */
+ uint32_t snap_bottom_on; /* 0x10000 = screen height */
+ uint32_t snap_bottom_off; /* 0x10000 = screen height */
+ uint32_t fuzz_x; /* 0x10000 = screen width */
+ uint32_t fuzz_y; /* 0x10000 = screen height */
+ int fuzz_p;
+ int fuzz_w;
+};
+
+#endif /* _LINUX_SYNAPTICS_I2C_RMI_H */