Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 52798a1..4ba9432 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -443,4 +443,38 @@
# TI's ST based wl128x FM radio
source "drivers/media/radio/wl128x/Kconfig"
+config RADIO_TAVARUA
+ tristate "Qualcomm Tavaraua I2C FM support"
+ depends on I2C && VIDEO_V4L2 && MARIMBA_CORE
+ default n
+ ---help---
+ Say Y here if you want to use the Qualcomm FM chip (Tavarua).
+ This FM chip uses I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-tavarua.
+
+config RADIO_IRIS
+ tristate "Qualcomm IRIS FM support"
+ depends on VIDEO_V4L2
+ default n
+ ---help---
+ Say Y here if you want to use the Qualcomm FM chip (IRIS).
+ This FM chip uses SMD interface
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-iris.
+
+
+config RADIO_IRIS_TRANSPORT
+ tristate "Qualcomm IRIS Transport"
+ depends on RADIO_IRIS
+ default n
+ ---help---
+ Say Y here if you want to use the Qualcomm FM chip (IRIS).
+ with SMD as transport.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-iris-transport.
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index f484a6e..3337f4b 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -26,5 +26,8 @@
obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
obj-$(CONFIG_RADIO_WL128X) += wl128x/
+obj-$(CONFIG_RADIO_TAVARUA) += radio-tavarua.o
+obj-$(CONFIG_RADIO_IRIS) += radio-iris.o
+obj-$(CONFIG_RADIO_IRIS_TRANSPORT) += radio-iris-transport.o
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/radio-iris-transport.c b/drivers/media/radio/radio-iris-transport.c
new file mode 100644
index 0000000..6628c9d
--- /dev/null
+++ b/drivers/media/radio/radio-iris-transport.c
@@ -0,0 +1,165 @@
+/*
+ * Qualcomm's FM Shared Memory Transport Driver
+ *
+ * FM HCI_SMD ( FM HCI Shared Memory Driver) is Qualcomm's Shared memory driver
+ * for the HCI protocol. This file is based on drivers/bluetooth/hci_vhci.c
+ *
+ * Copyright (c) 2000-2001, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <mach/msm_smd.h>
+#include <media/radio-iris.h>
+
+struct radio_data {
+ struct radio_hci_dev *hdev;
+ struct tasklet_struct rx_task;
+ struct smd_channel *fm_channel;
+};
+struct radio_data hs;
+
+static void radio_hci_smd_destruct(struct radio_hci_dev *hdev)
+{
+ radio_hci_unregister_dev(hs.hdev);
+}
+
+
+static void radio_hci_smd_recv_event(unsigned long temp)
+{
+ int len;
+ int rc;
+ struct sk_buff *skb;
+ unsigned char *buf;
+ struct radio_data *hsmd = &hs;
+ len = smd_read_avail(hsmd->fm_channel);
+
+ while (len) {
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ FMDERR("Memory not allocated for the socket");
+ return;
+ }
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ kfree_skb(skb);
+ FMDERR("Error in allocating buffer memory");
+ return;
+ }
+
+ rc = smd_read_from_cb(hsmd->fm_channel, (void *)buf, len);
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ skb_orphan(skb);
+ skb->dev = (struct net_device *)hs.hdev;
+
+ rc = radio_hci_recv_frame(skb);
+
+ kfree(buf);
+ len = smd_read_avail(hsmd->fm_channel);
+ }
+}
+
+static int radio_hci_smd_send_frame(struct sk_buff *skb)
+{
+ int len = 0;
+
+ len = smd_write(hs.fm_channel, skb->data, skb->len);
+ if (len < skb->len) {
+ FMDERR("Failed to write Data %d", len);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void radio_hci_smd_notify_cmd(void *data, unsigned int event)
+{
+ struct radio_hci_dev *hdev = hs.hdev;
+
+ if (!hdev) {
+ FMDERR("Frame for unknown HCI device (hdev=NULL)");
+ return;
+ }
+
+ switch (event) {
+ case SMD_EVENT_DATA:
+ tasklet_schedule(&hs.rx_task);
+ break;
+ case SMD_EVENT_OPEN:
+ case SMD_EVENT_CLOSE:
+ break;
+ default:
+ break;
+ }
+}
+
+static int radio_hci_smd_register_dev(struct radio_data *hsmd)
+{
+ struct radio_hci_dev *hdev;
+ int rc;
+
+ hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL);
+ hsmd->hdev = hdev;
+ tasklet_init(&hsmd->rx_task, radio_hci_smd_recv_event,
+ (unsigned long) hsmd);
+ hdev->send = radio_hci_smd_send_frame;
+ hdev->destruct = radio_hci_smd_destruct;
+
+ /* Open the SMD Channel and device and register the callback function */
+ rc = smd_named_open_on_edge("APPS_FM", SMD_APPS_WCNSS,
+ &hsmd->fm_channel, hdev, radio_hci_smd_notify_cmd);
+
+ if (rc < 0) {
+ FMDERR("Cannot open the command channel");
+ return -ENODEV;
+ }
+
+ smd_disable_read_intr(hsmd->fm_channel);
+
+ if (radio_hci_register_dev(hdev) < 0) {
+ FMDERR("Can't register HCI device");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void radio_hci_smd_deregister(void)
+{
+ smd_close(hs.fm_channel);
+ hs.fm_channel = 0;
+}
+
+static int radio_hci_smd_init(void)
+{
+ return radio_hci_smd_register_dev(&hs);
+}
+module_init(radio_hci_smd_init);
+
+static void __exit radio_hci_smd_exit(void)
+{
+ radio_hci_smd_deregister();
+}
+module_exit(radio_hci_smd_exit);
+
+MODULE_DESCRIPTION("Bluetooth SMD driver");
+MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
new file mode 100644
index 0000000..fe53ca8
--- /dev/null
+++ b/drivers/media/radio/radio-iris.c
@@ -0,0 +1,2220 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#define DRIVER_AUTHOR "Archana Ramchandran <archanar@codeaurora.org>"
+#define DRIVER_NAME "radio-iris"
+#define DRIVER_CARD "Qualcomm FM Radio Transceiver"
+#define DRIVER_DESC "Driver for Qualcomm FM Radio Transceiver "
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/atomic.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/radio-iris.h>
+#include <asm/unaligned.h>
+
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+static void radio_hci_cmd_task(unsigned long arg);
+static void radio_hci_rx_task(unsigned long arg);
+static struct video_device *video_get_dev(void);
+static DEFINE_RWLOCK(hci_task_lock);
+
+struct iris_device {
+ struct device *dev;
+ struct kfifo data_buf[IRIS_BUF_MAX];
+
+ int pending_xfrs[IRIS_XFR_MAX];
+ int xfr_bytes_left;
+ int xfr_in_progress;
+ struct completion sync_xfr_start;
+ int tune_req;
+
+ struct video_device *videodev;
+
+ struct mutex lock;
+ spinlock_t buf_lock[IRIS_BUF_MAX];
+ wait_queue_head_t event_queue;
+ wait_queue_head_t read_queue;
+
+ struct radio_hci_dev *fm_hdev;
+
+ struct v4l2_capability *g_cap;
+ struct v4l2_control *g_ctl;
+
+ struct hci_fm_mute_mode_req mute_mode;
+ struct hci_fm_stereo_mode_req stereo_mode;
+ struct hci_fm_station_rsp fm_st_rsp;
+ struct hci_fm_search_station_req srch_st;
+ struct hci_fm_search_rds_station_req srch_rds;
+ struct hci_fm_search_station_list_req srch_st_list;
+ struct hci_fm_recv_conf_req recv_conf;
+ struct hci_fm_rds_grp_req rds_grp;
+ unsigned char g_search_mode;
+ unsigned char g_scan_time;
+ unsigned int g_antenna;
+ unsigned int g_rds_grp_proc_ps;
+ enum iris_region_t region;
+ struct hci_fm_dbg_param_rsp st_dbg_param;
+ struct hci_ev_srch_list_compl srch_st_result;
+};
+
+static struct video_device *priv_videodev;
+
+static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 15,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SRCHMODE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search mode",
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SCANDWELL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search dwell time",
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SRCHON,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Search on/off",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_STATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "radio 0ff/rx/tx/reset",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 1,
+
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_REGION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "radio standard",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Signal Threshold",
+ .minimum = 0x80,
+ .maximum = 0x7F,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search PTY",
+ .minimum = 0,
+ .maximum = 31,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_PI,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search PI",
+ .minimum = 0,
+ .maximum = 0xFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Preset num",
+ .minimum = 0,
+ .maximum = 12,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_EMPHASIS,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Emphasis",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_RDS_STD,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "RDS standard",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_SPACING,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Channel spacing",
+ .minimum = 0,
+ .maximum = 2,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_RDSON,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "RDS on/off",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS group mask",
+ .minimum = 0,
+ .maximum = 0xFFFFFFFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS processing",
+ .minimum = 0,
+ .maximum = 0xFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS data groups to buffer",
+ .minimum = 1,
+ .maximum = 21,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_PSALL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "pass all ps strings",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_LP_MODE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Low power mode",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_ANTENNA,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "headset/internal",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Set PS REPEATCOUNT",
+ .minimum = 0,
+ .maximum = 15,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Stop PS NAME",
+ .minimum = 0,
+ .maximum = 1,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Stop RT",
+ .minimum = 0,
+ .maximum = 1,
+ },
+
+};
+
+static void iris_q_event(struct iris_device *radio,
+ enum iris_evt_t event)
+{
+ struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
+ unsigned char evt = event;
+ if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS]))
+ wake_up_interruptible(&radio->event_queue);
+}
+
+static int hci_send_frame(struct sk_buff *skb)
+{
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
+
+ if (!hdev) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ __net_timestamp(skb);
+
+ skb_orphan(skb);
+ return hdev->send(skb);
+}
+
+static void radio_hci_cmd_task(unsigned long arg)
+{
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
+ struct sk_buff *skb;
+ if (!(atomic_read(&hdev->cmd_cnt))
+ && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
+ FMDERR("%s command tx timeout", hdev->name);
+ atomic_set(&hdev->cmd_cnt, 1);
+ }
+
+ skb = skb_dequeue(&hdev->cmd_q);
+ if (atomic_read(&hdev->cmd_cnt) && skb) {
+ kfree_skb(hdev->sent_cmd);
+ hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
+ if (hdev->sent_cmd) {
+ atomic_dec(&hdev->cmd_cnt);
+ hci_send_frame(skb);
+ hdev->cmd_last_tx = jiffies;
+ } else {
+ skb_queue_head(&hdev->cmd_q, skb);
+ tasklet_schedule(&hdev->cmd_task);
+ }
+ }
+
+}
+
+static void radio_hci_rx_task(unsigned long arg)
+{
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
+ struct sk_buff *skb;
+
+ read_lock(&hci_task_lock);
+
+ skb = skb_dequeue(&hdev->rx_q);
+ radio_hci_event_packet(hdev, skb);
+
+ read_unlock(&hci_task_lock);
+}
+
+int radio_hci_register_dev(struct radio_hci_dev *hdev)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ if (!radio) {
+ FMDERR(":radio is null");
+ return -EINVAL;
+ }
+
+ if (!hdev) {
+ FMDERR("hdev is null");
+ return -EINVAL;
+ }
+
+ hdev->flags = 0;
+
+ tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long)
+ hdev);
+ tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long)
+ hdev);
+
+ init_waitqueue_head(&hdev->req_wait_q);
+
+ skb_queue_head_init(&hdev->rx_q);
+ skb_queue_head_init(&hdev->cmd_q);
+ skb_queue_head_init(&hdev->raw_q);
+
+ if (!radio)
+ FMDERR(":radio is null");
+
+ radio->fm_hdev = hdev;
+
+ return 0;
+}
+EXPORT_SYMBOL(radio_hci_register_dev);
+
+int radio_hci_unregister_dev(struct radio_hci_dev *hdev)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ if (!radio) {
+ FMDERR(":radio is null");
+ return -EINVAL;
+ }
+
+ tasklet_kill(&hdev->rx_task);
+ tasklet_kill(&hdev->cmd_task);
+ skb_queue_purge(&hdev->rx_q);
+ skb_queue_purge(&hdev->cmd_q);
+ skb_queue_purge(&hdev->raw_q);
+ kfree(radio->fm_hdev);
+ kfree(radio->videodev);
+
+ return 0;
+}
+EXPORT_SYMBOL(radio_hci_unregister_dev);
+
+int radio_hci_recv_frame(struct sk_buff *skb)
+{
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
+ if (!hdev) {
+ FMDERR("%s hdev is null while receiving frame", hdev->name);
+ kfree_skb(skb);
+ return -ENXIO;
+ }
+
+ __net_timestamp(skb);
+
+ radio_hci_event_packet(hdev, skb);
+
+ return 0;
+}
+EXPORT_SYMBOL(radio_hci_recv_frame);
+
+int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
+ void *param)
+{
+ int len = RADIO_HCI_COMMAND_HDR_SIZE + plen;
+ struct radio_hci_command_hdr *hdr;
+ struct sk_buff *skb;
+ int ret = 0;
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ FMDERR("%s no memory for command", hdev->name);
+ return -ENOMEM;
+ }
+
+ hdr = (struct radio_hci_command_hdr *) skb_put(skb,
+ RADIO_HCI_COMMAND_HDR_SIZE);
+ hdr->opcode = cpu_to_le16(opcode);
+ hdr->plen = plen;
+
+ if (plen)
+ memcpy(skb_put(skb, plen), param, plen);
+
+ skb->dev = (void *) hdev;
+
+ ret = hci_send_frame(skb);
+
+ return ret;
+}
+EXPORT_SYMBOL(radio_hci_send_cmd);
+
+static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_ENABLE_RECV_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_DISABLE_RECV_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_RECV_CONF_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ struct hci_fm_recv_conf_req *recv_conf_req =
+ (struct hci_fm_recv_conf_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_RECV_CONF_REQ);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)),
+ recv_conf_req);
+}
+
+static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_STATION_PARAM_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_mute_mode_req *mute_mode_req =
+ (struct hci_fm_mute_mode_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_MUTE_MODE_REQ);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)),
+ mute_mode_req);
+}
+
+static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_stereo_mode_req *stereo_mode_req =
+ (struct hci_fm_stereo_mode_req *) param;
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_STEREO_MODE_REQ);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)),
+ stereo_mode_req);
+}
+
+static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u8 antenna = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_ANTENNA);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna);
+}
+
+static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u8 sig_threshold = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_SIGNAL_THRESHOLD);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold),
+ &sig_threshold);
+}
+
+static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_SIGNAL_THRESHOLD);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_RADIO_TEXT_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_AF_LIST_REQ);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_search_stations_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_search_station_req *srch_stations =
+ (struct hci_fm_search_station_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SEARCH_STATIONS);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
+ srch_stations);
+}
+
+static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_search_rds_station_req *srch_stations =
+ (struct hci_fm_search_rds_station_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SEARCH_RDS_STATIONS);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
+ srch_stations);
+}
+
+static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_search_station_list_req *srch_list =
+ (struct hci_fm_search_station_list_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SEARCH_STATIONS_LIST);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)),
+ srch_list);
+}
+
+static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_CANCEL_SEARCH);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u32 fm_grps_process = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_RDS_GRP_PROCESS);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process),
+ &fm_grps_process);
+}
+
+static int hci_fm_tune_station_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u32 tune_freq = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_TUNE_STATION_REQ);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq);
+}
+
+static int hci_def_data_read_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_def_data_rd_req *def_data_rd =
+ (struct hci_fm_def_data_rd_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_DEFAULT_DATA_READ);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)),
+ def_data_rd);
+}
+
+static int hci_def_data_write_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_def_data_wr_req *def_data_wr =
+ (struct hci_fm_def_data_wr_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_DEFAULT_DATA_WRITE);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_wr)),
+ def_data_wr);
+}
+
+static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_RESET);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_GET_FEATURE_LIST);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u8 mode = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_DO_CALIBRATION);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode);
+}
+
+static int hci_read_grp_counters_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ __u8 reset_counters = param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_READ_GRP_COUNTERS);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters),
+ &reset_counters);
+}
+
+static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_peek_req *peek_data = (struct hci_fm_peek_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_PEEK_DATA);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)),
+ peek_data);
+}
+
+static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_poke_req *poke_data = (struct hci_fm_poke_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_POKE_DATA);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)),
+ poke_data);
+}
+
+static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_ssbi_req *ssbi_peek = (struct hci_fm_ssbi_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SSBI_PEEK_REG);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)),
+ ssbi_peek);
+}
+
+static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+ struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+ HCI_OCF_FM_SSBI_POKE_REG);
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)),
+ ssbi_poke);
+}
+
+static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ __u16 opcode = 0;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_STATION_DBG_PARAM);
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL);
+}
+
+static int radio_hci_err(__u16 code)
+{
+ switch (code) {
+ case 0:
+ return 0;
+ case 0x01:
+ return -EBADRQC;
+ case 0x02:
+ return -ENOTCONN;
+ case 0x03:
+ return -EIO;
+ case 0x07:
+ return -ENOMEM;
+ case 0x0c:
+ return -EBUSY;
+ case 0x11:
+ return -EOPNOTSUPP;
+ case 0x12:
+ return -EINVAL;
+ default:
+ return -ENOSYS;
+ }
+}
+
+static int __radio_hci_request(struct radio_hci_dev *hdev,
+ int (*req)(struct radio_hci_dev *hdev,
+ unsigned long param),
+ unsigned long param, __u32 timeout)
+{
+ int err = 0;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = req(hdev, param);
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ switch (hdev->req_status) {
+ case HCI_REQ_DONE:
+ case HCI_REQ_STATUS:
+ err = radio_hci_err(hdev->req_result);
+ break;
+
+ case HCI_REQ_CANCELED:
+ err = -hdev->req_result;
+ break;
+
+ default:
+ err = -ETIMEDOUT;
+ break;
+ }
+
+ hdev->req_status = hdev->req_result = 0;
+
+ return err;
+}
+
+static inline int radio_hci_request(struct radio_hci_dev *hdev,
+ int (*req)(struct
+ radio_hci_dev * hdev, unsigned long param),
+ unsigned long param, __u32 timeout)
+{
+ int ret = 0;
+
+ ret = __radio_hci_request(hdev, req, param, timeout);
+
+ return ret;
+}
+
+static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_recv_conf_req *set_recv_conf = arg;
+
+ ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned
+ long)set_recv_conf, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u32 tune_freq = *arg;
+
+ ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq,
+ RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_mute_mode_req *set_mute_conf = arg;
+
+ ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned
+ long)set_mute_conf, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_stereo_mode_req *set_stereo_conf = arg;
+
+ ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned
+ long)set_stereo_conf, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u8 antenna = *arg;
+
+ ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna,
+ RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_set_signal_threshold(__u8 *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u8 sig_threshold = *arg;
+
+ ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req,
+ sig_threshold, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_search_stations(struct hci_fm_search_station_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_search_station_req *srch_stations = arg;
+
+ ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned
+ long)srch_stations, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_search_rds_station_req *srch_stations = arg;
+
+ ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned
+ long)srch_stations, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_search_station_list
+ (struct hci_fm_search_station_list_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_search_station_list_req *srch_list = arg;
+
+ ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned
+ long)srch_list, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ return 0;
+}
+
+static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u32 fm_grps_process = *arg;
+
+ ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req,
+ fm_grps_process, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_def_data_rd_req *def_data_rd = arg;
+
+ ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned
+ long)def_data_rd, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_def_data_wr_req *def_data_wr = arg;
+
+ ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned
+ long)def_data_wr, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u8 mode = *arg;
+
+ ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode,
+ RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ __u8 reset_counters = *arg;
+
+ ret = radio_hci_request(hdev, hci_read_grp_counters_req,
+ reset_counters, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_peek_data(struct hci_fm_peek_req *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_peek_req *peek_data = arg;
+
+ ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned
+ long)peek_data, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_poke_data(struct hci_fm_poke_req *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_poke_req *poke_data = arg;
+
+ ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned
+ long)poke_data, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_ssbi_peek_reg(struct hci_fm_ssbi_req *arg,
+ struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_ssbi_req *ssbi_peek_reg = arg;
+
+ ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned
+ long)ssbi_peek_reg, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ struct hci_fm_ssbi_req *ssbi_poke_reg = arg;
+
+ ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned
+ long)ssbi_poke_reg, RADIO_HCI_TIMEOUT);
+
+ return ret;
+}
+
+static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev)
+{
+ int ret = 0;
+ unsigned long arg = 0;
+
+ switch (cmd) {
+ case HCI_FM_ENABLE_RECV_CMD:
+ ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_DISABLE_RECV_CMD:
+ ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_RECV_CONF_CMD:
+ ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_STATION_PARAM_CMD:
+ ret = radio_hci_request(hdev,
+ hci_fm_get_station_param_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_SIGNAL_TH_CMD:
+ ret = radio_hci_request(hdev,
+ hci_fm_get_sig_threshold_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_PROGRAM_SERVICE_CMD:
+ ret = radio_hci_request(hdev,
+ hci_fm_get_program_service_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_RADIO_TEXT_CMD:
+ ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_AF_LIST_CMD:
+ ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_CANCEL_SEARCH_CMD:
+ ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_RESET_CMD:
+ ret = radio_hci_request(hdev, hci_fm_reset_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_GET_FEATURES_CMD:
+ ret = radio_hci_request(hdev,
+ hci_fm_get_feature_lists_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ case HCI_FM_STATION_DBG_PARAM_CMD:
+ ret = radio_hci_request(hdev,
+ hci_fm_get_station_dbg_param_req, arg,
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT));
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
+{
+ hdev->req_result = result;
+ hdev->req_status = HCI_REQ_DONE;
+ wake_up_interruptible(&hdev->req_wait_q);
+}
+
+static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result)
+{
+ hdev->req_result = result;
+ hdev->req_status = HCI_REQ_STATUS;
+ wake_up_interruptible(&hdev->req_wait_q);
+}
+
+static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+
+ if (status)
+ return;
+
+ radio_hci_req_complete(hdev, status);
+}
+
+static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+ if (status)
+ return;
+
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
+
+ radio_hci_req_complete(hdev, status);
+}
+
+static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_fm_conf_rsp *rsp = (void *)skb->data;
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+ if (rsp->status)
+ return;
+
+ radio->recv_conf = rsp->recv_conf_rsp;
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_fm_conf_rsp *rsp = (void *)skb->data;
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+ if (rsp->status)
+ return;
+
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_fm_sig_threshold_rsp *rsp = (void *)skb->data;
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ struct v4l2_control *v4l_ctl = radio->g_ctl;
+
+ if (rsp->status)
+ return;
+
+ v4l_ctl->value = rsp->sig_threshold;
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ struct hci_fm_station_rsp *rsp = (void *)skb->data;
+ radio->fm_st_rsp = *(rsp);
+
+ /* Tune is always succesful */
+ radio_hci_req_complete(hdev, 0);
+}
+
+static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_fm_prgm_srv_rsp *rsp = (void *)skb->data;
+
+ if (rsp->status)
+ return;
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_fm_radio_txt_rsp *rsp = (void *)skb->data;
+
+ if (rsp->status)
+ return;
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
+
+ if (rsp->status)
+ return;
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_data_rd_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_fm_data_rd_rsp *rsp = (void *)skb->data;
+
+ if (rsp->status)
+ return;
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_fm_feature_list_rsp *rsp = (void *)skb->data;
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ struct v4l2_capability *v4l_cap = radio->g_cap;
+
+ if (rsp->status)
+ return;
+ v4l_cap->capabilities = (rsp->feature_mask & 0x000002) |
+ (rsp->feature_mask & 0x000001);
+
+ radio_hci_req_complete(hdev, rsp->status);
+}
+
+static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ struct hci_fm_dbg_param_rsp *rsp = (void *)skb->data;
+ radio->st_dbg_param = *(rsp);
+
+ if (radio->st_dbg_param.status)
+ return;
+
+ radio_hci_req_complete(hdev, radio->st_dbg_param.status);
+}
+
+static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_cmd_complete *cmd_compl_ev = (void *) skb->data;
+ __u16 opcode;
+
+ skb_pull(skb, sizeof(*cmd_compl_ev));
+
+ opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode);
+
+ switch (opcode) {
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
+ hci_cc_fm_enable_rsp(hdev, skb);
+ break;
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
+ hci_cc_conf_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
+ hci_cc_fm_disable_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
+ case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
+ case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
+ case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
+ hci_cc_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
+ hci_cc_sig_threshold_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
+ hci_cc_station_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
+ hci_cc_prg_srv_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
+ hci_cc_rd_txt_rsp(hdev, skb);
+ break;
+
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
+ hci_cc_af_list_rsp(hdev, skb);
+ break;
+
+ case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
+ hci_cc_data_rd_rsp(hdev, skb);
+ break;
+
+ case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
+ hci_cc_feature_list_rsp(hdev, skb);
+ break;
+
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
+ hci_cc_dbg_param_rsp(hdev, skb);
+ break;
+
+ default:
+ FMDERR("%s opcode 0x%x", hdev->name, opcode);
+ break;
+ }
+
+}
+
+static inline void hci_cmd_status_event(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_cmd_status *ev = (void *) skb->data;
+ radio_hci_status_complete(hdev, ev->status);
+}
+
+static inline void hci_ev_tune_status(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ int i;
+ int len;
+
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+
+ len = sizeof(struct hci_fm_station_rsp);
+
+ memcpy(&radio->fm_st_rsp.station_rsp, skb_pull(skb, len), len);
+
+ iris_q_event(radio, IRIS_EVT_TUNE_SUCC);
+
+ for (i = 0; i < IRIS_BUF_MAX; i++) {
+ if (i >= IRIS_BUF_RT_RDS)
+ kfifo_reset(&radio->data_buf[i]);
+ }
+
+ if (radio->fm_st_rsp.station_rsp.rssi)
+ iris_q_event(radio, IRIS_EVT_ABOVE_TH);
+ else
+ iris_q_event(radio, IRIS_EVT_BELOW_TH);
+
+ if (radio->fm_st_rsp.station_rsp.stereo_prg)
+ iris_q_event(radio, IRIS_EVT_STEREO);
+
+ if (radio->fm_st_rsp.station_rsp.mute_mode)
+ iris_q_event(radio, IRIS_EVT_MONO);
+
+ if (radio->fm_st_rsp.station_rsp.rds_sync_status)
+ iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
+ else
+ iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
+}
+
+static inline void hci_ev_search_compl(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE);
+}
+
+static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ struct hci_ev_srch_list_compl *ev = (void *) skb->data;
+ radio->srch_st_result = *ev;
+}
+
+static inline void hci_ev_search_next(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ iris_q_event(radio, IRIS_EVT_SCAN_NEXT);
+}
+
+static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct iris_device *radio = video_get_drvdata(video_get_dev());
+ __u8 st_status = *((__u8 *) skb->data);
+ if (st_status)
+ iris_q_event(radio, IRIS_EVT_STEREO);
+ else
+ iris_q_event(radio, IRIS_EVT_MONO);
+}
+
+void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct radio_hci_event_hdr *hdr = (void *) skb->data;
+ __u8 event = hdr->evt;
+
+ skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE);
+
+ switch (event) {
+ case HCI_EV_TUNE_STATUS:
+ hci_ev_tune_status(hdev, skb);
+ break;
+ case HCI_EV_SEARCH_PROGRESS:
+ case HCI_EV_SEARCH_RDS_PROGRESS:
+ case HCI_EV_SEARCH_LIST_PROGRESS:
+ hci_ev_search_next(hdev, skb);
+ break;
+ case HCI_EV_STEREO_STATUS:
+ hci_ev_stereo_status(hdev, skb);
+ break;
+ case HCI_EV_RDS_LOCK_STATUS:
+ case HCI_EV_SERVICE_AVAILABLE:
+ case HCI_EV_RDS_RX_DATA:
+ case HCI_EV_PROGRAM_SERVICE:
+ case HCI_EV_RADIO_TEXT:
+ case HCI_EV_FM_AF_LIST:
+ case HCI_EV_TX_RDS_GRP_COMPL:
+ case HCI_EV_TX_RDS_CONT_GRP_COMPL:
+ break;
+
+ case HCI_EV_CMD_COMPLETE:
+ hci_cmd_complete_event(hdev, skb);
+ break;
+
+ case HCI_EV_CMD_STATUS:
+ hci_cmd_status_event(hdev, skb);
+ break;
+
+ case HCI_EV_SEARCH_COMPLETE:
+ case HCI_EV_SEARCH_RDS_COMPLETE:
+ hci_ev_search_compl(hdev, skb);
+ break;
+
+ case HCI_EV_SEARCH_LIST_COMPLETE:
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * fops/IOCTL helper functions
+ */
+
+static int iris_search(struct iris_device *radio, int on, int dir)
+{
+ int retval = 0;
+ enum search_t srch = radio->g_search_mode & SRCH_MODE;
+
+ if (on) {
+ switch (srch) {
+ case SCAN_FOR_STRONG:
+ case SCAN_FOR_WEAK:
+ radio->srch_st_list.srch_list_dir = dir;
+ radio->srch_st_list.srch_list_mode = srch;
+ radio->srch_st_list.srch_list_max = 0;
+ retval = hci_fm_search_station_list(
+ &radio->srch_st_list, radio->fm_hdev);
+ break;
+ case RDS_SEEK_PTY:
+ case RDS_SCAN_PTY:
+ case RDS_SEEK_PI:
+ radio->srch_rds.srch_station.srch_mode = srch;
+ radio->srch_rds.srch_station.srch_dir = dir;
+ radio->srch_rds.srch_station.scan_time =
+ radio->g_scan_time;
+ retval = hci_fm_search_rds_stations(&radio->srch_rds,
+ radio->fm_hdev);
+ break;
+ default:
+ radio->srch_st.srch_mode = srch;
+ radio->srch_st.scan_time = radio->g_scan_time;
+ radio->srch_st.srch_dir = dir;
+ retval = hci_fm_search_stations(
+ &radio->srch_st, radio->fm_hdev);
+ break;
+ }
+
+ } else {
+ retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev);
+ }
+
+ return retval;
+}
+
+static int iris_set_region(struct iris_device *radio, int req_region)
+{
+ int retval;
+ radio->region = req_region;
+
+ switch (radio->region) {
+ case IRIS_REGION_US:
+ {
+ radio->recv_conf.band_low_limit = 88100;
+ radio->recv_conf.band_high_limit = 108000;
+ radio->recv_conf.emphasis = 0;
+ radio->recv_conf.hlsi = 0;
+ radio->recv_conf.ch_spacing = 0;
+ radio->recv_conf.rds_std = 0;
+ }
+ break;
+ case IRIS_REGION_EU:
+ {
+ radio->recv_conf.band_low_limit = 88100;
+ radio->recv_conf.band_high_limit = 108000;
+ radio->recv_conf.emphasis = 0;
+ radio->recv_conf.hlsi = 0;
+ radio->recv_conf.ch_spacing = 0;
+ radio->recv_conf.rds_std = 0;
+ }
+ break;
+ case IRIS_REGION_JAPAN:
+ {
+ radio->recv_conf.band_low_limit = 76000;
+ radio->recv_conf.band_high_limit = 108000;
+ radio->recv_conf.emphasis = 0;
+ radio->recv_conf.hlsi = 0;
+ radio->recv_conf.ch_spacing = 0;
+ }
+ break;
+ default:
+ {
+ radio->recv_conf.emphasis = 0;
+ radio->recv_conf.hlsi = 0;
+ radio->recv_conf.ch_spacing = 0;
+ radio->recv_conf.rds_std = 0;
+ }
+ break;
+ }
+
+
+ retval = hci_set_fm_recv_conf(
+ &radio->recv_conf,
+ radio->fm_hdev);
+
+ return retval;
+}
+
+static int iris_set_freq(struct iris_device *radio, unsigned int freq)
+{
+
+ int retval;
+ retval = hci_fm_tune_station(&freq, radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error while setting the frequency : %d\n", retval);
+ return retval;
+}
+
+
+static int iris_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ unsigned char i;
+ int retval = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) {
+ if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) {
+ memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc));
+ retval = 0;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int iris_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = radio->mute_mode.hard_mute;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
+ ctrl->value = radio->g_search_mode;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
+ ctrl->value = radio->g_scan_time;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCHON:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_STATE:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_IOVERC:
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
+ if (retval < 0)
+ return retval;
+ ctrl->value = radio->st_dbg_param.io_verc;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_INTDET:
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
+ if (retval < 0)
+ return retval;
+ ctrl->value = radio->st_dbg_param.in_det_out;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_REGION:
+ ctrl->value = radio->region;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
+ retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error get FM recv conf"
+ " %d\n", retval);
+ else
+ ctrl->value = radio->recv_conf.emphasis;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDS_STD:
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error get FM recv conf"
+ " %d\n", retval);
+ else
+ ctrl->value = radio->recv_conf.rds_std;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SPACING:
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error get FM recv conf"
+ " %d\n", retval);
+ else
+ ctrl->value = radio->recv_conf.ch_spacing;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSON:
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error get FM recv conf"
+ " %d\n", retval);
+ else
+ ctrl->value = radio->recv_conf.rds_std;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
+ ctrl->value = radio->rds_grp.rds_grp_enable_mask;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
+ ctrl->value = radio->rds_grp.rds_buf_size;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_PSALL:
+ ctrl->value = radio->g_rds_grp_proc_ps;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_LP_MODE:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_ANTENNA:
+ ctrl->value = radio->g_antenna;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ if (retval < 0)
+ FMDERR("get control failed with %d, id: %d\n",
+ retval, ctrl->id);
+ return retval;
+}
+
+static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrl)
+{
+ return -ENOTSUPP;
+}
+
+static int iris_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ unsigned int rds_grps_proc = 0;
+ __u8 temp_val = 0;
+ radio->recv_conf.emphasis = 0;
+ radio->recv_conf.ch_spacing = 0;
+ radio->recv_conf.hlsi = 0;
+ radio->recv_conf.band_low_limit = 87500;
+ radio->recv_conf.band_high_limit = 108000;
+ radio->recv_conf.rds_std = 0;
+
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ radio->mute_mode.hard_mute = ctrl->value;
+ radio->mute_mode.soft_mute = IOC_SFT_MUTE;
+ retval = hci_set_fm_mute_mode(
+ &radio->mute_mode,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error while set FM hard mute"" %d\n",
+ retval);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
+ radio->g_search_mode = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
+ radio->g_scan_time = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCHON:
+ iris_search(radio, ctrl->value, SRCH_DIR_UP);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_STATE:
+ if (ctrl->value == FM_RECV) {
+ retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
+ radio->fm_hdev);
+ } else {
+ if (ctrl->value == FM_OFF) {
+ retval = hci_cmd(
+ HCI_FM_DISABLE_RECV_CMD,
+ radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("Error on disable FM"
+ " %d\n", retval);
+ }
+ }
+ break;
+ case V4L2_CID_PRIVATE_IRIS_REGION:
+ retval = iris_set_region(radio, ctrl->value);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
+ temp_val = ctrl->value;
+ retval = hci_fm_set_signal_threshold(
+ &temp_val,
+ radio->fm_hdev);
+ if (retval < 0) {
+ FMDERR("Error while setting signal threshold\n");
+ break;
+ }
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
+ radio->srch_rds.srch_pty = ctrl->value;
+ radio->srch_st_list.srch_pty = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
+ radio->srch_rds.srch_pi = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_SPACING:
+ radio->recv_conf.ch_spacing = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
+ radio->recv_conf.emphasis = ctrl->value;
+ retval =
+ hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDS_STD:
+ radio->recv_conf.rds_std = ctrl->value;
+ retval =
+ hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSON:
+ radio->recv_conf.rds_std = ctrl->value;
+ retval =
+ hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
+ radio->rds_grp.rds_grp_enable_mask = ctrl->value;
+ retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
+ rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
+ retval = hci_fm_rds_grps_process(
+ &rds_grps_proc,
+ radio->fm_hdev);
+ break;
+ case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
+ radio->rds_grp.rds_buf_size = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_PSALL:
+ radio->g_rds_grp_proc_ps = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_IRIS_LP_MODE:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_ANTENNA:
+ temp_val = ctrl->value;
+ retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
+ break;
+ case V4L2_CID_RDS_TX_PTY:
+ break;
+ case V4L2_CID_RDS_TX_PI:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
+ break;
+ case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
+ break;
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+static int iris_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
+ if (retval < 0)
+ return retval;
+
+ tuner->type = V4L2_TUNER_RADIO;
+ tuner->rangelow = radio->recv_conf.band_low_limit * TUNE_PARAM;
+ tuner->rangehigh = radio->recv_conf.band_high_limit * TUNE_PARAM;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ tuner->capability = V4L2_TUNER_CAP_LOW;
+ tuner->signal = radio->fm_st_rsp.station_rsp.rssi;
+ tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg;
+ tuner->afc = 0;
+
+ return 0;
+}
+
+static int iris_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
+ radio->recv_conf.band_high_limit = tuner->rangehigh / TUNE_PARAM;
+ if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
+ radio->stereo_mode.stereo_mode = 0x01;
+ retval = hci_set_fm_stereo_mode(
+ &radio->stereo_mode,
+ radio->fm_hdev);
+ } else {
+ radio->stereo_mode.stereo_mode = 0x00;
+ retval = hci_set_fm_stereo_mode(
+ &radio->stereo_mode,
+ radio->fm_hdev);
+ }
+ if (retval < 0)
+ FMDERR(": set tuner failed with %d\n", retval);
+ return retval;
+}
+
+static int iris_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
+
+ freq->type = V4L2_TUNER_RADIO;
+ retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
+ if (retval < 0)
+ FMDERR("get frequency failed %d\n", retval);
+ else
+ freq->frequency =
+ radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
+ return retval;
+}
+
+static int iris_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = -1;
+ freq->frequency = freq->frequency / TUNE_PARAM;
+
+ if (freq->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ retval = iris_set_freq(radio, freq->frequency);
+ if (retval < 0)
+ FMDERR(" set frequency failed with %d\n", retval);
+ return retval;
+}
+
+static int iris_vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buffer)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ enum iris_buf_t buf_type = buffer->index;
+ struct kfifo *data_fifo;
+ unsigned char *buf = (unsigned char *)buffer->m.userptr;
+ unsigned int len = buffer->length;
+ if (!access_ok(VERIFY_WRITE, buf, len))
+ return -EFAULT;
+ if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) {
+ data_fifo = &radio->data_buf[buf_type];
+ if (buf_type == IRIS_BUF_EVENTS)
+ if (wait_event_interruptible(radio->event_queue,
+ kfifo_len(data_fifo)) < 0)
+ return -EINTR;
+ } else {
+ FMDERR("invalid buffer type\n");
+ return -EINVAL;
+ }
+ buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
+ &radio->buf_lock[buf_type]);
+
+ return 0;
+}
+
+static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return 0;
+
+}
+
+static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
+{
+ struct iris_device *radio = video_get_drvdata(video_devdata(file));
+ int dir;
+ if (seek->seek_upward)
+ dir = SRCH_DIR_UP;
+ else
+ dir = SRCH_DIR_DOWN;
+ return iris_search(radio, CTRL_ON, dir);
+}
+
+static int iris_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct iris_device *radio;
+ radio = video_get_drvdata(video_devdata(file));
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ radio->g_cap = capability;
+ return 0;
+}
+
+
+static const struct v4l2_ioctl_ops iris_ioctl_ops = {
+ .vidioc_querycap = iris_vidioc_querycap,
+ .vidioc_queryctrl = iris_vidioc_queryctrl,
+ .vidioc_g_ctrl = iris_vidioc_g_ctrl,
+ .vidioc_s_ctrl = iris_vidioc_s_ctrl,
+ .vidioc_g_tuner = iris_vidioc_g_tuner,
+ .vidioc_s_tuner = iris_vidioc_s_tuner,
+ .vidioc_g_frequency = iris_vidioc_g_frequency,
+ .vidioc_s_frequency = iris_vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = iris_vidioc_s_hw_freq_seek,
+ .vidioc_dqbuf = iris_vidioc_dqbuf,
+ .vidioc_g_fmt_type_private = iris_vidioc_g_fmt_type_private,
+ .vidioc_s_ext_ctrls = iris_vidioc_s_ext_ctrls,
+};
+
+static const struct v4l2_file_operations iris_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static struct video_device iris_viddev_template = {
+ .fops = &iris_fops,
+ .ioctl_ops = &iris_ioctl_ops,
+ .name = DRIVER_NAME,
+ .release = video_device_release,
+};
+
+static struct video_device *video_get_dev(void)
+{
+ return priv_videodev;
+}
+
+static int __init iris_probe(struct platform_device *pdev)
+{
+ struct iris_device *radio;
+ int retval;
+ int radio_nr = -1;
+ int i;
+
+ if (!pdev) {
+ FMDERR(": pdev is null\n");
+ return -ENOMEM;
+ }
+
+ radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL);
+ if (!radio) {
+ FMDERR(": Could not allocate radio device\n");
+ return -ENOMEM;
+ }
+
+ radio->dev = &pdev->dev;
+ platform_set_drvdata(pdev, radio);
+
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ FMDERR(": Could not allocate V4L device\n");
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ memcpy(radio->videodev, &iris_viddev_template,
+ sizeof(iris_viddev_template));
+
+ for (i = 0; i < IRIS_BUF_MAX; i++) {
+ int kfifo_alloc_rc = 0;
+ spin_lock_init(&radio->buf_lock[i]);
+
+ if (i == IRIS_BUF_RAW_RDS)
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+ rds_buf*3, GFP_KERNEL);
+ else
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+ STD_BUF_SIZE, GFP_KERNEL);
+
+ if (kfifo_alloc_rc != 0) {
+ FMDERR("failed allocating buffers %d\n",
+ kfifo_alloc_rc);
+ for (; i > -1; i--) {
+ kfifo_free(&radio->data_buf[i]);
+ kfree(radio);
+ return -ENOMEM;
+ }
+ }
+ }
+
+ mutex_init(&radio->lock);
+ init_completion(&radio->sync_xfr_start);
+ radio->tune_req = 0;
+ init_waitqueue_head(&radio->event_queue);
+ init_waitqueue_head(&radio->read_queue);
+
+ video_set_drvdata(radio->videodev, radio);
+
+ if (NULL == video_get_drvdata(radio->videodev))
+ FMDERR(": video_get_drvdata failed\n");
+
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ FMDERR(": Could not register video device\n");
+ video_device_release(radio->videodev);
+ for (; i > -1; i--)
+ kfifo_free(&radio->data_buf[i]);
+ kfree(radio);
+ return retval;
+ } else {
+ priv_videodev = kzalloc(sizeof(struct video_device),
+ GFP_KERNEL);
+ memcpy(priv_videodev, radio->videodev,
+ sizeof(struct video_device));
+ }
+ return 0;
+}
+
+
+static int __devexit iris_remove(struct platform_device *pdev)
+{
+ int i;
+ struct iris_device *radio = platform_get_drvdata(pdev);
+
+ video_unregister_device(radio->videodev);
+
+ for (i = 0; i < IRIS_BUF_MAX; i++)
+ kfifo_free(&radio->data_buf[i]);
+
+ kfree(radio);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver iris_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "iris_fm",
+ },
+ .remove = __devexit_p(iris_remove),
+};
+
+static int __init iris_radio_init(void)
+{
+ return platform_driver_probe(&iris_driver, iris_probe);
+}
+module_init(iris_radio_init);
+
+static void __exit iris_radio_exit(void)
+{
+ platform_driver_unregister(&iris_driver);
+}
+module_exit(iris_radio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
new file mode 100644
index 0000000..f04dfe5
--- /dev/null
+++ b/drivers/media/radio/radio-tavarua.c
@@ -0,0 +1,3755 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+/*
+ * Qualcomm Tavarua FM core driver
+ */
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Qualcomm"
+#define DRIVER_NAME "radio-tavarua"
+#define DRIVER_CARD "Qualcomm FM Radio Transceiver"
+#define DRIVER_DESC "I2C radio driver for Qualcomm FM Radio Transceiver "
+#define DRIVER_VERSION "1.0.0"
+
+#include <linux/version.h>
+#include <linux/init.h> /* Initdata */
+#include <linux/delay.h> /* udelay */
+#include <linux/uaccess.h> /* copy to/from user */
+#include <linux/kfifo.h> /* lock free circular buffer */
+#include <linux/param.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <media/v4l2-common.h>
+#include <asm/unaligned.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/unistd.h>
+#include <asm/atomic.h>
+#include <media/tavarua.h>
+#include <linux/mfd/marimba.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+/*
+regional parameters for radio device
+*/
+struct region_params_t {
+ enum tavarua_region_t region;
+ unsigned int band_high;
+ unsigned int band_low;
+ char emphasis;
+ char rds_std;
+ char spacing;
+};
+
+struct srch_params_t {
+ unsigned short srch_pi;
+ unsigned char srch_pty;
+ unsigned int preset_num;
+ int get_list;
+};
+
+/* Main radio device structure,
+acts as a shadow copy of the
+actual tavaura registers */
+struct tavarua_device {
+ struct video_device *videodev;
+ /* driver management */
+ int users;
+ /* top level driver data */
+ struct marimba *marimba;
+ struct device *dev;
+ /* platform specific functionality */
+ struct marimba_fm_platform_data *pdata;
+ unsigned int chipID;
+ /*RDS buffers + Radio event buffer*/
+ struct kfifo data_buf[TAVARUA_BUF_MAX];
+ /* search paramters */
+ struct srch_params_t srch_params;
+ /* keep track of pending xfrs */
+ int pending_xfrs[TAVARUA_XFR_MAX];
+ int xfr_bytes_left;
+ int xfr_in_progress;
+ /* Transmit data */
+ enum tavarua_xfr_ctrl_t tx_mode;
+ /* synchrnous xfr data */
+ unsigned char sync_xfr_regs[XFR_REG_NUM];
+ struct completion sync_xfr_start;
+ struct completion sync_req_done;
+ int tune_req;
+ /* internal register status */
+ unsigned char registers[RADIO_REGISTERS];
+ /* regional settings */
+ struct region_params_t region_params;
+ /* power mode */
+ int lp_mode;
+ int handle_irq;
+ /* global lock */
+ struct mutex lock;
+ /* buffer locks*/
+ spinlock_t buf_lock[TAVARUA_BUF_MAX];
+ /* work queue */
+ struct workqueue_struct *wqueue;
+ struct delayed_work work;
+ /* wait queue for blocking event read */
+ wait_queue_head_t event_queue;
+ /* wait queue for raw rds read */
+ wait_queue_head_t read_queue;
+ /* PTY for FM Tx */
+ int pty;
+ /* PI for FM TX */
+ int pi;
+ /*PS repeatcount for PS Tx */
+ int ps_repeatcount;
+};
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+static int wait_timeout = WAIT_TIMEOUT;
+/* Bahama's version*/
+static u8 bahama_version;
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+/* static variables */
+static struct tavarua_device *private_data;
+/* forward declerations */
+static int tavarua_disable_interrupts(struct tavarua_device *radio);
+static int tavarua_setup_interrupts(struct tavarua_device *radio,
+ enum radio_state_t state);
+static int tavarua_start(struct tavarua_device *radio,
+ enum radio_state_t state);
+static int tavarua_request_irq(struct tavarua_device *radio);
+static void start_pending_xfr(struct tavarua_device *radio);
+/* work function */
+static void read_int_stat(struct work_struct *work);
+
+static int is_bahama(void)
+{
+ int id = 0;
+
+ switch (id = adie_get_detected_connectivity_type()) {
+ case BAHAMA_ID:
+ FMDBG("It is Bahama\n");
+ return 1;
+
+ case MARIMBA_ID:
+ FMDBG("It is Marimba\n");
+ return 0;
+ default:
+ printk(KERN_ERR "%s: unexpected adie connectivity type: %d\n",
+ __func__, id);
+ return -ENODEV;
+ }
+}
+
+static int set_fm_slave_id(struct tavarua_device *radio)
+{
+ int bahama_present = is_bahama();
+
+ if (bahama_present == -ENODEV)
+ return -ENODEV;
+
+ if (bahama_present)
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA_FM;
+ else
+ radio->marimba->mod_id = MARIMBA_SLAVE_ID_FM;
+
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_isr
+=============================================================================*/
+/**
+ This function is called when GPIO is toggled. This functions queues the event
+ to interrupt queue, which is later handled by isr handling funcion.
+ i.e. INIT_DELAYED_WORK(&radio->work, read_int_stat);
+
+ @param irq: irq that is toggled.
+ @param dev_id: structure pointer passed by client.
+
+ @return IRQ_HANDLED.
+*/
+static irqreturn_t tavarua_isr(int irq, void *dev_id)
+{
+ struct tavarua_device *radio = dev_id;
+ /* schedule a tasklet to handle host intr */
+ /* The call to queue_delayed_work ensures that a minimum delay (in jiffies)
+ * passes before the work is actually executed. The return value from the
+ * function is nonzero if the work_struct was actually added to queue
+ * (otherwise, it may have already been there and will not be added a second
+ * time).
+ */
+ queue_delayed_work(radio->wqueue, &radio->work,
+ msecs_to_jiffies(TAVARUA_DELAY));
+ return IRQ_HANDLED;
+}
+
+/**************************************************************************
+ * Interface to radio internal registers over top level marimba driver
+ *************************************************************************/
+
+/*=============================================================================
+FUNCTION: tavarua_read_registers
+=============================================================================*/
+/**
+ This function is called to read a number of bytes from an I2C interface.
+ The bytes read are stored in internal register status (shadow copy).
+
+ @param radio: structure pointer passed by client.
+ @param offset: register offset.
+ @param len: num of bytes.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_read_registers(struct tavarua_device *radio,
+ unsigned char offset, int len)
+{
+ int retval = 0, i = 0;
+ retval = set_fm_slave_id(radio);
+
+ if (retval == -ENODEV)
+ return retval;
+
+ FMDBG_I2C("I2C Slave: %x, Read Offset(%x): Data [",
+ radio->marimba->mod_id,
+ offset);
+
+ retval = marimba_read(radio->marimba, offset,
+ &radio->registers[offset], len);
+
+ if (retval > 0) {
+ for (i = 0; i < len; i++)
+ FMDBG_I2C("%02x ", radio->registers[offset+i]);
+ FMDBG_I2C(" ]\n");
+
+ }
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_write_register
+=============================================================================*/
+/**
+ This function is called to write a byte over the I2C interface.
+ The corresponding shadow copy is stored in internal register status.
+
+ @param radio: structure pointer passed by client.
+ @param offset: register offset.
+ @param value: buffer to be written to the registers.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_write_register(struct tavarua_device *radio,
+ unsigned char offset, unsigned char value)
+{
+ int retval;
+ retval = set_fm_slave_id(radio);
+
+ if (retval == -ENODEV)
+ return retval;
+
+ FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
+ radio->marimba->mod_id,
+ offset);
+ retval = marimba_write(radio->marimba, offset, &value, 1);
+ if (retval > 0) {
+ if (offset < RADIO_REGISTERS) {
+ radio->registers[offset] = value;
+ FMDBG_I2C("%02x ", radio->registers[offset]);
+ }
+ FMDBG_I2C(" ]\n");
+ }
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_write_registers
+=============================================================================*/
+/**
+ This function is called to write a number of bytes over the I2C interface.
+ The corresponding shadow copy is stored in internal register status.
+
+ @param radio: structure pointer passed by client.
+ @param offset: register offset.
+ @param buf: buffer to be written to the registers.
+ @param len: num of bytes.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_write_registers(struct tavarua_device *radio,
+ unsigned char offset, unsigned char *buf, int len)
+{
+
+ int i;
+ int retval;
+ retval = set_fm_slave_id(radio);
+
+ if (retval == -ENODEV)
+ return retval;
+
+ FMDBG_I2C("I2C Slave: %x, Write Offset(%x): Data[",
+ radio->marimba->mod_id,
+ offset);
+ retval = marimba_write(radio->marimba, offset, buf, len);
+ if (retval > 0) { /* if write successful, update internal state too */
+ for (i = 0; i < len; i++) {
+ if ((offset+i) < RADIO_REGISTERS) {
+ radio->registers[offset+i] = buf[i];
+ FMDBG_I2C("%x ", radio->registers[offset+i]);
+ }
+ }
+ FMDBG_I2C(" ]\n");
+ }
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: read_data_blocks
+=============================================================================*/
+/**
+ This function reads Raw RDS blocks from Core regs to driver
+ internal regs (shadow copy).
+
+ @param radio: structure pointer passed by client.
+ @param offset: register offset.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int read_data_blocks(struct tavarua_device *radio, unsigned char offset)
+{
+ /* read all 3 RDS blocks */
+ return tavarua_read_registers(radio, offset, RDS_BLOCK*4);
+}
+
+/*=============================================================================
+FUNCTION: tavarua_rds_read
+=============================================================================*/
+/**
+ This is a rds processing function reads that reads Raw RDS blocks from Core
+ regs to driver internal regs (shadow copy). It then fills the V4L2 RDS buffer,
+ which is read by App using JNI interface.
+
+ @param radio: structure pointer passed by client.
+
+ @return None.
+*/
+static void tavarua_rds_read(struct tavarua_device *radio)
+{
+ struct kfifo *rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
+ unsigned char blocknum;
+ unsigned char tmp[3];
+
+ if (read_data_blocks(radio, RAW_RDS) < 0)
+ return;
+ /* copy all four RDS blocks to internal buffer */
+ for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
+ /* Fill the V4L2 RDS buffer */
+ put_unaligned(cpu_to_le16(radio->registers[RAW_RDS +
+ blocknum*RDS_BLOCK]), (unsigned short *) tmp);
+ tmp[2] = blocknum; /* offset name */
+ tmp[2] |= blocknum << 3; /* received offset */
+ tmp[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ kfifo_in_locked(rds_buf, tmp, 3, &radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
+ }
+ /* wake up read queue */
+ if (kfifo_len(rds_buf))
+ wake_up_interruptible(&radio->read_queue);
+
+}
+
+/*=============================================================================
+FUNCTION: request_read_xfr
+=============================================================================*/
+/**
+ This function sets the desired MODE in the XFRCTRL register and also sets the
+ CTRL field to read.
+ This is an asynchronous way of reading the XFR registers. Client would request
+ by setting the desired mode in the XFRCTRL register and then would initiate
+ the actual data register read by calling copy_from_xfr up on SOC signals
+ success.
+
+ NOTE:
+
+ The Data Transfer (XFR) registers are used to pass various data and
+ configuration parameters between the Core and host processor.
+
+ To read from the XFR registers, the host processor must set the desired MODE
+ in the XFRCTRL register and set the CTRL field to read. The Core will then
+ populate the XFRDAT0 - XFRDAT15 registers with the defined mode bytes. The
+ Core will set the TRANSFER interrupt status bit and interrupt the host if the
+ TRANSFERCTRL interrupt control bit is set. The host can then extract the XFR
+ mode bytes once it detects that the Core has updated the registers.
+
+ @param radio: structure pointer passed by client.
+
+ @return Always returns 0.
+*/
+static int request_read_xfr(struct tavarua_device *radio,
+ enum tavarua_xfr_ctrl_t mode){
+
+ tavarua_write_register(radio, XFRCTRL, mode);
+ msleep(TAVARUA_DELAY);
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: copy_from_xfr
+=============================================================================*/
+/**
+ This function is used to read XFR mode bytes once it detects that the Core
+ has updated the registers. It also updates XFR regs to the appropriate
+ internal buffer n bytes.
+
+ NOTE:
+
+ This function should be used in conjuction with request_read_xfr. Refer
+ request_read_xfr for XFR mode transaction details.
+
+ @param radio: structure pointer passed by client.
+ @param buf_type: Index into RDS/Radio event buffer to use.
+ @param len: num of bytes.
+
+ @return Always returns 0.
+*/
+static int copy_from_xfr(struct tavarua_device *radio,
+ enum tavarua_buf_t buf_type, unsigned int n){
+
+ struct kfifo *data_fifo = &radio->data_buf[buf_type];
+ unsigned char *xfr_regs = &radio->registers[XFRCTRL+1];
+ kfifo_in_locked(data_fifo, xfr_regs, n, &radio->buf_lock[buf_type]);
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: write_to_xfr
+=============================================================================*/
+/**
+ This function sets the desired MODE in the XFRCTRL register and it also sets
+ the CTRL field and data to write.
+ This also writes all the XFRDATx registers with the desired input buffer.
+
+ NOTE:
+
+ The Data Transfer (XFR) registers are used to pass various data and
+ configuration parameters between the Core and host processor.
+
+ To write data to the Core, the host processor updates XFRDAT0 - XFRDAT15 with
+ the appropriate mode bytes. The host processor must then set the desired MODE
+ in the XFRCTRL register and set the CTRL field to write. The core will detect
+ that the XFRCTRL register was written to and will read the XFR mode bytes.
+ After reading all the mode bytes, the Core will set the TRANSFER interrupt
+ status bit and interrupt the host if the TRANSFERCTRL interrupt control bit
+ is set.
+
+ @param radio: structure pointer passed by client.
+ @param mode: XFR mode to write in XFRCTRL register.
+ @param buf: buffer to be written to the registers.
+ @param len: num of bytes.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int write_to_xfr(struct tavarua_device *radio, unsigned char mode,
+ char *buf, int len)
+{
+ char buffer[len+1];
+ memcpy(buffer+1, buf, len);
+ /* buffer[0] corresponds to XFRCTRL register
+ set the CTRL bit to 1 for write mode
+ */
+ buffer[0] = ((1<<7) | mode);
+ return tavarua_write_registers(radio, XFRCTRL, buffer, sizeof(buffer));
+}
+
+/*=============================================================================
+FUNCTION: xfr_intf_own
+=============================================================================*/
+/**
+ This function is used to check if there is any pending XFR mode operation.
+ If yes, wait for it to complete, else update the flag to indicate XFR
+ operation is in progress
+
+ @param radio: structure pointer passed by client.
+
+ @return 0 on success.
+ -ETIME on timeout.
+*/
+static int xfr_intf_own(struct tavarua_device *radio)
+{
+
+ mutex_lock(&radio->lock);
+ if (radio->xfr_in_progress) {
+ radio->pending_xfrs[TAVARUA_XFR_SYNC] = 1;
+ mutex_unlock(&radio->lock);
+ if (!wait_for_completion_timeout(&radio->sync_xfr_start,
+ msecs_to_jiffies(wait_timeout)))
+ return -ETIME;
+ } else {
+ FMDBG("gained ownership of xfr\n");
+ radio->xfr_in_progress = 1;
+ mutex_unlock(&radio->lock);
+ }
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: sync_read_xfr
+=============================================================================*/
+/**
+ This function is used to do synchronous XFR read operation.
+
+ @param radio: structure pointer passed by client.
+ @param xfr_type: XFR mode to write in XFRCTRL register.
+ @param buf: buffer to be read from the core.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int sync_read_xfr(struct tavarua_device *radio,
+ enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
+{
+ int retval;
+ retval = xfr_intf_own(radio);
+ if (retval < 0)
+ return retval;
+ retval = tavarua_write_register(radio, XFRCTRL, xfr_type);
+
+ if (retval >= 0) {
+ /* Wait for interrupt i.e. complete
+ (&radio->sync_req_done); call */
+ if (!wait_for_completion_timeout(&radio->sync_req_done,
+ msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
+ retval = -ETIME;
+ } else {
+ memcpy(buf, radio->sync_xfr_regs, XFR_REG_NUM);
+ }
+ }
+ radio->xfr_in_progress = 0;
+ start_pending_xfr(radio);
+ FMDBG("%s: %d\n", __func__, retval);
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: sync_write_xfr
+=============================================================================*/
+/**
+ This function is used to do synchronous XFR write operation.
+
+ @param radio: structure pointer passed by client.
+ @param xfr_type: XFR mode to write in XFRCTRL register.
+ @param buf: buffer to be written to the core.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int sync_write_xfr(struct tavarua_device *radio,
+ enum tavarua_xfr_ctrl_t xfr_type, unsigned char *buf)
+{
+ int retval;
+ retval = xfr_intf_own(radio);
+ if (retval < 0)
+ return retval;
+ retval = write_to_xfr(radio, xfr_type, buf, XFR_REG_NUM);
+
+ if (retval >= 0) {
+ /* Wait for interrupt i.e. complete
+ (&radio->sync_req_done); call */
+ if (!wait_for_completion_timeout(&radio->sync_req_done,
+ msecs_to_jiffies(wait_timeout)) || (retval < 0)) {
+ FMDBG("Write xfr timeout");
+ }
+ }
+ radio->xfr_in_progress = 0;
+ start_pending_xfr(radio);
+ FMDBG("%s: %d\n", __func__, retval);
+ return retval;
+}
+
+
+/*=============================================================================
+FUNCTION: start_pending_xfr
+=============================================================================*/
+/**
+ This function checks if their are any pending xfr interrupts and if
+ the interrupts are either RDS PS, RDS RT, RDS AF, SCANNEXT, SEARCH or SYNC
+ then initiates corresponding read operation. Preference is given to RAW RDS
+ data (SYNC) over processed data (PS, RT, AF, etc) from core.
+
+ @param radio: structure pointer passed by client.
+
+ @return None.
+*/
+static void start_pending_xfr(struct tavarua_device *radio)
+{
+ int i;
+ enum tavarua_xfr_t xfr;
+ for (i = 0; i < TAVARUA_XFR_MAX; i++) {
+ if (radio->pending_xfrs[i]) {
+ radio->xfr_in_progress = 1;
+ xfr = (enum tavarua_xfr_t)i;
+ switch (xfr) {
+ /* priority given to synchronous xfrs */
+ case TAVARUA_XFR_SYNC:
+ complete(&radio->sync_xfr_start);
+ break;
+ /* asynchrnous xfrs */
+ case TAVARUA_XFR_SRCH_LIST:
+ request_read_xfr(radio, RX_STATIONS_0);
+ break;
+ case TAVARUA_XFR_RT_RDS:
+ request_read_xfr(radio, RDS_RT_0);
+ break;
+ case TAVARUA_XFR_PS_RDS:
+ request_read_xfr(radio, RDS_PS_0);
+ break;
+ case TAVARUA_XFR_AF_LIST:
+ request_read_xfr(radio, RDS_AF_0);
+ break;
+ default:
+ FMDERR("%s: Unsupported XFR %d\n",
+ __func__, xfr);
+ }
+ radio->pending_xfrs[i] = 0;
+ FMDBG("resurrect xfr %d\n", i);
+ }
+ }
+ return;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_q_event
+=============================================================================*/
+/**
+ This function is called to queue an event for user.
+
+ NOTE:
+ Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or
+ filled (output) buffer in the driver's incoming queue.
+
+ Pleaes refer tavarua_probe where we register different ioctl's for FM.
+
+ @param radio: structure pointer passed by client.
+ @param event: event to be queued.
+
+ @return None.
+*/
+static void tavarua_q_event(struct tavarua_device *radio,
+ enum tavarua_evt_t event)
+{
+
+ struct kfifo *data_b = &radio->data_buf[TAVARUA_BUF_EVENTS];
+ unsigned char evt = event;
+ FMDBG("updating event_q with event %x\n", event);
+ if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[TAVARUA_BUF_EVENTS]))
+ wake_up_interruptible(&radio->event_queue);
+}
+
+/*=============================================================================
+FUNCTION: tavarua_start_xfr
+=============================================================================*/
+/**
+ This function is called to process interrupts which require multiple XFR
+ operations (RDS search, RDS PS, RDS RT, etc). if any XFR operation is
+ already in progress we store information about pending interrupt, which
+ will be processed in future when current pending operation is done.
+
+ @param radio: structure pointer passed by client.
+ @param pending_id: XFR operation (which requires multiple XFR operations in
+ steps) to start.
+ @param xfr_id: XFR mode to write in XFRCTRL register.
+
+ @return None.
+*/
+static void tavarua_start_xfr(struct tavarua_device *radio,
+ enum tavarua_xfr_t pending_id, enum tavarua_xfr_ctrl_t xfr_id)
+{
+ if (radio->xfr_in_progress)
+ radio->pending_xfrs[pending_id] = 1;
+ else {
+ radio->xfr_in_progress = 1;
+ request_read_xfr(radio, xfr_id);
+ }
+}
+
+/*=============================================================================
+FUNCTION: tavarua_handle_interrupts
+=============================================================================*/
+/**
+ This function processes the interrupts.
+
+ NOTE:
+ tavarua_q_event is used to queue events in App buffer. i.e. App calls the
+ VIDIOC_QBUF ioctl to enqueue an empty (capturing) buffer, which is filled
+ by tavarua_q_event call.
+
+ Any async event that requires multiple steps, i.e. search, RT, PS, etc is
+ handled one at a time. (We preserve other interrupts when processing one).
+ Sync interrupts are given priority.
+
+ @param radio: structure pointer passed by client.
+
+ @return None.
+*/
+static void tavarua_handle_interrupts(struct tavarua_device *radio)
+{
+ int i;
+ int retval;
+ unsigned char xfr_status;
+ if (!radio->handle_irq) {
+ FMDBG("IRQ happend, but I wont handle it\n");
+ return;
+ }
+ mutex_lock(&radio->lock);
+ tavarua_read_registers(radio, STATUS_REG1, STATUS_REG_NUM);
+
+ FMDBG("INTSTAT1 <%x>\n", radio->registers[STATUS_REG1]);
+ FMDBG("INTSTAT2 <%x>\n", radio->registers[STATUS_REG2]);
+ FMDBG("INTSTAT3 <%x>\n", radio->registers[STATUS_REG3]);
+
+ if (radio->registers[STATUS_REG1] & READY) {
+ complete(&radio->sync_req_done);
+ tavarua_q_event(radio, TAVARUA_EVT_RADIO_READY);
+ }
+
+ /* Tune completed */
+ if (radio->registers[STATUS_REG1] & TUNE) {
+ if (radio->tune_req) {
+ complete(&radio->sync_req_done);
+ radio->tune_req = 0;
+ }
+ tavarua_q_event(radio, TAVARUA_EVT_TUNE_SUCC);
+ if (radio->srch_params.get_list) {
+ tavarua_start_xfr(radio, TAVARUA_XFR_SRCH_LIST,
+ RX_STATIONS_0);
+ }
+ radio->srch_params.get_list = 0;
+ radio->xfr_in_progress = 0;
+ radio->xfr_bytes_left = 0;
+ for (i = 0; i < TAVARUA_BUF_MAX; i++) {
+ if (i >= TAVARUA_BUF_RT_RDS)
+ kfifo_reset(&radio->data_buf[i]);
+ }
+ for (i = 0; i < TAVARUA_XFR_MAX; i++) {
+ if (i >= TAVARUA_XFR_RT_RDS)
+ radio->pending_xfrs[i] = 0;
+ }
+ retval = tavarua_read_registers(radio, TUNECTRL, 1);
+ /* send to user station parameters */
+ if (retval > -1) {
+ /* Signal strength */
+ if (!(radio->registers[TUNECTRL] & SIGSTATE))
+ tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
+ else
+ tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
+ /* mono/stereo */
+ if ((radio->registers[TUNECTRL] & MOSTSTATE))
+ tavarua_q_event(radio, TAVARUA_EVT_STEREO);
+ else
+ tavarua_q_event(radio, TAVARUA_EVT_MONO);
+ /* is RDS available */
+ if ((radio->registers[TUNECTRL] & RDSSYNC))
+ tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
+ else
+ tavarua_q_event(radio,
+ TAVARUA_EVT_RDS_NOT_AVAIL);
+ }
+
+ } else {
+ if (radio->tune_req) {
+ FMDERR("Tune INT is pending\n");
+ mutex_unlock(&radio->lock);
+ return;
+ }
+ }
+ /* Search completed (read FREQ) */
+ if (radio->registers[STATUS_REG1] & SEARCH)
+ tavarua_q_event(radio, TAVARUA_EVT_SEEK_COMPLETE);
+
+ /* Scanning for next station */
+ if (radio->registers[STATUS_REG1] & SCANNEXT)
+ tavarua_q_event(radio, TAVARUA_EVT_SCAN_NEXT);
+
+ /* Signal indicator change (read SIGSTATE) */
+ if (radio->registers[STATUS_REG1] & SIGNAL) {
+ retval = tavarua_read_registers(radio, TUNECTRL, 1);
+ if (retval > -1) {
+ if (!(radio->registers[TUNECTRL] & SIGSTATE))
+ tavarua_q_event(radio, TAVARUA_EVT_BELOW_TH);
+ else
+ tavarua_q_event(radio, TAVARUA_EVT_ABOVE_TH);
+ }
+ }
+
+ /* RDS synchronization state change (read RDSSYNC) */
+ if (radio->registers[STATUS_REG1] & SYNC) {
+ retval = tavarua_read_registers(radio, TUNECTRL, 1);
+ if (retval > -1) {
+ if ((radio->registers[TUNECTRL] & RDSSYNC))
+ tavarua_q_event(radio, TAVARUA_EVT_RDS_AVAIL);
+ else
+ tavarua_q_event(radio,
+ TAVARUA_EVT_RDS_NOT_AVAIL);
+ }
+ }
+
+ /* Audio Control indicator (read AUDIOIND) */
+ if (radio->registers[STATUS_REG1] & AUDIO) {
+ retval = tavarua_read_registers(radio, AUDIOIND, 1);
+ if (retval > -1) {
+ if ((radio->registers[AUDIOIND] & 0x01))
+ tavarua_q_event(radio, TAVARUA_EVT_STEREO);
+ else
+ tavarua_q_event(radio, TAVARUA_EVT_MONO);
+ }
+ }
+
+ /* interrupt register 2 */
+
+ /* New unread RDS data group available */
+ if (radio->registers[STATUS_REG2] & RDSDAT) {
+ FMDBG("Raw RDS Available\n");
+ tavarua_rds_read(radio);
+ tavarua_q_event(radio, TAVARUA_EVT_NEW_RAW_RDS);
+ }
+
+ /* New RDS Program Service Table available */
+ if (radio->registers[STATUS_REG2] & RDSPS) {
+ FMDBG("New PS RDS\n");
+ tavarua_start_xfr(radio, TAVARUA_XFR_PS_RDS, RDS_PS_0);
+ }
+
+ /* New RDS Radio Text available */
+ if (radio->registers[STATUS_REG2] & RDSRT) {
+ FMDBG("New RT RDS\n");
+ tavarua_start_xfr(radio, TAVARUA_XFR_RT_RDS, RDS_RT_0);
+ }
+
+ /* New RDS Radio Text available */
+ if (radio->registers[STATUS_REG2] & RDSAF) {
+ FMDBG("New AF RDS\n");
+ tavarua_start_xfr(radio, TAVARUA_XFR_AF_LIST, RDS_AF_0);
+ }
+ /* Trasmitter an RDS Group */
+ if (radio->registers[STATUS_REG2] & TXRDSDAT) {
+ FMDBG("New TXRDSDAT\n");
+ tavarua_q_event(radio, TAVARUA_EVT_TXRDSDAT);
+ }
+
+ /* Complete RDS buffer is available for transmission */
+ if (radio->registers[STATUS_REG2] & TXRDSDONE) {
+ FMDBG("New TXRDSDAT\n");
+ tavarua_q_event(radio, TAVARUA_EVT_TXRDSDONE);
+ }
+ /* interrupt register 3 */
+
+ /* Data transfer (XFR) completed */
+ if (radio->registers[STATUS_REG3] & TRANSFER) {
+ FMDBG("XFR Interrupt\n");
+ tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
+ FMDBG("XFRCTRL IS: %x\n", radio->registers[XFRCTRL]);
+ xfr_status = radio->registers[XFRCTRL];
+ switch (xfr_status) {
+ case RDS_PS_0:
+ FMDBG("PS Header\n");
+ copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, 5);
+ radio->xfr_bytes_left = (radio->registers[XFRCTRL+1] &
+ 0x0F) * 8;
+ FMDBG("PS RDS Length: %d\n", radio->xfr_bytes_left);
+ if ((radio->xfr_bytes_left > 0) &&
+ (radio->xfr_bytes_left < 97))
+ request_read_xfr(radio, RDS_PS_1);
+ else
+ radio->xfr_in_progress = 0;
+ break;
+ case RDS_PS_1:
+ case RDS_PS_2:
+ case RDS_PS_3:
+ case RDS_PS_4:
+ case RDS_PS_5:
+ case RDS_PS_6:
+ FMDBG("PS Data\n");
+ copy_from_xfr(radio, TAVARUA_BUF_PS_RDS, XFR_REG_NUM);
+ radio->xfr_bytes_left -= XFR_REG_NUM;
+ if (radio->xfr_bytes_left > 0) {
+ if ((xfr_status + 1) > RDS_PS_6)
+ request_read_xfr(radio, RDS_PS_6);
+ else
+ request_read_xfr(radio, xfr_status+1);
+ } else {
+ radio->xfr_in_progress = 0;
+ tavarua_q_event(radio, TAVARUA_EVT_NEW_PS_RDS);
+ }
+ break;
+ case RDS_RT_0:
+ FMDBG("RT Header\n");
+ copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, 5);
+ radio->xfr_bytes_left = radio->registers[XFRCTRL+1]
+ & 0x7F;
+ FMDBG("RT RDS Length: %d\n", radio->xfr_bytes_left);
+ /*RT_1 to RT_4 16 byte registers so 64 bytes */
+ if ((radio->xfr_bytes_left > 0)
+ && (radio->xfr_bytes_left < 65))
+ request_read_xfr(radio, RDS_RT_1);
+ break;
+ case RDS_RT_1:
+ case RDS_RT_2:
+ case RDS_RT_3:
+ case RDS_RT_4:
+ FMDBG("xfr interrupt RT data\n");
+ copy_from_xfr(radio, TAVARUA_BUF_RT_RDS, XFR_REG_NUM);
+ radio->xfr_bytes_left -= XFR_REG_NUM;
+ if (radio->xfr_bytes_left > 0)
+ request_read_xfr(radio, xfr_status+1);
+ else {
+ radio->xfr_in_progress = 0;
+ tavarua_q_event(radio, TAVARUA_EVT_NEW_RT_RDS);
+ }
+ break;
+ case RDS_AF_0:
+ copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
+ XFR_REG_NUM);
+ radio->xfr_bytes_left = radio->registers[XFRCTRL+5]-11;
+ if (radio->xfr_bytes_left > 0)
+ request_read_xfr(radio, RDS_AF_1);
+ else
+ radio->xfr_in_progress = 0;
+ break;
+ case RDS_AF_1:
+ copy_from_xfr(radio, TAVARUA_BUF_AF_LIST,
+ radio->xfr_bytes_left);
+ tavarua_q_event(radio, TAVARUA_EVT_NEW_AF_LIST);
+ radio->xfr_in_progress = 0;
+ break;
+ case RX_CONFIG:
+ case RADIO_CONFIG:
+ case RDS_CONFIG:
+ memcpy(radio->sync_xfr_regs,
+ &radio->registers[XFRCTRL+1], XFR_REG_NUM);
+ complete(&radio->sync_req_done);
+ break;
+ case RX_STATIONS_0:
+ FMDBG("Search list has %d stations\n",
+ radio->registers[XFRCTRL+1]);
+ radio->xfr_bytes_left = radio->registers[XFRCTRL+1]*2;
+ if (radio->xfr_bytes_left > 14) {
+ copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+ XFR_REG_NUM);
+ request_read_xfr(radio, RX_STATIONS_1);
+ } else if (radio->xfr_bytes_left) {
+ FMDBG("In else RX_STATIONS_0\n");
+ copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+ radio->xfr_bytes_left+1);
+ tavarua_q_event(radio,
+ TAVARUA_EVT_NEW_SRCH_LIST);
+ radio->xfr_in_progress = 0;
+ }
+ break;
+ case RX_STATIONS_1:
+ FMDBG("In RX_STATIONS_1");
+ copy_from_xfr(radio, TAVARUA_BUF_SRCH_LIST,
+ radio->xfr_bytes_left);
+ tavarua_q_event(radio, TAVARUA_EVT_NEW_SRCH_LIST);
+ radio->xfr_in_progress = 0;
+ break;
+ case PHY_TXGAIN:
+ FMDBG("read PHY_TXGAIN is successful");
+ complete(&radio->sync_req_done);
+ break;
+ case (0x80 | RX_CONFIG):
+ case (0x80 | RADIO_CONFIG):
+ case (0x80 | RDS_CONFIG):
+ case (0x80 | INT_CTRL):
+ complete(&radio->sync_req_done);
+ break;
+ case (0x80 | RDS_RT_0):
+ FMDBG("RT Header Sent\n");
+ complete(&radio->sync_req_done);
+ break;
+ case (0x80 | RDS_RT_1):
+ case (0x80 | RDS_RT_2):
+ case (0x80 | RDS_RT_3):
+ case (0x80 | RDS_RT_4):
+ FMDBG("xfr interrupt RT data Sent\n");
+ complete(&radio->sync_req_done);
+ break;
+ /*TX Specific transfer */
+ case (0x80 | RDS_PS_0):
+ FMDBG("PS Header Sent\n");
+ complete(&radio->sync_req_done);
+ break;
+ case (0x80 | RDS_PS_1):
+ case (0x80 | RDS_PS_2):
+ case (0x80 | RDS_PS_3):
+ case (0x80 | RDS_PS_4):
+ case (0x80 | RDS_PS_5):
+ case (0x80 | RDS_PS_6):
+ FMDBG("xfr interrupt PS data Sent\n");
+ complete(&radio->sync_req_done);
+ break;
+ case (0x80 | PHY_TXGAIN):
+ FMDBG("write PHY_TXGAIN is successful");
+ complete(&radio->sync_req_done);
+ break;
+ default:
+ FMDERR("UNKNOWN XFR = %d\n", xfr_status);
+ }
+ if (!radio->xfr_in_progress)
+ start_pending_xfr(radio);
+
+ }
+
+ /* Error occurred. Read ERRCODE to determine cause */
+ if (radio->registers[STATUS_REG3] & ERROR) {
+#ifdef FM_DEBUG
+ unsigned char xfr_buf[XFR_REG_NUM];
+ int retval = sync_read_xfr(radio, ERROR_CODE, xfr_buf);
+ FMDBG("retval of ERROR_CODE read : %d\n", retval);
+#endif
+ FMDERR("ERROR STATE\n");
+ }
+
+ mutex_unlock(&radio->lock);
+ FMDBG("Work is done\n");
+
+}
+
+/*=============================================================================
+FUNCTION: read_int_stat
+=============================================================================*/
+/**
+ This function is scheduled whenever there is an interrupt pending in interrupt
+ queue. i.e. kfmradio.
+
+ Whenever there is a GPIO interrupt, a delayed work will be queued in to the
+ 'kfmradio' work queue. Upon execution of this work in the queue, a a call
+ to read_int_stat function will be made , which would in turn handle the
+ interrupts by reading the INTSTATx registers.
+ NOTE:
+ Tasks to be run out of a workqueue need to be packaged in a struct
+ work_struct structure.
+
+ @param work: work_struct structure.
+
+ @return None.
+*/
+static void read_int_stat(struct work_struct *work)
+{
+ struct tavarua_device *radio = container_of(work,
+ struct tavarua_device, work.work);
+ tavarua_handle_interrupts(radio);
+}
+
+/*************************************************************************
+ * irq helper functions
+ ************************************************************************/
+
+/*=============================================================================
+FUNCTION: tavarua_request_irq
+=============================================================================*/
+/**
+ This function is called to acquire a FM GPIO and enable FM interrupts.
+
+ @param radio: structure pointer passed by client.
+
+ @return 0 if success else otherwise.
+*/
+static int tavarua_request_irq(struct tavarua_device *radio)
+{
+ int retval;
+ int irq = radio->pdata->irq;
+ if (radio == NULL)
+ return -EINVAL;
+
+ /* A workqueue created with create_workqueue() will have one worker thread
+ * for each CPU on the system; create_singlethread_workqueue(), instead,
+ * creates a workqueue with a single worker process. The name of the queue
+ * is limited to ten characters; it is only used for generating the "command"
+ * for the kernel thread(s) (which can be seen in ps or top).
+ */
+ radio->wqueue = create_singlethread_workqueue("kfmradio");
+ if (!radio->wqueue)
+ return -ENOMEM;
+ /* allocate an interrupt line */
+ /* On success, request_irq() returns 0 if everything goes as
+ planned. Your interrupt handler will start receiving its
+ interrupts immediately. On failure, request_irq()
+ returns:
+ -EINVAL
+ The IRQ number you requested was either
+ invalid or reserved, or your passed a NULL
+ pointer for the handler() parameter.
+
+ -EBUSY The IRQ you requested is already being
+ handled, and the IRQ cannot be shared.
+
+ -ENXIO The m68k returns this value for an invalid
+ IRQ number.
+ */
+ /* Use request_any_context_irq, So that it might work for nested or
+ nested interrupts. in MSM8x60, FM is connected to PMIC GPIO and it
+ is a nested interrupt*/
+ retval = request_any_context_irq(irq, tavarua_isr,
+ IRQ_TYPE_EDGE_FALLING, "fm interrupt", radio);
+ if (retval < 0) {
+ FMDERR("Couldn't acquire FM gpio %d\n", irq);
+ return retval;
+ } else {
+ FMDBG("FM GPIO %d registered\n", irq);
+ }
+ retval = enable_irq_wake(irq);
+ if (retval < 0) {
+ FMDERR("Could not enable FM interrupt\n ");
+ free_irq(irq , radio);
+ }
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_disable_irq
+=============================================================================*/
+/**
+ This function is called to disable FM irq and free up FM interrupt handling
+ resources.
+
+ @param radio: structure pointer passed by client.
+
+ @return 0 if success else otherwise.
+*/
+static int tavarua_disable_irq(struct tavarua_device *radio)
+{
+ int irq;
+ if (!radio)
+ return -EINVAL;
+ irq = radio->pdata->irq;
+ disable_irq_wake(irq);
+ cancel_delayed_work_sync(&radio->work);
+ flush_workqueue(radio->wqueue);
+ free_irq(irq, radio);
+ destroy_workqueue(radio->wqueue);
+ return 0;
+}
+
+/*************************************************************************
+ * fops/IOCTL helper functions
+ ************************************************************************/
+
+/*=============================================================================
+FUNCTION: tavarua_search
+=============================================================================*/
+/**
+ This interface sets the search control features.
+
+ @param radio: structure pointer passed by client.
+ @param on: The value of a control.
+ @param dir: FM search direction.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_search(struct tavarua_device *radio, int on, int dir)
+{
+ enum search_t srch = radio->registers[SRCHCTRL] & SRCH_MODE;
+
+ FMDBG("In tavarua_search\n");
+ if (on) {
+ radio->registers[SRCHRDS1] = 0x00;
+ radio->registers[SRCHRDS2] = 0x00;
+ /* Set freq band */
+ switch (srch) {
+ case SCAN_FOR_STRONG:
+ case SCAN_FOR_WEAK:
+ radio->srch_params.get_list = 1;
+ radio->registers[SRCHRDS2] =
+ radio->srch_params.preset_num;
+ break;
+ case RDS_SEEK_PTY:
+ case RDS_SCAN_PTY:
+ radio->registers[SRCHRDS2] =
+ radio->srch_params.srch_pty;
+ break;
+ case RDS_SEEK_PI:
+ radio->registers[SRCHRDS1] =
+ (radio->srch_params.srch_pi & 0xFF00) >> 8;
+ radio->registers[SRCHRDS2] =
+ (radio->srch_params.srch_pi & 0x00FF);
+ break;
+ default:
+ break;
+ }
+ radio->registers[SRCHCTRL] |= SRCH_ON;
+ } else {
+ radio->registers[SRCHCTRL] &= ~SRCH_ON;
+ radio->srch_params.get_list = 0;
+ }
+ radio->registers[SRCHCTRL] = (dir << 3) |
+ (radio->registers[SRCHCTRL] & 0xF7);
+
+ FMDBG("SRCHCTRL <%x>\n", radio->registers[SRCHCTRL]);
+ FMDBG("Search Started\n");
+ return tavarua_write_registers(radio, SRCHRDS1,
+ &radio->registers[SRCHRDS1], 3);
+}
+
+/*=============================================================================
+FUNCTION: tavarua_set_region
+=============================================================================*/
+/**
+ This interface configures the FM radio.
+
+ @param radio: structure pointer passed by client.
+ @param req_region: FM band types. These types defines the FM band minimum and
+ maximum frequencies in the FM band.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_set_region(struct tavarua_device *radio,
+ int req_region)
+{
+ int retval = 0;
+ unsigned char xfr_buf[XFR_REG_NUM];
+ unsigned char value;
+ unsigned int spacing = 0.100 * FREQ_MUL;
+ unsigned int band_low, band_high;
+ unsigned int low_band_limit = 76.0 * FREQ_MUL;
+ enum tavarua_region_t region = req_region;
+
+ /* Set freq band */
+ switch (region) {
+ case TAVARUA_REGION_US:
+ case TAVARUA_REGION_EU:
+ case TAVARUA_REGION_JAPAN_WIDE:
+ SET_REG_FIELD(radio->registers[RDCTRL], 0,
+ RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
+ break;
+ case TAVARUA_REGION_JAPAN:
+ SET_REG_FIELD(radio->registers[RDCTRL], 1,
+ RDCTRL_BAND_OFFSET, RDCTRL_BAND_MASK);
+ break;
+ default:
+ retval = sync_read_xfr(radio, RADIO_CONFIG, xfr_buf);
+ if (retval < 0) {
+ FMDERR("failed to get RADIO_CONFIG\n");
+ return retval;
+ }
+ band_low = (radio->region_params.band_low -
+ low_band_limit) / spacing;
+ band_high = (radio->region_params.band_high -
+ low_band_limit) / spacing;
+ FMDBG("low_band: %x, high_band: %x\n", band_low, band_high);
+ xfr_buf[0] = band_low >> 8;
+ xfr_buf[1] = band_low & 0xFF;
+ xfr_buf[2] = band_high >> 8;
+ xfr_buf[3] = band_high & 0xFF;
+ retval = sync_write_xfr(radio, RADIO_CONFIG, xfr_buf);
+ if (retval < 0) {
+ FMDERR("Could not set regional settings\n");
+ return retval;
+ }
+ break;
+ }
+
+ /* Set channel spacing */
+ switch (region) {
+ case TAVARUA_REGION_US:
+ case TAVARUA_REGION_EU:
+ value = 0;
+ break;
+ case TAVARUA_REGION_JAPAN:
+ value = 1;
+ break;
+ case TAVARUA_REGION_JAPAN_WIDE:
+ value = 2;
+ break;
+ default:
+ value = radio->region_params.spacing;
+ }
+
+ SET_REG_FIELD(radio->registers[RDCTRL], value,
+ RDCTRL_CHSPACE_OFFSET, RDCTRL_CHSPACE_MASK);
+
+ /* Set De-emphasis and soft band range*/
+ switch (region) {
+ case TAVARUA_REGION_US:
+ case TAVARUA_REGION_JAPAN:
+ case TAVARUA_REGION_JAPAN_WIDE:
+ value = 0;
+ break;
+ case TAVARUA_REGION_EU:
+ value = 1;
+ break;
+ default:
+ value = radio->region_params.emphasis;
+ }
+
+ SET_REG_FIELD(radio->registers[RDCTRL], value,
+ RDCTRL_DEEMPHASIS_OFFSET, RDCTRL_DEEMPHASIS_MASK);
+
+ /* set RDS standard */
+ switch (region) {
+ default:
+ value = radio->region_params.rds_std;
+ break;
+ case TAVARUA_REGION_US:
+ value = 0;
+ break;
+ case TAVARUA_REGION_EU:
+ value = 1;
+ break;
+ }
+ SET_REG_FIELD(radio->registers[RDSCTRL], value,
+ RDSCTRL_STANDARD_OFFSET, RDSCTRL_STANDARD_MASK);
+
+ FMDBG("RDSCTRLL %x\n", radio->registers[RDSCTRL]);
+ retval = tavarua_write_register(radio, RDSCTRL,
+ radio->registers[RDSCTRL]);
+ if (retval < 0)
+ return retval;
+
+ FMDBG("RDCTRL: %x\n", radio->registers[RDCTRL]);
+ retval = tavarua_write_register(radio, RDCTRL,
+ radio->registers[RDCTRL]);
+ if (retval < 0) {
+ FMDERR("Could not set region in rdctrl\n");
+ return retval;
+ }
+
+ /* setting soft band */
+ switch (region) {
+ case TAVARUA_REGION_US:
+ case TAVARUA_REGION_EU:
+ radio->region_params.band_low = 87.5 * FREQ_MUL;
+ radio->region_params.band_high = 108 * FREQ_MUL;
+ break;
+ case TAVARUA_REGION_JAPAN:
+ radio->region_params.band_low = 76 * FREQ_MUL;
+ radio->region_params.band_high = 90 * FREQ_MUL;
+ break;
+ case TAVARUA_REGION_JAPAN_WIDE:
+ radio->region_params.band_low = 90 * FREQ_MUL;
+ radio->region_params.band_high = 108 * FREQ_MUL;
+ break;
+ default:
+ break;
+ }
+ radio->region_params.region = region;
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_get_freq
+=============================================================================*/
+/**
+ This interface gets the current frequency.
+
+ @param radio: structure pointer passed by client.
+ @param freq: struct v4l2_frequency. This will be set to the resultant
+ frequency in units of 62.5 kHz on success.
+
+ NOTE:
+ To get the current tuner or modulator radio frequency applications set the
+ tuner field of a struct v4l2_frequency to the respective tuner or modulator
+ number (only input devices have tuners, only output devices have modulators),
+ zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
+ pointer to this structure. The driver stores the current frequency in the
+ frequency field.
+
+ Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner or
+ struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set, in
+ units of 62.5 Hz.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_get_freq(struct tavarua_device *radio,
+ struct v4l2_frequency *freq)
+{
+ int retval;
+ unsigned short chan;
+ unsigned int band_bottom;
+ unsigned int spacing;
+ band_bottom = radio->region_params.band_low;
+ spacing = 0.100 * FREQ_MUL;
+ /* read channel */
+ retval = tavarua_read_registers(radio, FREQ, 2);
+ chan = radio->registers[FREQ];
+
+ /* Frequency (MHz) = 100 (kHz) x Channel + Bottom of Band (MHz) */
+ freq->frequency = spacing * chan + band_bottom;
+ if (radio->registers[TUNECTRL] & ADD_OFFSET)
+ freq->frequency += 800;
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_set_freq
+=============================================================================*/
+/**
+ This interface sets the current frequency.
+
+ @param radio: structure pointer passed by client.
+ @param freq: desired frequency sent by the client in 62.5 kHz units.
+
+ NOTE:
+ To change the current tuner or modulator radio frequency, applications
+ initialize the tuner, type and frequency fields, and the reserved array of a
+ struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer to
+ this structure. When the requested frequency is not possible the driver
+ assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
+ write-only ioctl, it does not return the actual new frequency.
+
+ Tuning frequency is in units of 62.5 kHz, or if the struct v4l2_tuner
+ or struct v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set,
+ in units of 62.5 Hz.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_set_freq(struct tavarua_device *radio, unsigned int freq)
+{
+
+ unsigned int band_bottom;
+ unsigned char chan;
+ unsigned char cmd[] = {0x00, 0x00};
+ unsigned int spacing;
+ int retval;
+ band_bottom = radio->region_params.band_low;
+ spacing = 0.100 * FREQ_MUL;
+ if ((freq % 1600) == 800) {
+ cmd[1] = ADD_OFFSET;
+ freq -= 800;
+ }
+ /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / 100 (kHz) */
+ chan = (freq - band_bottom) / spacing;
+
+ cmd[0] = chan;
+ cmd[1] |= TUNE_STATION;
+ radio->tune_req = 1;
+ retval = tavarua_write_registers(radio, FREQ, cmd, 2);
+ if (retval < 0)
+ radio->tune_req = 0;
+ return retval;
+
+}
+
+/**************************************************************************
+ * File Operations Interface
+ *************************************************************************/
+
+/*=============================================================================
+FUNCTION: tavarua_fops_read
+=============================================================================*/
+/**
+ This function is called when a process, which already opened the dev file,
+ attempts to read from it.
+
+ In case of tavarua driver, it is called to read RDS data.
+
+ @param file: file descriptor.
+ @param buf: The buffer to fill with data.
+ @param count: The length of the buffer in bytes.
+ @param ppos: Our offset in the file.
+
+ @return The number of bytes put into the buffer on sucess.
+ -EFAULT if there is no access to user buffer
+*/
+static ssize_t tavarua_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ struct kfifo *rds_buf = &radio->data_buf[TAVARUA_BUF_RAW_RDS];
+
+ /* block if no new data available */
+ while (!kfifo_len(rds_buf)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+ if (wait_event_interruptible(radio->read_queue,
+ kfifo_len(rds_buf)) < 0)
+ return -EINTR;
+ }
+
+ /* calculate block count from byte count */
+ count /= BYTES_PER_BLOCK;
+
+
+ /* check if we can write to the user buffer */
+ if (!access_ok(VERIFY_WRITE, buf, count*BYTES_PER_BLOCK))
+ return -EFAULT;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ return kfifo_out_locked(rds_buf, buf, count*BYTES_PER_BLOCK,
+ &radio->buf_lock[TAVARUA_BUF_RAW_RDS]);
+}
+
+/*=============================================================================
+FUNCTION: tavarua_fops_write
+=============================================================================*/
+/**
+ This function is called when a process, which already opened the dev file,
+ attempts to write to it.
+
+ In case of tavarua driver, it is called to write RDS data to host.
+
+ @param file: file descriptor.
+ @param buf: The buffer which has data to write.
+ @param count: The length of the buffer.
+ @param ppos: Our offset in the file.
+
+ @return The number of bytes written from the buffer.
+*/
+static ssize_t tavarua_fops_write(struct file *file, const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ int bytes_to_copy;
+ int bytes_copied = 0;
+ int bytes_left;
+ int chunk_index = 0;
+ unsigned char tx_data[XFR_REG_NUM];
+ /* Disable TX of this type first */
+ switch (radio->tx_mode) {
+ case TAVARUA_TX_RT:
+ bytes_left = min((int)count, MAX_RT_LENGTH);
+ tx_data[1] = 0;
+ break;
+ case TAVARUA_TX_PS:
+ bytes_left = min((int)count, MAX_PS_LENGTH);
+ tx_data[4] = 0;
+ break;
+ default:
+ FMDERR("%s: Unknown TX mode\n", __func__);
+ return -1;
+ }
+ retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
+ if (retval < 0)
+ return retval;
+
+ /* send payload to FM hardware */
+ while (bytes_left) {
+ chunk_index++;
+ bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+ if (copy_from_user(tx_data, data + bytes_copied, bytes_to_copy))
+ return -EFAULT;
+ retval = sync_write_xfr(radio, radio->tx_mode +
+ chunk_index, tx_data);
+ if (retval < 0)
+ return retval;
+
+ bytes_copied += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ }
+
+ /* send the header */
+ switch (radio->tx_mode) {
+ case TAVARUA_TX_RT:
+ FMDBG("Writing RT header\n");
+ tx_data[0] = bytes_copied;
+ tx_data[1] = TX_ON | 0x03; /* on | PTY */
+ tx_data[2] = 0x12; /* PI high */
+ tx_data[3] = 0x34; /* PI low */
+ break;
+ case TAVARUA_TX_PS:
+ FMDBG("Writing PS header\n");
+ tx_data[0] = chunk_index;
+ tx_data[1] = 0x03; /* PTY */
+ tx_data[2] = 0x12; /* PI high */
+ tx_data[3] = 0x34; /* PI low */
+ tx_data[4] = TX_ON | 0x01;
+ break;
+ default:
+ FMDERR("%s: Unknown TX mode\n", __func__);
+ return -1;
+ }
+ retval = sync_write_xfr(radio, radio->tx_mode, tx_data);
+ if (retval < 0)
+ return retval;
+ FMDBG("done writing: %d\n", retval);
+ return bytes_copied;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_fops_open
+=============================================================================*/
+/**
+ This function is called when a process tries to open the device file, like
+ "cat /dev/mycharfile"
+
+ @param file: file descriptor.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_fops_open(struct file *file)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = -ENODEV;
+ unsigned char value;
+ /* FM core bring up */
+ int i = 0;
+ char fm_ctl0_part1[] = { 0xCA, 0xCE, 0xD6 };
+ char fm_ctl1[] = { 0x03 };
+ char fm_ctl0_part2[] = { 0xB6, 0xB7 };
+ char buffer[] = {0x00, 0x48, 0x8A, 0x8E, 0x97, 0xB7};
+ int bahama_present = -ENODEV;
+
+ mutex_lock(&radio->lock);
+ if (radio->users) {
+ mutex_unlock(&radio->lock);
+ return -EBUSY;
+ } else {
+ radio->users++;
+ }
+ mutex_unlock(&radio->lock);
+
+ /* initial gpio pin config & Power up */
+ retval = radio->pdata->fm_setup(radio->pdata);
+ if (retval) {
+ printk(KERN_ERR "%s: failed config gpio & pmic\n", __func__);
+ goto open_err_setup;
+ }
+ if (radio->pdata->config_i2s_gpio != NULL) {
+ retval = radio->pdata->config_i2s_gpio(FM_I2S_ON);
+ if (retval) {
+ printk(KERN_ERR "%s: failed config gpio\n", __func__);
+ goto config_i2s_err;
+ }
+ }
+ /* enable irq */
+ retval = tavarua_request_irq(radio);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: failed to request irq\n", __func__);
+ goto open_err_req_irq;
+ }
+ /* call top level marimba interface here to enable FM core */
+ FMDBG("initializing SoC\n");
+
+ bahama_present = is_bahama();
+
+ if (bahama_present == -ENODEV)
+ return -ENODEV;
+
+ if (bahama_present)
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ else
+ radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+
+ value = FM_ENABLE;
+ retval = marimba_write_bit_mask(radio->marimba,
+ MARIMBA_XO_BUFF_CNTRL, &value, 1, value);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n",
+ __func__);
+ goto open_err_all;
+ }
+
+
+ /* Bring up FM core */
+ if (bahama_present) {
+
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ /* Read the Bahama version*/
+ retval = marimba_read_bit_mask(radio->marimba,
+ 0x00, &bahama_version, 1, 0x1F);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: version read failed",
+ __func__);
+ goto open_err_all;
+ }
+ /* Check for Bahama V2 variant*/
+ if (bahama_version == 0x09) {
+
+ /* In case of Bahama v2, forcefully enable the
+ * internal analog and digital voltage controllers
+ */
+ value = 0x06;
+ /* value itself used as mask in these writes*/
+ retval = marimba_write_bit_mask(radio->marimba,
+ BAHAMA_LDO_DREG_CTL0, &value, 1, value);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:0xF0 write failed\n",
+ __func__);
+ goto open_err_all;
+ }
+ value = 0x86;
+ retval = marimba_write_bit_mask(radio->marimba,
+ BAHAMA_LDO_AREG_CTL0, &value, 1, value);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:0xF4 write failed\n",
+ __func__);
+ goto open_err_all;
+ }
+ }
+
+ /*write FM mode*/
+ retval = tavarua_write_register(radio, BAHAMA_FM_MODE_REG,
+ BAHAMA_FM_MODE_NORMAL);
+ if (retval < 0) {
+ printk(KERN_ERR "failed to set the FM mode: %d\n",
+ retval);
+ goto open_err_all;
+ }
+ /*Write first sequence of bytes to FM_CTL0*/
+ for (i = 0; i < 3; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL0:set-1 failure: %d\n",
+ retval);
+ goto open_err_all;
+ }
+ }
+ /*Write the FM_CTL1 sequence*/
+ for (i = 0; i < 1; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL1 write failure: %d\n",
+ retval);
+ goto open_err_all;
+ }
+ }
+ /*Write second sequence of bytes to FM_CTL0*/
+ for (i = 0; i < 2; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL0:set-2 failure: %d\n",
+ retval);
+ goto open_err_all;
+ }
+ }
+ } else {
+ retval = tavarua_write_registers(radio, LEAKAGE_CNTRL,
+ buffer, 6);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: failed to bring up FM Core\n",
+ __func__);
+ goto open_err_all;
+ }
+ }
+ /* Wait for interrupt i.e. complete(&radio->sync_req_done); call */
+ /*Initialize the completion variable for
+ for the proper behavior*/
+ init_completion(&radio->sync_req_done);
+ if (!wait_for_completion_timeout(&radio->sync_req_done,
+ msecs_to_jiffies(wait_timeout))) {
+ retval = -1;
+ FMDERR("Timeout waiting for initialization\n");
+ }
+
+ /* get Chip ID */
+ retval = tavarua_write_register(radio, XFRCTRL, CHIPID);
+ if (retval < 0)
+ goto open_err_all;
+ msleep(TAVARUA_DELAY);
+ tavarua_read_registers(radio, XFRCTRL, XFR_REG_NUM+1);
+ if (radio->registers[XFRCTRL] != CHIPID)
+ goto open_err_all;
+
+ radio->chipID = (radio->registers[XFRCTRL+2] << 24) |
+ (radio->registers[XFRCTRL+5] << 16) |
+ (radio->registers[XFRCTRL+6] << 8) |
+ (radio->registers[XFRCTRL+7]);
+
+ printk(KERN_WARNING DRIVER_NAME ": Chip ID %x\n", radio->chipID);
+ if (radio->chipID == MARIMBA_A0) {
+ printk(KERN_WARNING DRIVER_NAME ": Unsupported hardware: %x\n",
+ radio->chipID);
+ retval = -1;
+ goto open_err_all;
+ }
+
+ radio->handle_irq = 0;
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ marimba_set_fm_status(radio->marimba, true);
+ return 0;
+
+
+open_err_all:
+ /*Disable FM in case of error*/
+ value = 0x00;
+ marimba_write_bit_mask(radio->marimba, MARIMBA_XO_BUFF_CNTRL,
+ &value, 1, value);
+ tavarua_disable_irq(radio);
+open_err_req_irq:
+ if (radio->pdata->config_i2s_gpio != NULL)
+ radio->pdata->config_i2s_gpio(FM_I2S_OFF);
+config_i2s_err:
+ radio->pdata->fm_shutdown(radio->pdata);
+open_err_setup:
+ radio->handle_irq = 1;
+ radio->users = 0;
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_fops_release
+=============================================================================*/
+/**
+ This function is called when a process closes the device file.
+
+ @param file: file descriptor.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_fops_release(struct file *file)
+{
+ int retval;
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ unsigned char value;
+ int i = 0;
+ /*FM Core shutdown sequence for Bahama*/
+ char fm_ctl0_part1[] = { 0xB7 };
+ char fm_ctl1[] = { 0x03 };
+ char fm_ctl0_part2[] = { 0x9F, 0x48, 0x02 };
+ int bahama_present = -ENODEV;
+ /*FM Core shutdown sequence for Marimba*/
+ char buffer[] = {0x18, 0xB7, 0x48};
+ bool bt_status = false;
+ int index;
+ /* internal regulator controllers DREG_CTL0, AREG_CTL0
+ * has to be kept in the valid state based on the bt status.
+ * 1st row is the state when no clients are active,
+ * and the second when bt is in on state.
+ */
+ char internal_vreg_ctl[2][2] = {
+ { 0x04, 0x84 },
+ { 0x00, 0x80 }
+ };
+
+ if (!radio)
+ return -ENODEV;
+ FMDBG("In %s", __func__);
+
+ /* disable radio ctrl */
+ retval = tavarua_write_register(radio, RDCTRL, 0x00);
+
+ FMDBG("%s, Disable IRQs\n", __func__);
+ /* disable irq */
+ retval = tavarua_disable_irq(radio);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: failed to disable irq\n", __func__);
+ return retval;
+ }
+
+ bahama_present = is_bahama();
+
+ if (bahama_present == -ENODEV)
+ return -ENODEV;
+
+ if (bahama_present) {
+ /*Write first sequence of bytes to FM_CTL0*/
+ for (i = 0; i < 1; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL0_REG, fm_ctl0_part1[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL0:Set-1 failure: %d\n",
+ retval);
+ break;
+ }
+ }
+ /*Write the FM_CTL1 sequence*/
+ for (i = 0; i < 1; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL1_REG, fm_ctl1[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL1 failure: %d\n",
+ retval);
+ break;
+ }
+ }
+ /*Write second sequence of bytes to FM_CTL0*/
+ for (i = 0; i < 3; i++) {
+ retval = tavarua_write_register(radio,
+ BAHAMA_FM_CTL0_REG, fm_ctl0_part2[i]);
+ if (retval < 0) {
+ printk(KERN_ERR "FM_CTL0:Set-2 failure: %d\n",
+ retval);
+ break;
+ }
+ }
+ } else {
+
+ retval = tavarua_write_registers(radio, FM_CTL0,
+ buffer, sizeof(buffer)/sizeof(buffer[0]));
+ if (retval < 0) {
+ printk(KERN_ERR "%s: failed to bring down the FM Core\n",
+ __func__);
+ return retval;
+ }
+ }
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ bt_status = marimba_get_bt_status(radio->marimba);
+ /* Set the index based on the bt status*/
+ index = bt_status ? 1 : 0;
+ /* Check for Bahama's existance and Bahama V2 variant*/
+ if (bahama_present && (bahama_version == 0x09)) {
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ /* actual value itself used as mask*/
+ retval = marimba_write_bit_mask(radio->marimba,
+ BAHAMA_LDO_DREG_CTL0, &internal_vreg_ctl[bt_status][0],
+ 1, internal_vreg_ctl[index][0]);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:0xF0 write failed\n", __func__);
+ return retval;
+ }
+ /* actual value itself used as mask*/
+ retval = marimba_write_bit_mask(radio->marimba,
+ BAHAMA_LDO_AREG_CTL0, &internal_vreg_ctl[bt_status][1],
+ 1, internal_vreg_ctl[index][1]);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:0xF4 write failed\n", __func__);
+ return retval;
+ }
+ } else {
+ /* disable fm core */
+ radio->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+ }
+
+ value = 0x00;
+ retval = marimba_write_bit_mask(radio->marimba, MARIMBA_XO_BUFF_CNTRL,
+ &value, 1, FM_ENABLE);
+ if (retval < 0) {
+ printk(KERN_ERR "%s:XO_BUFF_CNTRL write failed\n", __func__);
+ return retval;
+ }
+ FMDBG("%s, Calling fm_shutdown\n", __func__);
+ /* teardown gpio and pmic */
+ radio->pdata->fm_shutdown(radio->pdata);
+ if (radio->pdata->config_i2s_gpio != NULL)
+ radio->pdata->config_i2s_gpio(FM_I2S_OFF);
+ radio->handle_irq = 1;
+ radio->users = 0;
+ radio->marimba->mod_id = SLAVE_ID_BAHAMA;
+ marimba_set_fm_status(radio->marimba, false);
+ return 0;
+}
+
+/*
+ * tavarua_fops - file operations interface
+ */
+static const struct v4l2_file_operations tavarua_fops = {
+ .owner = THIS_MODULE,
+ .read = tavarua_fops_read,
+ .write = tavarua_fops_write,
+ .ioctl = video_ioctl2,
+ .open = tavarua_fops_open,
+ .release = tavarua_fops_release,
+};
+
+/*************************************************************************
+ * Video4Linux Interface
+ *************************************************************************/
+
+/*
+ * tavarua_v4l2_queryctrl - query control
+ */
+static struct v4l2_queryctrl tavarua_v4l2_queryctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 15,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SRCHMODE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search mode",
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search dwell time",
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SRCHON,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Search on/off",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_STATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "radio 0ff/rx/tx/reset",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 1,
+
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_REGION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "radio standard",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Signal Threshold",
+ .minimum = 0x80,
+ .maximum = 0x7F,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search PTY",
+ .minimum = 0,
+ .maximum = 31,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Search PI",
+ .minimum = 0,
+ .maximum = 0xFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Preset num",
+ .minimum = 0,
+ .maximum = 12,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Emphasis",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "RDS standard",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_SPACING,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Channel spacing",
+ .minimum = 0,
+ .maximum = 2,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_RDSON,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "RDS on/off",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS group mask",
+ .minimum = 0,
+ .maximum = 0xFFFFFFFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS processing",
+ .minimum = 0,
+ .maximum = 0xFF,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "RDS data groups to buffer",
+ .minimum = 1,
+ .maximum = 21,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_PSALL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "pass all ps strings",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Low power mode",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "headset/internal",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ /* Private controls for FM TX*/
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Set PS REPEATCOUNT",
+ .minimum = 0,
+ .maximum = 15,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Stop PS NAME",
+ .minimum = 0,
+ .maximum = 1,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Stop RT",
+ .minimum = 0,
+ .maximum = 1,
+ },
+
+};
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_querycap
+=============================================================================*/
+/**
+ This function is called to query device capabilities.
+
+ NOTE:
+ All V4L2 devices support the VIDIOC_QUERYCAP ioctl. It is used to identify
+ kernel devices compatible with this specification and to obtain information
+ about driver and hardware capabilities. The ioctl takes a pointer to a struct
+ v4l2_capability which is filled by the driver. When the driver is not
+ compatible with this specification the ioctl returns an EINVAL error code.
+
+ @param file: File descriptor returned by open().
+ @param capability: pointer to struct v4l2_capability.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The device is not compatible with this specification.
+*/
+static int tavarua_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ sprintf(capability->bus_info, "I2C");
+ capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+ capability->version = radio->chipID;
+
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_queryctrl
+=============================================================================*/
+/**
+ This function is called to query the device and driver for supported video
+ controls (enumerate control items).
+
+ NOTE:
+ To query the attributes of a control, the applications set the id field of
+ a struct v4l2_queryctrl and call the VIDIOC_QUERYCTRL ioctl with a pointer
+ to this structure. The driver fills the rest of the structure or returns an
+ EINVAL error code when the id is invalid.
+
+ @param file: File descriptor returned by open().
+ @param qc: pointer to struct v4l2_queryctrl.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The struct v4l2_queryctrl id is invalid.
+*/
+static int tavarua_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ unsigned char i;
+ int retval = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(tavarua_v4l2_queryctrl); i++) {
+ if (qc->id && qc->id == tavarua_v4l2_queryctrl[i].id) {
+ memcpy(qc, &(tavarua_v4l2_queryctrl[i]), sizeof(*qc));
+ retval = 0;
+ break;
+ }
+ }
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": query conv4ltrol failed with %d\n", retval);
+
+ return retval;
+}
+static int peek_MPX_DCC(struct tavarua_device *radio)
+{
+ int retval = 0;
+ unsigned char xfr_buf[XFR_REG_NUM];
+ int MPX_DCC[] = { 0 };
+ int DCC = 0;
+ int ct = 0;
+ unsigned char size = 0;
+
+ /*
+ Poking the MPX_DCC_BYPASS register to freeze the
+ value of MPX_DCC from changing while we access it
+ */
+
+ /*Poking the MPX_DCC_BYPASS register : 0x88C0 */
+ size = 0x01;
+ xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+ xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
+ xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
+ xfr_buf[3] = 0x01;
+
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
+ if (retval < 0) {
+ FMDBG("Failed to write\n");
+ return retval;
+ }
+ /*Wait for the XFR interrupt */
+ msleep(TAVARUA_DELAY*15);
+
+ for (ct = 0; ct < 5; ct++)
+ xfr_buf[ct] = 0;
+
+ /* Peeking Regs 0x88C2-0x88C4 */
+ size = 0x03;
+ xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+ xfr_buf[1] = MPX_DCC_PEEK_MSB_REG1;
+ xfr_buf[2] = MPX_DCC_PEEK_LSB_REG1;
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+ if (retval < 0) {
+ FMDBG("Failed to write\n");
+ return retval;
+ }
+ /*Wait for the XFR interrupt */
+ msleep(TAVARUA_DELAY*10);
+ retval = tavarua_read_registers(radio, XFRDAT0, 3);
+ if (retval < 0) {
+ printk(KERN_INFO "INT_DET: Read failure\n");
+ return retval;
+ }
+ MPX_DCC[0] = (int)radio->registers[XFRDAT0];
+ MPX_DCC[1] = (int)radio->registers[XFRDAT1];
+ MPX_DCC[2] = (int)radio->registers[XFRDAT2];
+
+ /*
+ Form the final MPX_DCC parameter
+ MPX_DCC[0] will form the LSB part
+ MPX_DCC[1] will be the middle part and 4 bits of
+ MPX_DCC[2] will be the MSB par of the 20-bit signed MPX_DCC
+ */
+
+ DCC = ((int)MPX_DCC[2] << 16) | ((int)MPX_DCC[1] << 8) |
+ ((int)MPX_DCC[0]);
+
+ /*
+ if bit-19 is '1',set remaining bits to '1' & make it -tive
+ */
+ if (DCC & 0x00080000) {
+ FMDBG(KERN_INFO "bit-19 is '1'\n");
+ DCC |= 0xFFF00000;
+ }
+
+ /*
+ Poking the MPX_DCC_BYPASS register to be back to normal
+ */
+
+ /*Poking the MPX_DCC_BYPASS register : 0x88C0 */
+ size = 0x01;
+ xfr_buf[0] = (XFR_POKE_MODE | (size << 1));
+ xfr_buf[1] = MPX_DCC_BYPASS_POKE_MSB;
+ xfr_buf[2] = MPX_DCC_BYPASS_POKE_LSB;
+ xfr_buf[3] = 0x00;
+
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 4);
+ if (retval < 0) {
+ FMDBG("Failed to write\n");
+ return retval;
+ }
+ /*Wait for the XFR interrupt */
+ msleep(TAVARUA_DELAY*10);
+
+ return DCC;
+}
+/*=============================================================================
+FUNCTION: tavarua_vidioc_g_ctrl
+=============================================================================*/
+/**
+ This function is called to get the value of a control.
+
+ NOTE:
+ To get the current value of a control, applications initialize the id field
+ of a struct v4l2_control and call the VIDIOC_G_CTRL ioctl with a pointer to
+ this structure.
+
+ When the id is invalid drivers return an EINVAL error code. When the value is
+ out of bounds drivers can choose to take the closest valid value or return an
+ ERANGE error code, whatever seems more appropriate.
+
+ @param file: File descriptor returned by open().
+ @param ctrl: pointer to struct v4l2_control.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The struct v4l2_control id is invalid.
+ @return ERANGE: The struct v4l2_control value is out of bounds.
+ @return EBUSY: The control is temporarily not changeable, possibly because
+ another applications took over control of the device function this control
+ belongs to.
+*/
+static int tavarua_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ unsigned char xfr_buf[XFR_REG_NUM];
+ signed char cRmssiThreshold;
+ signed char ioc;
+ unsigned char size = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = radio->registers[IOCTRL] & 0x03 ;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
+ ctrl->value = radio->registers[SRCHCTRL] & SRCH_MODE;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
+ ctrl->value = (radio->registers[SRCHCTRL] & SCAN_DWELL) >> 4;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
+ ctrl->value = (radio->registers[SRCHCTRL] & SRCH_ON) >> 7 ;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_STATE:
+ ctrl->value = (radio->registers[RDCTRL] & 0x03);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_IOVERC:
+ retval = tavarua_read_registers(radio, IOVERC, 1);
+ if (retval < 0)
+ return retval;
+ ioc = radio->registers[IOVERC];
+ ctrl->value = ioc;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_INTDET:
+ size = 0x1;
+ xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+ xfr_buf[1] = INTDET_PEEK_MSB;
+ xfr_buf[2] = INTDET_PEEK_LSB;
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+ if (retval < 0) {
+ FMDBG("Failed to write\n");
+ return retval;
+ }
+ FMDBG("INT_DET:Sync write success\n");
+ /*Wait for the XFR interrupt */
+ msleep(TAVARUA_DELAY*10);
+ /* Read the XFRDAT0 register populated by FM SoC */
+ retval = tavarua_read_registers(radio, XFRDAT0, 3);
+ if (retval < 0) {
+ FMDBG("INT_DET: Read failure\n");
+ return retval;
+ }
+ ctrl->value = radio->registers[XFRDAT0];
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_MPX_DCC:
+ ctrl->value = peek_MPX_DCC(radio);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_REGION:
+ ctrl->value = radio->region_params.region;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
+ retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+ if (retval < 0) {
+ FMDBG("[G IOCTL=V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+ FMDBG("sync_read_xfr error: [retval=%d]\n", retval);
+ break;
+ }
+ /* Since RMSSI Threshold is signed value */
+ cRmssiThreshold = (signed char)xfr_buf[0];
+ ctrl->value = cRmssiThreshold;
+ FMDBG("cRmssiThreshold: %d\n", cRmssiThreshold);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
+ ctrl->value = radio->srch_params.srch_pty;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
+ ctrl->value = radio->srch_params.srch_pi;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
+ ctrl->value = radio->srch_params.preset_num;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
+ ctrl->value = radio->region_params.emphasis;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
+ ctrl->value = radio->region_params.rds_std;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SPACING:
+ ctrl->value = radio->region_params.spacing;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSON:
+ ctrl->value = radio->registers[RDSCTRL] & RDS_ON;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ if (retval > -1)
+ ctrl->value = (xfr_buf[8] << 24) |
+ (xfr_buf[9] << 16) |
+ (xfr_buf[10] << 8) |
+ xfr_buf[11];
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
+ retval = tavarua_read_registers(radio, ADVCTRL, 1);
+ if (retval > -1)
+ ctrl->value = radio->registers[ADVCTRL];
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ if (retval > -1)
+ ctrl->value = xfr_buf[1];
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_PSALL:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ if (retval > -1)
+ ctrl->value = xfr_buf[12] & RDS_CONFIG_PSALL;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
+ ctrl->value = radio->lp_mode;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
+ ctrl->value = GET_REG_FIELD(radio->registers[IOCTRL],
+ IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": get control failed with %d, id: %d\n", retval, ctrl->id);
+
+ return retval;
+}
+
+static int tavarua_vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrl)
+{
+ int retval = 0;
+ int bytes_to_copy;
+ int bytes_copied = 0;
+ int bytes_left = 0;
+ int chunk_index = 0;
+ char tx_data[XFR_REG_NUM];
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ char *data = NULL;
+ int extra_name_byte = 0;
+ int name_bytes = 0;
+
+ switch ((ctrl->controls[0]).id) {
+ case V4L2_CID_RDS_TX_PS_NAME: {
+ FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
+ /*Pass a sample PS string */
+
+ chunk_index = 0;
+ bytes_copied = 0;
+ bytes_left = min((int)(ctrl->controls[0]).size,
+ MAX_PS_LENGTH);
+ data = (ctrl->controls[0]).string;
+
+ /* send payload to FM hardware */
+ while (bytes_left) {
+ chunk_index++;
+ FMDBG("chunk is %d", chunk_index);
+ bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+ /*Clear the tx_data */
+ memset(tx_data, 0, XFR_REG_NUM);
+ if (copy_from_user(tx_data,
+ data + bytes_copied, bytes_to_copy))
+ return -EFAULT;
+ retval = sync_write_xfr(radio,
+ RDS_PS_0 + chunk_index, tx_data);
+ if (retval < 0) {
+ FMDBG("sync_write_xfr: %d", retval);
+ return retval;
+ }
+ bytes_copied += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ }
+ memset(tx_data, 0, XFR_REG_NUM);
+ /*Write the PS Header*/
+ FMDBG("Writing PS header\n");
+ extra_name_byte = (bytes_copied%8) ? 1 : 0;
+ name_bytes = (bytes_copied/8) + extra_name_byte;
+ /*8 bytes are grouped as 1 name */
+ tx_data[0] = (name_bytes) & MASK_TXREPCOUNT;
+ tx_data[1] = radio->pty & MASK_PTY; /* PTY */
+ tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
+ tx_data[3] = radio->pi & MASK_PI_LSB;
+ /* TX ctrl + repeatCount*/
+ tx_data[4] = TX_ON |
+ (radio->ps_repeatcount & MASK_TXREPCOUNT);
+ retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
+ if (retval < 0) {
+ FMDBG("sync_write_xfr returned %d", retval);
+ return retval;
+ }
+ } break;
+ case V4L2_CID_RDS_TX_RADIO_TEXT: {
+ chunk_index = 0;
+ bytes_copied = 0;
+ FMDBG("In V4L2_CID_RDS_TX_RADIO_TEXT\n");
+ /*Pass a sample PS string */
+ FMDBG("Passed RT String : %s\n",
+ (ctrl->controls[0]).string);
+ bytes_left =
+ min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
+ data = (ctrl->controls[0]).string;
+ /* send payload to FM hardware */
+ while (bytes_left) {
+ chunk_index++;
+ bytes_to_copy = min(bytes_left, XFR_REG_NUM);
+ memset(tx_data, 0, XFR_REG_NUM);
+ if (copy_from_user(tx_data,
+ data + bytes_copied, bytes_to_copy))
+ return -EFAULT;
+ retval = sync_write_xfr(radio,
+ RDS_RT_0 + chunk_index, tx_data);
+ if (retval < 0)
+ return retval;
+ bytes_copied += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ }
+ /*Write the RT Header */
+ tx_data[0] = bytes_copied;
+ /* PTY */
+ tx_data[1] = TX_ON | ((radio->pty & MASK_PTY) >> 8);
+ /* PI high */
+ tx_data[2] = ((radio->pi & MASK_PI_MSB) >> 8);
+ /* PI low */
+ tx_data[3] = radio->pi & MASK_PI_LSB;
+ retval = sync_write_xfr(radio, RDS_RT_0 , tx_data);
+ if (retval < 0)
+ return retval;
+ FMDBG("done RT writing: %d\n", retval);
+ } break;
+ default:
+ {
+ FMDBG("Shouldn't reach here\n");
+ retval = -1;
+ }
+ }
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_s_ctrl
+=============================================================================*/
+/**
+ This function is called to set the value of a control.
+
+ NOTE:
+ To change the value of a control, applications initialize the id and value
+ fields of a struct v4l2_control and call the VIDIOC_S_CTRL ioctl.
+
+ When the id is invalid drivers return an EINVAL error code. When the value is
+ out of bounds drivers can choose to take the closest valid value or return an
+ ERANGE error code, whatever seems more appropriate.
+
+ @param file: File descriptor returned by open().
+ @param ctrl: pointer to struct v4l2_control.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The struct v4l2_control id is invalid.
+ @return ERANGE: The struct v4l2_control value is out of bounds.
+ @return EBUSY: The control is temporarily not changeable, possibly because
+ another applications took over control of the device function this control
+ belongs to.
+*/
+static int tavarua_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ unsigned char value;
+ unsigned char xfr_buf[XFR_REG_NUM];
+ unsigned char tx_data[XFR_REG_NUM];
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ value = (radio->registers[IOCTRL] & ~IOC_HRD_MUTE) |
+ (ctrl->value & 0x03);
+ retval = tavarua_write_register(radio, IOCTRL, value);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCHMODE:
+ value = (radio->registers[SRCHCTRL] & ~SRCH_MODE) |
+ ctrl->value;
+ radio->registers[SRCHCTRL] = value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SCANDWELL:
+ value = (radio->registers[SRCHCTRL] & ~SCAN_DWELL) |
+ (ctrl->value << 4);
+ radio->registers[SRCHCTRL] = value;
+ break;
+ /* start/stop search */
+ case V4L2_CID_PRIVATE_TAVARUA_SRCHON:
+ FMDBG("starting search\n");
+ tavarua_search(radio, ctrl->value, SRCH_DIR_UP);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_STATE:
+ /* check if already on */
+ radio->handle_irq = 1;
+ if (((ctrl->value == FM_RECV) || (ctrl->value == FM_TRANS))
+ && !(radio->registers[RDCTRL] &
+ ctrl->value)) {
+ FMDBG("clearing flags\n");
+ init_completion(&radio->sync_xfr_start);
+ init_completion(&radio->sync_req_done);
+ radio->xfr_in_progress = 0;
+ radio->xfr_bytes_left = 0;
+ FMDBG("turning on ..\n");
+ retval = tavarua_start(radio, ctrl->value);
+ if (retval >= 0) {
+ FMDBG("Setting audio path ...\n");
+ retval = tavarua_set_audio_path(
+ TAVARUA_AUDIO_OUT_DIGITAL_ON,
+ TAVARUA_AUDIO_OUT_ANALOG_OFF);
+ if (retval < 0) {
+ FMDERR("Error in tavarua_set_audio_path"
+ " %d\n", retval);
+ }
+ /* Enabling 'SoftMute' and 'SignalBlending' features */
+ value = (radio->registers[IOCTRL] |
+ IOC_SFT_MUTE | IOC_SIG_BLND);
+ retval = tavarua_write_register(radio, IOCTRL, value);
+ if (retval < 0)
+ FMDBG("SMute and SBlending not enabled\n");
+ }
+ }
+ /* check if off */
+ else if ((ctrl->value == FM_OFF) && radio->registers[RDCTRL]) {
+ FMDBG("turning off...\n");
+ retval = tavarua_write_register(radio, RDCTRL,
+ ctrl->value);
+ /*Make it synchronous
+ Block it till READY interrupt
+ Wait for interrupt i.e.
+ complete(&radio->sync_req_done)
+ */
+
+ if (retval >= 0) {
+
+ if (!wait_for_completion_timeout(
+ &radio->sync_req_done,
+ msecs_to_jiffies(wait_timeout)))
+ FMDBG("turning off timedout...\n");
+ }
+ }
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_REGION:
+ retval = tavarua_set_region(radio, ctrl->value);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH:
+ retval = sync_read_xfr(radio, RX_CONFIG, xfr_buf);
+ if (retval < 0) {
+ FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+ FMDERR("sync_read_xfr [retval=%d]\n", retval);
+ break;
+ }
+ /* RMSSI Threshold is a signed 8 bit value */
+ xfr_buf[0] = (unsigned char)ctrl->value;
+ xfr_buf[1] = (unsigned char)ctrl->value;
+ xfr_buf[4] = 0x01;
+ retval = sync_write_xfr(radio, RX_CONFIG, xfr_buf);
+ if (retval < 0) {
+ FMDERR("V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH]\n");
+ FMDERR("sync_write_xfr [retval=%d]\n", retval);
+ break;
+ }
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY:
+ radio->srch_params.srch_pty = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_PI:
+ radio->srch_params.srch_pi = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT:
+ radio->srch_params.preset_num = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_EMPHASIS:
+ radio->region_params.emphasis = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDS_STD:
+ radio->region_params.rds_std = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_SPACING:
+ radio->region_params.spacing = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSON:
+ retval = 0;
+ if (ctrl->value != (radio->registers[RDSCTRL] & RDS_ON)) {
+ value = radio->registers[RDSCTRL] | ctrl->value;
+ retval = tavarua_write_register(radio, RDSCTRL, value);
+ }
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ if (retval < 0)
+ break;
+ xfr_buf[8] = (ctrl->value & 0xFF000000) >> 24;
+ xfr_buf[9] = (ctrl->value & 0x00FF0000) >> 16;
+ xfr_buf[10] = (ctrl->value & 0x0000FF00) >> 8;
+ xfr_buf[11] = (ctrl->value & 0x000000FF);
+ retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC:
+ value = radio->registers[ADVCTRL] | ctrl->value ;
+ retval = tavarua_write_register(radio, ADVCTRL, value);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ if (retval < 0)
+ break;
+ xfr_buf[1] = ctrl->value;
+ retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_PSALL:
+ retval = sync_read_xfr(radio, RDS_CONFIG, xfr_buf);
+ value = ctrl->value & RDS_CONFIG_PSALL;
+ if (retval < 0)
+ break;
+ xfr_buf[12] &= ~RDS_CONFIG_PSALL;
+ xfr_buf[12] |= value;
+ retval = sync_write_xfr(radio, RDS_CONFIG, xfr_buf);
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_LP_MODE:
+ retval = 0;
+ if (ctrl->value == radio->lp_mode)
+ break;
+ if (ctrl->value) {
+ FMDBG("going into low power mode\n");
+ retval = tavarua_disable_interrupts(radio);
+ } else {
+ FMDBG("going into normal power mode\n");
+ tavarua_setup_interrupts(radio,
+ (radio->registers[RDCTRL] & 0x03));
+ }
+ break;
+ case V4L2_CID_PRIVATE_TAVARUA_ANTENNA:
+ SET_REG_FIELD(radio->registers[IOCTRL], ctrl->value,
+ IOC_ANTENNA_OFFSET, IOC_ANTENNA_MASK);
+ break;
+ /* TX Controls */
+
+ case V4L2_CID_RDS_TX_PTY: {
+ radio->pty = ctrl->value;
+ } break;
+ case V4L2_CID_RDS_TX_PI: {
+ radio->pi = ctrl->value;
+ } break;
+ case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME: {
+ FMDBG("In STOP_RDS_TX_PS_NAME\n");
+ /*Pass a sample PS string */
+ memset(tx_data, '0', XFR_REG_NUM);
+ FMDBG("Writing PS header\n");
+ retval = sync_write_xfr(radio, RDS_PS_0, tx_data);
+ FMDBG("retval of PS Header write: %d", retval);
+
+ } break;
+
+ case V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT: {
+ memset(tx_data, '0', XFR_REG_NUM);
+ FMDBG("Writing RT header\n");
+ retval = sync_write_xfr(radio, RDS_RT_0, tx_data);
+ FMDBG("retval of Header write: %d", retval);
+
+ } break;
+
+ case V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT: {
+ radio->ps_repeatcount = ctrl->value;
+ } break;
+ case V4L2_CID_TUNE_POWER_LEVEL: {
+ unsigned char tx_power_lvl_config[FM_TX_PWR_LVL_MAX+1] = {
+ 0x85, /* tx_da<5:3> = 0 lpf<2:0> = 5*/
+ 0x95, /* tx_da<5:3> = 2 lpf<2:0> = 5*/
+ 0x9D, /* tx_da<5:3> = 3 lpf<2:0> = 5*/
+ 0xA5, /* tx_da<5:3> = 4 lpf<2:0> = 5*/
+ 0xAD, /* tx_da<5:3> = 5 lpf<2:0> = 5*/
+ 0xB5, /* tx_da<5:3> = 6 lpf<2:0> = 5*/
+ 0xBD, /* tx_da<5:3> = 7 lpf<2:0> = 5*/
+ 0xBF /* tx_da<5:3> = 7 lpf<2:0> = 7*/
+ };
+ if (ctrl->value > FM_TX_PWR_LVL_MAX)
+ ctrl->value = FM_TX_PWR_LVL_MAX;
+ if (ctrl->value < FM_TX_PWR_LVL_0)
+ ctrl->value = FM_TX_PWR_LVL_0;
+ retval = sync_read_xfr(radio, PHY_TXGAIN, xfr_buf);
+ FMDBG("return for PHY_TXGAIN is %d", retval);
+ if (retval < 0) {
+ FMDBG("read failed");
+ break;
+ }
+ xfr_buf[2] = tx_power_lvl_config[ctrl->value];
+ retval = sync_write_xfr(radio, PHY_TXGAIN, xfr_buf);
+ FMDBG("return for write PHY_TXGAIN is %d", retval);
+ if (retval < 0)
+ FMDBG("write failed");
+ } break;
+
+ default:
+ retval = -EINVAL;
+ }
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": set control failed with %d, id : %d\n", retval, ctrl->id);
+
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_g_tuner
+=============================================================================*/
+/**
+ This function is called to get tuner attributes.
+
+ NOTE:
+ To query the attributes of a tuner, applications initialize the index field
+ and zero out the reserved array of a struct v4l2_tuner and call the
+ VIDIOC_G_TUNER ioctl with a pointer to this structure. Drivers fill the rest
+ of the structure or return an EINVAL error code when the index is out of
+ bounds. To enumerate all tuners applications shall begin at index zero,
+ incrementing by one until the driver returns EINVAL.
+
+ @param file: File descriptor returned by open().
+ @param tuner: pointer to struct v4l2_tuner.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The struct v4l2_tuner index is out of bounds.
+*/
+static int tavarua_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
+ unsigned char xfr_buf[XFR_REG_NUM];
+ char rmssi = 0;
+ unsigned char size = 0;
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ /* read status rssi */
+ retval = tavarua_read_registers(radio, IOCTRL, 1);
+ if (retval < 0)
+ return retval;
+ /* read RMSSI */
+ size = 0x1;
+ xfr_buf[0] = (XFR_PEEK_MODE | (size << 1));
+ xfr_buf[1] = RMSSI_PEEK_MSB;
+ xfr_buf[2] = RMSSI_PEEK_LSB;
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, 3);
+ msleep(TAVARUA_DELAY*10);
+ retval = tavarua_read_registers(radio, XFRDAT0, 3);
+ rmssi = radio->registers[XFRDAT0];
+ tuner->signal = rmssi;
+
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+ tuner->rangelow = radio->region_params.band_low;
+ tuner->rangehigh = radio->region_params.band_high;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ tuner->capability = V4L2_TUNER_CAP_LOW;
+
+ /* Stereo indicator == Stereo (instead of Mono) */
+ if (radio->registers[IOCTRL] & IOC_MON_STR)
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+ /* automatic frequency control: -1: freq to low, 1 freq to high */
+ tuner->afc = 0;
+
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_s_tuner
+=============================================================================*/
+/**
+ This function is called to set tuner attributes. Used to set mono/stereo mode.
+
+ NOTE:
+ Tuners have two writable properties, the audio mode and the radio frequency.
+ To change the audio mode, applications initialize the index, audmode and
+ reserved fields and call the VIDIOC_S_TUNER ioctl. This will not change the
+ current tuner, which is determined by the current video input. Drivers may
+ choose a different audio mode if the requested mode is invalid or unsupported.
+ Since this is a write-only ioctl, it does not return the actually selected
+ audio mode.
+
+ To change the radio frequency the VIDIOC_S_FREQUENCY ioctl is available.
+
+ @param file: File descriptor returned by open().
+ @param tuner: pointer to struct v4l2_tuner.
+
+ @return On success 0 is returned, else error code.
+ @return -EINVAL: The struct v4l2_tuner index is out of bounds.
+*/
+static int tavarua_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
+ int audmode;
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ FMDBG("%s: set low to %d\n", __func__, tuner->rangelow);
+ radio->region_params.band_low = tuner->rangelow;
+ radio->region_params.band_high = tuner->rangehigh;
+ if (tuner->audmode == V4L2_TUNER_MODE_MONO)
+ /* Mono */
+ audmode = (radio->registers[IOCTRL] | IOC_MON_STR);
+ else
+ /* Stereo */
+ audmode = (radio->registers[IOCTRL] & ~IOC_MON_STR);
+ retval = tavarua_write_register(radio, IOCTRL, audmode);
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": set tuner failed with %d\n", retval);
+
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_g_frequency
+=============================================================================*/
+/**
+ This function is called to get tuner or modulator radio frequency.
+
+ NOTE:
+ To get the current tuner or modulator radio frequency applications set the
+ tuner field of a struct v4l2_frequency to the respective tuner or modulator
+ number (only input devices have tuners, only output devices have modulators),
+ zero out the reserved array and call the VIDIOC_G_FREQUENCY ioctl with a
+ pointer to this structure. The driver stores the current frequency in the
+ frequency field.
+
+ @param file: File descriptor returned by open().
+ @param freq: pointer to struct v4l2_frequency. This will be set to the
+ resultant
+ frequency in 62.5 khz on success.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The tuner index is out of bounds or the value in the type
+ field is wrong.
+*/
+static int tavarua_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ freq->type = V4L2_TUNER_RADIO;
+ return tavarua_get_freq(radio, freq);
+
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_s_frequency
+=============================================================================*/
+/**
+ This function is called to set tuner or modulator radio frequency.
+
+ NOTE:
+ To change the current tuner or modulator radio frequency applications
+ initialize the tuner, type and frequency fields, and the reserved array of
+ a struct v4l2_frequency and call the VIDIOC_S_FREQUENCY ioctl with a pointer
+ to this structure. When the requested frequency is not possible the driver
+ assumes the closest possible value. However VIDIOC_S_FREQUENCY is a
+ write-only ioctl, it does not return the actual new frequency.
+
+ @param file: File descriptor returned by open().
+ @param freq: pointer to struct v4l2_frequency.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The tuner index is out of bounds or the value in the type
+ field is wrong.
+*/
+static int tavarua_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = -1;
+ struct v4l2_frequency getFreq;
+
+ FMDBG("%s\n", __func__);
+
+ if (freq->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ FMDBG("Calling tavarua_set_freq\n");
+
+ INIT_COMPLETION(radio->sync_req_done);
+ retval = tavarua_set_freq(radio, freq->frequency);
+ if (retval < 0) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": set frequency failed with %d\n", retval);
+ } else {
+ /* Wait for interrupt i.e. complete
+ (&radio->sync_req_done); call */
+ if (!wait_for_completion_timeout(&radio->sync_req_done,
+ msecs_to_jiffies(wait_timeout))) {
+ FMDERR("Timeout: No Tune response");
+ retval = tavarua_get_freq(radio, &getFreq);
+ radio->tune_req = 0;
+ if (retval > 0) {
+ if (getFreq.frequency == freq->frequency) {
+ /** This is success, queut the event*/
+ tavarua_q_event(radio,
+ TAVARUA_EVT_TUNE_SUCC);
+ return 0;
+ } else {
+ return -EIO;
+ }
+ }
+ }
+ }
+ radio->tune_req = 0;
+ return retval;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_dqbuf
+=============================================================================*/
+/**
+ This function is called to exchange a buffer with the driver.
+ This is main buffer function, in essense its equivalent to a blocking
+ read call.
+
+ Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or
+ displayed (output) buffer from the driver's outgoing queue. They just set
+ the type and memory fields of a struct v4l2_buffer as above, when VIDIOC_DQBUF
+ is called with a pointer to this structure the driver fills the remaining
+ fields or returns an error code.
+
+ NOTE:
+ By default VIDIOC_DQBUF blocks when no buffer is in the outgoing queue.
+ When the O_NONBLOCK flag was given to the open() function, VIDIOC_DQBUF
+ returns immediately with an EAGAIN error code when no buffer is available.
+
+ @param file: File descriptor returned by open().
+ @param buffer: pointer to struct v4l2_buffer.
+
+ @return On success 0 is returned, else error code.
+ @return EAGAIN: Non-blocking I/O has been selected using O_NONBLOCK and no
+ buffer was in the outgoing queue.
+ @return EINVAL: The buffer type is not supported, or the index is out of
+ bounds, or no buffers have been allocated yet, or the userptr or length are
+ invalid.
+ @return ENOMEM: Not enough physical or virtual memory was available to enqueue
+ a user pointer buffer.
+ @return EIO: VIDIOC_DQBUF failed due to an internal error. Can also indicate
+ temporary problems like signal loss. Note the driver might dequeue an (empty)
+ buffer despite returning an error, or even stop capturing.
+*/
+static int tavarua_vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buffer)
+{
+
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ enum tavarua_buf_t buf_type = buffer->index;
+ struct kfifo *data_fifo;
+ unsigned char *buf = (unsigned char *)buffer->m.userptr;
+ unsigned int len = buffer->length;
+ FMDBG("%s: requesting buffer %d\n", __func__, buf_type);
+ /* check if we can access the user buffer */
+ if (!access_ok(VERIFY_WRITE, buf, len))
+ return -EFAULT;
+ if ((buf_type < TAVARUA_BUF_MAX) && (buf_type >= 0)) {
+ data_fifo = &radio->data_buf[buf_type];
+ if (buf_type == TAVARUA_BUF_EVENTS) {
+ if (wait_event_interruptible(radio->event_queue,
+ kfifo_len(data_fifo)) < 0) {
+ return -EINTR;
+ }
+ }
+ } else {
+ FMDERR("invalid buffer type\n");
+ return -EINVAL;
+ }
+ buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
+ &radio->buf_lock[buf_type]);
+
+ return 0;
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_g_fmt_type_private
+=============================================================================*/
+/**
+ This function is here to make the v4l2 framework happy.
+ We cannot use private buffers without it.
+
+ @param file: File descriptor returned by open().
+ @param f: pointer to struct v4l2_format.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The tuner index is out of bounds or the value in the type
+ field is wrong.
+*/
+static int tavarua_vidioc_g_fmt_type_private(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return 0;
+
+}
+
+/*=============================================================================
+FUNCTION: tavarua_vidioc_s_hw_freq_seek
+=============================================================================*/
+/**
+ This function is called to perform a hardware frequency seek.
+
+ Start a hardware frequency seek from the current frequency. To do this
+ applications initialize the tuner, type, seek_upward and wrap_around fields,
+ and zero out the reserved array of a struct v4l2_hw_freq_seek and call the
+ VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer to this structure.
+
+ This ioctl is supported if the V4L2_CAP_HW_FREQ_SEEK capability is set.
+
+ @param file: File descriptor returned by open().
+ @param seek: pointer to struct v4l2_hw_freq_seek.
+
+ @return On success 0 is returned, else error code.
+ @return EINVAL: The tuner index is out of bounds or the value in the type
+ field is wrong.
+ @return EAGAIN: The ioctl timed-out. Try again.
+*/
+static int tavarua_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
+{
+ struct tavarua_device *radio = video_get_drvdata(video_devdata(file));
+ int dir;
+ if (seek->seek_upward)
+ dir = SRCH_DIR_UP;
+ else
+ dir = SRCH_DIR_DOWN;
+ FMDBG("starting search\n");
+ return tavarua_search(radio, CTRL_ON, dir);
+}
+
+/*
+ * tavarua_viddev_tamples - video device interface
+ */
+static const struct v4l2_ioctl_ops tavarua_ioctl_ops = {
+ .vidioc_querycap = tavarua_vidioc_querycap,
+ .vidioc_queryctrl = tavarua_vidioc_queryctrl,
+ .vidioc_g_ctrl = tavarua_vidioc_g_ctrl,
+ .vidioc_s_ctrl = tavarua_vidioc_s_ctrl,
+ .vidioc_g_tuner = tavarua_vidioc_g_tuner,
+ .vidioc_s_tuner = tavarua_vidioc_s_tuner,
+ .vidioc_g_frequency = tavarua_vidioc_g_frequency,
+ .vidioc_s_frequency = tavarua_vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = tavarua_vidioc_s_hw_freq_seek,
+ .vidioc_dqbuf = tavarua_vidioc_dqbuf,
+ .vidioc_g_fmt_type_private = tavarua_vidioc_g_fmt_type_private,
+ .vidioc_s_ext_ctrls = tavarua_vidioc_s_ext_ctrls,
+};
+
+static struct video_device tavarua_viddev_template = {
+ .fops = &tavarua_fops,
+ .ioctl_ops = &tavarua_ioctl_ops,
+ .name = DRIVER_NAME,
+ .release = video_device_release,
+};
+
+/*==============================================================
+FUNCTION: FmQSocCom_EnableInterrupts
+==============================================================*/
+/**
+ This function enable interrupts.
+
+ @param radio: structure pointer passed by client.
+ @param state: FM radio state (receiver/transmitter/off/reset).
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_setup_interrupts(struct tavarua_device *radio,
+ enum radio_state_t state)
+{
+ int retval;
+ unsigned char int_ctrl[XFR_REG_NUM];
+
+ if (!radio->lp_mode)
+ return 0;
+
+ int_ctrl[STATUS_REG1] = READY | TUNE | SEARCH | SCANNEXT |
+ SIGNAL | INTF | SYNC | AUDIO;
+ if (state == FM_RECV)
+ int_ctrl[STATUS_REG2] = RDSDAT | RDSRT | RDSPS | RDSAF;
+ else
+ int_ctrl[STATUS_REG2] = TXRDSDAT | TXRDSDONE;
+
+ int_ctrl[STATUS_REG3] = TRANSFER | ERROR;
+
+ /* use xfr for interrupt setup */
+ if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
+ || radio->chipID == BAHAMA_2_0) {
+ FMDBG("Setting interrupts\n");
+ retval = sync_write_xfr(radio, INT_CTRL, int_ctrl);
+ /* use register write to setup interrupts */
+ } else {
+ retval = tavarua_write_register(radio,
+ STATUS_REG1, int_ctrl[STATUS_REG1]);
+ if (retval < 0)
+ return retval;
+
+ retval = tavarua_write_register(radio,
+ STATUS_REG2, int_ctrl[STATUS_REG2]);
+ if (retval < 0)
+ return retval;
+
+ retval = tavarua_write_register(radio,
+ STATUS_REG3, int_ctrl[STATUS_REG3]);
+ if (retval < 0)
+ return retval;
+ }
+
+ radio->lp_mode = 0;
+ /* tavarua_handle_interrupts force reads all the interrupt status
+ * registers and it is not valid for MBA 2.1
+ */
+ if ((radio->chipID != MARIMBA_2_1) && (radio->chipID != BAHAMA_1_0)
+ && (radio->chipID != BAHAMA_2_0))
+ tavarua_handle_interrupts(radio);
+
+ return retval;
+
+}
+
+/*==============================================================
+FUNCTION: tavarua_disable_interrupts
+==============================================================*/
+/**
+ This function disables interrupts.
+
+ @param radio: structure pointer passed by client.
+
+ @return => 0 if successful.
+ @return < 0 if failure.
+*/
+static int tavarua_disable_interrupts(struct tavarua_device *radio)
+{
+ unsigned char lpm_buf[XFR_REG_NUM];
+ int retval;
+ if (radio->lp_mode)
+ return 0;
+ FMDBG("%s\n", __func__);
+ /* In Low power mode, disable all the interrupts that are not being
+ waited by the Application */
+ lpm_buf[STATUS_REG1] = TUNE | SEARCH | SCANNEXT;
+ lpm_buf[STATUS_REG2] = 0x00;
+ lpm_buf[STATUS_REG3] = TRANSFER;
+ /* use xfr for interrupt setup */
+ wait_timeout = 100;
+ if (radio->chipID == MARIMBA_2_1 || radio->chipID == BAHAMA_1_0
+ || radio->chipID == BAHAMA_2_0)
+ retval = sync_write_xfr(radio, INT_CTRL, lpm_buf);
+ /* use register write to setup interrupts */
+ else
+ retval = tavarua_write_registers(radio, STATUS_REG1, lpm_buf,
+ ARRAY_SIZE(lpm_buf));
+
+ /*INT_CTL writes may fail with TIME_OUT as all the
+ interrupts have been disabled
+ */
+ if (retval > -1 || retval == -ETIME) {
+ radio->lp_mode = 1;
+ /*Consider timeout as a valid case here*/
+ retval = 0;
+ }
+ wait_timeout = WAIT_TIMEOUT;
+ return retval;
+
+}
+
+/*==============================================================
+FUNCTION: tavarua_start
+==============================================================*/
+/**
+ Starts/enables the device (FM radio).
+
+ @param radio: structure pointer passed by client.
+ @param state: FM radio state (receiver/transmitter/off/reset).
+
+ @return On success 0 is returned, else error code.
+*/
+static int tavarua_start(struct tavarua_device *radio,
+ enum radio_state_t state)
+{
+
+ int retval;
+ FMDBG("%s <%d>\n", __func__, state);
+ /* set geographic region */
+ radio->region_params.region = TAVARUA_REGION_US;
+
+ /* set radio mode */
+ retval = tavarua_write_register(radio, RDCTRL, state);
+ if (retval < 0)
+ return retval;
+ /* wait for radio to init */
+ msleep(RADIO_INIT_TIME);
+ /* enable interrupts */
+ tavarua_setup_interrupts(radio, state);
+ /* default region is US */
+ radio->region_params.band_low = US_LOW_BAND * FREQ_MUL;
+ radio->region_params.band_high = US_HIGH_BAND * FREQ_MUL;
+
+ return 0;
+}
+
+/*==============================================================
+FUNCTION: tavarua_suspend
+==============================================================*/
+/**
+ Save state and stop all devices in system.
+
+ @param pdev: platform device to be suspended.
+ @param state: Power state to put each device in.
+
+ @return On success 0 is returned, else error code.
+*/
+static int tavarua_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct tavarua_device *radio = platform_get_drvdata(pdev);
+ int retval;
+ int users = 0;
+ printk(KERN_INFO DRIVER_NAME "%s: radio suspend\n\n", __func__);
+ if (radio) {
+ mutex_lock(&radio->lock);
+ users = radio->users;
+ mutex_unlock(&radio->lock);
+ if (users) {
+ retval = tavarua_disable_interrupts(radio);
+ if (retval < 0) {
+ printk(KERN_INFO DRIVER_NAME
+ "tavarua_suspend error %d\n", retval);
+ return -EIO;
+ }
+ }
+ }
+ return 0;
+}
+
+/*==============================================================
+FUNCTION: tavarua_resume
+==============================================================*/
+/**
+ Restore state of each device in system.
+
+ @param pdev: platform device to be resumed.
+
+ @return On success 0 is returned, else error code.
+*/
+static int tavarua_resume(struct platform_device *pdev)
+{
+
+ struct tavarua_device *radio = platform_get_drvdata(pdev);
+ int retval;
+ int users = 0;
+ printk(KERN_INFO DRIVER_NAME "%s: radio resume\n\n", __func__);
+ if (radio) {
+ mutex_lock(&radio->lock);
+ users = radio->users;
+ mutex_unlock(&radio->lock);
+
+ if (users) {
+ retval = tavarua_setup_interrupts(radio,
+ (radio->registers[RDCTRL] & 0x03));
+ if (retval < 0) {
+ printk(KERN_INFO DRIVER_NAME "Error in \
+ tavarua_resume %d\n", retval);
+ return -EIO;
+ }
+ }
+ }
+ return 0;
+}
+
+/*==============================================================
+FUNCTION: tavarua_set_audio_path
+==============================================================*/
+/**
+ This function will configure the audio path to and from the
+ FM core.
+
+ This interface is expected to be called from the multimedia
+ driver's thread. This interface should only be called when
+ the FM hardware is enabled. If the FM hardware is not
+ currently enabled, this interface will return an error.
+
+ @param digital_on: Digital audio from the FM core should be enabled/disbled.
+ @param analog_on: Analog audio from the FM core should be enabled/disbled.
+
+ @return On success 0 is returned, else error code.
+*/
+int tavarua_set_audio_path(int digital_on, int analog_on)
+{
+ struct tavarua_device *radio = private_data;
+ int rx_on = radio->registers[RDCTRL] & FM_RECV;
+ if (!radio)
+ return -ENOMEM;
+ /* RX */
+ FMDBG("%s: digital: %d analog: %d\n", __func__, digital_on, analog_on);
+ SET_REG_FIELD(radio->registers[AUDIOCTRL],
+ ((rx_on && analog_on) ? 1 : 0),
+ AUDIORX_ANALOG_OFFSET,
+ AUDIORX_ANALOG_MASK);
+ SET_REG_FIELD(radio->registers[AUDIOCTRL],
+ ((rx_on && digital_on) ? 1 : 0),
+ AUDIORX_DIGITAL_OFFSET,
+ AUDIORX_DIGITAL_MASK);
+ SET_REG_FIELD(radio->registers[AUDIOCTRL],
+ (rx_on ? 0 : 1),
+ AUDIOTX_OFFSET,
+ AUDIOTX_MASK);
+ /*
+
+ I2S Master/Slave configuration:
+ Setting the FM SoC as I2S Master/Slave
+ 'false' - FM SoC is I2S Slave
+ 'true' - FM SoC is I2S Master
+
+ We get this infomation from the respective target's board file :
+ MSM7x30 - FM SoC is I2S Slave
+ MSM8x60 - FM SoC is I2S Slave
+ MSM7x27A - FM SoC is I2S Master
+ */
+
+ if (!radio->pdata->is_fm_soc_i2s_master) {
+ FMDBG("FM SoC is I2S Slave\n");
+ SET_REG_FIELD(radio->registers[AUDIOCTRL],
+ (0),
+ I2SCTRL_OFFSET,
+ I2SCTRL_MASK);
+ } else {
+ FMDBG("FM SoC is I2S Master\n");
+ SET_REG_FIELD(radio->registers[AUDIOCTRL],
+ (1),
+ I2SCTRL_OFFSET,
+ I2SCTRL_MASK);
+ }
+ FMDBG("%s: %x\n", __func__, radio->registers[AUDIOCTRL]);
+ return tavarua_write_register(radio, AUDIOCTRL,
+ radio->registers[AUDIOCTRL]);
+
+}
+
+/*==============================================================
+FUNCTION: tavarua_probe
+==============================================================*/
+/**
+ Once called this functions initiates, allocates resources and registers video
+ tuner device with the v4l2 framework.
+
+ NOTE:
+ probe() should verify that the specified device hardware
+ actually exists; sometimes platform setup code can't be sure. The probing
+ can use device resources, including clocks, and device platform_data.
+
+ @param pdev: platform device to be probed.
+
+ @return On success 0 is returned, else error code.
+ -ENOMEM in low memory cases
+*/
+static int __init tavarua_probe(struct platform_device *pdev)
+{
+
+ struct marimba_fm_platform_data *tavarua_pdata;
+ struct tavarua_device *radio;
+ int retval;
+ int i;
+ FMDBG("%s: probe called\n", __func__);
+ /* private data allocation */
+ radio = kzalloc(sizeof(struct tavarua_device), GFP_KERNEL);
+ if (!radio) {
+ retval = -ENOMEM;
+ goto err_initial;
+ }
+
+ radio->marimba = platform_get_drvdata(pdev);
+ tavarua_pdata = pdev->dev.platform_data;
+ radio->pdata = tavarua_pdata;
+ radio->dev = &pdev->dev;
+ platform_set_drvdata(pdev, radio);
+
+ /* video device allocation */
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev)
+ goto err_radio;
+
+ /* initial configuration */
+ memcpy(radio->videodev, &tavarua_viddev_template,
+ sizeof(tavarua_viddev_template));
+
+ /*allocate internal buffers for decoded rds and event buffer*/
+ for (i = 0; i < TAVARUA_BUF_MAX; i++) {
+ int kfifo_alloc_rc=0;
+ spin_lock_init(&radio->buf_lock[i]);
+
+ if (i == TAVARUA_BUF_RAW_RDS)
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+ rds_buf*3, GFP_KERNEL);
+ else
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+ STD_BUF_SIZE, GFP_KERNEL);
+
+ if (kfifo_alloc_rc!=0) {
+ printk(KERN_ERR "%s: failed allocating buffers %d\n",
+ __func__, kfifo_alloc_rc);
+ goto err_bufs;
+ }
+ }
+ /* init xfr status */
+ radio->users = 0;
+ radio->xfr_in_progress = 0;
+ radio->xfr_bytes_left = 0;
+ for (i = 0; i < TAVARUA_XFR_MAX; i++)
+ radio->pending_xfrs[i] = 0;
+
+ /* init transmit data */
+ radio->tx_mode = TAVARUA_TX_RT;
+ /* Init RT and PS Tx datas*/
+ radio->pty = 0;
+ radio->pi = 0;
+ radio->ps_repeatcount = 0;
+ /* init search params */
+ radio->srch_params.srch_pty = 0;
+ radio->srch_params.srch_pi = 0;
+ radio->srch_params.preset_num = 0;
+ radio->srch_params.get_list = 0;
+ /* radio initializes to low power mode */
+ radio->lp_mode = 1;
+ radio->handle_irq = 1;
+ /* init lock */
+ mutex_init(&radio->lock);
+ /* init completion flags */
+ init_completion(&radio->sync_xfr_start);
+ init_completion(&radio->sync_req_done);
+ radio->tune_req = 0;
+ /* initialize wait queue for event read */
+ init_waitqueue_head(&radio->event_queue);
+ /* initialize wait queue for raw rds read */
+ init_waitqueue_head(&radio->read_queue);
+
+ video_set_drvdata(radio->videodev, radio);
+ /*Start the worker thread for event handling and register read_int_stat
+ as worker function*/
+ INIT_DELAYED_WORK(&radio->work, read_int_stat);
+
+ /* register video device */
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": Could not register video device\n");
+ goto err_all;
+ }
+ private_data = radio;
+ return 0;
+
+err_all:
+ video_device_release(radio->videodev);
+err_bufs:
+ for (; i > -1; i--)
+ kfifo_free(&radio->data_buf[i]);
+err_radio:
+ kfree(radio);
+err_initial:
+ return retval;
+}
+
+/*==============================================================
+FUNCTION: tavarua_remove
+==============================================================*/
+/**
+ Removes the device.
+
+ @param pdev: platform device to be removed.
+
+ @return On success 0 is returned, else error code.
+*/
+static int __devexit tavarua_remove(struct platform_device *pdev)
+{
+ int i;
+ struct tavarua_device *radio = platform_get_drvdata(pdev);
+
+ /* disable irq */
+ tavarua_disable_irq(radio);
+
+ video_unregister_device(radio->videodev);
+
+ /* free internal buffers */
+ for (i = 0; i < TAVARUA_BUF_MAX; i++)
+ kfifo_free(&radio->data_buf[i]);
+
+ /* free state struct */
+ kfree(radio);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/*
+ Platform drivers follow the standard driver model convention, where
+ discovery/enumeration is handled outside the drivers, and drivers
+ provide probe() and remove() methods. They support power management
+ and shutdown notifications using the standard conventions.
+*/
+static struct platform_driver tavarua_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "marimba_fm",
+ },
+ .remove = __devexit_p(tavarua_remove),
+ .suspend = tavarua_suspend,
+ .resume = tavarua_resume,
+}; /* platform device we're adding */
+
+
+/*************************************************************************
+ * Module Interface
+ ************************************************************************/
+
+/*==============================================================
+FUNCTION: radio_module_init
+==============================================================*/
+/**
+ Module entry - add a platform-level device.
+
+ @return Returns zero if the driver registered and bound to a device, else
+ returns a negative error code when the driver not registered.
+*/
+static int __init radio_module_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+ return platform_driver_probe(&tavarua_driver, tavarua_probe);
+}
+
+/*==============================================================
+FUNCTION: radio_module_exit
+==============================================================*/
+/**
+ Module exit - removes a platform-level device.
+
+ NOTE:
+ Note that this function will also release all memory- and port-based
+ resources owned by the device (dev->resource).
+
+ @return none.
+*/
+static void __exit radio_module_exit(void)
+{
+ platform_driver_unregister(&tavarua_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(radio_module_init);
+module_exit(radio_module_exit);
+
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index bb53de7..95255fe 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1031,6 +1031,31 @@
This driver can be compiled as a module, called s2255drv.
endif # V4L_USB_DRIVERS
+
+#
+# MSM camera configuration
+#
+
+comment "Qualcomm MSM Camera And Video"
+
+menuconfig MSM_CAMERA
+ bool "Qualcomm MSM camera and video capture support"
+ depends on ARCH_MSM && VIDEO_V4L2 && I2C
+ default y
+ 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
+ default n
+ help
+ Enable printk() debug for msm camera
+
+
+source "drivers/media/video/msm/Kconfig"
+
endif # VIDEO_CAPTURE_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f0fecd6..724c7a3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -10,8 +10,8 @@
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
+ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o videobuf-core.o videobuf-msm-mem.o videobuf-dma-contig.o
# V4L2 core modules
@@ -182,6 +182,7 @@
obj-y += davinci/
+obj-$(CONFIG_MSM_CAMERA) += msm/
obj-$(CONFIG_ARCH_OMAP) += omap/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
new file mode 100644
index 0000000..6ba1abd
--- /dev/null
+++ b/drivers/media/video/msm/Kconfig
@@ -0,0 +1,194 @@
+config MSM_CAMERA_V4L2
+ bool "MSM Camera V4L2 Interface"
+ depends on MSM_CAMERA
+ default n
+ ---help---
+ This flag enables V4L2 interface of MSM
+ camera driver. If enabled, application interacts
+ with /dev/video0 through V4L2 APIs. Otherwise,
+ native APIs are used through /dev/config0, /dev/frame0,
+ and /dev/control0.
+
+comment "Camera Sensor Selection"
+config MT9T013
+ bool "Sensor mt9t013 (BAYER 3M)"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !ARCH_MSM8960 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ MICRON 3M Bayer Sensor with AutoFocus
+config MT9D113
+ bool "Sensor mt9d113 (YUV 2M)"
+ depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ MICRON 2M YUV Sensor
+ This sensor is the front camera on QT8660.
+ This uses csi mipi interface.
+ This sensor is used only on QT device.
+config MT9D112
+ bool "Sensor mt9d112 (YUV 2M)"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !ARCH_MSM8960 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ MICRON 2M YUV Sensor
+config IMX074
+ bool "Sensor IMX074 (BAYER 13.5M)"
+ depends on MSM_CAMERA && (ARCH_MSM8X60 || ARCH_MSM8960)
+ default y
+ ---help---
+ SONY 13.5 MP Bayer Sensor
+config WEBCAM_OV7692
+ bool "Sensor OV7692 (VGA YUV)"
+ depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ Omni Vision VGA YUV Sensor.
+config WEBCAM_OV9726
+ bool "Sensor OV9726 (VGA Bayer)"
+ depends on MSM_CAMERA && (ARCH_MSM8X60 || ARCH_MSM7X30 || ARCH_MSM7X27A) && !MSM_CAMERA_V4L2
+ default n
+ ---help---
+ Omni Vision VGA Bayer Sensor.
+# This Senosr is used as a webcam.
+# This uses the CSI interface.
+config VX6953
+ bool "Sensor VX6953 (BAYER 5M)"
+ depends on MSM_CAMERA && ARCH_MSM7X30
+ default y
+ ---help---
+ STM 5M Bayer Sensor with EDOF
+config SN12M0PZ
+ bool "Sensor sn12m0pz (Bayer 12 MP)"
+ depends on MSM_CAMERA && ARCH_MSM7X30 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ Sony 12 MP Bayer Sensor
+config MT9P012
+ bool "Sensor mt9p012 (BAYER 5M)"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ MICRON 5M Bayer Sensor with Autofocus
+
+choice
+ prompt "AF module"
+ depends on MT9P012 && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default MSM_CAMERA_AF_FOXCONN
+
+config MSM_CAMERA_AF_FOXCONN
+ bool "FOXCONN Module"
+ help
+ This driver supports FOXCONN AF module for 5M Bayer sensor
+
+config MSM_CAMERA_AF_BAM
+ bool "BAM Module"
+ help
+ This driver supports BAM AF module for 5M Bayer sensor
+
+endchoice
+
+config MT9P012_KM
+ bool "Sensor mt9p012 KM module (BAYER 5M)"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ MICRON 5M Bayer Sensor KM modules with Autofocus
+
+config MT9E013
+ bool "Sensor mt9e013 module (BAYER 8M)"
+ depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM7X27A) && !MSM_CAMERA_V4L2
+ default n
+ ---help---
+ Aptina 8M Bayer Sensor modules with Autofocus
+
+config S5K3E2FX
+ bool "Sensor s5k3e2fx (Samsung 5M)"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ Samsung 5M with Autofocus
+
+config QS_S5K4E1
+ bool "Sensor qs_s5k4e1 (Samsung 5M)"
+ depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ default y
+ ---help---
+ Samsung 5M with Autofocus
+
+config S5K4E1
+ bool "Sensor Sensor s5k4e1 (Samsung 5M)"
+ depends on MSM_CAMERA
+ default n
+ ---help---
+ Support for S5k4E1 samsung sensor driver.
+ It is a Bayer 5MP sensor with auto focus and it supports
+ two mipi lanes, required for msm7x2xA platform.
+ Say Y here if this is msm7x2xA variant platform.
+
+config MSM_CAMERA_FLASH_SC628A
+ bool "Qualcomm MSM camera sc628a flash support"
+ depends on MSM_CAMERA
+ default n
+ ---help---
+ Enable support for LED flash for msm camera.
+ It is a samtech charge pump flash driver and it
+ supports spotlight and flash light modes with
+ differrent current levels.
+
+config IMX072
+ bool "Sensor imx072 (Sony 5M)"
+ default n
+ ---help---
+ Support for IMX072 sony sensor driver.
+ It is a Bayer 5MP sensor with auto focus and it supports
+ two mipi lanes, required for msm7x2xA platform.
+ Say Y here if this is msm7x2xA variant platform.
+
+config OV2720
+ bool "Sensor ov2720 (Omnivision 2MP)"
+ depends on MSM_CAMERA && ARCH_MSM8960
+ default y
+
+config VB6801
+ bool "Sensor vb6801"
+ depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
+ ---help---
+ 5M with flash
+
+config MSM_CAMERA_FLASH
+ bool "Qualcomm MSM camera flash support"
+ depends on MSM_CAMERA
+ default y
+ ---help---
+ Enable support for LED flash for msm camera
+
+config MSM_CAMERA_SENSOR
+ bool "Qualcomm MSM camera sensor support"
+ depends on MSM_CAMERA
+ default y
+
+config MSM_GEMINI
+ tristate "Qualcomm MSM Gemini Jpeg Engine support"
+ depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
+ default n
+ ---help---
+ Enable support for Gemini Jpeg Engine
+
+config MSM_VPE
+ tristate "Qualcomm MSM Video Pre-processing Engine support"
+ depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60)
+ default y
+ ---help---
+ Enable support for Video Pre-processing Engine
+
+config QUP_EXCLUSIVE_TO_CAMERA
+ bool "QUP exclusive to camera"
+ depends on MSM_CAMERA
+ default y
+ ---help---
+ This flag enabled states that QUP
+ is exclusive to camera. In case this
+ is disabled, the lvs1 voltage is enabled
+ by QUP in the board file as QUP is used by
+ applications other than camera.
+
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
new file mode 100644
index 0000000..c366834
--- /dev/null
+++ b/drivers/media/video/msm/Makefile
@@ -0,0 +1,46 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ifeq ($(GCC_VERSION),0404)
+CFLAGS_REMOVE_msm_vfe8x.o = -Wframe-larger-than=1024
+endif
+
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+ obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o
+else
+ obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
+endif
+obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o
+obj-$(CONFIG_MSM_CAMERA_FLASH) += flash.o
+obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
+obj-$(CONFIG_ARCH_MSM_ARM11) += msm_vfe7x.o msm_io7x.o
+obj-$(CONFIG_ARCH_MSM7X27A) += msm_vfe7x27a.o msm_io_7x27a.o
+obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31.o msm_io_vfe31.o msm_vpe1.o
+obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o
+obj-$(CONFIG_ARCH_MSM8X60) += msm_vfe31.o msm_io_8x60.o msm_vpe1.o
+obj-$(CONFIG_ARCH_MSM8960) += msm_io_8960.o msm_ispif.o msm_vfe32.o msm_vpe1.o
+obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o
+obj-$(CONFIG_SN12M0PZ) += sn12m0pz.o sn12m0pz_reg.o
+obj-$(CONFIG_MT9P012) += mt9p012_reg.o
+obj-$(CONFIG_MSM_CAMERA_AF_FOXCONN) += mt9p012_fox.o
+obj-$(CONFIG_MSM_CAMERA_AF_BAM) += mt9p012_bam.o
+obj-$(CONFIG_MT9P012_KM) += mt9p012_km.o mt9p012_km_reg.o
+obj-$(CONFIG_MT9E013) += mt9e013.o mt9e013_reg.o
+obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o
+obj-$(CONFIG_S5K4E1) += s5k4e1.o s5k4e1_reg.o
+#FIXME: Merge the two ifeq causes VX6953 preview not coming up.
+ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
+ obj-$(CONFIG_VX6953) += vx6953_v4l2.o vx6953_reg_v4l2.o
+ obj-$(CONFIG_IMX074) += imx074_v4l2.o imx074_reg.o
+else
+ obj-$(CONFIG_VX6953) += vx6953.o vx6953_reg.o
+ obj-$(CONFIG_IMX074) += imx074.o imx074_reg.o
+endif
+obj-$(CONFIG_QS_S5K4E1) += qs_s5k4e1.o qs_s5k4e1_reg.o
+obj-$(CONFIG_VB6801) += vb6801.o
+obj-$(CONFIG_IMX072) += imx072.o imx072_reg.o
+obj-$(CONFIG_OV2720) += ov2720.o
+obj-$(CONFIG_WEBCAM_OV9726) += ov9726.o ov9726_reg.o
+obj-$(CONFIG_WEBCAM_OV7692) += ov7692.o
+obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o
+
+obj-$(CONFIG_MT9D113) += mt9d113.o mt9d113_reg.o
+obj-$(CONFIG_MSM_GEMINI) += msm_gemini_dev.o msm_gemini_sync.o msm_gemini_core.o msm_gemini_hw.o msm_gemini_platform.o
diff --git a/drivers/media/video/msm/flash.c b/drivers/media/video/msm/flash.c
new file mode 100644
index 0000000..cd81125
--- /dev/null
+++ b/drivers/media/video/msm/flash.c
@@ -0,0 +1,563 @@
+
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/leds-pmic8058.h>
+#include <linux/pwm.h>
+#include <linux/pmic8058-pwm.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <mach/pmic.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+
+struct timer_list timer_flash;
+
+enum msm_cam_flash_stat{
+ MSM_CAM_FLASH_OFF,
+ MSM_CAM_FLASH_ON,
+};
+
+#if defined CONFIG_MSM_CAMERA_FLASH_SC628A
+static struct sc628a_work_t *sc628a_flash;
+static struct i2c_client *sc628a_client;
+static DECLARE_WAIT_QUEUE_HEAD(sc628a_wait_queue);
+
+struct sc628a_work_t {
+ struct work_struct work;
+};
+
+static const struct i2c_device_id sc628a_i2c_id[] = {
+ {"sc628a", 0},
+ { }
+};
+
+static int32_t sc628a_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(sc628a_client->adapter, msg, 1) < 0) {
+ pr_err("sc628a_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t sc628a_i2c_write_b_flash(uint8_t waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = waddr;
+ buf[1] = bdata;
+
+ rc = sc628a_i2c_txdata(sc628a_client->addr, buf, 2);
+ if (rc < 0) {
+ pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+
+static int sc628a_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&sc628a_wait_queue);
+ return 0;
+}
+
+static int sc628a_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("sc628a_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ sc628a_flash = kzalloc(sizeof(struct sc628a_work_t), GFP_KERNEL);
+ if (!sc628a_flash) {
+ pr_err("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, sc628a_flash);
+ sc628a_init_client(client);
+ sc628a_client = client;
+
+ msleep(50);
+
+ CDBG("sc628a_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ pr_err("sc628a_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static struct i2c_driver sc628a_i2c_driver = {
+ .id_table = sc628a_i2c_id,
+ .probe = sc628a_i2c_probe,
+ .remove = __exit_p(sc628a_i2c_remove),
+ .driver = {
+ .name = "sc628a",
+ },
+};
+#endif
+
+static int config_flash_gpio_table(enum msm_cam_flash_stat stat,
+ struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+ int rc = 0, i = 0;
+ int msm_cam_flash_gpio_tbl[][2] = {
+ {sfdata->flash_trigger, 1},
+ {sfdata->flash_charge, 1},
+ {sfdata->flash_charge_done, 0}
+ };
+
+ if (stat == MSM_CAM_FLASH_ON) {
+ for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+ rc = gpio_request(msm_cam_flash_gpio_tbl[i][0],
+ "CAM_FLASH_GPIO");
+ if (unlikely(rc < 0)) {
+ pr_err("%s not able to get gpio\n", __func__);
+ for (i--; i >= 0; i--)
+ gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+ break;
+ }
+ if (msm_cam_flash_gpio_tbl[i][1])
+ gpio_direction_output(
+ msm_cam_flash_gpio_tbl[i][0], 0);
+ else
+ gpio_direction_input(
+ msm_cam_flash_gpio_tbl[i][0]);
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+ gpio_direction_input(msm_cam_flash_gpio_tbl[i][0]);
+ gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+ }
+ }
+ return rc;
+}
+
+int msm_camera_flash_current_driver(
+ struct msm_camera_sensor_flash_current_driver *current_driver,
+ unsigned led_state)
+{
+ int rc = 0;
+#if defined CONFIG_LEDS_PMIC8058
+ int idx;
+ const struct pmic8058_leds_platform_data *driver_channel =
+ current_driver->driver_channel;
+ int num_leds = driver_channel->num_leds;
+
+ CDBG("%s: led_state = %d\n", __func__, led_state);
+
+ /* Evenly distribute current across all channels */
+ switch (led_state) {
+ case MSM_CAMERA_LED_OFF:
+ for (idx = 0; idx < num_leds; ++idx) {
+ rc = pm8058_set_led_current(
+ driver_channel->leds[idx].id, 0);
+ if (rc < 0)
+ pr_err(
+ "%s: FAIL name = %s, rc = %d\n",
+ __func__,
+ driver_channel->leds[idx].name,
+ rc);
+ }
+ break;
+
+ case MSM_CAMERA_LED_LOW:
+ for (idx = 0; idx < num_leds; ++idx) {
+ rc = pm8058_set_led_current(
+ driver_channel->leds[idx].id,
+ current_driver->low_current/num_leds);
+ if (rc < 0)
+ pr_err(
+ "%s: FAIL name = %s, rc = %d\n",
+ __func__,
+ driver_channel->leds[idx].name,
+ rc);
+ }
+ break;
+
+ case MSM_CAMERA_LED_HIGH:
+ for (idx = 0; idx < num_leds; ++idx) {
+ rc = pm8058_set_led_current(
+ driver_channel->leds[idx].id,
+ current_driver->high_current/num_leds);
+ if (rc < 0)
+ pr_err(
+ "%s: FAIL name = %s, rc = %d\n",
+ __func__,
+ driver_channel->leds[idx].name,
+ rc);
+ }
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("msm_camera_flash_led_pmic8058: return %d\n", rc);
+#endif /* CONFIG_LEDS_PMIC8058 */
+#if defined CONFIG_MSM_CAMERA_FLASH_SC628A
+ if (!sc628a_client) {
+ rc = i2c_add_driver(&sc628a_i2c_driver);
+ if (rc < 0 || sc628a_client == NULL) {
+ rc = -ENOTSUPP;
+ pr_err("I2C add driver failed");
+ return rc;
+ }
+ rc = gpio_request(current_driver->led1, "sc628a");
+ if (!rc) {
+ gpio_direction_output(current_driver->led1, 0);
+ gpio_set_value_cansleep(current_driver->led1, 1);
+ } else
+ i2c_del_driver(&sc628a_i2c_driver);
+ rc = gpio_request(current_driver->led2, "sc628a");
+ if (!rc) {
+ gpio_direction_output(current_driver->led2, 0);
+ gpio_set_value_cansleep(current_driver->led2, 1);
+ } else {
+ i2c_del_driver(&sc628a_i2c_driver);
+ gpio_free(current_driver->led1);
+ }
+ }
+ switch (led_state) {
+ case MSM_CAMERA_LED_OFF:
+ sc628a_i2c_write_b_flash(0x02, 0x0);
+ break;
+ case MSM_CAMERA_LED_LOW:
+ sc628a_i2c_write_b_flash(0x02, 0x06);
+ break;
+ case MSM_CAMERA_LED_HIGH:
+ sc628a_i2c_write_b_flash(0x02, 0x49);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+#endif
+
+ return rc;
+}
+
+
+static int msm_camera_flash_pwm(
+ struct msm_camera_sensor_flash_pwm *pwm,
+ unsigned led_state)
+{
+ int rc = 0;
+ int PWM_PERIOD = USEC_PER_SEC / pwm->freq;
+
+ static struct pwm_device *flash_pwm;
+
+ if (!flash_pwm) {
+ flash_pwm = pwm_request(pwm->channel, "camera-flash");
+ if (flash_pwm == NULL || IS_ERR(flash_pwm)) {
+ pr_err("%s: FAIL pwm_request(): flash_pwm=%p\n",
+ __func__, flash_pwm);
+ flash_pwm = NULL;
+ return -ENXIO;
+ }
+ }
+
+ switch (led_state) {
+ case MSM_CAMERA_LED_LOW:
+ rc = pwm_config(flash_pwm,
+ (PWM_PERIOD/pwm->max_load)*pwm->low_load,
+ PWM_PERIOD);
+ if (rc >= 0)
+ rc = pwm_enable(flash_pwm);
+ break;
+
+ case MSM_CAMERA_LED_HIGH:
+ rc = pwm_config(flash_pwm,
+ (PWM_PERIOD/pwm->max_load)*pwm->high_load,
+ PWM_PERIOD);
+ if (rc >= 0)
+ rc = pwm_enable(flash_pwm);
+ break;
+
+ case MSM_CAMERA_LED_OFF:
+ pwm_disable(flash_pwm);
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ return rc;
+}
+
+int msm_camera_flash_pmic(
+ struct msm_camera_sensor_flash_pmic *pmic,
+ unsigned led_state)
+{
+ int rc = 0;
+
+ switch (led_state) {
+ case MSM_CAMERA_LED_OFF:
+ rc = pmic->pmic_set_current(pmic->led_src_1, 0);
+ if (pmic->num_of_src > 1)
+ rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+ break;
+
+ case MSM_CAMERA_LED_LOW:
+ rc = pmic->pmic_set_current(pmic->led_src_1,
+ pmic->low_current);
+ if (pmic->num_of_src > 1)
+ rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+ break;
+
+ case MSM_CAMERA_LED_HIGH:
+ rc = pmic->pmic_set_current(pmic->led_src_1,
+ pmic->high_current);
+ if (pmic->num_of_src > 1)
+ rc = pmic->pmic_set_current(pmic->led_src_2,
+ pmic->high_current);
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("flash_set_led_state: return %d\n", rc);
+
+ return rc;
+}
+
+int32_t msm_camera_flash_set_led_state(
+ struct msm_camera_sensor_flash_data *fdata, unsigned led_state)
+{
+ int32_t rc;
+
+ CDBG("flash_set_led_state: %d flash_sr_type=%d\n", led_state,
+ fdata->flash_src->flash_sr_type);
+
+ if (fdata->flash_type != MSM_CAMERA_FLASH_LED)
+ return -ENODEV;
+
+ switch (fdata->flash_src->flash_sr_type) {
+ case MSM_CAMERA_FLASH_SRC_PMIC:
+ rc = msm_camera_flash_pmic(&fdata->flash_src->_fsrc.pmic_src,
+ led_state);
+ break;
+
+ case MSM_CAMERA_FLASH_SRC_PWM:
+ rc = msm_camera_flash_pwm(&fdata->flash_src->_fsrc.pwm_src,
+ led_state);
+ break;
+
+ case MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER:
+ rc = msm_camera_flash_current_driver(
+ &fdata->flash_src->_fsrc.current_driver_src,
+ led_state);
+ break;
+
+ default:
+ rc = -ENODEV;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_strobe_flash_xenon_charge(int32_t flash_charge,
+ int32_t charge_enable, uint32_t flash_recharge_duration)
+{
+ gpio_set_value_cansleep(flash_charge, charge_enable);
+ if (charge_enable) {
+ timer_flash.expires = jiffies +
+ msecs_to_jiffies(flash_recharge_duration);
+ /* add timer for the recharge */
+ if (!timer_pending(&timer_flash))
+ add_timer(&timer_flash);
+ } else
+ del_timer_sync(&timer_flash);
+ return 0;
+}
+
+static void strobe_flash_xenon_recharge_handler(unsigned long data)
+{
+ unsigned long flags;
+ struct msm_camera_sensor_strobe_flash_data *sfdata =
+ (struct msm_camera_sensor_strobe_flash_data *)data;
+
+ spin_lock_irqsave(&sfdata->timer_lock, flags);
+ msm_strobe_flash_xenon_charge(sfdata->flash_charge, 1,
+ sfdata->flash_recharge_duration);
+ spin_unlock_irqrestore(&sfdata->timer_lock, flags);
+
+ return;
+}
+
+static irqreturn_t strobe_flash_charge_ready_irq(int irq_num, void *data)
+{
+ struct msm_camera_sensor_strobe_flash_data *sfdata =
+ (struct msm_camera_sensor_strobe_flash_data *)data;
+
+ /* put the charge signal to low */
+ gpio_set_value_cansleep(sfdata->flash_charge, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int msm_strobe_flash_xenon_init(
+ struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&sfdata->spin_lock, flags);
+ if (!sfdata->state) {
+
+ rc = config_flash_gpio_table(MSM_CAM_FLASH_ON, sfdata);
+ if (rc < 0) {
+ pr_err("%s: gpio_request failed\n", __func__);
+ goto go_out;
+ }
+ rc = request_irq(sfdata->irq, strobe_flash_charge_ready_irq,
+ IRQF_TRIGGER_RISING, "charge_ready", sfdata);
+ if (rc < 0) {
+ pr_err("%s: request_irq failed %d\n", __func__, rc);
+ goto go_out;
+ }
+
+ spin_lock_init(&sfdata->timer_lock);
+ /* setup timer */
+ init_timer(&timer_flash);
+ timer_flash.function = strobe_flash_xenon_recharge_handler;
+ timer_flash.data = (unsigned long)sfdata;
+ }
+ sfdata->state++;
+go_out:
+ spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+
+ return rc;
+}
+
+static int msm_strobe_flash_xenon_release
+(struct msm_camera_sensor_strobe_flash_data *sfdata, int32_t final_release)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sfdata->spin_lock, flags);
+ if (sfdata->state > 0) {
+ if (final_release)
+ sfdata->state = 0;
+ else
+ sfdata->state--;
+
+ if (!sfdata->state) {
+ free_irq(sfdata->irq, sfdata);
+ config_flash_gpio_table(MSM_CAM_FLASH_OFF, sfdata);
+ if (timer_pending(&timer_flash))
+ del_timer_sync(&timer_flash);
+ }
+ }
+ spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+ return 0;
+}
+
+static void msm_strobe_flash_xenon_fn_init
+ (struct msm_strobe_flash_ctrl *strobe_flash_ptr)
+{
+ strobe_flash_ptr->strobe_flash_init =
+ msm_strobe_flash_xenon_init;
+ strobe_flash_ptr->strobe_flash_charge =
+ msm_strobe_flash_xenon_charge;
+ strobe_flash_ptr->strobe_flash_release =
+ msm_strobe_flash_xenon_release;
+}
+
+int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype)
+{
+ int rc = 0;
+ switch (sftype) {
+ case MSM_CAMERA_STROBE_FLASH_XENON:
+ if (sync->sdata->strobe_flash_data) {
+ msm_strobe_flash_xenon_fn_init(&sync->sfctrl);
+ rc = sync->sfctrl.strobe_flash_init(
+ sync->sdata->strobe_flash_data);
+ } else
+ return -ENODEV;
+ break;
+ default:
+ rc = -ENODEV;
+ }
+ return rc;
+}
+
+int msm_strobe_flash_ctrl(struct msm_camera_sensor_strobe_flash_data *sfdata,
+ struct strobe_flash_ctrl_data *strobe_ctrl)
+{
+ int rc = 0;
+ switch (strobe_ctrl->type) {
+ case STROBE_FLASH_CTRL_INIT:
+ if (!sfdata)
+ return -ENODEV;
+ rc = msm_strobe_flash_xenon_init(sfdata);
+ break;
+ case STROBE_FLASH_CTRL_CHARGE:
+ rc = msm_strobe_flash_xenon_charge(sfdata->flash_charge,
+ strobe_ctrl->charge_en,
+ sfdata->flash_recharge_duration);
+ break;
+ case STROBE_FLASH_CTRL_RELEASE:
+ if (sfdata)
+ rc = msm_strobe_flash_xenon_release(sfdata, 0);
+ break;
+ default:
+ pr_err("Invalid Strobe Flash State\n");
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int msm_flash_ctrl(struct msm_camera_sensor_info *sdata,
+ struct flash_ctrl_data *flash_info)
+{
+ int rc = 0;
+ switch (flash_info->flashtype) {
+ case LED_FLASH:
+ rc = msm_camera_flash_set_led_state(sdata->flash_data,
+ flash_info->ctrl_data.led_state);
+ break;
+ case STROBE_FLASH:
+ rc = msm_strobe_flash_ctrl(sdata->strobe_flash_data,
+ &(flash_info->ctrl_data.strobe_ctrl));
+ break;
+ default:
+ pr_err("Invalid Flash MODE\n");
+ rc = -EINVAL;
+ }
+ return rc;
+}
diff --git a/drivers/media/video/msm/imx072.c b/drivers/media/video/msm/imx072.c
new file mode 100644
index 0000000..d9ee051
--- /dev/null
+++ b/drivers/media/video/msm/imx072.c
@@ -0,0 +1,1164 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "imx072.h"
+
+/* SENSOR REGISTER DEFINES */
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME 0x0202
+/* Gain */
+#define REG_GLOBAL_GAIN 0x0204
+
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES 0x0340
+#define REG_LINE_LENGTH_PCK 0x0342
+
+/* 16bit address - 8 bit context register structure */
+#define Q8 0x00000100
+#define Q10 0x00000400
+#define IMX072_MASTER_CLK_RATE 24000000
+#define IMX072_OFFSET 3
+
+/* AF Total steps parameters */
+#define IMX072_AF_I2C_ADDR 0x18
+#define IMX072_TOTAL_STEPS_NEAR_TO_FAR 30
+
+static uint16_t imx072_step_position_table[IMX072_TOTAL_STEPS_NEAR_TO_FAR+1];
+static uint16_t imx072_nl_region_boundary1;
+static uint16_t imx072_nl_region_code_per_step1;
+static uint16_t imx072_l_region_code_per_step = 12;
+static uint16_t imx072_sw_damping_time_wait = 8;
+static uint16_t imx072_af_initial_code = 350;
+static uint16_t imx072_damping_threshold = 10;
+
+struct imx072_work_t {
+ struct work_struct work;
+};
+
+static struct imx072_work_t *imx072_sensorw;
+static struct i2c_client *imx072_client;
+
+struct imx072_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ uint16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum imx072_resolution_t prev_res;
+ enum imx072_resolution_t pict_res;
+ enum imx072_resolution_t curr_res;
+ enum imx072_test_mode_t set_test;
+ enum imx072_cam_mode_t cam_mode;
+};
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static bool CSI_CONFIG;
+static struct imx072_ctrl_t *imx072_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(imx072_wait_queue);
+DEFINE_MUTEX(imx072_mut);
+
+#ifdef CONFIG_DEBUG_FS
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+#endif
+
+static int imx072_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(imx072_client->adapter, msgs, 2) < 0) {
+ pr_err("imx072_i2c_rxdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t imx072_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(imx072_client->adapter, msg, 1) < 0) {
+ pr_err("imx072_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t imx072_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = imx072_i2c_rxdata(imx072_client->addr>>1, buf, rlen);
+ if (rc < 0) {
+ pr_err("imx072_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("imx072_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+ return rc;
+}
+
+static int32_t imx072_i2c_write_w_sensor(unsigned short waddr,
+ uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+ rc = imx072_i2c_txdata(imx072_client->addr>>1, buf, 4);
+ if (rc < 0) {
+ pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+ return rc;
+}
+
+static int32_t imx072_i2c_write_b_sensor(unsigned short waddr,
+ uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = imx072_i2c_txdata(imx072_client->addr>>1, buf, 3);
+ if (rc < 0)
+ pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ return rc;
+}
+
+static int32_t imx072_i2c_write_b_af(uint8_t msb, uint8_t lsb)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[2];
+
+ buf[0] = msb;
+ buf[1] = lsb;
+ rc = imx072_i2c_txdata(IMX072_AF_I2C_ADDR>>1, buf, 2);
+ if (rc < 0)
+ pr_err("af_i2c_write faield msb = 0x%x lsb = 0x%x",
+ msb, lsb);
+ return rc;
+}
+
+static int32_t imx072_i2c_write_w_table(struct imx072_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = imx072_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static void imx072_group_hold_on(void)
+{
+ imx072_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+}
+
+static void imx072_group_hold_off(void)
+{
+ imx072_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void imx072_start_stream(void)
+{
+ imx072_i2c_write_b_sensor(0x0100, 0x01);
+}
+
+static void imx072_stop_stream(void)
+{
+ imx072_i2c_write_b_sensor(0x0100, 0x00);
+}
+
+static void imx072_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider, d1, d2;
+
+ d1 = prev_frame_length_lines * 0x00000400 / snap_frame_length_lines;
+ d2 = prev_line_length_pck * 0x00000400 / snap_line_length_pck;
+ divider = d1 * d2 / 0x400;
+
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+}
+
+static uint16_t imx072_get_prev_lines_pf(void)
+{
+ return prev_frame_length_lines;
+}
+
+static uint16_t imx072_get_prev_pixels_pl(void)
+{
+ return prev_line_length_pck;
+}
+
+static uint16_t imx072_get_pict_lines_pf(void)
+{
+ return snap_frame_length_lines;
+}
+
+static uint16_t imx072_get_pict_pixels_pl(void)
+{
+ return snap_line_length_pck;
+}
+
+static uint32_t imx072_get_pict_max_exp_lc(void)
+{
+ return snap_frame_length_lines * 24;
+}
+
+static int32_t imx072_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ total_lines_per_frame = (uint16_t)
+ ((prev_frame_length_lines *
+ imx072_ctrl->fps_divider)/0x400);
+ imx072_ctrl->fps_divider = fps->fps_div;
+ imx072_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ imx072_group_hold_on();
+ rc = imx072_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES,
+ total_lines_per_frame);
+ imx072_group_hold_off();
+ return rc;
+}
+
+static int32_t imx072_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines = 0;
+ uint8_t offset;
+ int32_t rc = 0;
+ if (imx072_ctrl->curr_res == imx072_ctrl->prev_res)
+ fl_lines = prev_frame_length_lines;
+ else if (imx072_ctrl->curr_res == imx072_ctrl->pict_res)
+ fl_lines = snap_frame_length_lines;
+ line = (line * imx072_ctrl->fps_divider) / Q10;
+ offset = IMX072_OFFSET;
+ if (line > (fl_lines - offset))
+ fl_lines = line + offset;
+
+ imx072_group_hold_on();
+ rc = imx072_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES, fl_lines);
+ rc = imx072_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+ rc = imx072_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+ imx072_group_hold_off();
+ return rc;
+}
+
+static int32_t imx072_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = imx072_write_exp_gain(gain, line);
+ return rc;
+}
+
+static int32_t imx072_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ struct msm_camera_csi_params imx072_csi_params;
+
+ imx072_stop_stream();
+ msleep(30);
+ if (update_type == REG_INIT) {
+ msleep(20);
+ CSI_CONFIG = 0;
+ imx072_i2c_write_w_table(imx072_regs.rec_settings,
+ imx072_regs.rec_size);
+ } else if (update_type == UPDATE_PERIODIC) {
+#ifdef CONFIG_DEBUG_FS
+ cam_debug_init();
+#endif
+ msleep(20);
+ if (!CSI_CONFIG) {
+ imx072_csi_params.lane_cnt = 2;
+ imx072_csi_params.data_format = CSI_10BIT;
+ imx072_csi_params.lane_assign = 0xe4;
+ imx072_csi_params.dpcm_scheme = 0;
+ imx072_csi_params.settle_cnt = 0x18;
+ msm_camio_vfe_clk_rate_set(192000000);
+ rc = msm_camio_csi_config(&imx072_csi_params);
+ msleep(100);
+ CSI_CONFIG = 1;
+ }
+ imx072_i2c_write_w_table(
+ imx072_regs.conf_array[rt].conf,
+ imx072_regs.conf_array[rt].size);
+ imx072_start_stream();
+ msleep(30);
+ }
+ return rc;
+}
+
+static int32_t imx072_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ /* change sensor resolution if needed */
+ if (imx072_sensor_setting(UPDATE_PERIODIC,
+ imx072_ctrl->prev_res) < 0)
+ return rc;
+
+ imx072_ctrl->curr_res = imx072_ctrl->prev_res;
+ imx072_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t imx072_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /*change sensor resolution if needed */
+ if (imx072_ctrl->curr_res != imx072_ctrl->pict_res) {
+ if (imx072_sensor_setting(UPDATE_PERIODIC,
+ imx072_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ imx072_ctrl->curr_res = imx072_ctrl->pict_res;
+ imx072_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t imx072_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /* change sensor resolution if needed */
+ if (imx072_ctrl->curr_res != imx072_ctrl->pict_res) {
+ if (imx072_sensor_setting(UPDATE_PERIODIC,
+ imx072_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ imx072_ctrl->curr_res = imx072_ctrl->pict_res;
+ imx072_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t imx072_mode_init(int mode, struct sensor_init_cfg init_info)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ if (mode != imx072_ctrl->cam_mode) {
+ imx072_ctrl->prev_res = init_info.prev_res;
+ imx072_ctrl->pict_res = init_info.pict_res;
+ imx072_ctrl->cam_mode = mode;
+
+ prev_frame_length_lines =
+ imx072_regs.conf_array[imx072_ctrl->prev_res].
+ conf[IMX072_FRAME_LENGTH_LINES_HI].wdata << 8 |
+ imx072_regs.conf_array[imx072_ctrl->prev_res].
+ conf[IMX072_FRAME_LENGTH_LINES_LO].wdata;
+ prev_line_length_pck =
+ imx072_regs.conf_array[imx072_ctrl->prev_res].
+ conf[IMX072_LINE_LENGTH_PCK_HI].wdata << 8 |
+ imx072_regs.conf_array[imx072_ctrl->prev_res].
+ conf[IMX072_LINE_LENGTH_PCK_LO].wdata;
+ snap_frame_length_lines =
+ imx072_regs.conf_array[imx072_ctrl->pict_res].
+ conf[IMX072_FRAME_LENGTH_LINES_HI].wdata << 8 |
+ imx072_regs.conf_array[imx072_ctrl->pict_res].
+ conf[IMX072_FRAME_LENGTH_LINES_LO].wdata;
+ snap_line_length_pck =
+ imx072_regs.conf_array[imx072_ctrl->pict_res].
+ conf[IMX072_LINE_LENGTH_PCK_HI].wdata << 8 |
+ imx072_regs.conf_array[imx072_ctrl->pict_res].
+ conf[IMX072_LINE_LENGTH_PCK_LO].wdata;
+
+ rc = imx072_sensor_setting(REG_INIT,
+ imx072_ctrl->prev_res);
+ }
+ return rc;
+}
+
+static int32_t imx072_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ imx072_ctrl->prev_res = res;
+ rc = imx072_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ imx072_ctrl->pict_res = res;
+ rc = imx072_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ imx072_ctrl->pict_res = res;
+ rc = imx072_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+#define DIV_CEIL(x, y) ((x/y + ((x%y) ? 1 : 0)))
+static int32_t imx072_move_focus(int direction,
+ int32_t num_steps)
+{
+ int32_t rc = 0;
+ int16_t step_direction, dest_lens_position, dest_step_position;
+ uint8_t code_val_msb, code_val_lsb;
+ int16_t next_lens_position, target_dist, small_step;
+
+ if (direction == MOVE_NEAR)
+ step_direction = 1;
+ else if (direction == MOVE_FAR)
+ step_direction = -1;
+ else {
+ pr_err("Illegal focus direction\n");
+ return -EINVAL;
+ }
+ dest_step_position = imx072_ctrl->curr_step_pos +
+ (step_direction * num_steps);
+
+ if (dest_step_position < 0)
+ dest_step_position = 0;
+ else if (dest_step_position > IMX072_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+
+ if (dest_step_position == imx072_ctrl->curr_step_pos) {
+ CDBG("imx072 same position No-Move exit\n");
+ return rc;
+ }
+ CDBG("%s Index = [%d]\n", __func__, dest_step_position);
+
+ dest_lens_position = imx072_step_position_table[dest_step_position];
+ CDBG("%s lens_position value = %d\n", __func__, dest_lens_position);
+ target_dist = step_direction * (dest_lens_position -
+ imx072_ctrl->curr_lens_pos);
+ if (step_direction < 0 && (target_dist >=
+ (imx072_step_position_table[imx072_damping_threshold]
+ - imx072_af_initial_code))) {
+ small_step = DIV_CEIL(target_dist, 10);
+ imx072_sw_damping_time_wait = 30;
+ } else {
+ small_step = DIV_CEIL(target_dist, 4);
+ imx072_sw_damping_time_wait = 20;
+ }
+
+ CDBG("%s: small_step:%d, wait_time:%d\n", __func__, small_step,
+ imx072_sw_damping_time_wait);
+ for (next_lens_position = imx072_ctrl->curr_lens_pos +
+ (step_direction * small_step);
+ (step_direction * next_lens_position) <=
+ (step_direction * dest_lens_position);
+ next_lens_position += (step_direction * small_step)) {
+
+ code_val_msb = ((next_lens_position & 0x03F0) >> 4);
+ code_val_lsb = ((next_lens_position & 0x000F) << 4);
+ CDBG("position value = %d\n", next_lens_position);
+ CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+ CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+ rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+ if (rc < 0) {
+ pr_err("imx072_move_focus failed writing i2c\n");
+ return rc;
+ }
+ imx072_ctrl->curr_lens_pos = next_lens_position;
+ usleep(imx072_sw_damping_time_wait*100);
+ }
+ if (imx072_ctrl->curr_lens_pos != dest_lens_position) {
+ code_val_msb = ((dest_lens_position & 0x03F0) >> 4);
+ code_val_lsb = ((dest_lens_position & 0x000F) << 4);
+ CDBG("position value = %d\n", dest_lens_position);
+ CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+ CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+ rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+ if (rc < 0) {
+ pr_err("imx072_move_focus failed writing i2c\n");
+ return rc;
+ }
+ usleep(imx072_sw_damping_time_wait * 100);
+ }
+ imx072_ctrl->curr_lens_pos = dest_lens_position;
+ imx072_ctrl->curr_step_pos = dest_step_position;
+ return rc;
+
+}
+
+static int32_t imx072_init_focus(void)
+{
+ uint8_t i;
+ int32_t rc = 0;
+
+ imx072_step_position_table[0] = imx072_af_initial_code;
+ for (i = 1; i <= IMX072_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ if (i <= imx072_nl_region_boundary1)
+ imx072_step_position_table[i] =
+ imx072_step_position_table[i-1]
+ + imx072_nl_region_code_per_step1;
+ else
+ imx072_step_position_table[i] =
+ imx072_step_position_table[i-1]
+ + imx072_l_region_code_per_step;
+
+ if (imx072_step_position_table[i] > 1023)
+ imx072_step_position_table[i] = 1023;
+ }
+ imx072_ctrl->curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int32_t imx072_set_default_focus(void)
+{
+ int32_t rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+ int16_t dest_lens_position = 0;
+
+ CDBG("%s Index = [%d]\n", __func__, 0);
+ if (imx072_ctrl->curr_step_pos != 0)
+ rc = imx072_move_focus(MOVE_FAR,
+ imx072_ctrl->curr_step_pos);
+ else {
+ dest_lens_position = imx072_af_initial_code;
+ code_val_msb = ((dest_lens_position & 0x03F0) >> 4);
+ code_val_lsb = ((dest_lens_position & 0x000F) << 4);
+
+ CDBG("position value = %d\n", dest_lens_position);
+ CDBG("movefocus vcm_msb = %d\n", code_val_msb);
+ CDBG("movefocus vcm_lsb = %d\n", code_val_lsb);
+ rc = imx072_i2c_write_b_af(code_val_msb, code_val_lsb);
+ if (rc < 0) {
+ pr_err("imx072_set_default_focus failed writing i2c\n");
+ return rc;
+ }
+
+ imx072_ctrl->curr_lens_pos = dest_lens_position;
+ imx072_ctrl->curr_step_pos = 0;
+
+ }
+ usleep(5000);
+ return rc;
+}
+
+static int32_t imx072_af_power_down(void)
+{
+ int32_t rc = 0;
+ int32_t i = 0;
+ int16_t dest_lens_position = imx072_af_initial_code;
+
+ if (imx072_ctrl->curr_lens_pos != 0) {
+ rc = imx072_set_default_focus();
+ CDBG("%s after imx072_set_default_focus\n", __func__);
+ msleep(40);
+ /*to avoid the sound during the power off.
+ brings the actuator to mechanical infinity gradually.*/
+ for (i = 0; i < IMX072_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ dest_lens_position = dest_lens_position -
+ (imx072_af_initial_code /
+ IMX072_TOTAL_STEPS_NEAR_TO_FAR);
+ CDBG("position value = %d\n", dest_lens_position);
+ rc = imx072_i2c_write_b_af(
+ ((dest_lens_position & 0x03F0) >> 4),
+ ((dest_lens_position & 0x000F) << 4));
+ CDBG("count = %d\n", i);
+ msleep(20);
+ if (rc < 0) {
+ pr_err("imx072_set_default_focus failed writing i2c\n");
+ return rc;
+ }
+ }
+ rc = imx072_i2c_write_b_af(0x00, 00);
+ msleep(40);
+ }
+ rc = imx072_i2c_write_b_af(0x80, 00);
+ return rc;
+}
+
+static int32_t imx072_power_down(void)
+{
+ int32_t rc = 0;
+
+ rc = imx072_af_power_down();
+ return rc;
+}
+
+static int imx072_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ pr_err("probe done\n");
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int imx072_probe_init_sensor(
+ const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "imx072");
+ CDBG(" imx072_probe_init_sensor\n");
+ if (!rc) {
+ pr_err("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(50);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(20);
+ } else {
+ goto init_probe_done;
+ }
+
+ CDBG(" imx072_probe_init_sensor is called\n");
+ rc = imx072_i2c_read(0x0, &chipid, 2);
+ CDBG("ID: %d\n", chipid);
+ /* 4. Compare sensor ID to IMX072 ID: */
+ if (chipid != 0x0045) {
+ rc = -ENODEV;
+ pr_err("imx072_probe_init_sensor chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ pr_err(" imx072_probe_init_sensor fails\n");
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ imx072_probe_init_done(data);
+init_probe_done:
+ pr_err(" imx072_probe_init_sensor finishes\n");
+ return rc;
+}
+
+int imx072_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ imx072_ctrl = kzalloc(sizeof(struct imx072_ctrl_t), GFP_KERNEL);
+ if (!imx072_ctrl) {
+ pr_err("imx072_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ imx072_ctrl->fps_divider = 1 * 0x00000400;
+ imx072_ctrl->pict_fps_divider = 1 * 0x00000400;
+ imx072_ctrl->set_test = TEST_OFF;
+ imx072_ctrl->cam_mode = MODE_INVALID;
+
+ if (data)
+ imx072_ctrl->sensordata = data;
+ if (rc < 0) {
+ pr_err("Calling imx072_sensor_open_init fail1\n");
+ return rc;
+ }
+ CDBG("%s: %d\n", __func__, __LINE__);
+ /* enable mclk first */
+ msm_camio_clk_rate_set(IMX072_MASTER_CLK_RATE);
+ rc = imx072_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ imx072_init_focus();
+ imx072_ctrl->fps = 30*Q8;
+ if (rc < 0) {
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ goto init_fail;
+ } else
+ goto init_done;
+init_fail:
+ pr_err("init_fail\n");
+ imx072_probe_init_done(data);
+init_done:
+ pr_err("init_done\n");
+ return rc;
+}
+
+static int imx072_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&imx072_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id imx072_i2c_id[] = {
+ {"imx072", 0},
+ { }
+};
+
+static int imx072_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("imx072_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ imx072_sensorw = kzalloc(sizeof(struct imx072_work_t),
+ GFP_KERNEL);
+ if (!imx072_sensorw) {
+ pr_err("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, imx072_sensorw);
+ imx072_init_client(client);
+ imx072_client = client;
+
+ msleep(50);
+
+ CDBG("imx072_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ pr_err("imx072_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int imx072_send_wb_info(struct wb_info_cfg *wb)
+{
+ return 0;
+
+}
+
+static int __exit imx072_remove(struct i2c_client *client)
+{
+ struct imx072_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ imx072_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver imx072_i2c_driver = {
+ .id_table = imx072_i2c_id,
+ .probe = imx072_i2c_probe,
+ .remove = __exit_p(imx072_i2c_remove),
+ .driver = {
+ .name = "imx072",
+ },
+};
+
+int imx072_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;
+ mutex_lock(&imx072_mut);
+ CDBG("imx072_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ imx072_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 =
+ imx072_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 =
+ imx072_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 =
+ imx072_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 =
+ imx072_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 =
+ imx072_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 = imx072_set_fps(&(cdata.cfg.fps));
+ break;
+ case CFG_SET_EXP_GAIN:
+ rc = imx072_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = imx072_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_MODE:
+ rc = imx072_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ rc = imx072_power_down();
+ break;
+ case CFG_MOVE_FOCUS:
+ rc = imx072_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_SET_DEFAULT_FOCUS:
+ imx072_set_default_focus();
+ break;
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+ case CFG_SET_EFFECT:
+ break;
+ case CFG_SEND_WB_INFO:
+ rc = imx072_send_wb_info(
+ &(cdata.cfg.wb_info));
+ break;
+ case CFG_SENSOR_INIT:
+ rc = imx072_mode_init(cdata.mode,
+ cdata.cfg.init_info);
+ break;
+ case CFG_SET_LENS_SHADING:
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&imx072_mut);
+
+ return rc;
+}
+
+static int imx072_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&imx072_mut);
+ imx072_power_down();
+ gpio_set_value_cansleep(imx072_ctrl->sensordata->sensor_reset, 0);
+ msleep(20);
+ gpio_free(imx072_ctrl->sensordata->sensor_reset);
+ kfree(imx072_ctrl);
+ imx072_ctrl = NULL;
+ pr_err("imx072_release completed\n");
+ mutex_unlock(&imx072_mut);
+
+ return rc;
+}
+
+static int imx072_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&imx072_i2c_driver);
+ if (rc < 0 || imx072_client == NULL) {
+ rc = -ENOTSUPP;
+ pr_err("I2C add driver failed");
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(IMX072_MASTER_CLK_RATE);
+ rc = imx072_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = imx072_sensor_open_init;
+ s->s_release = imx072_sensor_release;
+ s->s_config = imx072_sensor_config;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ imx072_probe_init_done(info);
+ pr_info("imx072_sensor_probe : SUCCESS\n");
+ return rc;
+
+probe_fail:
+ pr_err("imx072_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __imx072_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, imx072_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __imx072_probe,
+ .driver = {
+ .name = "msm_camera_imx072",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init imx072_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(imx072_init);
+void imx072_exit(void)
+{
+ i2c_del_driver(&imx072_i2c_driver);
+}
+MODULE_DESCRIPTION("Aptina 8 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+#ifdef CONFIG_DEBUG_FS
+static bool streaming = 1;
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+ int rc = 0;
+
+ if (val) {
+ imx072_start_stream();
+ streaming = 1;
+ } else {
+ imx072_stop_stream();
+ streaming = 0;
+ }
+
+ return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+ *val = streaming;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+ cam_debug_stream_set, "%llu\n");
+
+
+
+static int imx072_set_af_codestep(void *data, u64 val)
+{
+ imx072_l_region_code_per_step = val;
+ imx072_init_focus();
+ return 0;
+}
+
+static int imx072_get_af_codestep(void *data, u64 *val)
+{
+ *val = imx072_l_region_code_per_step;
+ return 0;
+}
+
+static uint16_t imx072_linear_total_step = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+static int imx072_set_linear_total_step(void *data, u64 val)
+{
+ imx072_linear_total_step = val;
+ return 0;
+}
+
+static int imx072_af_linearity_test(void *data, u64 *val)
+{
+ int i = 0;
+
+ imx072_set_default_focus();
+ msleep(3000);
+ for (i = 0; i < imx072_linear_total_step; i++) {
+ imx072_move_focus(MOVE_NEAR, 1);
+ CDBG("moved to index =[%d]\n", i);
+ msleep(1000);
+ }
+
+ for (i = 0; i < imx072_linear_total_step; i++) {
+ imx072_move_focus(MOVE_FAR, 1);
+ CDBG("moved to index =[%d]\n", i);
+ msleep(1000);
+ }
+ return 0;
+}
+
+static uint16_t imx072_step_val = IMX072_TOTAL_STEPS_NEAR_TO_FAR;
+static uint8_t imx072_step_dir = MOVE_NEAR;
+static int imx072_af_step_config(void *data, u64 val)
+{
+ imx072_step_val = val & 0xFFFF;
+ imx072_step_dir = (val >> 16) & 0x1;
+ return 0;
+}
+
+static int imx072_af_step(void *data, u64 *val)
+{
+ int i = 0;
+ int dir = MOVE_NEAR;
+ imx072_set_default_focus();
+ msleep(3000);
+ if (imx072_step_dir == 1)
+ dir = MOVE_FAR;
+
+ for (i = 0; i < imx072_step_val; i += 4) {
+ imx072_move_focus(dir, 4);
+ msleep(1000);
+ }
+ imx072_set_default_focus();
+ msleep(3000);
+ return 0;
+}
+
+static int imx072_af_set_resolution(void *data, u64 val)
+{
+ imx072_init_focus();
+ return 0;
+}
+
+static int imx072_af_get_resolution(void *data, u64 *val)
+{
+ *val = 0xFF;
+ return 0;
+}
+
+
+
+DEFINE_SIMPLE_ATTRIBUTE(af_codeperstep, imx072_get_af_codestep,
+ imx072_set_af_codestep, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_linear, imx072_af_linearity_test,
+ imx072_set_linear_total_step, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step, imx072_af_step,
+ imx072_af_step_config, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step_res, imx072_af_get_resolution,
+ imx072_af_set_resolution, "%llu\n");
+
+static int cam_debug_init(void)
+{
+ struct dentry *cam_dir;
+ debugfs_base = debugfs_create_dir("sensor", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ cam_dir = debugfs_create_dir("imx072", debugfs_base);
+ if (!cam_dir)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_stream))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_codeperstep", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &af_codeperstep))
+ return -ENOMEM;
+ if (!debugfs_create_file("af_linear", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &af_linear))
+ return -ENOMEM;
+ if (!debugfs_create_file("af_step", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &af_step))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_step_res", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &af_step_res))
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
diff --git a/drivers/media/video/msm/imx072.h b/drivers/media/video/msm/imx072.h
new file mode 100644
index 0000000..e3d279f
--- /dev/null
+++ b/drivers/media/video/msm/imx072.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef IMX072_H
+#define IMX072_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct imx072_reg imx072_regs;
+
+struct imx072_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct imx072_i2c_conf_array {
+ struct imx072_i2c_reg_conf *conf;
+ unsigned short size;
+};
+
+enum imx072_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum imx072_resolution_t {
+ QTR_2D_SIZE,
+ FULL_2D_SIZE,
+ QTR_3D_SIZE,
+ FULL_3D_SIZE,
+ INVALID_SIZE
+};
+enum imx072_setting {
+ RES_PREVIEW,
+ RES_CAPTURE,
+ RES_3D_PREVIEW,
+ RES_3D_CAPTURE
+};
+enum imx072_cam_mode_t {
+ MODE_2D_RIGHT,
+ MODE_2D_LEFT,
+ MODE_3D,
+ MODE_INVALID
+};
+enum imx072_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 imx072_reg_mode {
+ IMX072_FRAME_LENGTH_LINES_HI = 0,
+ IMX072_FRAME_LENGTH_LINES_LO,
+ IMX072_LINE_LENGTH_PCK_HI,
+ IMX072_LINE_LENGTH_PCK_LO,
+};
+
+struct imx072_reg {
+ const struct imx072_i2c_reg_conf *rec_settings;
+ const unsigned short rec_size;
+ const struct imx072_i2c_conf_array *conf_array;
+};
+#endif /* IMX072_H */
diff --git a/drivers/media/video/msm/imx072_reg.c b/drivers/media/video/msm/imx072_reg.c
new file mode 100644
index 0000000..ea75548
--- /dev/null
+++ b/drivers/media/video/msm/imx072_reg.c
@@ -0,0 +1,153 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "imx072.h"
+
+struct imx072_i2c_reg_conf imx072_prev_settings[] = {
+ {0x0340, 0x03},/*frame_length*/
+ {0x0341, 0xF7},/*frame_length*/
+ {0x0342, 0x0A},/*line_length*/
+ {0x0343, 0xE0},/*line_length*/
+ {0x0344, 0x00},/*x_addr_start*/
+ {0x0345, 0x00},/*x_addr_start*/
+ {0x0346, 0x00},/*y_addr_start*/
+ {0x0347, 0x00},/*y_addr_start*/
+ {0x0348, 0x0A},/*x_addr_end*/
+ {0x0349, 0x2F},/*x_addr_end*/
+ {0x034A, 0x07},/*y_addr_end*/
+ {0x034B, 0xA7},/*y_addr_end*/
+ {0x034C, 0x05},/*x_out_size*/
+ {0x034D, 0x18},/*x_out_size*/
+ {0x034E, 0x03},/*y_out_size*/
+ {0x034F, 0xD4},/*y_out_size*/
+ {0x0381, 0x01},/*x_even_inc*/
+ {0x0383, 0x03},/*x_odd_inc*/
+ {0x0385, 0x01},/*y_even_inc*/
+ {0x0387, 0x03},/*y_odd_inc*/
+ {0x3016, 0x06},/*VMODEADD*/
+ {0x3017, 0x40},
+ {0x3069, 0x24},
+ {0x306A, 0x00},
+ {0x306B, 0xCB},
+ {0x306C, 0x07},
+ {0x30E8, 0x86},
+ {0x3304, 0x03},
+ {0x3305, 0x02},
+ {0x3306, 0x0A},
+ {0x3307, 0x02},
+ {0x3308, 0x11},
+ {0x3309, 0x04},
+ {0x330A, 0x05},
+ {0x330B, 0x04},
+ {0x330C, 0x05},
+ {0x330D, 0x04},
+ {0x330E, 0x01},
+ {0x3301, 0x80},
+};
+
+struct imx072_i2c_reg_conf imx072_snap_settings[] = {
+ {0x0340, 0x07},/*frame_length*/
+ {0x0341, 0xEE},/*frame_length*/
+ {0x0342, 0x0A},/*line_length*/
+ {0x0343, 0xE0},/*line_length*/
+ {0x0344, 0x00},/*x_addr_start*/
+ {0x0345, 0x00},/*x_addr_start*/
+ {0x0346, 0x00},/*y_addr_start*/
+ {0x0347, 0x00},/*y_addr_start*/
+ {0x0348, 0x0A},/*x_addr_end*/
+ {0x0349, 0x2F},/*x_addr_end*/
+ {0x034A, 0x07},/*y_addr_end*/
+ {0x034B, 0xA7},/*y_addr_end*/
+ {0x034C, 0x0A},/*x_out_size*/
+ {0x034D, 0x30},/*x_out_size*/
+ {0x034E, 0x07},/*y_out_size*/
+ {0x034F, 0xA8},/*y_out_size*/
+ {0x0381, 0x01},/*x_even_inc*/
+ {0x0383, 0x01},/*x_odd_inc*/
+ {0x0385, 0x01},/*y_even_inc*/
+ {0x0387, 0x01},/*y_odd_inc*/
+ {0x3016, 0x06},/*VMODEADD*/
+ {0x3017, 0x40},
+ {0x3069, 0x24},
+ {0x306A, 0x00},
+ {0x306B, 0xCB},
+ {0x306C, 0x07},
+ {0x30E8, 0x06},
+ {0x3304, 0x05},
+ {0x3305, 0x04},
+ {0x3306, 0x15},
+ {0x3307, 0x02},
+ {0x3308, 0x11},
+ {0x3309, 0x07},
+ {0x330A, 0x05},
+ {0x330B, 0x04},
+ {0x330C, 0x05},
+ {0x330D, 0x04},
+ {0x330E, 0x01},
+ {0x3301, 0x00},
+};
+
+struct imx072_i2c_reg_conf imx072_recommend_settings[] = {
+ {0x0307, 0x12},
+ {0x302B, 0x4B},
+ {0x0101, 0x03},
+ {0x300A, 0x80},
+ {0x3014, 0x08},
+ {0x3015, 0x37},
+ {0x3017, 0x40},
+ {0x301C, 0x01},
+ {0x3031, 0x28},
+ {0x3040, 0x00},
+ {0x3041, 0x60},
+ {0x3051, 0x24},
+ {0x3053, 0x34},
+ {0x3055, 0x3B},
+ {0x3057, 0xC0},
+ {0x3060, 0x30},
+ {0x3065, 0x00},
+ {0x30AA, 0x88},
+ {0x30AB, 0x1C},
+ {0x30B0, 0x32},
+ {0x30B2, 0x83},
+ {0x30D3, 0x04},
+ {0x310E, 0xDD},
+ {0x31A4, 0xD8},
+ {0x31A6, 0x17},
+ {0x31AC, 0xCF},
+ {0x31AE, 0xF1},
+ {0x31B4, 0xD8},
+ {0x31B6, 0x17},
+ {0x3304, 0x05},
+ {0x3305, 0x04},
+ {0x3306, 0x15},
+ {0x3307, 0x02},
+ {0x3308, 0x11},
+ {0x3309, 0x07},
+ {0x330A, 0x05},
+ {0x330B, 0x04},
+ {0x330C, 0x05},
+ {0x330D, 0x04},
+ {0x330E, 0x01},
+ {0x30d8, 0x20},
+};
+
+struct imx072_i2c_conf_array imx072_confs[] = {
+ {&imx072_prev_settings[0], ARRAY_SIZE(imx072_prev_settings)},
+ {&imx072_snap_settings[0], ARRAY_SIZE(imx072_snap_settings)},
+};
+
+struct imx072_reg imx072_regs = {
+ .rec_settings = &imx072_recommend_settings[0],
+ .rec_size = ARRAY_SIZE(imx072_recommend_settings),
+ .conf_array = &imx072_confs[0],
+};
diff --git a/drivers/media/video/msm/imx074.c b/drivers/media/video/msm/imx074.c
new file mode 100644
index 0000000..636b402
--- /dev/null
+++ b/drivers/media/video/msm/imx074.c
@@ -0,0 +1,1414 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "imx074.h"
+
+/*SENSOR REGISTER DEFINES*/
+#define IMX074_EEPROM_SLAVE_ADDR 0x52
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+#define REG_MODE_SELECT 0x100
+#define MODE_SELECT_STANDBY_MODE 0x00
+#define MODE_SELECT_STREAM 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+/* PLL registers */
+#define REG_PLL_MULTIPLIER 0x0307
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLSTATIM 0x302b
+#define REG_3024 0x3024
+#define REG_IMAGE_ORIENTATION 0x0101
+#define REG_VNDMY_ABLMGSHLMT 0x300a
+#define REG_Y_OPBADDR_START_DI 0x3014
+#define REG_3015 0x3015
+#define REG_301C 0x301C
+#define REG_302C 0x302C
+#define REG_3031 0x3031
+#define REG_3041 0x3041
+#define REG_3051 0x3051
+#define REG_3053 0x3053
+#define REG_3057 0x3057
+#define REG_305C 0x305C
+#define REG_305D 0x305D
+#define REG_3060 0x3060
+#define REG_3065 0x3065
+#define REG_30AA 0x30AA
+#define REG_30AB 0x30AB
+#define REG_30B0 0x30B0
+#define REG_30B2 0x30B2
+#define REG_30D3 0x30D3
+#define REG_3106 0x3106
+#define REG_310C 0x310C
+#define REG_3304 0x3304
+#define REG_3305 0x3305
+#define REG_3306 0x3306
+#define REG_3307 0x3307
+#define REG_3308 0x3308
+#define REG_3309 0x3309
+#define REG_330A 0x330A
+#define REG_330B 0x330B
+#define REG_330C 0x330C
+#define REG_330D 0x330D
+#define REG_330F 0x330F
+#define REG_3381 0x3381
+
+/* mode setting */
+#define REG_FRAME_LENGTH_LINES_HI 0x0340
+#define REG_FRAME_LENGTH_LINES_LO 0x0341
+#define REG_YADDR_START 0x0347
+#define REG_YAAAR_END 0x034b
+#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
+#define REG_X_EVEN_INC 0x0381
+#define REG_X_ODD_INC 0x0383
+#define REG_Y_EVEN_INC 0x0385
+#define REG_Y_ODD_INC 0x0387
+#define REG_HMODEADD 0x3001
+#define REG_VMODEADD 0x3016
+#define REG_VAPPLINE_START 0x3069
+#define REG_VAPPLINE_END 0x306b
+#define REG_SHUTTER 0x3086
+#define REG_HADDAVE 0x30e8
+#define REG_LANESEL 0x3301
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+
+#define REG_LINE_LENGTH_PCK_HI 0x0342
+#define REG_LINE_LENGTH_PCK_LO 0x0343
+/*..... TYPE DECLARATIONS.....*/
+#define IMX074_OFFSET 3
+#define IMX074_DEFAULT_MASTER_CLK_RATE 24000000
+/* Full Size */
+#define IMX074_FULL_SIZE_WIDTH 4208
+#define IMX074_FULL_SIZE_HEIGHT 3120
+#define IMX074_FULL_SIZE_DUMMY_PIXELS 0
+#define IMX074_FULL_SIZE_DUMMY_LINES 0
+/* Quarter Size */
+#define IMX074_QTR_SIZE_WIDTH 2104
+#define IMX074_QTR_SIZE_HEIGHT 1560
+#define IMX074_QTR_SIZE_DUMMY_PIXELS 0
+#define IMX074_QTR_SIZE_DUMMY_LINES 0
+/* Blanking as measured on the scope */
+/* Full Size */
+#define IMX074_HRZ_FULL_BLK_PIXELS 264
+#define IMX074_VER_FULL_BLK_LINES 96
+/* Quarter Size */
+#define IMX074_HRZ_QTR_BLK_PIXELS 2368
+#define IMX074_VER_QTR_BLK_LINES 21
+#define Q8 0x100
+#define Q10 0x400
+#define IMX074_AF_I2C_SLAVE_ID 0x72
+#define IMX074_STEPS_NEAR_TO_CLOSEST_INF 52
+#define IMX074_TOTAL_STEPS_NEAR_TO_FAR 52
+static uint32_t imx074_l_region_code_per_step = 2;
+
+struct imx074_work_t {
+ struct work_struct work;
+};
+
+static struct imx074_work_t *imx074_sensorw;
+static struct i2c_client *imx074_client;
+
+struct imx074_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+ enum imx074_resolution_t prev_res;
+ enum imx074_resolution_t pict_res;
+ enum imx074_resolution_t curr_res;
+ enum imx074_test_mode_t set_test;
+ unsigned short imgaddr;
+};
+static uint8_t imx074_delay_msecs_stdby = 5;
+static uint16_t imx074_delay_msecs_stream = 5;
+static int32_t config_csi;
+
+static struct imx074_ctrl_t *imx074_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(imx074_wait_queue);
+DEFINE_MUTEX(imx074_mut);
+
+/*=============================================================*/
+
+static int imx074_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 = 2,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+ CDBG("imx074_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+static int32_t imx074_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(imx074_client->adapter, msg, 1) < 0) {
+ CDBG("imx074_i2c_txdata faild 0x%x\n", imx074_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int32_t imx074_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = imx074_i2c_rxdata(imx074_client->addr, buf, rlen);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ return rc;
+}
+
+static int imx074_af_i2c_rxdata_b(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+ CDBG("imx074_i2c_rxdata_b failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t imx074_i2c_read_w_eeprom(unsigned short raddr,
+ unsigned short *rdata)
+{
+ int32_t rc;
+ unsigned char buf;
+ if (!rdata)
+ return -EIO;
+ /* Read 2 bytes in sequence */
+ buf = (raddr & 0x00FF);
+ rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = buf<<8;
+
+ /* Read Second byte of data */
+ buf = (raddr & 0x00FF) + 1;
+ rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata |= buf;
+ return rc;
+}
+
+static int32_t imx074_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = imx074_i2c_txdata(imx074_client->addr, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+static int16_t imx074_i2c_write_b_af(unsigned short saddr,
+ unsigned short baddr, unsigned short bdata)
+{
+ int32_t rc;
+ unsigned char buf[2];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ rc = imx074_i2c_txdata(saddr, buf, 2);
+ if (rc < 0)
+ CDBG("AFi2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!",
+ saddr, baddr, bdata);
+ return rc;
+}
+
+static int32_t imx074_i2c_write_w_table(struct imx074_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = imx074_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+static int16_t imx074_af_init(void)
+{
+ int32_t rc;
+ /* Initialize waveform */
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x01, 0xA9);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x02, 0xD2);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x03, 0x0C);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x04, 0x14);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x05, 0xB6);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x06, 0x4F);
+ return rc;
+}
+
+static void imx074_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+ uint32_t divider, d1;
+ uint32_t pclk_mult;/*Q10 */
+ /* Total frame_length_lines and line_length_pck for preview */
+ preview_frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ /* Total frame_length_lines and line_length_pck for snapshot */
+ snapshot_frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+ d1 = preview_frame_length_lines * 0x00010000 /
+ snapshot_frame_length_lines;
+ pclk_mult =
+ (uint32_t) ((imx074_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00010000) /
+ (imx074_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+ divider = d1 * pclk_mult / 0x00010000;
+ *pfps = (uint16_t) (fps * divider / 0x00010000);
+}
+
+static uint16_t imx074_get_prev_lines_pf(void)
+{
+ if (imx074_ctrl->prev_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES;
+ else
+ return IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t imx074_get_prev_pixels_pl(void)
+{
+ if (imx074_ctrl->prev_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_WIDTH + IMX074_HRZ_QTR_BLK_PIXELS;
+ else
+ return IMX074_FULL_SIZE_WIDTH + IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t imx074_get_pict_lines_pf(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ else
+ return IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+}
+
+static uint16_t imx074_get_pict_pixels_pl(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_WIDTH +
+ IMX074_HRZ_QTR_BLK_PIXELS;
+ else
+ return IMX074_FULL_SIZE_WIDTH +
+ IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t imx074_get_pict_max_exp_lc(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return (IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES)*24;
+ else
+ return (IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t imx074_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ imx074_ctrl->fps_divider = fps->fps_div;
+ imx074_ctrl->pict_fps_divider = fps->pict_fps_div;
+ if (imx074_ctrl->curr_res == QTR_SIZE) {
+ total_lines_per_frame = (uint16_t)(((IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES) *
+ imx074_ctrl->fps_divider) / 0x400);
+ } else {
+ total_lines_per_frame = (uint16_t)(((IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES) *
+ imx074_ctrl->pict_fps_divider) / 0x400);
+ }
+ if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ ((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+ return rc;
+ if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+ return rc;
+}
+
+static int32_t imx074_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ static uint16_t max_legal_gain = 0x00E0;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_time_msb, intg_time_lsb;
+ uint8_t frame_length_line_msb, frame_length_line_lsb;
+ uint16_t frame_length_lines;
+ int32_t rc = -1;
+
+ CDBG("imx074_write_exp_gain : gain = %d line = %d", gain, line);
+ if (imx074_ctrl->curr_res == QTR_SIZE) {
+ frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ frame_length_lines = frame_length_lines *
+ imx074_ctrl->fps_divider / 0x400;
+ } else {
+ frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+ frame_length_lines = frame_length_lines *
+ imx074_ctrl->pict_fps_divider / 0x400;
+ }
+ if (line > (frame_length_lines - IMX074_OFFSET))
+ frame_length_lines = line + IMX074_OFFSET;
+
+ CDBG("imx074 setting line = %d\n", line);
+
+
+ CDBG("imx074 setting frame_length_lines = %d\n",
+ frame_length_lines);
+
+ if (gain > max_legal_gain)
+ /* range: 0 to 224 */
+ gain = max_legal_gain;
+
+ /* update gain registers */
+ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lsb = (uint8_t) (gain & 0x00FF);
+
+ frame_length_line_msb = (uint8_t) ((frame_length_lines & 0xFF00) >> 8);
+ frame_length_line_lsb = (uint8_t) (frame_length_lines & 0x00FF);
+
+ /* update line count registers */
+ intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+ intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+ rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+ CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_HI = 0x%X\n",
+ gain_msb);
+ rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_HI,
+ gain_msb);
+ if (rc < 0)
+ return rc;
+ CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_LO = 0x%X\n",
+ gain_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ gain_lsb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_FRAME_LENGTH_LINES_HI = 0x%X\n",
+ frame_length_line_msb);
+ rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ frame_length_line_msb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_FRAME_LENGTH_LINES_LO = 0x%X\n",
+ frame_length_line_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ frame_length_line_lsb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_HI = 0x%X\n",
+ intg_time_msb);
+ rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+ intg_time_msb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_LO = 0x%X\n",
+ intg_time_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+ intg_time_lsb);
+ if (rc < 0)
+ return rc;
+
+ rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t imx074_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = imx074_write_exp_gain(gain, line);
+ return rc;
+}
+
+static int32_t imx074_move_focus(int direction,
+ int32_t num_steps)
+{
+ int32_t step_direction, dest_step_position, bit_mask;
+ int32_t rc = 0;
+
+ if (num_steps == 0)
+ return rc;
+
+ if (direction == MOVE_NEAR) {
+ step_direction = 1;
+ bit_mask = 0x80;
+ } else if (direction == MOVE_FAR) {
+ step_direction = -1;
+ bit_mask = 0x00;
+ } else {
+ CDBG("imx074_move_focus: Illegal focus direction");
+ return -EINVAL;
+ }
+ dest_step_position = imx074_ctrl->curr_step_pos +
+ (step_direction * num_steps);
+ if (dest_step_position < 0)
+ dest_step_position = 0;
+ else if (dest_step_position > IMX074_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = IMX074_TOTAL_STEPS_NEAR_TO_FAR;
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00,
+ ((num_steps * imx074_l_region_code_per_step) | bit_mask));
+ CDBG("%s: Index: %d\n", __func__, dest_step_position);
+ imx074_ctrl->curr_step_pos = dest_step_position;
+ return rc;
+}
+
+
+static int32_t imx074_set_default_focus(uint8_t af_step)
+{
+ int32_t rc;
+ /* Initialize to infinity */
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+ imx074_ctrl->curr_step_pos = 0;
+ return rc;
+}
+static int32_t imx074_test(enum imx074_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* Set mo to 2 inorder to enable test pattern*/
+ if (imx074_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+static int32_t imx074_sensor_setting(int update_type, int rt)
+{
+ int32_t rc = 0;
+ struct msm_camera_csi_params imx074_csi_params;
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct imx074_i2c_reg_conf init_tbl[] = {
+ {REG_PRE_PLL_CLK_DIV,
+ imx074_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLSTATIM,
+ imx074_regs.reg_pat_init[0].
+ plstatim},
+ {REG_3024,
+ imx074_regs.reg_pat_init[0].
+ reg_3024},
+ {REG_IMAGE_ORIENTATION,
+ imx074_regs.reg_pat_init[0].
+ image_orientation},
+ {REG_VNDMY_ABLMGSHLMT,
+ imx074_regs.reg_pat_init[0].
+ vndmy_ablmgshlmt},
+ {REG_Y_OPBADDR_START_DI,
+ imx074_regs.reg_pat_init[0].
+ y_opbaddr_start_di},
+ {REG_3015,
+ imx074_regs.reg_pat_init[0].
+ reg_0x3015},
+ {REG_301C,
+ imx074_regs.reg_pat_init[0].
+ reg_0x301c},
+ {REG_302C,
+ imx074_regs.reg_pat_init[0].
+ reg_0x302c},
+ {REG_3031,
+ imx074_regs.reg_pat_init[0].reg_0x3031},
+ {REG_3041,
+ imx074_regs.reg_pat_init[0].reg_0x3041},
+ {REG_3051,
+ imx074_regs.reg_pat_init[0].reg_0x3051},
+ {REG_3053,
+ imx074_regs.reg_pat_init[0].reg_0x3053},
+ {REG_3057,
+ imx074_regs.reg_pat_init[0].reg_0x3057},
+ {REG_305C,
+ imx074_regs.reg_pat_init[0].reg_0x305c},
+ {REG_305D,
+ imx074_regs.reg_pat_init[0].reg_0x305d},
+ {REG_3060,
+ imx074_regs.reg_pat_init[0].reg_0x3060},
+ {REG_3065,
+ imx074_regs.reg_pat_init[0].reg_0x3065},
+ {REG_30AA,
+ imx074_regs.reg_pat_init[0].reg_0x30aa},
+ {REG_30AB,
+ imx074_regs.reg_pat_init[0].reg_0x30ab},
+ {REG_30B0,
+ imx074_regs.reg_pat_init[0].reg_0x30b0},
+ {REG_30B2,
+ imx074_regs.reg_pat_init[0].reg_0x30b2},
+ {REG_30D3,
+ imx074_regs.reg_pat_init[0].reg_0x30d3},
+ {REG_3106,
+ imx074_regs.reg_pat_init[0].reg_0x3106},
+ {REG_310C,
+ imx074_regs.reg_pat_init[0].reg_0x310c},
+ {REG_3304,
+ imx074_regs.reg_pat_init[0].reg_0x3304},
+ {REG_3305,
+ imx074_regs.reg_pat_init[0].reg_0x3305},
+ {REG_3306,
+ imx074_regs.reg_pat_init[0].reg_0x3306},
+ {REG_3307,
+ imx074_regs.reg_pat_init[0].reg_0x3307},
+ {REG_3308,
+ imx074_regs.reg_pat_init[0].reg_0x3308},
+ {REG_3309,
+ imx074_regs.reg_pat_init[0].reg_0x3309},
+ {REG_330A,
+ imx074_regs.reg_pat_init[0].reg_0x330a},
+ {REG_330B,
+ imx074_regs.reg_pat_init[0].reg_0x330b},
+ {REG_330C,
+ imx074_regs.reg_pat_init[0].reg_0x330c},
+ {REG_330D,
+ imx074_regs.reg_pat_init[0].reg_0x330d},
+ {REG_330F,
+ imx074_regs.reg_pat_init[0].reg_0x330f},
+ {REG_3381,
+ imx074_regs.reg_pat_init[0].reg_0x3381},
+ };
+ struct imx074_i2c_reg_conf init_mode_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_PLL_MULTIPLIER,
+ imx074_regs.reg_pat[rt].
+ pll_multiplier},
+ {REG_FRAME_LENGTH_LINES_HI,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_YADDR_START ,
+ imx074_regs.reg_pat[rt].
+ y_addr_start},
+ {REG_YAAAR_END,
+ imx074_regs.reg_pat[rt].
+ y_add_end},
+ {REG_X_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB ,
+ imx074_regs.reg_pat[rt].
+ y_output_size_lsb},
+ {REG_X_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ x_even_inc},
+ {REG_X_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ x_odd_inc},
+ {REG_Y_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ y_even_inc},
+ {REG_Y_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ y_odd_inc},
+ {REG_HMODEADD,
+ imx074_regs.reg_pat[rt].
+ hmodeadd},
+ {REG_VMODEADD,
+ imx074_regs.reg_pat[rt].
+ vmodeadd},
+ {REG_VAPPLINE_START,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_start},
+ {REG_VAPPLINE_END,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_end},
+ {REG_SHUTTER,
+ imx074_regs.reg_pat[rt].
+ shutter},
+ {REG_HADDAVE,
+ imx074_regs.reg_pat[rt].
+ haddave},
+ {REG_LANESEL,
+ imx074_regs.reg_pat[rt].
+ lanesel},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF},
+
+ };
+ /* reset fps_divider */
+ imx074_ctrl->fps = 30 * Q8;
+ imx074_ctrl->fps_divider = 1 * 0x400;
+ /* stop streaming */
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ if (rc < 0)
+ return rc;
+ msleep(imx074_delay_msecs_stdby);
+ rc = imx074_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+ rc = imx074_i2c_write_w_table(&init_mode_tbl[0],
+ ARRAY_SIZE(init_mode_tbl));
+ if (rc < 0)
+ return rc;
+ rc = imx074_test(imx074_ctrl->set_test);
+ return rc;
+ }
+ break;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct imx074_i2c_reg_conf mode_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_PLL_MULTIPLIER,
+ imx074_regs.reg_pat[rt].
+ pll_multiplier},
+ {REG_FRAME_LENGTH_LINES_HI,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_YADDR_START ,
+ imx074_regs.reg_pat[rt].
+ y_addr_start},
+ {REG_YAAAR_END,
+ imx074_regs.reg_pat[rt].
+ y_add_end},
+ {REG_X_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB ,
+ imx074_regs.reg_pat[rt].
+ y_output_size_lsb},
+ {REG_X_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ x_even_inc},
+ {REG_X_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ x_odd_inc},
+ {REG_Y_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ y_even_inc},
+ {REG_Y_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ y_odd_inc},
+ {REG_HMODEADD,
+ imx074_regs.reg_pat[rt].
+ hmodeadd},
+ {REG_VMODEADD,
+ imx074_regs.reg_pat[rt].
+ vmodeadd},
+ {REG_VAPPLINE_START,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_start},
+ {REG_VAPPLINE_END,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_end},
+ {REG_SHUTTER,
+ imx074_regs.reg_pat[rt].
+ shutter},
+ {REG_HADDAVE,
+ imx074_regs.reg_pat[rt].
+ haddave},
+ {REG_LANESEL,
+ imx074_regs.reg_pat[rt].
+ lanesel},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF},
+ };
+
+ /* stop streaming */
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ msleep(imx074_delay_msecs_stdby);
+ if (config_csi == 0) {
+ imx074_csi_params.lane_cnt = 4;
+ imx074_csi_params.data_format = CSI_10BIT;
+ imx074_csi_params.lane_assign = 0xe4;
+ imx074_csi_params.dpcm_scheme = 0;
+ imx074_csi_params.settle_cnt = 0x14;
+ rc = msm_camio_csi_config(&imx074_csi_params);
+ /*imx074_delay_msecs_stdby*/
+ msleep(imx074_delay_msecs_stream);
+ config_csi = 1;
+ }
+ rc = imx074_i2c_write_w_table(&mode_tbl[0],
+ ARRAY_SIZE(mode_tbl));
+ if (rc < 0)
+ return rc;
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM);
+ if (rc < 0)
+ return rc;
+ msleep(imx074_delay_msecs_stream);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+
+static int32_t imx074_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->prev_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ } else {
+ rt = RES_CAPTURE;
+ }
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->prev_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t imx074_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+ if (imx074_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ } else {
+ rt = RES_CAPTURE;
+ }
+ }
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+static int32_t imx074_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+ if (imx074_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ } else {
+ rt = RES_CAPTURE;
+ }
+ }
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+static int32_t imx074_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = imx074_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = imx074_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = imx074_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+static int32_t imx074_power_down(void)
+{
+ imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ msleep(imx074_delay_msecs_stdby);
+ return 0;
+}
+static int imx074_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ gpio_direction_input(data->sensor_reset);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int imx074_read_eeprom_data(struct sensor_cfg_data *cfg)
+{
+ int32_t rc = 0;
+ uint16_t eepromdata = 0;
+ uint8_t addr = 0;
+
+ addr = 0x10;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.r_over_g = eepromdata;
+
+ addr = 0x12;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.b_over_g = eepromdata;
+
+ addr = 0x14;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.gr_over_gb = eepromdata;
+
+ addr = 0x1A;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.macro_2_inf = eepromdata;
+
+ addr = 0x1C;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.inf_2_macro = eepromdata;
+
+ addr = 0x1E;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.stroke_amt = eepromdata;
+
+ addr = 0x20;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.af_pos_1m = eepromdata;
+
+ addr = 0x22;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.af_pos_inf = eepromdata;
+
+ return rc;
+}
+
+static int imx074_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ unsigned short chipidl, chipidh;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "imx074");
+ CDBG(" imx074_probe_init_sensor \n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ usleep_range(5000, 6000);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ usleep_range(5000, 6000);
+ } else {
+ CDBG("gpio reset fail");
+ goto init_probe_done;
+ }
+ CDBG("imx074_probe_init_sensor is called\n");
+ /* 3. Read sensor Model ID: */
+ rc = imx074_i2c_read(0x0000, &chipidh, 1);
+ if (rc < 0) {
+ CDBG("Model read failed\n");
+ goto init_probe_fail;
+ }
+ rc = imx074_i2c_read(0x0001, &chipidl, 1);
+ if (rc < 0) {
+ CDBG("Model read failed\n");
+ goto init_probe_fail;
+ }
+ CDBG("imx074 model_id = 0x%x 0x%x\n", chipidh, chipidl);
+ /* 4. Compare sensor ID to IMX074 ID: */
+ if (chipidh != 0x00 || chipidl != 0x74) {
+ rc = -ENODEV;
+ CDBG("imx074_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ CDBG("imx074_probe_init_sensor fails\n");
+ imx074_probe_init_done(data);
+init_probe_done:
+ CDBG(" imx074_probe_init_sensor finishes\n");
+ return rc;
+ }
+static int32_t imx074_poweron_af(void)
+{
+ int32_t rc = 0;
+ CDBG("imx074 enable AF actuator, gpio = %d\n",
+ imx074_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(imx074_ctrl->sensordata->vcm_pwd, "imx074");
+ if (!rc) {
+ gpio_direction_output(imx074_ctrl->sensordata->vcm_pwd, 1);
+ msleep(20);
+ rc = imx074_af_init();
+ if (rc < 0)
+ CDBG("imx074 AF initialisation failed\n");
+ } else {
+ CDBG("%s: AF PowerON gpio_request failed %d\n", __func__, rc);
+ }
+ return rc;
+}
+static void imx074_poweroff_af(void)
+{
+ gpio_set_value_cansleep(imx074_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(imx074_ctrl->sensordata->vcm_pwd);
+}
+/* camsensor_iu060f_imx074_reset */
+int imx074_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling imx074_sensor_open_init\n");
+ imx074_ctrl = kzalloc(sizeof(struct imx074_ctrl_t), GFP_KERNEL);
+ if (!imx074_ctrl) {
+ CDBG("imx074_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ imx074_ctrl->fps_divider = 1 * 0x00000400;
+ imx074_ctrl->pict_fps_divider = 1 * 0x00000400;
+ imx074_ctrl->fps = 30 * Q8;
+ imx074_ctrl->set_test = TEST_OFF;
+ imx074_ctrl->prev_res = QTR_SIZE;
+ imx074_ctrl->pict_res = FULL_SIZE;
+ imx074_ctrl->curr_res = INVALID_SIZE;
+ config_csi = 0;
+
+ if (data)
+ imx074_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+ usleep_range(1000, 2000);
+ rc = imx074_probe_init_sensor(data);
+ if (rc < 0) {
+ CDBG("Calling imx074_sensor_open_init fail\n");
+ goto probe_fail;
+ }
+
+ rc = imx074_sensor_setting(REG_INIT, RES_PREVIEW);
+ if (rc < 0) {
+ CDBG("imx074_sensor_setting failed\n");
+ goto init_fail;
+ }
+ if (machine_is_msm8x60_fluid())
+ rc = imx074_poweron_af();
+ else
+ rc = imx074_af_init();
+ if (rc < 0) {
+ CDBG("AF initialisation failed\n");
+ goto init_fail;
+ } else
+ goto init_done;
+probe_fail:
+ CDBG(" imx074_sensor_open_init probe fail\n");
+ kfree(imx074_ctrl);
+ return rc;
+init_fail:
+ CDBG(" imx074_sensor_open_init fail\n");
+ imx074_probe_init_done(data);
+ kfree(imx074_ctrl);
+init_done:
+ CDBG("imx074_sensor_open_init done\n");
+ return rc;
+}
+static int imx074_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&imx074_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id imx074_i2c_id[] = {
+ {"imx074", 0},
+ { }
+};
+
+static int imx074_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("imx074_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ imx074_sensorw = kzalloc(sizeof(struct imx074_work_t), GFP_KERNEL);
+ if (!imx074_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, imx074_sensorw);
+ imx074_init_client(client);
+ imx074_client = client;
+
+
+ CDBG("imx074_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("imx074_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int __exit imx074_remove(struct i2c_client *client)
+{
+ struct imx074_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ imx074_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver imx074_i2c_driver = {
+ .id_table = imx074_i2c_id,
+ .probe = imx074_i2c_probe,
+ .remove = __exit_p(imx074_i2c_remove),
+ .driver = {
+ .name = "imx074",
+ },
+};
+
+int imx074_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;
+ mutex_lock(&imx074_mut);
+ CDBG("imx074_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 = imx074_set_fps(&(cdata.cfg.fps));
+ break;
+ case CFG_SET_EXP_GAIN:
+ rc =
+ imx074_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ imx074_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_MODE:
+ rc = imx074_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ rc = imx074_power_down();
+ break;
+ case CFG_GET_CALIB_DATA:
+ rc = imx074_read_eeprom_data(&cdata);
+ if (rc < 0)
+ break;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(cdata)))
+ rc = -EFAULT;
+ break;
+ case CFG_MOVE_FOCUS:
+ rc =
+ imx074_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ imx074_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = IMX074_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 = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&imx074_mut);
+
+ return rc;
+}
+static int imx074_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&imx074_mut);
+ if (machine_is_msm8x60_fluid())
+ imx074_poweroff_af();
+ imx074_power_down();
+ gpio_set_value_cansleep(imx074_ctrl->sensordata->sensor_reset, 0);
+ msleep(5);
+ gpio_direction_input(imx074_ctrl->sensordata->sensor_reset);
+ gpio_free(imx074_ctrl->sensordata->sensor_reset);
+ kfree(imx074_ctrl);
+ imx074_ctrl = NULL;
+ CDBG("imx074_release completed\n");
+ mutex_unlock(&imx074_mut);
+
+ return rc;
+}
+
+static int imx074_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&imx074_i2c_driver);
+ if (rc < 0 || imx074_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+ rc = imx074_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = imx074_sensor_open_init;
+ s->s_release = imx074_sensor_release;
+ s->s_config = imx074_sensor_config;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+ imx074_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("imx074_sensor_probe: SENSOR PROBE FAILS!\n");
+ i2c_del_driver(&imx074_i2c_driver);
+ return rc;
+}
+
+static int __imx074_probe(struct platform_device *pdev)
+{
+
+ return msm_camera_drv_start(pdev, imx074_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __imx074_probe,
+ .driver = {
+ .name = "msm_camera_imx074",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init imx074_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(imx074_init);
+
+MODULE_DESCRIPTION("Sony 13 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/imx074.h b/drivers/media/video/msm/imx074.h
new file mode 100644
index 0000000..8be0fb7
--- /dev/null
+++ b/drivers/media/video/msm/imx074.h
@@ -0,0 +1,118 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef IMX074_H
+#define IMX074_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct imx074_reg imx074_regs;
+struct reg_struct_init {
+ /* PLL setting */
+ uint8_t pre_pll_clk_div; /* 0x0305 */
+ uint8_t plstatim; /* 0x302b */
+ uint8_t reg_3024; /*ox3024*/
+ uint8_t image_orientation; /* 0x0101*/
+ uint8_t vndmy_ablmgshlmt; /*0x300a*/
+ uint8_t y_opbaddr_start_di; /*0x3014*/
+ uint8_t reg_0x3015; /*0x3015*/
+ uint8_t reg_0x301c; /*0x301c*/
+ uint8_t reg_0x302c; /*0x302c*/
+ uint8_t reg_0x3031; /*0x3031*/
+ uint8_t reg_0x3041; /* 0x3041 */
+ uint8_t reg_0x3051; /* 0x3051 */
+ uint8_t reg_0x3053; /* 0x3053 */
+ uint8_t reg_0x3057; /* 0x3057 */
+ uint8_t reg_0x305c; /* 0x305c */
+ uint8_t reg_0x305d; /* 0x305d */
+ uint8_t reg_0x3060; /* 0x3060 */
+ uint8_t reg_0x3065; /* 0x3065 */
+ uint8_t reg_0x30aa; /* 0x30aa */
+ uint8_t reg_0x30ab;
+ uint8_t reg_0x30b0;
+ uint8_t reg_0x30b2;
+ uint8_t reg_0x30d3;
+ uint8_t reg_0x3106;
+ uint8_t reg_0x310c;
+ uint8_t reg_0x3304;
+ uint8_t reg_0x3305;
+ uint8_t reg_0x3306;
+ uint8_t reg_0x3307;
+ uint8_t reg_0x3308;
+ uint8_t reg_0x3309;
+ uint8_t reg_0x330a;
+ uint8_t reg_0x330b;
+ uint8_t reg_0x330c;
+ uint8_t reg_0x330d;
+ uint8_t reg_0x330f;
+ uint8_t reg_0x3381;
+};
+
+struct reg_struct {
+ uint8_t pll_multiplier; /* 0x0307 */
+ uint8_t frame_length_lines_hi; /* 0x0340*/
+ uint8_t frame_length_lines_lo; /* 0x0341*/
+ uint8_t y_addr_start; /* 0x347 */
+ uint8_t y_add_end; /* 0x034b */
+ 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 hmodeadd; /* 0x3001 */
+ uint8_t vmodeadd; /* 0x3016 */
+ uint8_t vapplinepos_start;/*ox3069*/
+ uint8_t vapplinepos_end;/*306b*/
+ uint8_t shutter; /* 0x3086 */
+ uint8_t haddave; /* 0x30e8 */
+ uint8_t lanesel; /* 0x3301 */
+};
+
+struct imx074_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+enum imx074_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum imx074_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+enum imx074_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+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
+};
+
+struct imx074_reg {
+ const struct reg_struct_init *reg_pat_init;
+ const struct reg_struct *reg_pat;
+};
+#endif /* IMX074_H */
diff --git a/drivers/media/video/msm/imx074_reg.c b/drivers/media/video/msm/imx074_reg.c
new file mode 100644
index 0000000..ccc9b2f
--- /dev/null
+++ b/drivers/media/video/msm/imx074_reg.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include "imx074.h"
+const struct reg_struct_init imx074_reg_init[1] = {
+ {
+ /* PLL setting */
+ 0x02, /* pll_divider 0x0305 */
+ 0x4B, /* plstatim 0x302b */
+ 0x03, /* reg_3024 */
+ 0x00, /* image_orientation 0x0101 */
+ 0x80, /* vndmy_ablmgshlmt 0x300a*/
+ 0x08, /* y_opbaddr_start_di 3014*/
+ 0x37, /* 0x3015*/
+ 0x01, /* 0x301c*/
+ 0x05, /* 0x302c*/
+ 0x26, /* 0x3031*/
+ 0x60, /* 0x3041*/
+ 0x24, /* 0x3051 CLK DIV*/
+ 0x34, /* 0x3053*/
+ 0xc0, /* 0x3057*/
+ 0x09, /* 0x305c*/
+ 0x07, /* 0x305d */
+ 0x30, /* 0x3060 */
+ 0x00, /* 0x3065 */
+ 0x08, /* 0x30aa */
+ 0x1c, /* 0x30ab */
+ 0x32, /* 0x30b0 */
+ 0x83, /* 0x30b2 */
+ 0x04, /* 0x30d3 */
+ 0x78, /* 0x3106 */
+ 0x82, /* 0x310c */
+ 0x05, /* 0x3304 */
+ 0x04, /* 0x3305 */
+ 0x11, /* 0x3306 */
+ 0x02, /* 0x3307 */
+ 0x0c, /* 0x3308 */
+ 0x06, /* 0x3309 */
+ 0x08, /* 0x330a */
+ 0x04, /* 0x330b */
+ 0x08, /* 0x330c */
+ 0x06, /* 0x330d */
+ 0x01, /* 0x330f */
+ 0x00, /* 0x3381 */
+
+ }
+};
+
+/* Preview / Snapshot register settings */
+const struct reg_struct imx074_reg_pat[2] = {
+ /*preview*/
+ {
+ 0x2D, /*pll_multiplier*/
+ 0x06, /*frame_length_lines_hi 0x0340*/
+ 0x2D, /* frame_length_lines_lo 0x0341*/
+ 0x00, /* y_addr_start 0x347 */
+ 0x2F, /* y_add_end 0x034b */
+ 0x08, /* x_output_size_msb0x034c */
+ 0x38, /* x_output_size_lsb0x034d */
+ 0x06, /* y_output_size_msb0x034e */
+ 0x18, /* y_output_size_lsb0x034f */
+ 0x01, /* x_even_inc 0x0381 */
+ 0x03, /* x_odd_inc 0x0383 */
+ 0x01, /* y_even_inc 0x0385 */
+ 0x03, /* y_odd_inc 0x0387 */
+ 0x80, /* hmodeadd0x3001 */
+ 0x16, /* vmodeadd0x3016 */
+ 0x24, /* vapplinepos_startox3069*/
+ 0x53, /* vapplinepos_end306b*/
+ 0x00,/* shutter 0x3086 */
+ 0x80, /* haddave 0x30e8 */
+ 0x83, /* lanesel 0x3301 */
+ },
+
+ /*snapshot*/
+ {
+ 0x26, /*pll_multiplier*/
+ 0x0C, /* frame_length_lines_hi 0x0340*/
+ 0x90, /* frame_length_lines_lo 0x0341*/
+ 0x00, /* y_addr_start 0x347 */
+ 0x2F, /* y_add_end 0x034b */
+ 0x10, /* x_output_size_msb0x034c */
+ 0x70, /* x_output_size_lsb0x034d */
+ 0x0c, /* y_output_size_msb0x034e */
+ 0x30, /* y_output_size_lsb0x034f */
+ 0x01, /* x_even_inc 0x0381 */
+ 0x01, /* x_odd_inc 0x0383 */
+ 0x01, /* y_even_inc 0x0385 */
+ 0x01, /* y_odd_inc 0x0387 */
+ 0x00, /* hmodeadd0x3001 */
+ 0x06, /* vmodeadd0x3016 */
+ 0x24, /* vapplinepos_startox3069*/
+ 0x53, /* vapplinepos_end306b*/
+ 0x00, /* shutter 0x3086 */
+ 0x00, /* haddave 0x30e8 */
+ 0x03, /* lanesel 0x3301 */
+ }
+};
+struct imx074_reg imx074_regs = {
+ .reg_pat_init = &imx074_reg_init[0],
+ .reg_pat = &imx074_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/imx074_v4l2.c b/drivers/media/video/msm/imx074_v4l2.c
new file mode 100644
index 0000000..18a653d
--- /dev/null
+++ b/drivers/media/video/msm/imx074_v4l2.c
@@ -0,0 +1,1476 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <media/v4l2-subdev.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <asm/mach-types.h>
+#include "imx074.h"
+#include "msm.h"
+
+/*SENSOR REGISTER DEFINES*/
+#define IMX074_EEPROM_SLAVE_ADDR 0x52
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+#define REG_MODE_SELECT 0x100
+#define MODE_SELECT_STANDBY_MODE 0x00
+#define MODE_SELECT_STREAM 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+/* PLL registers */
+#define REG_PLL_MULTIPLIER 0x0307
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLSTATIM 0x302b
+#define REG_3024 0x3024
+#define REG_IMAGE_ORIENTATION 0x0101
+#define REG_VNDMY_ABLMGSHLMT 0x300a
+#define REG_Y_OPBADDR_START_DI 0x3014
+#define REG_3015 0x3015
+#define REG_301C 0x301C
+#define REG_302C 0x302C
+#define REG_3031 0x3031
+#define REG_3041 0x3041
+#define REG_3051 0x3051
+#define REG_3053 0x3053
+#define REG_3057 0x3057
+#define REG_305C 0x305C
+#define REG_305D 0x305D
+#define REG_3060 0x3060
+#define REG_3065 0x3065
+#define REG_30AA 0x30AA
+#define REG_30AB 0x30AB
+#define REG_30B0 0x30B0
+#define REG_30B2 0x30B2
+#define REG_30D3 0x30D3
+#define REG_3106 0x3106
+#define REG_310C 0x310C
+#define REG_3304 0x3304
+#define REG_3305 0x3305
+#define REG_3306 0x3306
+#define REG_3307 0x3307
+#define REG_3308 0x3308
+#define REG_3309 0x3309
+#define REG_330A 0x330A
+#define REG_330B 0x330B
+#define REG_330C 0x330C
+#define REG_330D 0x330D
+#define REG_330F 0x330F
+#define REG_3381 0x3381
+
+/* mode setting */
+#define REG_FRAME_LENGTH_LINES_HI 0x0340
+#define REG_FRAME_LENGTH_LINES_LO 0x0341
+#define REG_YADDR_START 0x0347
+#define REG_YAAAR_END 0x034b
+#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
+#define REG_X_EVEN_INC 0x0381
+#define REG_X_ODD_INC 0x0383
+#define REG_Y_EVEN_INC 0x0385
+#define REG_Y_ODD_INC 0x0387
+#define REG_HMODEADD 0x3001
+#define REG_VMODEADD 0x3016
+#define REG_VAPPLINE_START 0x3069
+#define REG_VAPPLINE_END 0x306b
+#define REG_SHUTTER 0x3086
+#define REG_HADDAVE 0x30e8
+#define REG_LANESEL 0x3301
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+
+#define REG_LINE_LENGTH_PCK_HI 0x0342
+#define REG_LINE_LENGTH_PCK_LO 0x0343
+/*..... TYPE DECLARATIONS.....*/
+#define IMX074_OFFSET 3
+#define IMX074_DEFAULT_MASTER_CLK_RATE 24000000
+/* Full Size */
+#define IMX074_FULL_SIZE_WIDTH 4208
+#define IMX074_FULL_SIZE_HEIGHT 3120
+#define IMX074_FULL_SIZE_DUMMY_PIXELS 0
+#define IMX074_FULL_SIZE_DUMMY_LINES 0
+/* Quarter Size */
+#define IMX074_QTR_SIZE_WIDTH 2104
+#define IMX074_QTR_SIZE_HEIGHT 1560
+#define IMX074_QTR_SIZE_DUMMY_PIXELS 0
+#define IMX074_QTR_SIZE_DUMMY_LINES 0
+/* Blanking as measured on the scope */
+/* Full Size */
+#define IMX074_HRZ_FULL_BLK_PIXELS 264
+#define IMX074_VER_FULL_BLK_LINES 96
+/* Quarter Size */
+#define IMX074_HRZ_QTR_BLK_PIXELS 2368
+#define IMX074_VER_QTR_BLK_LINES 21
+#define Q8 0x100
+#define Q10 0x400
+#define IMX074_AF_I2C_SLAVE_ID 0x72
+#define IMX074_STEPS_NEAR_TO_CLOSEST_INF 52
+#define IMX074_TOTAL_STEPS_NEAR_TO_FAR 52
+static uint32_t imx074_l_region_code_per_step = 2;
+
+struct imx074_work_t {
+ struct work_struct work;
+};
+
+static struct imx074_work_t *imx074_sensorw;
+static struct i2c_client *imx074_client;
+
+struct imx074_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+ enum imx074_resolution_t prev_res;
+ enum imx074_resolution_t pict_res;
+ enum imx074_resolution_t curr_res;
+ enum imx074_test_mode_t set_test;
+ unsigned short imgaddr;
+
+ struct v4l2_subdev *sensor_dev;
+ struct imx074_format *fmt;
+};
+static uint8_t imx074_delay_msecs_stdby = 5;
+static uint16_t imx074_delay_msecs_stream = 5;
+static int32_t config_csi;
+
+static struct imx074_ctrl_t *imx074_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(imx074_wait_queue);
+DEFINE_MUTEX(imx074_mut);
+
+struct imx074_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+/*=============================================================*/
+
+static int imx074_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 = 2,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+ CDBG("imx074_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+static int32_t imx074_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(imx074_client->adapter, msg, 1) < 0) {
+ CDBG("imx074_i2c_txdata faild 0x%x\n", imx074_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int32_t imx074_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = imx074_i2c_rxdata(imx074_client->addr, buf, rlen);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ return rc;
+}
+
+static int imx074_af_i2c_rxdata_b(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) {
+ CDBG("imx074_i2c_rxdata_b failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t imx074_i2c_read_w_eeprom(unsigned short raddr,
+ unsigned short *rdata)
+{
+ int32_t rc;
+ unsigned char buf;
+ if (!rdata)
+ return -EIO;
+ /* Read 2 bytes in sequence */
+ buf = (raddr & 0x00FF);
+ rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = buf<<8;
+
+ /* Read Second byte of data */
+ buf = (raddr & 0x00FF) + 1;
+ rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1);
+ if (rc < 0) {
+ CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata |= buf;
+ return rc;
+}
+
+static int32_t imx074_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = imx074_i2c_txdata(imx074_client->addr, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+static int16_t imx074_i2c_write_b_af(unsigned short saddr,
+ unsigned short baddr, unsigned short bdata)
+{
+ int32_t rc;
+ unsigned char buf[2];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ rc = imx074_i2c_txdata(saddr, buf, 2);
+ if (rc < 0)
+ CDBG("AFi2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!",
+ saddr, baddr, bdata);
+ return rc;
+}
+
+static int32_t imx074_i2c_write_w_table(struct imx074_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = imx074_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+static int16_t imx074_af_init(void)
+{
+ int32_t rc;
+ /* Initialize waveform */
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x01, 0xA9);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x02, 0xD2);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x03, 0x0C);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x04, 0x14);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x05, 0xB6);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x06, 0x4F);
+ return rc;
+}
+
+static void imx074_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+ uint32_t divider, d1;
+ uint32_t pclk_mult;/*Q10 */
+ /* Total frame_length_lines and line_length_pck for preview */
+ preview_frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ /* Total frame_length_lines and line_length_pck for snapshot */
+ snapshot_frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+ d1 = preview_frame_length_lines * 0x00010000 /
+ snapshot_frame_length_lines;
+ pclk_mult =
+ (uint32_t) ((imx074_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00010000) /
+ (imx074_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+ divider = d1 * pclk_mult / 0x00010000;
+ *pfps = (uint16_t) (fps * divider / 0x00010000);
+}
+
+static uint16_t imx074_get_prev_lines_pf(void)
+{
+ if (imx074_ctrl->prev_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES;
+ else
+ return IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t imx074_get_prev_pixels_pl(void)
+{
+ if (imx074_ctrl->prev_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_WIDTH + IMX074_HRZ_QTR_BLK_PIXELS;
+ else
+ return IMX074_FULL_SIZE_WIDTH + IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t imx074_get_pict_lines_pf(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ else
+ return IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+}
+
+static uint16_t imx074_get_pict_pixels_pl(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return IMX074_QTR_SIZE_WIDTH +
+ IMX074_HRZ_QTR_BLK_PIXELS;
+ else
+ return IMX074_FULL_SIZE_WIDTH +
+ IMX074_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t imx074_get_pict_max_exp_lc(void)
+{
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ return (IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES)*24;
+ else
+ return (IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t imx074_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ imx074_ctrl->fps_divider = fps->fps_div;
+ imx074_ctrl->pict_fps_divider = fps->pict_fps_div;
+ total_lines_per_frame = (uint16_t)(((IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES) * imx074_ctrl->fps_divider) / 0x400);
+ if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ ((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+ return rc;
+ if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+ return rc;
+}
+
+static int32_t imx074_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ static uint16_t max_legal_gain = 0x00E0;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_time_msb, intg_time_lsb;
+ uint8_t frame_length_line_msb, frame_length_line_lsb;
+ uint16_t frame_length_lines;
+ int32_t rc = -1;
+ CDBG("imx074_write_exp_gain : gain = %d line = %d", gain, line);
+ if (imx074_ctrl->curr_res == QTR_SIZE) {
+ frame_length_lines = IMX074_QTR_SIZE_HEIGHT +
+ IMX074_VER_QTR_BLK_LINES;
+ frame_length_lines = frame_length_lines *
+ imx074_ctrl->fps_divider / 0x400;
+ } else {
+ frame_length_lines = IMX074_FULL_SIZE_HEIGHT +
+ IMX074_VER_FULL_BLK_LINES;
+ frame_length_lines = frame_length_lines *
+ imx074_ctrl->pict_fps_divider / 0x400;
+ }
+ if (line > (frame_length_lines - IMX074_OFFSET))
+ frame_length_lines = line + IMX074_OFFSET;
+
+ CDBG("imx074 setting line = %d\n", line);
+
+
+ CDBG("imx074 setting frame_length_lines = %d\n",
+ frame_length_lines);
+
+ if (gain > max_legal_gain)
+ /* range: 0 to 224 */
+ gain = max_legal_gain;
+
+ /* update gain registers */
+ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lsb = (uint8_t) (gain & 0x00FF);
+
+ frame_length_line_msb = (uint8_t) ((frame_length_lines & 0xFF00) >> 8);
+ frame_length_line_lsb = (uint8_t) (frame_length_lines & 0x00FF);
+
+ /* update line count registers */
+ intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+ intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+ rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+ CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_HI = 0x%X\n",
+ gain_msb);
+ rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_HI,
+ gain_msb);
+ if (rc < 0)
+ return rc;
+ CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_LO = 0x%X\n",
+ gain_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ gain_lsb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_FRAME_LENGTH_LINES_HI = 0x%X\n",
+ frame_length_line_msb);
+ rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ frame_length_line_msb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_FRAME_LENGTH_LINES_LO = 0x%X\n",
+ frame_length_line_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ frame_length_line_lsb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_HI = 0x%X\n",
+ intg_time_msb);
+ rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+ intg_time_msb);
+ if (rc < 0)
+ return rc;
+
+ CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_LO = 0x%X\n",
+ intg_time_lsb);
+ rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+ intg_time_lsb);
+ if (rc < 0)
+ return rc;
+
+ rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t imx074_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = imx074_write_exp_gain(gain, line);
+ return rc;
+}
+
+static int32_t imx074_move_focus(int direction,
+ int32_t num_steps)
+{
+ int32_t step_direction, dest_step_position, bit_mask;
+ int32_t rc = 0;
+
+ if (num_steps == 0)
+ return rc;
+
+ if (direction == MOVE_NEAR) {
+ step_direction = 1;
+ bit_mask = 0x80;
+ } else if (direction == MOVE_FAR) {
+ step_direction = -1;
+ bit_mask = 0x00;
+ } else {
+ CDBG("imx074_move_focus: Illegal focus direction");
+ return -EINVAL;
+ }
+ dest_step_position = imx074_ctrl->curr_step_pos +
+ (step_direction * num_steps);
+ if (dest_step_position < 0)
+ dest_step_position = 0;
+ else if (dest_step_position > IMX074_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = IMX074_TOTAL_STEPS_NEAR_TO_FAR;
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00,
+ ((num_steps * imx074_l_region_code_per_step) | bit_mask));
+ CDBG("%s: Index: %d\n", __func__, dest_step_position);
+ imx074_ctrl->curr_step_pos = dest_step_position;
+ return rc;
+}
+
+
+static int32_t imx074_set_default_focus(uint8_t af_step)
+{
+ int32_t rc;
+ /* Initialize to infinity */
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F);
+ imx074_ctrl->curr_step_pos = 0;
+ return rc;
+}
+static int32_t imx074_test(enum imx074_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* Set mo to 2 inorder to enable test pattern*/
+ if (imx074_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+static int32_t imx074_sensor_setting(int update_type, int rt)
+{
+ int32_t rc = 0;
+ struct msm_camera_csid_params imx074_csid_params;
+ struct msm_camera_csiphy_params imx074_csiphy_params;
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct imx074_i2c_reg_conf init_tbl[] = {
+ {REG_PRE_PLL_CLK_DIV,
+ imx074_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLSTATIM,
+ imx074_regs.reg_pat_init[0].
+ plstatim},
+ {REG_3024,
+ imx074_regs.reg_pat_init[0].
+ reg_3024},
+ {REG_IMAGE_ORIENTATION,
+ imx074_regs.reg_pat_init[0].
+ image_orientation},
+ {REG_VNDMY_ABLMGSHLMT,
+ imx074_regs.reg_pat_init[0].
+ vndmy_ablmgshlmt},
+ {REG_Y_OPBADDR_START_DI,
+ imx074_regs.reg_pat_init[0].
+ y_opbaddr_start_di},
+ {REG_3015,
+ imx074_regs.reg_pat_init[0].
+ reg_0x3015},
+ {REG_301C,
+ imx074_regs.reg_pat_init[0].
+ reg_0x301c},
+ {REG_302C,
+ imx074_regs.reg_pat_init[0].
+ reg_0x302c},
+ {REG_3031,
+ imx074_regs.reg_pat_init[0].reg_0x3031},
+ {REG_3041,
+ imx074_regs.reg_pat_init[0].reg_0x3041},
+ {REG_3051,
+ imx074_regs.reg_pat_init[0].reg_0x3051},
+ {REG_3053,
+ imx074_regs.reg_pat_init[0].reg_0x3053},
+ {REG_3057,
+ imx074_regs.reg_pat_init[0].reg_0x3057},
+ {REG_305C,
+ imx074_regs.reg_pat_init[0].reg_0x305c},
+ {REG_305D,
+ imx074_regs.reg_pat_init[0].reg_0x305d},
+ {REG_3060,
+ imx074_regs.reg_pat_init[0].reg_0x3060},
+ {REG_3065,
+ imx074_regs.reg_pat_init[0].reg_0x3065},
+ {REG_30AA,
+ imx074_regs.reg_pat_init[0].reg_0x30aa},
+ {REG_30AB,
+ imx074_regs.reg_pat_init[0].reg_0x30ab},
+ {REG_30B0,
+ imx074_regs.reg_pat_init[0].reg_0x30b0},
+ {REG_30B2,
+ imx074_regs.reg_pat_init[0].reg_0x30b2},
+ {REG_30D3,
+ imx074_regs.reg_pat_init[0].reg_0x30d3},
+ {REG_3106,
+ imx074_regs.reg_pat_init[0].reg_0x3106},
+ {REG_310C,
+ imx074_regs.reg_pat_init[0].reg_0x310c},
+ {REG_3304,
+ imx074_regs.reg_pat_init[0].reg_0x3304},
+ {REG_3305,
+ imx074_regs.reg_pat_init[0].reg_0x3305},
+ {REG_3306,
+ imx074_regs.reg_pat_init[0].reg_0x3306},
+ {REG_3307,
+ imx074_regs.reg_pat_init[0].reg_0x3307},
+ {REG_3308,
+ imx074_regs.reg_pat_init[0].reg_0x3308},
+ {REG_3309,
+ imx074_regs.reg_pat_init[0].reg_0x3309},
+ {REG_330A,
+ imx074_regs.reg_pat_init[0].reg_0x330a},
+ {REG_330B,
+ imx074_regs.reg_pat_init[0].reg_0x330b},
+ {REG_330C,
+ imx074_regs.reg_pat_init[0].reg_0x330c},
+ {REG_330D,
+ imx074_regs.reg_pat_init[0].reg_0x330d},
+ {REG_330F,
+ imx074_regs.reg_pat_init[0].reg_0x330f},
+ {REG_3381,
+ imx074_regs.reg_pat_init[0].reg_0x3381},
+ };
+ struct imx074_i2c_reg_conf init_mode_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_PLL_MULTIPLIER,
+ imx074_regs.reg_pat[rt].
+ pll_multiplier},
+ {REG_FRAME_LENGTH_LINES_HI,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_YADDR_START ,
+ imx074_regs.reg_pat[rt].
+ y_addr_start},
+ {REG_YAAAR_END,
+ imx074_regs.reg_pat[rt].
+ y_add_end},
+ {REG_X_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB ,
+ imx074_regs.reg_pat[rt].
+ y_output_size_lsb},
+ {REG_X_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ x_even_inc},
+ {REG_X_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ x_odd_inc},
+ {REG_Y_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ y_even_inc},
+ {REG_Y_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ y_odd_inc},
+ {REG_HMODEADD,
+ imx074_regs.reg_pat[rt].
+ hmodeadd},
+ {REG_VMODEADD,
+ imx074_regs.reg_pat[rt].
+ vmodeadd},
+ {REG_VAPPLINE_START,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_start},
+ {REG_VAPPLINE_END,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_end},
+ {REG_SHUTTER,
+ imx074_regs.reg_pat[rt].
+ shutter},
+ {REG_HADDAVE,
+ imx074_regs.reg_pat[rt].
+ haddave},
+ {REG_LANESEL,
+ imx074_regs.reg_pat[rt].
+ lanesel},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF},
+
+ };
+ /* reset fps_divider */
+ imx074_ctrl->fps = 30 * Q8;
+ imx074_ctrl->fps_divider = 1 * 0x400;
+ /* stop streaming */
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ if (rc < 0)
+ return rc;
+ msleep(imx074_delay_msecs_stdby);
+ rc = imx074_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+ rc = imx074_i2c_write_w_table(&init_mode_tbl[0],
+ ARRAY_SIZE(init_mode_tbl));
+ if (rc < 0)
+ return rc;
+ rc = imx074_test(imx074_ctrl->set_test);
+ return rc;
+ }
+ break;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct imx074_i2c_reg_conf mode_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_PLL_MULTIPLIER,
+ imx074_regs.reg_pat[rt].
+ pll_multiplier},
+ {REG_FRAME_LENGTH_LINES_HI,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ imx074_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_YADDR_START ,
+ imx074_regs.reg_pat[rt].
+ y_addr_start},
+ {REG_YAAAR_END,
+ imx074_regs.reg_pat[rt].
+ y_add_end},
+ {REG_X_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_msb},
+ {REG_X_OUTPUT_SIZE_LSB,
+ imx074_regs.reg_pat[rt].
+ x_output_size_lsb},
+ {REG_Y_OUTPUT_SIZE_MSB,
+ imx074_regs.reg_pat[rt].
+ y_output_size_msb},
+ {REG_Y_OUTPUT_SIZE_LSB ,
+ imx074_regs.reg_pat[rt].
+ y_output_size_lsb},
+ {REG_X_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ x_even_inc},
+ {REG_X_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ x_odd_inc},
+ {REG_Y_EVEN_INC,
+ imx074_regs.reg_pat[rt].
+ y_even_inc},
+ {REG_Y_ODD_INC,
+ imx074_regs.reg_pat[rt].
+ y_odd_inc},
+ {REG_HMODEADD,
+ imx074_regs.reg_pat[rt].
+ hmodeadd},
+ {REG_VMODEADD,
+ imx074_regs.reg_pat[rt].
+ vmodeadd},
+ {REG_VAPPLINE_START,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_start},
+ {REG_VAPPLINE_END,
+ imx074_regs.reg_pat[rt].
+ vapplinepos_end},
+ {REG_SHUTTER,
+ imx074_regs.reg_pat[rt].
+ shutter},
+ {REG_HADDAVE,
+ imx074_regs.reg_pat[rt].
+ haddave},
+ {REG_LANESEL,
+ imx074_regs.reg_pat[rt].
+ lanesel},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF},
+ };
+
+ /* stop streaming */
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ msleep(imx074_delay_msecs_stdby);
+ rc = imx074_i2c_write_w_table(&mode_tbl[0],
+ ARRAY_SIZE(mode_tbl));
+ if (config_csi == 0) {
+ struct msm_camera_csid_vc_cfg imx074_vccfg[] = {
+ {0, CSI_RAW10, CSI_DECODE_10BIT},
+ {1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+ };
+ imx074_csid_params.lane_cnt = 4;
+ imx074_csid_params.lane_assign = 0xe4;
+ imx074_csid_params.lut_params.num_cid =
+ ARRAY_SIZE(imx074_vccfg);
+ imx074_csid_params.lut_params.vc_cfg =
+ &imx074_vccfg[0];
+ imx074_csiphy_params.lane_cnt = 4;
+ imx074_csiphy_params.settle_cnt = 0x1B;
+ rc = msm_camio_csid_config(&imx074_csid_params);
+ v4l2_subdev_notify(imx074_ctrl->sensor_dev,
+ NOTIFY_CID_CHANGE, NULL);
+ mb();
+ rc = msm_camio_csiphy_config
+ (&imx074_csiphy_params);
+ mb();
+ /*imx074_delay_msecs_stdby*/
+ msleep(imx074_delay_msecs_stream);
+ config_csi = 1;
+ }
+ if (rc < 0)
+ return rc;
+ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM);
+ if (rc < 0)
+ return rc;
+ msleep(imx074_delay_msecs_stream);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+
+static int32_t imx074_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->prev_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->prev_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t imx074_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+ }
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+static int32_t imx074_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */
+ /* change sensor resolution if needed */
+ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) {
+ if (imx074_ctrl->pict_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+ }
+ if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ imx074_ctrl->curr_res = imx074_ctrl->pict_res;
+ imx074_ctrl->sensormode = mode;
+ return rc;
+}
+static int32_t imx074_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = imx074_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = imx074_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = imx074_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+static int32_t imx074_power_down(void)
+{
+ imx074_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ msleep(imx074_delay_msecs_stdby);
+ return 0;
+}
+static int imx074_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ gpio_direction_input(data->sensor_reset);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int imx074_read_eeprom_data(struct sensor_cfg_data *cfg)
+{
+ int32_t rc = 0;
+ uint16_t eepromdata = 0;
+ uint8_t addr = 0;
+
+ addr = 0x10;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.r_over_g = eepromdata;
+
+ addr = 0x12;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.b_over_g = eepromdata;
+
+ addr = 0x14;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.gr_over_gb = eepromdata;
+
+ addr = 0x1A;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.macro_2_inf = eepromdata;
+
+ addr = 0x1C;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.inf_2_macro = eepromdata;
+
+ addr = 0x1E;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.stroke_amt = eepromdata;
+
+ addr = 0x20;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.af_pos_1m = eepromdata;
+
+ addr = 0x22;
+ rc = imx074_i2c_read_w_eeprom(addr, &eepromdata);
+ if (rc < 0) {
+ CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr);
+ return rc;
+ }
+ cfg->cfg.calib_info.af_pos_inf = eepromdata;
+
+ return rc;
+}
+
+static int imx074_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ unsigned short chipidl, chipidh;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "imx074");
+ CDBG("imx074_probe_init_sensor\n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ usleep_range(5000, 6000);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ usleep_range(5000, 6000);
+ } else {
+ CDBG("gpio reset fail");
+ goto init_probe_done;
+ }
+ CDBG("imx074_probe_init_sensor is called\n");
+ /* 3. Read sensor Model ID: */
+ rc = imx074_i2c_read(0x0000, &chipidh, 1);
+ if (rc < 0) {
+ CDBG("Model read failed\n");
+ goto init_probe_fail;
+ }
+ rc = imx074_i2c_read(0x0001, &chipidl, 1);
+ if (rc < 0) {
+ CDBG("Model read failed\n");
+ goto init_probe_fail;
+ }
+ CDBG("imx074 model_id = 0x%x 0x%x\n", chipidh, chipidl);
+ /* 4. Compare sensor ID to IMX074 ID: */
+ if (chipidh != 0x00 || chipidl != 0x74) {
+ rc = -ENODEV;
+ CDBG("imx074_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ CDBG("imx074_probe_init_sensor fails\n");
+ imx074_probe_init_done(data);
+init_probe_done:
+ CDBG(" imx074_probe_init_sensor finishes\n");
+ return rc;
+ }
+static int32_t imx074_poweron_af(void)
+{
+ int32_t rc = 0;
+ CDBG("imx074 enable AF actuator, gpio = %d\n",
+ imx074_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(imx074_ctrl->sensordata->vcm_pwd, "imx074");
+ if (!rc) {
+ gpio_direction_output(imx074_ctrl->sensordata->vcm_pwd, 1);
+ msleep(20);
+ rc = imx074_af_init();
+ if (rc < 0)
+ CDBG("imx074 AF initialisation failed\n");
+ } else {
+ CDBG("%s: AF PowerON gpio_request failed %d\n", __func__, rc);
+ }
+ return rc;
+}
+static void imx074_poweroff_af(void)
+{
+ gpio_set_value_cansleep(imx074_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(imx074_ctrl->sensordata->vcm_pwd);
+}
+/* camsensor_iu060f_imx074_reset */
+int imx074_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling imx074_sensor_open_init\n");
+ imx074_ctrl->fps_divider = 1 * 0x00000400;
+ imx074_ctrl->pict_fps_divider = 1 * 0x00000400;
+ imx074_ctrl->fps = 30 * Q8;
+ imx074_ctrl->set_test = TEST_OFF;
+ imx074_ctrl->prev_res = QTR_SIZE;
+ imx074_ctrl->pict_res = FULL_SIZE;
+ imx074_ctrl->curr_res = INVALID_SIZE;
+ config_csi = 0;
+
+ if (data)
+ imx074_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+ usleep_range(1000, 2000);
+ rc = imx074_probe_init_sensor(data);
+ if (rc < 0) {
+ CDBG("Calling imx074_sensor_open_init fail\n");
+ goto probe_fail;
+ }
+
+ rc = imx074_sensor_setting(REG_INIT, RES_PREVIEW);
+ if (rc < 0) {
+ CDBG("imx074_sensor_setting failed\n");
+ goto init_fail;
+ }
+ if (machine_is_msm8x60_fluid())
+ rc = imx074_poweron_af();
+ else
+ rc = imx074_af_init();
+ if (rc < 0) {
+ CDBG("AF initialisation failed\n");
+ goto init_fail;
+ } else
+ goto init_done;
+probe_fail:
+ CDBG(" imx074_sensor_open_init probe fail\n");
+ kfree(imx074_ctrl);
+ return rc;
+init_fail:
+ CDBG(" imx074_sensor_open_init fail\n");
+ imx074_probe_init_done(data);
+ kfree(imx074_ctrl);
+init_done:
+ CDBG("imx074_sensor_open_init done\n");
+ return rc;
+}
+static int imx074_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&imx074_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id imx074_i2c_id[] = {
+ {"imx074", 0},
+ { }
+};
+
+static int imx074_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("imx074_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ imx074_sensorw = kzalloc(sizeof(struct imx074_work_t), GFP_KERNEL);
+ if (!imx074_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, imx074_sensorw);
+ imx074_init_client(client);
+ imx074_client = client;
+
+
+ CDBG("imx074_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("imx074_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int __exit imx074_remove(struct i2c_client *client)
+{
+ struct imx074_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ imx074_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver imx074_i2c_driver = {
+ .id_table = imx074_i2c_id,
+ .probe = imx074_i2c_probe,
+ .remove = __exit_p(imx074_i2c_remove),
+ .driver = {
+ .name = "imx074",
+ },
+};
+
+int imx074_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;
+ mutex_lock(&imx074_mut);
+ CDBG("imx074_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 =
+ imx074_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 = imx074_set_fps(&(cdata.cfg.fps));
+ break;
+ case CFG_SET_EXP_GAIN:
+ rc =
+ imx074_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ imx074_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_MODE:
+ rc = imx074_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ rc = imx074_power_down();
+ break;
+ case CFG_GET_CALIB_DATA:
+ rc = imx074_read_eeprom_data(&cdata);
+ if (rc < 0)
+ break;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(cdata)))
+ rc = -EFAULT;
+ break;
+ case CFG_MOVE_FOCUS:
+ rc =
+ imx074_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ imx074_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = IMX074_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 = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&imx074_mut);
+
+ return rc;
+}
+static int imx074_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&imx074_mut);
+ if (machine_is_msm8x60_fluid())
+ imx074_poweroff_af();
+ imx074_power_down();
+ gpio_set_value_cansleep(imx074_ctrl->sensordata->sensor_reset, 0);
+ usleep_range(5000, 6000);
+ gpio_direction_input(imx074_ctrl->sensordata->sensor_reset);
+ gpio_free(imx074_ctrl->sensordata->sensor_reset);
+ CDBG("imx074_release completed\n");
+ mutex_unlock(&imx074_mut);
+
+ return rc;
+}
+
+static int imx074_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&imx074_i2c_driver);
+ if (rc < 0 || imx074_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE);
+ rc = imx074_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = imx074_sensor_open_init;
+ s->s_release = imx074_sensor_release;
+ s->s_config = imx074_sensor_config;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+ imx074_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("imx074_sensor_probe: SENSOR PROBE FAILS!\n");
+ i2c_del_driver(&imx074_i2c_driver);
+ return rc;
+}
+
+static struct imx074_format imx074_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+ /* more can be supported, to be added later */
+};
+
+static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ printk(KERN_DEBUG "Index is %d\n", index);
+ if ((unsigned int)index >= ARRAY_SIZE(imx074_subdev_info))
+ return -EINVAL;
+
+ *code = imx074_subdev_info[index].code;
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops imx074_subdev_core_ops;
+static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
+ .enum_mbus_fmt = imx074_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx074_subdev_ops = {
+ .core = &imx074_subdev_core_ops,
+ .video = &imx074_subdev_video_ops,
+};
+
+
+static int imx074_sensor_probe_cb(const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = imx074_sensor_probe(info, s);
+ if (rc < 0)
+ return rc;
+
+ imx074_ctrl = kzalloc(sizeof(struct imx074_ctrl_t), GFP_KERNEL);
+ if (!imx074_ctrl) {
+ CDBG("imx074_sensor_probe failed!\n");
+ return -ENOMEM;
+ }
+
+ /* probe is successful, init a v4l2 subdevice */
+ printk(KERN_DEBUG "going into v4l2_i2c_subdev_init\n");
+ if (sdev) {
+ v4l2_i2c_subdev_init(sdev, imx074_client,
+ &imx074_subdev_ops);
+ imx074_ctrl->sensor_dev = sdev;
+ }
+ return rc;
+}
+
+static int __imx074_probe(struct platform_device *pdev)
+{
+ return msm_sensor_register(pdev, imx074_sensor_probe_cb);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __imx074_probe,
+ .driver = {
+ .name = "msm_camera_imx074",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init imx074_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(imx074_init);
+
+MODULE_DESCRIPTION("Sony 13 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
new file mode 100644
index 0000000..da28403
--- /dev/null
+++ b/drivers/media/video/msm/msm.c
@@ -0,0 +1,2080 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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/proc_fs.h>
+#include "msm.h"
+
+
+#define MSM_MAX_CAMERA_SENSORS 5
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static unsigned msm_camera_v4l2_nr = -1;
+static struct msm_cam_server_dev g_server_dev;
+static struct class *msm_class;
+static dev_t msm_devno;
+static int vnode_count;
+
+module_param(msm_camera_v4l2_nr, uint, 0644);
+MODULE_PARM_DESC(msm_camera_v4l2_nr, "videoX start number, -1 is autodetect");
+
+static int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
+ struct video_device *pvdev);
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ D("%s\n", __func__);
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_info("%s: queue %s new max is %d\n", __func__,
+ queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ D("%s: woke up %s\n", __func__, queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+/* callback function from all subdevices of a msm_cam_v4l2_device */
+static void msm_cam_v4l2_subdev_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct msm_cam_v4l2_device *pcam;
+
+ if (sd == NULL)
+ return;
+
+ pcam = to_pcam(sd->v4l2_dev);
+
+ if (pcam == NULL)
+ return;
+
+ /* forward to media controller for any changes*/
+ if (pcam->mctl.mctl_notify) {
+ pcam->mctl.mctl_notify(&pcam->mctl, notification, arg);
+ }
+}
+
+static int msm_ctrl_cmd_done(void __user *arg)
+{
+ void __user *uptr;
+ struct msm_queue_cmd *qcmd;
+ struct msm_ctrl_cmd *command = &g_server_dev.ctrl;
+
+ D("%s\n", __func__);
+
+ if (copy_from_user(command, arg,
+ sizeof(struct msm_ctrl_cmd)))
+ return -EINVAL;
+
+ qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ atomic_set(&qcmd->on_heap, 0);
+ uptr = command->value;
+ qcmd->command = command;
+
+ if (command->length > 0) {
+ command->value = g_server_dev.ctrl_data;
+ if (command->length > sizeof(g_server_dev.ctrl_data)) {
+ pr_err("%s: user data %d is too big (max %d)\n",
+ __func__, command->length,
+ sizeof(g_server_dev.ctrl_data));
+ return -EINVAL;
+ }
+ if (copy_from_user(command->value, uptr, command->length))
+ return -EINVAL;
+ }
+
+ msm_enqueue(&g_server_dev.ctrl_q, &qcmd->list_control);
+ return 0;
+}
+
+/* send control command to config and wait for results*/
+static int msm_server_control(struct msm_cam_server_dev *server_dev,
+ struct msm_ctrl_cmd *out)
+{
+ int rc = 0;
+ void *value;
+ struct msm_queue_cmd *rcmd;
+ struct msm_ctrl_cmd *ctrlcmd;
+ struct msm_device_queue *queue = &server_dev->ctrl_q;
+
+ struct v4l2_event v4l2_evt;
+ struct msm_isp_stats_event_ctrl *isp_event;
+ D("%s\n", __func__);
+
+ v4l2_evt.type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2;
+
+ /* setup event object to transfer the command; */
+ isp_event = (struct msm_isp_stats_event_ctrl *)v4l2_evt.u.data;
+ isp_event->resptype = MSM_CAM_RESP_V4L2;
+ isp_event->isp_data.ctrl = *out;
+
+ /* now send command to config thread in usersspace,
+ * and wait for results */
+ v4l2_event_queue(server_dev->server_command_queue.pvdev,
+ &v4l2_evt);
+
+ D("%s v4l2_event_queue: type = 0x%x\n", __func__, v4l2_evt.type);
+
+ /* wait for config return status */
+ D("Waiting for config status\n");
+ rc = wait_event_interruptible_timeout(queue->wait,
+ !list_empty_careful(&queue->list),
+ out->timeout_ms);
+ D("Waiting is over for config status\n");
+ if (list_empty_careful(&queue->list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0) {
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ return rc;
+ }
+ }
+
+ rcmd = msm_dequeue(queue, list_control);
+ BUG_ON(!rcmd);
+ D("%s Finished servicing ioctl\n", __func__);
+
+ ctrlcmd = (struct msm_ctrl_cmd *)(rcmd->command);
+ value = out->value;
+ if (ctrlcmd->length > 0)
+ memcpy(value, ctrlcmd->value, ctrlcmd->length);
+
+ memcpy(out, ctrlcmd, sizeof(struct msm_ctrl_cmd));
+ out->value = value;
+
+ free_qcmd(rcmd);
+ D("%s: rc %d\n", __func__, rc);
+ /* rc is the time elapsed. */
+ if (rc >= 0) {
+ /* TODO: Refactor msm_ctrl_cmd::status field */
+ if (out->status == 0)
+ rc = -1;
+ else if (out->status == 1)
+ rc = 0;
+ else
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*send open command to server*/
+static int msm_send_open_server(void)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s\n", __func__);
+ ctrlcmd.type = MSM_V4L2_OPEN;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+static int msm_send_close_server(void)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s\n", __func__);
+ ctrlcmd.type = MSM_V4L2_CLOSE;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+static int msm_server_set_fmt(struct msm_cam_v4l2_device *pcam, int idx,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+ struct msm_ctrl_cmd ctrlcmd;
+
+ D("%s: %d, %d, 0x%x\n", __func__,
+ pfmt->fmt.pix.width, pfmt->fmt.pix.height,
+ pfmt->fmt.pix.pixelformat);
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ D("%s, Attention! Wrong buf-type %d\n", __func__, pfmt->type);
+
+ for (i = 0; i < pcam->num_fmts; i++)
+ if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+ break;
+ if (i == pcam->num_fmts) {
+ pr_err("%s: User requested pixelformat %x not supported\n",
+ __func__, pix->pixelformat);
+ return -EINVAL;
+ }
+
+ ctrlcmd.type = MSM_V4L2_VID_CAP_TYPE;
+ ctrlcmd.length = MSM_V4L2_DIMENSION_SIZE;
+ ctrlcmd.value = (void *)pfmt->fmt.pix.priv;
+ ctrlcmd.timeout_ms = 10000;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ if (rc >= 0) {
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.width = pix->width;
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.height = pix->height;
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.field = pix->field;
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat =
+ pix->pixelformat;
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline =
+ pix->bytesperline;
+ pcam->dev_inst[idx]->vid_bufq.field = pix->field;
+ pcam->dev_inst[idx]->sensor_pxlcode
+ = pcam->usr_fmts[i].pxlcode;
+ D("%s:inst=0x%x,idx=%d,width=%d,heigth=%d\n",
+ __func__, (u32)pcam->dev_inst[idx], idx,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.width,
+ pcam->dev_inst[idx]->vid_fmt.fmt.pix.height);
+ }
+
+ return rc;
+}
+
+static int msm_server_streamon(struct msm_cam_v4l2_device *pcam, int idx)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ D("%s\n", __func__);
+ ctrlcmd.type = MSM_V4L2_STREAM_ON;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+ ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+static int msm_server_streamoff(struct msm_cam_v4l2_device *pcam, int idx)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+
+ D("%s, pcam = 0x%x\n", __func__, (u32)pcam);
+ ctrlcmd.type = MSM_V4L2_STREAM_OFF;
+ ctrlcmd.timeout_ms = 10000;
+ ctrlcmd.length = 0;
+ ctrlcmd.value = NULL;
+ ctrlcmd.stream_type = pcam->dev_inst[idx]->image_mode;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+static int msm_server_proc_ctrl_cmd(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl, int is_set_cmd)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd, *tmp_cmd;
+ uint8_t *ctrl_data = NULL;
+ void __user *uptr_cmd;
+ void __user *uptr_value;
+ uint32_t cmd_len = sizeof(struct msm_ctrl_cmd);
+ uint32_t value_len;
+
+ tmp_cmd = (struct msm_ctrl_cmd *)ctrl->value;
+ uptr_cmd = (void __user *)ctrl->value;
+ uptr_value = (void __user *)tmp_cmd->value;
+ value_len = tmp_cmd->length;
+
+ D("%s: cmd type = %d, up1=0x%x, ulen1=%d, up2=0x%x, ulen2=%d\n",
+ __func__, tmp_cmd->type, (uint32_t)uptr_cmd, cmd_len,
+ (uint32_t)uptr_value, tmp_cmd->length);
+
+ ctrl_data = kzalloc(value_len+cmd_len, GFP_KERNEL);
+ if (ctrl_data == 0) {
+ pr_err("%s could not allocate memory\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+ tmp_cmd = (struct msm_ctrl_cmd *)ctrl_data;
+ if (copy_from_user((void *)ctrl_data, uptr_cmd,
+ cmd_len)) {
+ pr_err("%s: copy_from_user failed.\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ tmp_cmd->value = (void *)(ctrl_data+cmd_len);
+ if (uptr_value && tmp_cmd->length > 0) {
+ if (copy_from_user((void *)tmp_cmd->value, uptr_value,
+ value_len)) {
+ pr_err("%s: copy_from_user failed, size=%d\n",
+ __func__, value_len);
+ rc = -EINVAL;
+ goto end;
+ }
+ } else
+ tmp_cmd->value = NULL;
+
+ ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
+ ctrlcmd.length = cmd_len + value_len;
+ ctrlcmd.value = (void *)ctrl_data;
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ pr_err("%s: msm_server_control rc=%d\n", __func__, rc);
+ if (rc == 0) {
+ if (uptr_value && tmp_cmd->length > 0 &&
+ copy_to_user((void __user *)uptr_value,
+ (void *)(ctrl_data+cmd_len), tmp_cmd->length)) {
+ pr_err("%s: copy_to_user failed, size=%d\n",
+ __func__, tmp_cmd->length);
+ rc = -EINVAL;
+ goto end;
+ }
+ tmp_cmd->value = uptr_value;
+ if (copy_to_user((void __user *)uptr_cmd,
+ (void *)tmp_cmd, cmd_len)) {
+ pr_err("%s: copy_to_user failed in cpy, size=%d\n",
+ __func__, cmd_len);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+end:
+ pr_err("%s: END, type = %d, vaddr = 0x%x, vlen = %d, status = %d, rc = %d\n",
+ __func__, tmp_cmd->type, (uint32_t)tmp_cmd->value,
+ tmp_cmd->length, tmp_cmd->status, rc);
+ kfree(ctrl_data);
+ return rc;
+}
+
+static int msm_server_s_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(ctrl == NULL);
+
+ if (ctrl && ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+ return msm_server_proc_ctrl_cmd(pcam, ctrl, 1);
+
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_SET_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_control);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+ ctrlcmd.vnode_id = pcam->vnode_id;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ return rc;
+}
+
+static int msm_server_g_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(ctrl == NULL);
+ if (ctrl && ctrl->id == MSM_V4L2_PID_CTRL_CMD)
+ return msm_server_proc_ctrl_cmd(pcam, ctrl, 0);
+
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_GET_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_control);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, ctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+
+ /* send command to config thread in usersspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+
+ ctrl->value = ((struct v4l2_control *)ctrlcmd.value)->value;
+
+ return rc;
+}
+
+static int msm_server_q_ctrl(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_queryctrl *queryctrl)
+{
+ int rc = 0;
+ struct msm_ctrl_cmd ctrlcmd;
+ uint8_t ctrl_data[max_control_command_size];
+
+ WARN_ON(queryctrl == NULL);
+ memset(ctrl_data, 0, sizeof(ctrl_data));
+
+ ctrlcmd.type = MSM_V4L2_QUERY_CTRL;
+ ctrlcmd.length = sizeof(struct v4l2_queryctrl);
+ ctrlcmd.value = (void *)ctrl_data;
+ memcpy(ctrlcmd.value, queryctrl, ctrlcmd.length);
+ ctrlcmd.timeout_ms = 1000;
+
+ /* send command to config thread in userspace, and get return value */
+ rc = msm_server_control(&g_server_dev, &ctrlcmd);
+ D("%s: rc = %d\n", __func__, rc);
+
+ if (rc >= 0)
+ memcpy(queryctrl, ctrlcmd.value, sizeof(struct v4l2_queryctrl));
+
+ return rc;
+}
+
+static int msm_server_get_fmt(struct msm_cam_v4l2_device *pcam,
+ int idx, struct v4l2_format *pfmt)
+{
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+
+ pix->width = pcam->dev_inst[idx]->vid_fmt.fmt.pix.width;
+ pix->height = pcam->dev_inst[idx]->vid_fmt.fmt.pix.height;
+ pix->field = pcam->dev_inst[idx]->vid_fmt.fmt.pix.field;
+ pix->pixelformat = pcam->dev_inst[idx]->vid_fmt.fmt.pix.pixelformat;
+ pix->bytesperline = pcam->dev_inst[idx]->vid_fmt.fmt.pix.bytesperline;
+ pix->colorspace = pcam->dev_inst[idx]->vid_fmt.fmt.pix.colorspace;
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ return 0;
+}
+
+static int msm_server_try_fmt(struct msm_cam_v4l2_device *pcam,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_pix_format *pix = &pfmt->fmt.pix;
+ struct v4l2_mbus_framefmt sensor_fmt;
+
+ D("%s: 0x%x\n", __func__, pix->pixelformat);
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err("%s: pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* check if the format is supported by this host-sensor combo */
+ for (i = 0; i < pcam->num_fmts; i++) {
+ D("%s: usr_fmts.fourcc: 0x%x\n", __func__,
+ pcam->usr_fmts[i].fourcc);
+ if (pcam->usr_fmts[i].fourcc == pix->pixelformat)
+ break;
+ }
+
+ if (i == pcam->num_fmts) {
+ pr_err("%s: Format %x not found\n", __func__, pix->pixelformat);
+ return -EINVAL;
+ }
+
+ sensor_fmt.width = pix->width;
+ sensor_fmt.height = pix->height;
+ sensor_fmt.field = pix->field;
+ sensor_fmt.colorspace = pix->colorspace;
+ sensor_fmt.code = pcam->usr_fmts[i].pxlcode;
+
+ pix->width = sensor_fmt.width;
+ pix->height = sensor_fmt.height;
+ pix->field = sensor_fmt.field;
+ pix->colorspace = sensor_fmt.colorspace;
+
+ return rc;
+}
+
+/*
+ *
+ * implementation of v4l2_ioctl_ops
+ *
+ */
+static int msm_camera_v4l2_querycap(struct file *f, void *pctx,
+ struct v4l2_capability *pcaps)
+{
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ /* some other day, some other time */
+ /*cap->version = LINUX_VERSION_CODE; */
+ strlcpy(pcaps->driver, pcam->pdev->name, sizeof(pcaps->driver));
+ pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int msm_camera_v4l2_queryctrl(struct file *f, void *pctx,
+ struct v4l2_queryctrl *pqctrl)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ mutex_lock(&pcam->vid_lock);
+ rc = msm_server_q_ctrl(pcam, pqctrl);
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+}
+
+static int msm_camera_v4l2_g_ctrl(struct file *f, void *pctx,
+ struct v4l2_control *c)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ mutex_lock(&pcam->vid_lock);
+ rc = msm_server_g_ctrl(pcam, c);
+ mutex_unlock(&pcam->vid_lock);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_s_ctrl(struct file *f, void *pctx,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+ mutex_lock(&pcam->vid_lock);
+ if (ctrl->id == MSM_V4L2_PID_CAM_MODE)
+ pcam->op_mode = ctrl->value;
+ rc = msm_server_s_ctrl(pcam, ctrl);
+ mutex_unlock(&pcam->vid_lock);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_reqbufs(struct file *f, void *pctx,
+ struct v4l2_requestbuffers *pb)
+{
+ int rc = 0;
+ int i = 0;
+ /*struct msm_cam_v4l2_device *pcam = video_drvdata(f);*/
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ if (!pb->count) {
+ if (pcam_inst->vid_bufq.streaming)
+ videobuf_stop(&pcam_inst->vid_bufq);
+ else
+ videobuf_queue_cancel(&pcam_inst->vid_bufq);
+
+ /* free the queue: function name is ambiguous it frees all
+ types of buffers (mmap or userptr - it doesn't matter) */
+ rc = videobuf_mmap_free(&pcam_inst->vid_bufq);
+ } else {
+ rc = videobuf_reqbufs(&pcam_inst->vid_bufq, pb);
+ if (rc < 0)
+ return rc;
+ /* Now initialize the local msm_frame_buffer structure */
+ for (i = 0; i < pb->count; i++) {
+ struct msm_frame_buffer *buf = container_of(
+ pcam_inst->vid_bufq.bufs[i],
+ struct msm_frame_buffer,
+ vidbuf);
+ buf->inuse = 0;
+ INIT_LIST_HEAD(&buf->vidbuf.queue);
+ }
+ }
+ pcam_inst->buf_count = pb->count;
+ return rc;
+}
+
+static int msm_camera_v4l2_querybuf(struct file *f, void *pctx,
+ struct v4l2_buffer *pb)
+{
+ /* get the video device */
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ return videobuf_querybuf(&pcam_inst->vid_bufq, pb);
+}
+
+static int msm_camera_v4l2_qbuf(struct file *f, void *pctx,
+ struct v4l2_buffer *pb)
+{
+ int rc = 0;
+ /* get the camera device */
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ rc = videobuf_qbuf(&pcam_inst->vid_bufq, pb);
+ D("%s, videobuf_qbuf returns %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_dqbuf(struct file *f, void *pctx,
+ struct v4l2_buffer *pb)
+{
+ int rc = 0;
+ /* get the camera device */
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ rc = videobuf_dqbuf(&pcam_inst->vid_bufq, pb, f->f_flags & O_NONBLOCK);
+ D("%s, videobuf_dqbuf returns %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_streamon(struct file *f, void *pctx,
+ enum v4l2_buf_type i)
+{
+ int rc = 0;
+ struct videobuf_buffer *buf;
+ int cnt = 0;
+ /* get the camera device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ D("%s Calling videobuf_streamon", __func__);
+ /* if HW streaming on is successful, start buffer streaming */
+ rc = videobuf_streamon(&pcam_inst->vid_bufq);
+ D("%s, videobuf_streamon returns %d\n", __func__, rc);
+
+ mutex_lock(&pcam->vid_lock);
+ /* turn HW (VFE/sensor) streaming */
+ rc = msm_server_streamon(pcam, pcam_inst->my_index);
+ mutex_unlock(&pcam->vid_lock);
+ D("%s rc = %d\n", __func__, rc);
+ if (rc < 0) {
+ pr_err("%s: hw failed to start streaming\n", __func__);
+ return rc;
+ }
+
+ list_for_each_entry(buf, &pcam_inst->vid_bufq.stream, stream) {
+ D("%s index %d, state %d\n", __func__, cnt, buf->state);
+ cnt++;
+ }
+
+ return rc;
+}
+
+static int msm_camera_v4l2_streamoff(struct file *f, void *pctx,
+ enum v4l2_buf_type i)
+{
+ int rc = 0;
+ /* get the camera device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ /* first turn of HW (VFE/sensor) streaming so that buffers are
+ not in use when we free the buffers */
+ mutex_lock(&pcam->vid_lock);
+ rc = msm_server_streamoff(pcam, pcam_inst->my_index);
+ mutex_unlock(&pcam->vid_lock);
+ if (rc < 0)
+ pr_err("%s: hw failed to stop streaming\n", __func__);
+
+ /* stop buffer streaming */
+ rc = videobuf_streamoff(&pcam_inst->vid_bufq);
+ D("%s, videobuf_streamoff returns %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_enum_fmt_cap(struct file *f, void *pctx,
+ struct v4l2_fmtdesc *pfmtdesc)
+{
+ /* get the video device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ const struct msm_isp_color_fmt *isp_fmt;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ if (pfmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (pfmtdesc->index >= pcam->num_fmts)
+ return -EINVAL;
+
+ isp_fmt = &pcam->usr_fmts[pfmtdesc->index];
+
+ if (isp_fmt->name)
+ strlcpy(pfmtdesc->description, isp_fmt->name,
+ sizeof(pfmtdesc->description));
+
+ pfmtdesc->pixelformat = isp_fmt->fourcc;
+
+ D("%s: [%d] 0x%x, %s\n", __func__, pfmtdesc->index,
+ isp_fmt->fourcc, isp_fmt->name);
+ return 0;
+}
+
+static int msm_camera_v4l2_g_fmt_cap(struct file *f,
+ void *pctx, struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ /* get the video device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ rc = msm_server_get_fmt(pcam, pcam_inst->my_index, pfmt);
+
+ D("%s: current_fmt->fourcc: 0x%08x, rc = %d\n", __func__,
+ pfmt->fmt.pix.pixelformat, rc);
+ return rc;
+}
+
+/* This function will readjust the format parameters based in HW
+ capabilities. Called by s_fmt_cap
+*/
+static int msm_camera_v4l2_try_fmt_cap(struct file *f, void *pctx,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ /* get the video device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ rc = msm_server_try_fmt(pcam, pfmt);
+ if (rc)
+ pr_err("Format %x not found, rc = %d\n",
+ pfmt->fmt.pix.pixelformat, rc);
+
+ return rc;
+}
+
+/* This function will reconfig the v4l2 driver and HW device, it should be
+ called after the streaming is stopped.
+*/
+static int msm_camera_v4l2_s_fmt_cap(struct file *f, void *pctx,
+ struct v4l2_format *pfmt)
+{
+ int rc;
+ void __user *uptr;
+ /* get the video device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ D("%s, inst=0x%x,idx=%d,priv = 0x%p\n",
+ __func__, (u32)pcam_inst, pcam_inst->my_index,
+ (void *)pfmt->fmt.pix.priv);
+ WARN_ON(pctx != f->private_data);
+
+ uptr = (void __user *)pfmt->fmt.pix.priv;
+ pfmt->fmt.pix.priv = (__u32)kzalloc(MSM_V4L2_DIMENSION_SIZE,
+ GFP_KERNEL);
+
+ if (!pfmt->fmt.pix.priv) {
+ pr_err("%s could not allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+ D("%s Copying priv data:n", __func__);
+ if (copy_from_user((void *)pfmt->fmt.pix.priv, uptr,
+ MSM_V4L2_DIMENSION_SIZE)) {
+ pr_err("%s: copy_from_user failed.\n", __func__);
+ kfree((void *)pfmt->fmt.pix.priv);
+ return -EINVAL;
+ }
+ D("%s Done Copying priv data\n", __func__);
+
+ mutex_lock(&pcam->vid_lock);
+
+ rc = msm_server_set_fmt(pcam, pcam_inst->my_index, pfmt);
+ if (rc < 0) {
+ pr_err("%s: msm_server_set_fmt Error: %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ if (copy_to_user(uptr, (const void *)pfmt->fmt.pix.priv,
+ MSM_V4L2_DIMENSION_SIZE)) {
+ pr_err("%s: copy_to_user failed\n", __func__);
+ rc = -EINVAL;
+ }
+
+done:
+ kfree((void *)pfmt->fmt.pix.priv);
+ pfmt->fmt.pix.priv = (__u32)uptr;
+
+ mutex_unlock(&pcam->vid_lock);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_g_jpegcomp(struct file *f, void *pctx,
+ struct v4l2_jpegcompression *pcomp)
+{
+ int rc = -EINVAL;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_s_jpegcomp(struct file *f, void *pctx,
+ struct v4l2_jpegcompression *pcomp)
+{
+ int rc = -EINVAL;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ return rc;
+}
+
+
+static int msm_camera_v4l2_g_crop(struct file *f, void *pctx,
+ struct v4l2_crop *a)
+{
+ int rc = -EINVAL;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ return rc;
+}
+
+static int msm_camera_v4l2_s_crop(struct file *f, void *pctx,
+ struct v4l2_crop *a)
+{
+ int rc = -EINVAL;
+
+ D("%s\n", __func__);
+ WARN_ON(pctx != f->private_data);
+
+ return rc;
+}
+
+/* Stream type-dependent parameter ioctls */
+static int msm_camera_v4l2_g_parm(struct file *f, void *pctx,
+ struct v4l2_streamparm *a)
+{
+ int rc = -EINVAL;
+ return rc;
+}
+static int msm_vidbuf_get_path(u32 extendedmode)
+{
+ switch (extendedmode) {
+ case MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL:
+ return OUTPUT_TYPE_T;
+ case MSM_V4L2_EXT_CAPTURE_MODE_MAIN:
+ return OUTPUT_TYPE_S;
+ case MSM_V4L2_EXT_CAPTURE_MODE_VIDEO:
+ return OUTPUT_TYPE_V;
+ case MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT:
+ case MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW:
+ default:
+ return OUTPUT_TYPE_P;
+ }
+}
+
+static int msm_camera_v4l2_s_parm(struct file *f, void *pctx,
+ struct v4l2_streamparm *a)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+ pcam_inst->image_mode = a->parm.capture.extendedmode;
+ pcam_inst->pcam->dev_inst_map[pcam_inst->image_mode] = pcam_inst;
+ pcam_inst->path = msm_vidbuf_get_path(pcam_inst->image_mode);
+ D("%spath=%d,rc=%d\n", __func__,
+ pcam_inst->path, rc);
+ return rc;
+}
+
+static int msm_camera_v4l2_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+
+ D("%s\n", __func__);
+ D("fh = 0x%x\n", (u32)fh);
+
+ /* handle special case where user wants to subscribe to all
+ the events */
+ D("sub->type = 0x%x\n", sub->type);
+
+ if (sub->type == V4L2_EVENT_ALL) {
+ /*sub->type = MSM_ISP_EVENT_START;*/
+ sub->type = V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_CTRL;
+
+ D("sub->type start = 0x%x\n", sub->type);
+ do {
+ rc = v4l2_event_subscribe(fh, sub);
+ if (rc < 0) {
+ D("%s: failed for evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ /* unsubscribe all events here and return */
+ sub->type = V4L2_EVENT_ALL;
+ v4l2_event_unsubscribe(fh, sub);
+ return rc;
+ } else
+ D("%s: subscribed evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ sub->type++;
+ D("sub->type while = 0x%x\n", sub->type);
+ } while (sub->type !=
+ V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_MAX);
+ } else {
+ D("sub->type not V4L2_EVENT_ALL = 0x%x\n", sub->type);
+ rc = v4l2_event_subscribe(fh, sub);
+ if (rc < 0)
+ D("%s: failed for evtType = 0x%x, rc = %d\n",
+ __func__, sub->type, rc);
+ }
+
+ D("%s: rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+
+ D("%s\n", __func__);
+ D("fh = 0x%x\n", (u32)fh);
+
+ rc = v4l2_event_unsubscribe(fh, sub);
+
+ D("%s: rc = %d\n", __func__, rc);
+ return rc;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
+ .vidioc_querycap = msm_camera_v4l2_querycap,
+
+ .vidioc_s_crop = msm_camera_v4l2_s_crop,
+ .vidioc_g_crop = msm_camera_v4l2_g_crop,
+
+ .vidioc_queryctrl = msm_camera_v4l2_queryctrl,
+ .vidioc_g_ctrl = msm_camera_v4l2_g_ctrl,
+ .vidioc_s_ctrl = msm_camera_v4l2_s_ctrl,
+
+ .vidioc_reqbufs = msm_camera_v4l2_reqbufs,
+ .vidioc_querybuf = msm_camera_v4l2_querybuf,
+ .vidioc_qbuf = msm_camera_v4l2_qbuf,
+ .vidioc_dqbuf = msm_camera_v4l2_dqbuf,
+
+ .vidioc_streamon = msm_camera_v4l2_streamon,
+ .vidioc_streamoff = msm_camera_v4l2_streamoff,
+
+ /* format ioctls */
+ .vidioc_enum_fmt_vid_cap = msm_camera_v4l2_enum_fmt_cap,
+ .vidioc_try_fmt_vid_cap = msm_camera_v4l2_try_fmt_cap,
+ .vidioc_g_fmt_vid_cap = msm_camera_v4l2_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = msm_camera_v4l2_s_fmt_cap,
+
+ .vidioc_g_jpegcomp = msm_camera_v4l2_g_jpegcomp,
+ .vidioc_s_jpegcomp = msm_camera_v4l2_s_jpegcomp,
+
+ /* Stream type-dependent parameter ioctls */
+ .vidioc_g_parm = msm_camera_v4l2_g_parm,
+ .vidioc_s_parm = msm_camera_v4l2_s_parm,
+
+ /* event subscribe/unsubscribe */
+ .vidioc_subscribe_event = msm_camera_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = msm_camera_v4l2_unsubscribe_event,
+};
+
+/* open an active camera session to manage the streaming logic */
+static int msm_cam_server_open_session(struct msm_cam_server_dev *ps,
+ struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+
+ if (!ps || !pcam) {
+ pr_err("%s NULL pointer passed in!\n", __func__);
+ return rc;
+ }
+
+ /* book keeping this camera session*/
+ ps->pcam_active = pcam;
+ atomic_inc(&ps->number_pcam_active);
+
+ D("config pcam = 0x%p\n", ps->pcam_active);
+
+ /* initialization the media controller module*/
+ msm_mctl_init_module(pcam);
+
+ /*yyan: for single VFE msms (8660, 8960v1), just populate the session
+ with our VFE devices that registered*/
+ pcam->mctl.sensor_sdev = &(pcam->sensor_sdev);
+
+ pcam->mctl.isp_sdev = ps->isp_subdev[0];
+ pcam->mctl.ispif_fns = &ps->ispif_fns;
+
+
+ /*yyan: 8960 bring up - no VPE and flash; populate later*/
+ pcam->mctl.vpe_sdev = NULL;
+ pcam->mctl.flash_sdev = NULL;
+
+ return rc;
+
+}
+
+/* close an active camera session to server */
+static int msm_cam_server_close_session(struct msm_cam_server_dev *ps,
+ struct msm_cam_v4l2_device *pcam)
+{
+ int rc = 0;
+ D("%s\n", __func__);
+
+ if (!ps || !pcam) {
+ D("%s NULL pointer passed in!\n", __func__);
+ return rc;
+ }
+
+
+ atomic_dec(&ps->number_pcam_active);
+ ps->pcam_active = NULL;
+
+ return rc;
+}
+/* v4l2_file_operations */
+static int msm_open(struct file *f)
+{
+ int i;
+ int rc = -EINVAL;
+ /*struct msm_isp_ops *p_isp = 0;*/
+ /* get the video device */
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst;
+
+ D("%s\n", __func__);
+
+ if (!pcam) {
+ pr_err("%s NULL pointer passed in!\n", __func__);
+ return rc;
+ }
+ mutex_lock(&pcam->vid_lock);
+ for (i = 0; i < MSM_DEV_INST_MAX; i++) {
+ if (pcam->dev_inst[i] == NULL) {
+ mutex_unlock(&pcam->vid_lock);
+ break;
+ }
+ }
+ /* if no instance is available, return error */
+ if (i == MSM_DEV_INST_MAX) {
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+ }
+ pcam_inst = kzalloc(sizeof(struct msm_cam_v4l2_dev_inst), GFP_KERNEL);
+ if (!pcam_inst) {
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+ }
+ pcam_inst->sensor_pxlcode = pcam->usr_fmts[0].pxlcode;
+ pcam_inst->my_index = i;
+ pcam_inst->pcam = pcam;
+ pcam->dev_inst[i] = pcam_inst;
+
+ D("%s for %s\n", __func__, pcam->pdev->name);
+ pcam->use_count++;
+ if (pcam->use_count == 1) {
+
+ rc = msm_cam_server_open_session(&g_server_dev, pcam);
+ if (rc < 0) {
+ pr_err("%s: cam_server_open_session failed %d\n",
+ __func__, rc);
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+ }
+
+ /* Should be set to sensor ops if any but right now its OK!! */
+ if (!pcam->mctl.mctl_open) {
+ D("%s: media contoller is not inited\n",
+ __func__);
+ mutex_unlock(&pcam->vid_lock);
+ return -ENODEV;
+ }
+
+ /* Now we really have to activate the camera */
+ D("%s: call mctl_open\n", __func__);
+ rc = pcam->mctl.mctl_open(&(pcam->mctl), MSM_APPS_ID_V4L2);
+
+ if (rc < 0) {
+ mutex_unlock(&pcam->vid_lock);
+ pr_err("%s: HW open failed rc = 0x%x\n", __func__, rc);
+ return rc;
+ }
+ pcam->mctl.sync.pcam_sync = pcam;
+
+ /* Register isp subdev */
+ rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
+ &pcam->mctl.isp_sdev->sd);
+ if (rc < 0) {
+ mutex_unlock(&pcam->vid_lock);
+ pr_err("%s: v4l2_device_register_subdev failed rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ /* Initialize the video queue */
+ rc = pcam->mctl.mctl_vidbuf_init(pcam_inst, &pcam_inst->vid_bufq);
+ if (rc < 0) {
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+ }
+
+
+ f->private_data = pcam_inst;
+
+ D("f->private_data = 0x%x, pcam = 0x%x\n",
+ (u32)f->private_data, (u32)pcam_inst);
+
+
+ if (pcam->use_count == 1) {
+ rc = msm_send_open_server();
+ if (rc < 0) {
+ mutex_unlock(&pcam->vid_lock);
+ pr_err("%s failed\n", __func__);
+ return rc;
+ }
+ }
+ mutex_unlock(&pcam->vid_lock);
+ /* rc = msm_cam_server_open_session(g_server_dev, pcam);*/
+ return rc;
+}
+
+static int msm_mmap(struct file *f, struct vm_area_struct *vma)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ rc = videobuf_mmap_mapper(&pcam_inst->vid_bufq, vma);
+
+ D("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
+ rc);
+
+ return rc;
+}
+
+static int msm_close(struct file *f)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ if (!pcam) {
+ pr_err("%s NULL pointer of camera device!\n", __func__);
+ return -EINVAL;
+ }
+
+
+ mutex_lock(&pcam->vid_lock);
+ pcam->use_count--;
+ pcam->dev_inst_map[pcam_inst->image_mode] = NULL;
+ videobuf_stop(&pcam_inst->vid_bufq);
+ /* free the queue: function name is ambiguous it frees all
+ types of buffers (mmap or userptr - it doesn't matter) */
+ rc = videobuf_mmap_free(&pcam_inst->vid_bufq);
+ if (rc < 0)
+ pr_err("%s: unable to free buffers\n", __func__);
+ pcam->dev_inst[pcam_inst->my_index] = NULL;
+ kfree(pcam_inst);
+ f->private_data = NULL;
+
+ if (pcam->use_count == 0) {
+ if (pcam->mctl.mctl_release) {
+ rc = pcam->mctl.mctl_release(&(pcam->mctl));
+ if (rc < 0)
+ pr_err("mctl_release fails %d\n", rc);
+ }
+
+ v4l2_device_unregister_subdev(&pcam->mctl.isp_sdev->sd);
+
+ rc = msm_cam_server_close_session(&g_server_dev, pcam);
+ if (rc < 0)
+ pr_err("msm_cam_server_close_session fails %d\n", rc);
+
+ rc = msm_send_close_server();
+ if (rc < 0)
+ pr_err("msm_send_close_server failed %d\n", rc);
+
+ dma_release_declared_memory(&pcam->pdev->dev);
+ }
+ mutex_unlock(&pcam->vid_lock);
+ return rc;
+}
+
+static unsigned int msm_poll(struct file *f, struct poll_table_struct *wait)
+{
+ int rc = 0;
+ struct msm_cam_v4l2_device *pcam = video_drvdata(f);
+ struct msm_cam_v4l2_dev_inst *pcam_inst = f->private_data;
+
+ D("%s\n", __func__);
+ if (!pcam) {
+ pr_err("%s NULL pointer of camera device!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!pcam_inst->vid_bufq.streaming) {
+ D("%s vid_bufq.streaming is off, inst=0x%x\n",
+ __func__, (u32)pcam_inst);
+ return -EINVAL;
+ }
+
+ rc |= videobuf_poll_stream(f, &pcam_inst->vid_bufq, wait);
+ D("%s returns, rc = 0x%x\n", __func__, rc);
+
+ return rc;
+}
+
+static unsigned int msm_poll_server(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+
+ D("%s\n", __func__);
+ poll_wait(fp,
+ &g_server_dev.server_command_queue.eventHandle.events->wait,
+ wait);
+ if (v4l2_event_pending(&g_server_dev.server_command_queue.eventHandle))
+ rc |= POLLPRI;
+
+ return rc;
+}
+static long msm_ioctl_server(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = -EINVAL;
+ struct v4l2_event ev;
+ struct msm_camera_info temp_cam_info;
+ struct msm_cam_config_dev_info temp_config_info;
+ struct v4l2_event_subscription temp_sub;
+ int i;
+
+ D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_GET_CAMERA_INFO:
+ if (copy_from_user(&temp_cam_info, (void __user *)arg,
+ sizeof(struct msm_camera_info))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ for (i = 0; i < g_server_dev.camera_info.num_cameras; i++) {
+ if (copy_to_user((void __user *)
+ temp_cam_info.video_dev_name[i],
+ g_server_dev.camera_info.video_dev_name[i],
+ strlen(g_server_dev.camera_info.video_dev_name[i]))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ temp_cam_info.has_3d_support[i] =
+ g_server_dev.camera_info.has_3d_support[i];
+ temp_cam_info.is_internal_cam[i] =
+ g_server_dev.camera_info.is_internal_cam[i];
+ temp_cam_info.s_mount_angle[i] =
+ g_server_dev.camera_info.s_mount_angle[i];
+ temp_cam_info.sensor_type[i] =
+ g_server_dev.camera_info.sensor_type[i];
+
+ }
+ temp_cam_info.num_cameras =
+ g_server_dev.camera_info.num_cameras;
+ if (copy_to_user((void __user *)arg,
+ &temp_cam_info,
+ sizeof(struct msm_camera_info))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = 0;
+ break;
+
+ case MSM_CAM_IOCTL_GET_CONFIG_INFO:
+ if (copy_from_user(&temp_config_info, (void __user *)arg,
+ sizeof(struct msm_cam_config_dev_info))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ for (i = 0;
+ i < g_server_dev.config_info.num_config_nodes; i++) {
+ if (copy_to_user(
+ (void __user *)temp_config_info.config_dev_name[i],
+ g_server_dev.config_info.config_dev_name[i],
+ strlen(g_server_dev.config_info.config_dev_name[i]))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ }
+ temp_config_info.num_config_nodes =
+ g_server_dev.config_info.num_config_nodes;
+ if (copy_to_user((void __user *)arg,
+ &temp_config_info,
+ sizeof(struct msm_cam_config_dev_info))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = 0;
+ break;
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ if (copy_from_user(&temp_sub, (void __user *)arg,
+ sizeof(struct v4l2_event_subscription))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = msm_camera_v4l2_subscribe_event
+ (&g_server_dev.server_command_queue.eventHandle,
+ &temp_sub);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ case VIDIOC_DQEVENT: {
+ void __user *u_ctrl_value = NULL;
+ struct msm_isp_stats_event_ctrl *u_isp_event;
+ struct msm_isp_stats_event_ctrl *k_isp_event;
+
+ /* Make a copy of control value and event data pointer */
+ D("%s: VIDIOC_DQEVENT\n", __func__);
+ if (copy_from_user(&ev, (void __user *)arg,
+ sizeof(struct v4l2_event)))
+ break;
+ u_isp_event = (struct msm_isp_stats_event_ctrl *)ev.u.data;
+ u_ctrl_value = u_isp_event->isp_data.ctrl.value;
+
+ rc = v4l2_event_dequeue(
+ &g_server_dev.server_command_queue.eventHandle,
+ &ev, fp->f_flags & O_NONBLOCK);
+ if (rc < 0) {
+ pr_err("no pending events?");
+ break;
+ }
+
+ k_isp_event = (struct msm_isp_stats_event_ctrl *)ev.u.data;
+ if (ev.type == V4L2_EVENT_PRIVATE_START+MSM_CAM_RESP_V4L2 &&
+ k_isp_event->isp_data.ctrl.length > 0) {
+ void *k_ctrl_value = k_isp_event->isp_data.ctrl.value;
+ if (copy_to_user(u_ctrl_value, k_ctrl_value,
+ u_isp_event->isp_data.ctrl.length)) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+ k_isp_event->isp_data.ctrl.value = u_ctrl_value;
+
+ if (copy_to_user((void __user *)arg, &ev,
+ sizeof(struct v4l2_event))) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ break;
+
+ case MSM_CAM_IOCTL_CTRL_CMD_DONE:
+ D("%s: MSM_CAM_IOCTL_CTRL_CMD_DONE\n", __func__);
+ rc = msm_ctrl_cmd_done((void __user *)arg);
+ break;
+
+ default:
+ break;
+ }
+ return rc;
+}
+
+static int msm_open_server(struct inode *inode, struct file *fp)
+{
+ int rc;
+ D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+
+ rc = nonseekable_open(inode, fp);
+ if (rc < 0) {
+ pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+ return rc;
+ }
+ g_server_dev.use_count++;
+ if (g_server_dev.use_count == 1)
+ msm_queue_init(&g_server_dev.ctrl_q, "control");
+
+ return rc;
+}
+
+static unsigned int msm_poll_config(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+ struct msm_cam_config_dev *config = fp->private_data;
+ if (config == NULL)
+ return -EINVAL;
+
+ D("%s\n", __func__);
+
+ poll_wait(fp,
+ &config->config_stat_event_queue.eventHandle.events->wait, wait);
+ if (v4l2_event_pending(&config->config_stat_event_queue.eventHandle))
+ rc |= POLLPRI;
+ return rc;
+}
+
+static long msm_ioctl_config(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+
+ int rc = 0;
+ struct v4l2_event ev;
+ struct msm_cam_config_dev *config_cam = fp->private_data;
+ struct v4l2_event_subscription temp_sub;
+
+ D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ /* memory management shall be handeld here*/
+ case MSM_CAM_IOCTL_REGISTER_PMEM:
+ return msm_register_pmem(
+ &config_cam->p_mctl->sync.pmem_stats,
+ (void __user *)arg);
+ break;
+
+ case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+ return msm_pmem_table_del(
+ &config_cam->p_mctl->sync.pmem_stats,
+ (void __user *)arg);
+ break;
+ case VIDIOC_SUBSCRIBE_EVENT:
+ if (copy_from_user(&temp_sub,
+ (void __user *)arg,
+ sizeof(struct v4l2_event_subscription))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = msm_camera_v4l2_subscribe_event
+ (&config_cam->config_stat_event_queue.eventHandle,
+ &temp_sub);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ if (copy_from_user(&temp_sub, (void __user *)arg,
+ sizeof(struct v4l2_event_subscription))) {
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = msm_camera_v4l2_unsubscribe_event
+ (&config_cam->config_stat_event_queue.eventHandle,
+ &temp_sub);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ case VIDIOC_DQEVENT: {
+ void __user *u_msg_value = NULL;
+ struct msm_isp_stats_event_ctrl *u_isp_event;
+ struct msm_isp_stats_event_ctrl *k_isp_event;
+
+ /* Make a copy of control value and event data pointer */
+ D("%s: VIDIOC_DQEVENT\n", __func__);
+ if (copy_from_user(&ev, (void __user *)arg,
+ sizeof(struct v4l2_event)))
+ break;
+ u_isp_event = (struct msm_isp_stats_event_ctrl *)ev.u.data;
+ u_msg_value = u_isp_event->isp_data.isp_msg.data;
+
+ rc = v4l2_event_dequeue(
+ &config_cam->config_stat_event_queue.eventHandle,
+ &ev, fp->f_flags & O_NONBLOCK);
+ if (rc < 0) {
+ pr_err("no pending events?");
+ break;
+ }
+
+ k_isp_event = (struct msm_isp_stats_event_ctrl *)ev.u.data;
+ if (ev.type ==
+ V4L2_EVENT_PRIVATE_START+MSM_CAM_RESP_STAT_EVT_MSG &&
+ k_isp_event->isp_data.isp_msg.len > 0) {
+ void *k_msg_value = k_isp_event->isp_data.isp_msg.data;
+ if (copy_to_user(u_msg_value, k_msg_value,
+ k_isp_event->isp_data.isp_msg.len)) {
+ rc = -EINVAL;
+ break;
+ }
+ kfree(k_msg_value);
+ }
+ k_isp_event->isp_data.isp_msg.data = u_msg_value;
+
+ if (copy_to_user((void __user *)arg, &ev,
+ sizeof(struct v4l2_event))) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ break;
+
+ default:{
+ /* For the rest of config command, forward to media controller*/
+ struct msm_cam_media_controller *p_mctl = config_cam->p_mctl;
+ if (p_mctl && p_mctl->mctl_cmd) {
+ rc = config_cam->p_mctl->mctl_cmd(p_mctl, cmd, arg);
+ } else {
+ rc = -EINVAL;
+ pr_err("%s: media controller is null\n", __func__);
+ }
+
+ break;
+ } /* end of default*/
+ } /* end of switch*/
+ return rc;
+}
+
+static int msm_open_config(struct inode *inode, struct file *fp)
+{
+ int rc;
+
+ struct msm_cam_config_dev *config_cam =
+ container_of(inode->i_cdev, struct msm_cam_config_dev, config_cdev);
+
+ D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
+
+ rc = nonseekable_open(inode, fp);
+ if (rc < 0) {
+ pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+ return rc;
+ }
+ config_cam->use_count++;
+
+ /*config_cam->isp_subdev = g_server_dev.pcam_active->mctl.isp_sdev;*/
+ /* assume there is only one active camera possible*/
+ config_cam->p_mctl = &g_server_dev.pcam_active->mctl;
+
+ INIT_HLIST_HEAD(&config_cam->p_mctl->sync.pmem_stats);
+ spin_lock_init(&config_cam->p_mctl->sync.pmem_stats_spinlock);
+
+ config_cam->p_mctl->config_device = config_cam;
+ fp->private_data = config_cam;
+ return rc;
+}
+
+static struct v4l2_file_operations g_msm_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .poll = msm_poll,
+ .mmap = msm_mmap,
+ .release = msm_close,
+ .ioctl = video_ioctl2,
+};
+
+/* Init a config node for ISP control,
+ which will create a config device (/dev/config0/ and plug in
+ ISP's operation "v4l2_ioctl_ops*"
+*/
+static const struct file_operations msm_fops_server = {
+ .owner = THIS_MODULE,
+ .open = msm_open_server,
+ .poll = msm_poll_server,
+ .unlocked_ioctl = msm_ioctl_server,
+};
+
+static const struct file_operations msm_fops_config = {
+ .owner = THIS_MODULE,
+ .open = msm_open_config,
+ .poll = msm_poll_config,
+ .unlocked_ioctl = msm_ioctl_config,
+};
+
+static int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
+ struct video_device *pvdev)
+{
+ int rc = 0;
+ /* v4l2_fh support */
+ spin_lock_init(&pvdev->fh_lock);
+ INIT_LIST_HEAD(&pvdev->fh_list);
+
+ rc = v4l2_fh_init(eventHandle, pvdev);
+ if (rc < 0)
+ return rc;
+
+ rc = v4l2_event_init(eventHandle);
+ if (rc < 0)
+ return rc;
+
+ /* queue of max size 30 */
+ rc = v4l2_event_alloc(eventHandle, 30);
+ if (rc < 0)
+ return rc;
+
+ v4l2_fh_add(eventHandle);
+ return rc;
+
+}
+
+static int msm_setup_config_dev(int node, char *device_name)
+{
+ int rc = -ENODEV;
+ struct device *device_config;
+ int dev_num = node;
+ dev_t devno;
+ struct msm_cam_config_dev *config_cam;
+
+ config_cam = kzalloc(sizeof(*config_cam), GFP_KERNEL);
+ if (!config_cam) {
+ pr_err("%s: could not allocate memory for msm_cam_config_device\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ D("%s\n", __func__);
+
+ devno = MKDEV(MAJOR(msm_devno), dev_num+1);
+ device_config = device_create(msm_class, NULL, devno, NULL,
+ "%s%d", device_name, dev_num);
+
+ if (IS_ERR(device_config)) {
+ rc = PTR_ERR(device_config);
+ pr_err("%s: error creating device: %d\n", __func__, rc);
+ return rc;
+ }
+
+ cdev_init(&config_cam->config_cdev,
+ &msm_fops_config);
+ config_cam->config_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&config_cam->config_cdev, devno, 1);
+ if (rc < 0) {
+ pr_err("%s: error adding cdev: %d\n", __func__, rc);
+ device_destroy(msm_class, devno);
+ return rc;
+ }
+ g_server_dev.config_info.config_dev_name[dev_num]
+ = dev_name(device_config);
+ D("%s Connected config device %s\n", __func__,
+ g_server_dev.config_info.config_dev_name[dev_num]);
+
+ config_cam->config_stat_event_queue.pvdev = video_device_alloc();
+ if (config_cam->config_stat_event_queue.pvdev == NULL) {
+ pr_err("%s: video_device_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ rc = msm_setup_v4l2_event_queue(
+ &config_cam->config_stat_event_queue.eventHandle,
+ config_cam->config_stat_event_queue.pvdev);
+ if (rc < 0)
+ pr_err("%s failed to initialize event queue\n", __func__);
+
+ return rc;
+}
+
+static int msm_setup_server_dev(int node, char *device_name)
+{
+ int rc = -ENODEV;
+ struct device *device_server;
+ int dev_num = node;
+ dev_t devno;
+
+ D("%s\n", __func__);
+
+ devno = MKDEV(MAJOR(msm_devno), dev_num);
+ device_server = device_create(msm_class, NULL,
+ devno, NULL, "%s", device_name);
+
+ if (IS_ERR(device_server)) {
+ rc = PTR_ERR(device_server);
+ pr_err("%s: error creating device: %d\n", __func__, rc);
+ return rc;
+ }
+
+ cdev_init(&g_server_dev.server_cdev, &msm_fops_server);
+ g_server_dev.server_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&g_server_dev.server_cdev, devno, 1);
+ if (rc < 0) {
+ pr_err("%s: error adding cdev: %d\n", __func__, rc);
+ device_destroy(msm_class, devno);
+ return rc;
+ }
+
+ g_server_dev.pcam_active = NULL;
+ g_server_dev.camera_info.num_cameras = 0;
+ atomic_set(&g_server_dev.number_pcam_active, 0);
+ g_server_dev.ispif_fns.ispif_config = NULL;
+
+ /*initialize fake video device and event queue*/
+
+ g_server_dev.server_command_queue.pvdev = video_device_alloc();
+ if (g_server_dev.server_command_queue.pvdev == NULL) {
+ pr_err("%s: video_device_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ rc = msm_setup_v4l2_event_queue(
+ &g_server_dev.server_command_queue.eventHandle,
+ g_server_dev.server_command_queue.pvdev);
+ if (rc < 0)
+ pr_err("%s failed to initialize event queue\n", __func__);
+
+ return rc;
+}
+
+static int msm_cam_dev_init(struct msm_cam_v4l2_device *pcam)
+{
+ int rc = -ENOMEM;
+ struct video_device *pvdev = NULL;
+ D("%s\n", __func__);
+
+ /* first register the v4l2 device */
+ pcam->v4l2_dev.dev = &pcam->pdev->dev;
+ rc = v4l2_device_register(pcam->v4l2_dev.dev, &pcam->v4l2_dev);
+ if (rc < 0)
+ return -EINVAL;
+ else
+ pcam->v4l2_dev.notify = msm_cam_v4l2_subdev_notify;
+
+
+ /* now setup video device */
+ pvdev = video_device_alloc();
+ if (pvdev == NULL) {
+ pr_err("%s: video_device_alloc failed\n", __func__);
+ return rc;
+ }
+
+ /* init video device's driver interface */
+ D("sensor name = %s, sizeof(pvdev->name)=%d\n",
+ pcam->pdev->name, sizeof(pvdev->name));
+
+ /* device info - strlcpy is safer than strncpy but
+ only if architecture supports*/
+ strlcpy(pvdev->name, pcam->pdev->name, sizeof(pvdev->name));
+
+ pvdev->release = video_device_release;
+ pvdev->fops = &g_msm_fops;
+ pvdev->ioctl_ops = &g_msm_ioctl_ops;
+ pvdev->minor = -1;
+ pvdev->vfl_type = 1;
+
+ /* register v4l2 video device to kernel as /dev/videoXX */
+ D("video_register_device\n");
+ rc = video_register_device(pvdev,
+ VFL_TYPE_GRABBER,
+ msm_camera_v4l2_nr);
+ if (rc) {
+ pr_err("%s: video_register_device failed\n", __func__);
+ goto reg_fail;
+ }
+ D("%s: video device registered as /dev/video%d\n",
+ __func__, pvdev->num);
+
+ /* connect pcam and video dev to each other */
+ pcam->pvdev = pvdev;
+ video_set_drvdata(pcam->pvdev, pcam);
+
+ /* If isp HW registeration is successful, then create event queue to
+ receievent event froms HW
+ */
+ /* yyan: no global - each sensor will create a new vidoe node! */
+ /* g_pmsm_camera_v4l2_dev = pmsm_camera_v4l2_dev; */
+ /* g_pmsm_camera_v4l2_dev->pvdev = pvdev; */
+
+ return rc ;
+
+reg_fail:
+ video_device_release(pvdev);
+ v4l2_device_unregister(&pcam->v4l2_dev);
+ pcam->v4l2_dev.dev = NULL;
+ return rc;
+}
+
+static int msm_sync_destroy(struct msm_sync *sync)
+{
+ if (sync)
+ wake_lock_destroy(&sync->wake_lock);
+ return 0;
+}
+static int msm_sync_init(struct msm_sync *sync,
+ struct platform_device *pdev, struct msm_sensor_ctrl *sctrl)
+{
+ int rc = 0;
+
+ sync->sdata = pdev->dev.platform_data;
+
+ wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera");
+
+ sync->pdev = pdev;
+ sync->sctrl = *sctrl;
+ sync->opencnt = 0;
+ mutex_init(&sync->lock);
+ D("%s: initialized %s\n", __func__, sync->sdata->sensor_name);
+ return rc;
+}
+
+int msm_ispif_register(struct msm_ispif_fns *ispif)
+{
+ int rc = -EINVAL;
+ if (ispif != NULL) {
+ /*save ispif into server dev*/
+ g_server_dev.ispif_fns.ispif_config = ispif->ispif_config;
+ g_server_dev.ispif_fns.ispif_start_intf_transfer
+ = ispif->ispif_start_intf_transfer;
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ispif_register);
+
+/* register a msm sensor into the msm device, which will probe the
+ sensor HW. if the HW exist then create a video device (/dev/videoX/)
+ to represent this sensor */
+int msm_sensor_register(struct platform_device *pdev,
+ int (*sensor_probe)(const struct msm_camera_sensor_info *,
+ struct v4l2_subdev *, struct msm_sensor_ctrl *))
+{
+
+ int rc = -EINVAL;
+ struct msm_camera_sensor_info *sdata = pdev->dev.platform_data;
+ struct msm_cam_v4l2_device *pcam;
+ struct v4l2_subdev *sdev;
+ struct msm_sensor_ctrl sctrl;
+
+ D("%s for %s\n", __func__, pdev->name);
+
+ /* allocate the memory for the camera device first */
+ pcam = kzalloc(sizeof(*pcam), GFP_KERNEL);
+ if (!pcam) {
+ pr_err("%s: could not allocate memory for msm_cam_v4l2_device\n",
+ __func__);
+ return -ENOMEM;
+ } else{
+ sdev = &(pcam->sensor_sdev);
+ snprintf(sdev->name, sizeof(sdev->name), "%s", pdev->name);
+ }
+
+ /* come sensor probe logic */
+ rc = msm_camio_probe_on(pdev);
+ if (rc < 0) {
+ kzfree(pcam);
+ return rc;
+ }
+
+ rc = sensor_probe(sdata, sdev, &sctrl);
+
+ msm_camio_probe_off(pdev);
+ if (rc < 0) {
+ pr_err("%s: failed to detect %s\n",
+ __func__,
+ sdata->sensor_name);
+ kzfree(pcam);
+ return rc;
+ }
+
+ /* if the probe is successfull, allocat the camera driver object
+ for this sensor */
+
+ pcam->sync = kzalloc(sizeof(struct msm_sync), GFP_ATOMIC);
+ if (!pcam->sync) {
+ pr_err("%s: could not allocate memory for msm_sync object\n",
+ __func__);
+ kzfree(pcam);
+ return -ENOMEM;
+ }
+
+ /* setup a manager object*/
+ rc = msm_sync_init(pcam->sync, pdev, &sctrl);
+ if (rc < 0)
+ goto failure;
+ D("%s: pcam =0x%p\n", __func__, pcam);
+ D("%s: pcam->sync =0x%p\n", __func__, pcam->sync);
+
+ (pcam->sync)->pcam_sync = pcam;
+ /* bind the driver device to the sensor device */
+ pcam->pdev = pdev;
+ pcam->sctrl = sctrl;
+
+ /* init the user count and lock*/
+ pcam->use_count = 0;
+ mutex_init(&pcam->vid_lock);
+
+ /* Initialize the formats supported */
+ rc = msm_mctl_init_user_formats(pcam);
+ if (rc < 0)
+ goto failure;
+
+ /* now initialize the camera device object */
+ rc = msm_cam_dev_init(pcam);
+ if (rc < 0)
+ goto failure;
+
+ g_server_dev.camera_info.video_dev_name
+ [g_server_dev.camera_info.num_cameras]
+ = video_device_node_name(pcam->pvdev);
+ D("%s Connected video device %s\n", __func__,
+ g_server_dev.camera_info.video_dev_name
+ [g_server_dev.camera_info.num_cameras]);
+ g_server_dev.camera_info.num_cameras++;
+
+ D("%s done, rc = %d\n", __func__, rc);
+ D("%s number of sensors connected is %d\n", __func__,
+ g_server_dev.camera_info.num_cameras);
+/*
+ if (g_server_dev.camera_info.num_cameras == 1) {
+ rc = add_axi_qos();
+ if (rc < 0)
+ goto failure;
+ }
+*/
+ /* register the subdevice, must be done for callbacks */
+ rc = v4l2_device_register_subdev(&pcam->v4l2_dev, sdev);
+ pcam->vnode_id = vnode_count++;
+ return rc;
+
+failure:
+ /* mutex_destroy not needed at this moment as the associated
+ implemenation of mutex_init is not consuming resources */
+ msm_sync_destroy(pcam->sync);
+ pcam->pdev = NULL;
+ kzfree(pcam->sync);
+ kzfree(pcam);
+ return rc;
+}
+EXPORT_SYMBOL(msm_sensor_register);
+
+static int __init msm_camera_init(void)
+{
+ int rc = 0, i;
+ /*for now just create a config 0 node
+ put logic here later to know how many configs to create*/
+ g_server_dev.config_info.num_config_nodes = 1;
+
+ rc = msm_isp_init_module(g_server_dev.config_info.num_config_nodes);
+ if (rc < 0) {
+ pr_err("Failed to initialize isp\n");
+ return rc;
+ }
+
+ if (!msm_class) {
+ rc = alloc_chrdev_region(&msm_devno, 0,
+ g_server_dev.config_info.num_config_nodes+1, "msm_camera");
+ if (rc < 0) {
+ pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ msm_class = class_create(THIS_MODULE, "msm_camera");
+ if (IS_ERR(msm_class)) {
+ rc = PTR_ERR(msm_class);
+ pr_err("%s: create device class failed: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ D("creating server and config nodes\n");
+ rc = msm_setup_server_dev(0, "video_msm");
+ if (rc < 0) {
+ pr_err("%s: failed to create server dev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ for (i = 0; i < g_server_dev.config_info.num_config_nodes; i++) {
+ rc = msm_setup_config_dev(i, "config");
+ if (rc < 0) {
+ pr_err("%s:failed to create config dev: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ msm_isp_register(&g_server_dev);
+ return rc;
+}
+
+static void __exit msm_camera_exit(void)
+{
+ msm_isp_unregister(&g_server_dev);
+}
+
+module_init(msm_camera_init);
+module_exit(msm_camera_exit);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
new file mode 100644
index 0000000..0049cc1
--- /dev/null
+++ b/drivers/media/video/msm/msm.h
@@ -0,0 +1,372 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef _MSM_H
+#define _MSM_H
+
+#ifdef __KERNEL__
+
+/* Header files */
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos_params.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-msm-mem.h>
+#include <mach/camera.h>
+
+#define MSM_V4L2_DIMENSION_SIZE 96
+
+#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)
+
+/* msm queue management APIs*/
+
+#define msm_dequeue(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+})
+
+#define msm_queue_drain(queue, member) do { \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ while (!list_empty(&__q->list)) { \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ free_qcmd(qcmd); \
+ }; \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while (0)
+
+static inline void free_qcmd(struct msm_queue_cmd *qcmd)
+{
+ if (!qcmd || !atomic_read(&qcmd->on_heap))
+ return;
+ if (!atomic_sub_return(1, &qcmd->on_heap))
+ kfree(qcmd);
+}
+
+/* message id for v4l2_subdev_notify*/
+enum msm_camera_v4l2_subdev_notify {
+ NOTIFY_CID_CHANGE, /* arg = msm_camera_csid_params */
+ NOTIFY_VFE_MSG_EVT, /* arg = msm_vfe_resp */
+ NOTIFY_INVALID
+};
+
+enum isp_vfe_cmd_id {
+ /*
+ *Important! Command_ID are arranged in order.
+ *Don't change!*/
+ ISP_VFE_CMD_ID_STREAM_ON,
+ ISP_VFE_CMD_ID_STREAM_OFF,
+ ISP_VFE_CMD_ID_FRAME_BUF_RELEASE
+};
+
+struct msm_cam_v4l2_device;
+struct msm_cam_v4l2_dev_inst;
+
+/* buffer for one video frame */
+struct msm_frame_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vidbuf;
+ enum v4l2_mbus_pixelcode pxlcode;
+ int inuse;
+ int active;
+};
+
+struct msm_isp_color_fmt {
+ char *name;
+ int depth;
+ int bitsperpxl;
+ u32 fourcc;
+ enum v4l2_mbus_pixelcode pxlcode;
+ enum v4l2_colorspace colorspace;
+};
+
+enum ispif_op_id {
+ /*
+ *Important! Command_ID are arranged in order.
+ *Don't change!*/
+ ISPIF_ENABLE,
+ ISPIF_DISABLE,
+ ISPIF_RESET,
+ ISPIF_CONFIG
+};
+
+struct msm_ispif_ops {
+
+ int (*ispif_op)(struct msm_ispif_ops *p_ispif,
+ enum ispif_op_id ispif_op_id_used, unsigned long arg);
+};
+
+struct msm_ispif_fns {
+ int (*ispif_config)(struct msm_ispif_params *ispif_params,
+ uint8_t num_of_intf);
+ int (*ispif_start_intf_transfer)
+ (struct msm_ispif_params *ispif_params);
+};
+
+extern int msm_ispif_init_module(struct msm_ispif_ops *p_ispif);
+
+/*"Media Controller" represents a camera steaming session, which consists
+ of a "sensor" device and an "isp" device (such as VFE, if needed),
+ connected via an "IO" device, (such as IPIF on 8960, or none on 8660)
+ plus other extra sub devices such as VPE and flash
+*/
+
+struct msm_cam_media_controller {
+
+ int (*mctl_open)(struct msm_cam_media_controller *p_mctl,
+ const char *const apps_id);
+ int (*mctl_cb)(void);
+ int (*mctl_notify)(struct msm_cam_media_controller *p_mctl,
+ unsigned int notification, void *arg);
+ int (*mctl_cmd)(struct msm_cam_media_controller *p_mctl,
+ unsigned int cmd, unsigned long arg);
+ int (*mctl_release)(struct msm_cam_media_controller *p_mctl);
+ int (*mctl_vidbuf_init)(struct msm_cam_v4l2_dev_inst *pcam,
+ struct videobuf_queue *);
+ int (*mctl_ufmt_init)(struct msm_cam_media_controller *p_mctl);
+
+ struct v4l2_device v4l2_dev;
+ struct v4l2_fh eventHandle; /* event queue to export events */
+ /* most-frequently accessed manager object*/
+ struct msm_sync sync;
+
+
+ /* the following reflect the HW topology information*/
+ /*mandatory*/
+ struct v4l2_subdev *sensor_sdev; /* sensor sub device */
+ struct v4l2_subdev mctl_sdev; /* media control sub device */
+ struct platform_device *plat_dev;
+ /*optional*/
+ struct msm_isp_ops *isp_sdev; /* isp sub device : camif/VFE */
+ struct v4l2_subdev *vpe_sdev; /* vpe sub device : VPE */
+ struct v4l2_subdev *flash_sdev; /* vpe sub device : VPE */
+ struct msm_cam_config_dev *config_device;
+ struct msm_ispif_fns *ispif_fns;
+
+ struct pm_qos_request_list pm_qos_req_list;
+};
+
+/* abstract camera device represents a VFE and connected sensor */
+struct msm_isp_ops {
+ char *config_dev_name;
+
+ /*int (*isp_init)(struct msm_cam_v4l2_device *pcam);*/
+ int (*isp_open)(struct v4l2_subdev *sd, struct msm_sync *sync);
+ int (*isp_config)(struct msm_cam_media_controller *pmctl,
+ unsigned int cmd, unsigned long arg);
+ int (*isp_enqueue)(struct msm_cam_media_controller *pcam,
+ struct msm_vfe_resp *data,
+ enum msm_queue qtype);
+ int (*isp_notify)(struct v4l2_subdev *sd, void *arg);
+
+ void (*isp_release)(struct msm_sync *psync);
+
+ /* vfe subdevice */
+ struct v4l2_subdev sd;
+};
+
+struct msm_isp_buf_info {
+ int type;
+ unsigned long buffer;
+ int fd;
+};
+#define MSM_DEV_INST_MAX 16
+struct msm_cam_v4l2_dev_inst {
+ struct videobuf_queue vid_bufq;
+ spinlock_t vb_irqlock;
+ struct v4l2_format vid_fmt;
+ /* senssor pixel code*/
+ enum v4l2_mbus_pixelcode sensor_pxlcode;
+ struct msm_cam_v4l2_device *pcam;
+ int my_index;
+ int image_mode;
+ int path;
+ int buf_count;
+};
+#define MSM_MAX_IMG_MODE 5
+/* abstract camera device for each sensor successfully probed*/
+struct msm_cam_v4l2_device {
+ /* standard device interfaces */
+ /* parent of video device to trace back */
+ struct device dev;
+ /* sensor's platform device*/
+ struct platform_device *pdev;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ /* will be registered as /dev/video*/
+ struct video_device *pvdev;
+ int use_count;
+ /* will be used to init/release HW */
+ struct msm_cam_media_controller mctl;
+ /* sensor subdevice */
+ struct v4l2_subdev sensor_sdev;
+ struct msm_sensor_ctrl sctrl;
+
+ /* parent device */
+ struct device *parent_dev;
+
+ struct mutex vid_lock;
+ /* v4l2 format support */
+ struct msm_isp_color_fmt *usr_fmts;
+ int num_fmts;
+ /* preview or snapshot */
+ u32 mode;
+ u32 memsize;
+
+ int op_mode;
+ int vnode_id;
+ struct msm_cam_v4l2_dev_inst *dev_inst[MSM_DEV_INST_MAX];
+ struct msm_cam_v4l2_dev_inst *dev_inst_map[MSM_MAX_IMG_MODE];
+ /* native config device */
+ struct cdev cdev;
+
+ /* most-frequently accessed manager object*/
+ struct msm_sync *sync;
+
+ /* The message queue is used by the control thread to send commands
+ * to the config thread, and also by the HW to send messages to the
+ * config thread. Thus it is the only queue that is accessed from
+ * both interrupt and process context.
+ */
+ /* struct msm_device_queue event_q; */
+
+ /* This queue used by the config thread to send responses back to the
+ * control thread. It is accessed only from a process context.
+ * TO BE REMOVED
+ */
+ struct msm_device_queue ctrl_q;
+
+ struct mutex lock;
+ uint8_t ctrl_data[max_control_command_size];
+ struct msm_ctrl_cmd ctrl;
+};
+static inline struct msm_cam_v4l2_device *to_pcam(
+ struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct msm_cam_v4l2_device, v4l2_dev);
+}
+
+/*pseudo v4l2 device and v4l2 event queue
+ for server and config cdevs*/
+struct v4l2_queue_util {
+ struct video_device *pvdev;
+ struct v4l2_fh eventHandle;
+};
+
+/* abstract config device for all sensor successfully probed*/
+struct msm_cam_config_dev {
+ struct cdev config_cdev;
+ struct v4l2_queue_util config_stat_event_queue;
+ int use_count;
+ /*struct msm_isp_ops* isp_subdev;*/
+ struct msm_cam_media_controller *p_mctl;
+};
+
+/* abstract camera server device for all sensor successfully probed*/
+struct msm_cam_server_dev {
+
+ /* config node device*/
+ struct cdev server_cdev;
+ /* info of sensors successfully probed*/
+ struct msm_camera_info camera_info;
+ /* info of configs successfully created*/
+ struct msm_cam_config_dev_info config_info;
+ /* active working camera device - only one allowed at this time*/
+ struct msm_cam_v4l2_device *pcam_active;
+ /* number of camera devices opened*/
+ atomic_t number_pcam_active;
+ struct v4l2_queue_util server_command_queue;
+ /* This queue used by the config thread to send responses back to the
+ * control thread. It is accessed only from a process context.
+ */
+ struct msm_device_queue ctrl_q;
+ uint8_t ctrl_data[max_control_command_size];
+ struct msm_ctrl_cmd ctrl;
+ int use_count;
+ /* all the registered ISP subdevice*/
+ struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];
+ struct msm_ispif_fns ispif_fns;
+
+};
+
+/* camera server related functions */
+
+
+/* ISP related functions */
+void msm_isp_vfe_dev_init(struct v4l2_subdev *vd);
+/*
+int msm_isp_register(struct msm_cam_v4l2_device *pcam);
+*/
+int msm_isp_register(struct msm_cam_server_dev *psvr);
+void msm_isp_unregister(struct msm_cam_server_dev *psvr);
+int msm_ispif_register(struct msm_ispif_fns *ispif);
+int msm_sensor_register(struct platform_device *pdev,
+ int (*sensor_probe)(const struct msm_camera_sensor_info *,
+ struct v4l2_subdev *, struct msm_sensor_ctrl *));
+int msm_isp_init_module(int g_num_config_nodes);
+
+int msm_mctl_init_module(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_init_user_formats(struct msm_cam_v4l2_device *pcam);
+int msm_mctl_buf_done(struct msm_cam_media_controller *pmctl,
+ int msg_type, uint32_t y_phy);
+/*Memory(PMEM) functions*/
+int msm_register_pmem(struct hlist_head *ptype, void __user *arg);
+int msm_pmem_table_del(struct hlist_head *ptype, void __user *arg);
+uint8_t msm_pmem_region_lookup(struct hlist_head *ptype,
+ int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount);
+uint8_t msm_pmem_region_lookup_2(struct hlist_head *ptype,
+ int pmem_type,
+ struct msm_pmem_region *reg,
+ uint8_t maxcount);
+uint8_t msm_pmem_region_lookup_3(struct msm_cam_v4l2_device *pcam, int idx,
+ struct msm_pmem_region *reg,
+ int mem_type);
+unsigned long msm_pmem_stats_vtop_lookup(
+ struct msm_sync *sync,
+ unsigned long buffer,
+ int fd);
+unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync,
+ unsigned long addr, int *fd);
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd, void *data,
+ struct platform_device *pdev);
+void msm_vfe_subdev_release(struct platform_device *pdev);
+
+int msm_isp_subdev_ioctl(struct v4l2_subdev *sd,
+ struct msm_vfe_cfg_cmd *cfgcmd, void *data);
+#endif /* __KERNEL__ */
+
+#endif /* _MSM_H */
diff --git a/drivers/media/video/msm/msm_axi_qos.c b/drivers/media/video/msm/msm_axi_qos.c
new file mode 100644
index 0000000..3969547
--- /dev/null
+++ b/drivers/media/video/msm/msm_axi_qos.c
@@ -0,0 +1,47 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <mach/camera.h>
+#define MSM_AXI_QOS_NAME "msm_camera"
+
+static struct clk *ebi1_clk;
+
+int add_axi_qos(void)
+{
+ ebi1_clk = clk_get(NULL, "ebi1_vfe_clk");
+ if (IS_ERR(ebi1_clk))
+ ebi1_clk = NULL;
+ else
+ clk_enable(ebi1_clk);
+
+ return 0;
+}
+
+int update_axi_qos(uint32_t rate)
+{
+ if (!ebi1_clk)
+ return 0;
+
+ return clk_set_rate(ebi1_clk, rate * 1000);
+}
+
+void release_axi_qos(void)
+{
+ if (!ebi1_clk)
+ return;
+
+ clk_disable(ebi1_clk);
+ clk_put(ebi1_clk);
+ ebi1_clk = NULL;
+}
diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c
new file mode 100644
index 0000000..3fddb8e
--- /dev/null
+++ b/drivers/media/video/msm/msm_camera.c
@@ -0,0 +1,4049 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+//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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <mach/board.h>
+
+#include <linux/uaccess.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>
+#include <linux/syscalls.h>
+#include <linux/hrtimer.h>
+DEFINE_MUTEX(ctrl_cmd_lock);
+
+#define CAMERA_STOP_VIDEO 58
+spinlock_t pp_prev_spinlock;
+spinlock_t pp_stereocam_spinlock;
+spinlock_t st_frame_spinlock;
+
+#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)
+#define MAX_PMEM_CFG_BUFFERS 10
+
+static struct class *msm_class;
+static dev_t msm_devno;
+static LIST_HEAD(msm_sensors);
+struct msm_control_device *g_v4l2_control_device;
+int g_v4l2_opencnt;
+static int camera_node;
+static enum msm_camera_type camera_type[MSM_MAX_CAMERA_SENSORS];
+static uint32_t sensor_mount_angle[MSM_MAX_CAMERA_SENSORS];
+
+static const char *vfe_config_cmd[] = {
+ "CMD_GENERAL", /* 0 */
+ "CMD_AXI_CFG_OUT1",
+ "CMD_AXI_CFG_SNAP_O1_AND_O2",
+ "CMD_AXI_CFG_OUT2",
+ "CMD_PICT_T_AXI_CFG",
+ "CMD_PICT_M_AXI_CFG", /* 5 */
+ "CMD_RAW_PICT_AXI_CFG",
+ "CMD_FRAME_BUF_RELEASE",
+ "CMD_PREV_BUF_CFG",
+ "CMD_SNAP_BUF_RELEASE",
+ "CMD_SNAP_BUF_CFG", /* 10 */
+ "CMD_STATS_DISABLE",
+ "CMD_STATS_AEC_AWB_ENABLE",
+ "CMD_STATS_AF_ENABLE",
+ "CMD_STATS_AEC_ENABLE",
+ "CMD_STATS_AWB_ENABLE", /* 15 */
+ "CMD_STATS_ENABLE",
+ "CMD_STATS_AXI_CFG",
+ "CMD_STATS_AEC_AXI_CFG",
+ "CMD_STATS_AF_AXI_CFG",
+ "CMD_STATS_AWB_AXI_CFG", /* 20 */
+ "CMD_STATS_RS_AXI_CFG",
+ "CMD_STATS_CS_AXI_CFG",
+ "CMD_STATS_IHIST_AXI_CFG",
+ "CMD_STATS_SKIN_AXI_CFG",
+ "CMD_STATS_BUF_RELEASE", /* 25 */
+ "CMD_STATS_AEC_BUF_RELEASE",
+ "CMD_STATS_AF_BUF_RELEASE",
+ "CMD_STATS_AWB_BUF_RELEASE",
+ "CMD_STATS_RS_BUF_RELEASE",
+ "CMD_STATS_CS_BUF_RELEASE", /* 30 */
+ "CMD_STATS_IHIST_BUF_RELEASE",
+ "CMD_STATS_SKIN_BUF_RELEASE",
+ "UPDATE_STATS_INVALID",
+ "CMD_AXI_CFG_SNAP_GEMINI",
+ "CMD_AXI_CFG_SNAP", /* 35 */
+ "CMD_AXI_CFG_PREVIEW",
+ "CMD_AXI_CFG_VIDEO",
+ "CMD_STATS_IHIST_ENABLE",
+ "CMD_STATS_RS_ENABLE",
+ "CMD_STATS_CS_ENABLE", /* 40 */
+ "CMD_VPE",
+ "CMD_AXI_CFG_VPE",
+ "CMD_AXI_CFG_SNAP_VPE",
+ "CMD_AXI_CFG_SNAP_THUMB_VPE",
+};
+#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; \
+})
+
+static inline void free_qcmd(struct msm_queue_cmd *qcmd)
+{
+ if (!qcmd || !atomic_read(&qcmd->on_heap))
+ return;
+ if (!atomic_sub_return(1, &qcmd->on_heap))
+ kfree(qcmd);
+}
+
+static void msm_region_init(struct msm_sync *sync)
+{
+ INIT_HLIST_HEAD(&sync->pmem_frames);
+ INIT_HLIST_HEAD(&sync->pmem_stats);
+ spin_lock_init(&sync->pmem_frame_spinlock);
+ spin_lock_init(&sync->pmem_stats_spinlock);
+}
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ CDBG("%s: queue %s new max is %d\n", __func__,
+ queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ CDBG("%s: woke up %s\n", __func__, queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void msm_enqueue_vpe(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ CDBG("%s: queue %s new max is %d\n", __func__,
+ queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ CDBG("%s: woke up %s\n", __func__, queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+#define msm_dequeue(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ if ((qcmd) && (&qcmd->member) && (&qcmd->member.next)) \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+})
+
+#define msm_delete_entry(queue, member, q_cmd) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(qcmd, &__q->list, member) \
+ if (qcmd == q_cmd) { \
+ __q->len--; \
+ list_del_init(&qcmd->member); \
+ CDBG("msm_delete_entry, match found\n");\
+ kfree(q_cmd); \
+ q_cmd = NULL; \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ q_cmd; \
+})
+
+#define msm_queue_drain(queue, member) do { \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ while (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ if (qcmd) { \
+ if ((&qcmd->member) && (&qcmd->member.next)) \
+ list_del_init(&qcmd->member); \
+ free_qcmd(qcmd); \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->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)) {
+ CDBG(" 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 check_pmem_info(struct msm_pmem_info *info, int len)
+{
+ if (info->offset < len &&
+ info->offset + info->len <= len &&
+ info->y_off < len &&
+ info->cbcr_off < len)
+ return 0;
+
+ pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n",
+ __func__,
+ info->offset,
+ info->len,
+ info->y_off,
+ info->cbcr_off,
+ len);
+ return -EINVAL;
+}
+static int msm_pmem_table_add(struct hlist_head *ptype,
+ struct msm_pmem_info *info, spinlock_t* pmem_spinlock,
+ struct msm_sync *sync)
+{
+ struct file *file;
+ unsigned long paddr;
+ unsigned long kvstart;
+ unsigned long len;
+ int rc;
+ struct msm_pmem_region *region;
+ unsigned long flags;
+
+
+ rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file);
+ if (rc < 0) {
+ pr_err("%s: get_pmem_file fd %d error %d\n",
+ __func__,
+ info->fd, rc);
+ return rc;
+ }
+
+ if (!info->len)
+ info->len = len;
+
+ rc = check_pmem_info(info, len);
+ if (rc < 0)
+ return rc;
+
+ paddr += info->offset;
+ len = info->len;
+
+ spin_lock_irqsave(pmem_spinlock, flags);
+ if (check_overlap(ptype, paddr, len) < 0) {
+ spin_unlock_irqrestore(pmem_spinlock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(pmem_spinlock, flags);
+
+
+ region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ spin_lock_irqsave(pmem_spinlock, flags);
+ INIT_HLIST_NODE(®ion->list);
+
+ region->paddr = paddr;
+ region->len = len;
+ region->file = file;
+ memcpy(®ion->info, info, sizeof(region->info));
+
+ hlist_add_head(&(region->list), ptype);
+ spin_unlock_irqrestore(pmem_spinlock, flags);
+ CDBG("%s: type %d, paddr 0x%lx, vaddr 0x%lx\n",
+ __func__, info->type, paddr, (unsigned long)info->vaddr);
+
+ 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,
+ spinlock_t *pmem_spinlock)
+{
+ struct msm_pmem_region *region;
+ struct msm_pmem_region *regptr;
+ struct hlist_node *node, *n;
+ unsigned long flags = 0;
+
+ uint8_t rc = 0;
+
+ regptr = reg;
+ spin_lock_irqsave(pmem_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ if (region->info.type == pmem_type && region->info.active) {
+ *regptr = *region;
+ rc += 1;
+ if (rc >= maxcount)
+ break;
+ regptr++;
+ }
+ }
+ spin_unlock_irqrestore(pmem_spinlock, flags);
+ /* After lookup failure, dump all the list entries...*/
+ if (rc == 0) {
+ pr_err("%s: pmem_type = %d\n", __func__, pmem_type);
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ pr_err("listed region->info.type = %d, active = %d",
+ region->info.type, region->info.active);
+ }
+
+ }
+ return rc;
+}
+
+static uint8_t msm_pmem_region_lookup_2(struct hlist_head *ptype,
+ int pmem_type,
+ struct msm_pmem_region *reg,
+ uint8_t maxcount,
+ spinlock_t *pmem_spinlock)
+{
+ struct msm_pmem_region *region;
+ struct msm_pmem_region *regptr;
+ struct hlist_node *node, *n;
+ uint8_t rc = 0;
+ unsigned long flags = 0;
+ regptr = reg;
+ spin_lock_irqsave(pmem_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ CDBG("%s:info.type=%d, pmem_type = %d,"
+ "info.active = %d\n",
+ __func__, region->info.type, pmem_type, region->info.active);
+
+ if (region->info.type == pmem_type && region->info.active) {
+ CDBG("%s:info.type=%d, pmem_type = %d,"
+ "info.active = %d,\n",
+ __func__, region->info.type, pmem_type,
+ region->info.active);
+ *regptr = *region;
+ region->info.type = MSM_PMEM_VIDEO;
+ rc += 1;
+ if (rc >= maxcount)
+ break;
+ regptr++;
+ }
+ }
+ spin_unlock_irqrestore(pmem_spinlock, flags);
+ return rc;
+}
+
+static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync,
+ unsigned long pyaddr,
+ unsigned long pcbcraddr,
+ struct msm_pmem_info *pmem_info,
+ int clear_active)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ if (pyaddr == (region->paddr + region->info.y_off) &&
+ pcbcraddr == (region->paddr +
+ region->info.cbcr_off) &&
+ region->info.active) {
+ /* offset since we could pass vaddr inside
+ * a registerd pmem buffer
+ */
+ memcpy(pmem_info, ®ion->info, sizeof(*pmem_info));
+ if (clear_active)
+ region->info.active = 0;
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+ flags);
+ return 0;
+ }
+ }
+ /* After lookup failure, dump all the list entries... */
+ pr_err("%s, for pyaddr 0x%lx, pcbcraddr 0x%lx\n",
+ __func__, pyaddr, pcbcraddr);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ pr_err("listed pyaddr 0x%lx, pcbcraddr 0x%lx, active = %d",
+ (region->paddr + region->info.y_off),
+ (region->paddr + region->info.cbcr_off),
+ region->info.active);
+ }
+
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+ return -EINVAL;
+}
+
+static int msm_pmem_frame_ptov_lookup2(struct msm_sync *sync,
+ unsigned long pyaddr,
+ struct msm_pmem_info *pmem_info,
+ int clear_active)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ if (pyaddr == (region->paddr + region->info.y_off) &&
+ region->info.active) {
+ /* offset since we could pass vaddr inside
+ * a registerd pmem buffer
+ */
+ memcpy(pmem_info, ®ion->info, sizeof(*pmem_info));
+ if (clear_active)
+ region->info.active = 0;
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+ flags);
+ return 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+ return -EINVAL;
+}
+
+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;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ if (addr == region->paddr && region->info.active) {
+ /* offset since we could pass vaddr inside a
+ * registered pmem buffer */
+ *fd = region->info.fd;
+ region->info.active = 0;
+ spin_unlock_irqrestore(&sync->pmem_stats_spinlock,
+ flags);
+ return (unsigned long)(region->info.vaddr);
+ }
+ }
+ /* After lookup failure, dump all the list entries... */
+ pr_err("%s, lookup failure, for paddr 0x%lx\n",
+ __func__, addr);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ pr_err("listed paddr 0x%lx, active = %d",
+ region->paddr,
+ region->info.active);
+ }
+ spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+
+ 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, int change_flag)
+{
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+ hlist_for_each_entry_safe(region,
+ node, n, &sync->pmem_frames, list) {
+ if (((unsigned long)(region->info.vaddr) == buffer) &&
+ (region->info.y_off == yoff) &&
+ (region->info.cbcr_off == cbcroff) &&
+ (region->info.fd == fd) &&
+ (region->info.active == 0)) {
+ if (change_flag)
+ region->info.active = 1;
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock,
+ flags);
+ return region->paddr;
+ }
+ }
+ /* After lookup failure, dump all the list entries... */
+ pr_err("%s, failed for vaddr 0x%lx, yoff %d cbcroff %d\n",
+ __func__, buffer, yoff, cbcroff);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) {
+ pr_err("listed vaddr 0x%p, cbcroff %d, active = %d",
+ (region->info.vaddr),
+ (region->info.cbcr_off),
+ region->info.active);
+ }
+
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+
+ 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;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ if (((unsigned long)(region->info.vaddr) == buffer) &&
+ (region->info.fd == fd) &&
+ region->info.active == 0) {
+ region->info.active = 1;
+ spin_unlock_irqrestore(&sync->pmem_stats_spinlock,
+ flags);
+ return region->paddr;
+ }
+ }
+ /* After lookup failure, dump all the list entries... */
+ pr_err("%s,look up error for vaddr %ld\n",
+ __func__, buffer);
+ hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) {
+ pr_err("listed vaddr 0x%p, active = %d",
+ region->info.vaddr,
+ region->info.active);
+ }
+ spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+
+ 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;
+ unsigned long flags = 0;
+
+ switch (pinfo->type) {
+ case MSM_PMEM_PREVIEW:
+ case MSM_PMEM_THUMBNAIL:
+ case MSM_PMEM_MAINIMG:
+ case MSM_PMEM_RAW_MAINIMG:
+ case MSM_PMEM_C2D:
+ case MSM_PMEM_MAINIMG_VPE:
+ case MSM_PMEM_THUMBNAIL_VPE:
+ spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n,
+ &sync->pmem_frames, list) {
+
+ if (pinfo->type == region->info.type &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ CDBG("%s: type %d, vaddr 0x%p\n",
+ __func__, pinfo->type, pinfo->vaddr);
+ }
+ }
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+ break;
+
+ case MSM_PMEM_VIDEO:
+ case MSM_PMEM_VIDEO_VPE:
+ spin_lock_irqsave(&sync->pmem_frame_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n,
+ &sync->pmem_frames, list) {
+
+ if (((region->info.type == MSM_PMEM_VIDEO) ||
+ (region->info.type == MSM_PMEM_VIDEO_VPE)) &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ CDBG("%s: type %d, vaddr 0x%p\n",
+ __func__, pinfo->type, pinfo->vaddr);
+ }
+ }
+ spin_unlock_irqrestore(&sync->pmem_frame_spinlock, flags);
+ break;
+
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ spin_lock_irqsave(&sync->pmem_stats_spinlock, flags);
+ hlist_for_each_entry_safe(region, node, n,
+ &sync->pmem_stats, list) {
+
+ if (pinfo->type == region->info.type &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+ put_pmem_file(region->file);
+ kfree(region);
+ CDBG("%s: type %d, vaddr 0x%p\n",
+ __func__, pinfo->type, pinfo->vaddr);
+ }
+ }
+ spin_unlock_irqrestore(&sync->pmem_stats_spinlock, flags);
+ 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)
+{
+ int rc = 0;
+
+ struct msm_pmem_info pmem_info;
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_vfe_resp *vdata;
+ struct msm_vfe_phy_info *pphy;
+
+ qcmd = msm_dequeue(&sync->frame_q, list_frame);
+
+ if (!qcmd) {
+ pr_err("%s: no preview frame.\n", __func__);
+ return -EAGAIN;
+ }
+
+ if ((!qcmd->command) && (qcmd->error_code & MSM_CAMERA_ERR_MASK)) {
+ frame->error_code = qcmd->error_code;
+ pr_err("%s: fake frame with camera error code = %d\n",
+ __func__, frame->error_code);
+ goto err;
+ }
+
+ vdata = (struct msm_vfe_resp *)(qcmd->command);
+ pphy = &vdata->phy;
+
+ rc = msm_pmem_frame_ptov_lookup(sync,
+ pphy->y_phy,
+ pphy->cbcr_phy,
+ &pmem_info,
+ 1); /* Clear the active flag */
+
+ if (rc < 0) {
+ pr_err("%s: cannot get frame, invalid lookup address "
+ "y %x cbcr %x\n",
+ __func__,
+ pphy->y_phy,
+ pphy->cbcr_phy);
+ goto err;
+ }
+
+ frame->ts = qcmd->ts;
+ frame->buffer = (unsigned long)pmem_info.vaddr;
+ frame->y_off = pmem_info.y_off;
+ frame->cbcr_off = pmem_info.cbcr_off;
+ frame->fd = pmem_info.fd;
+ frame->path = vdata->phy.output_id;
+ frame->frame_id = vdata->phy.frame_id;
+
+ CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n",
+ __func__,
+ pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer);
+
+err:
+ free_qcmd(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;
+
+ mutex_lock(&sync->lock);
+ if (sync->croplen && (!sync->stereocam_enabled)) {
+ if (frame.croplen != sync->croplen) {
+ pr_err("%s: invalid frame croplen %d,"
+ "expecting %d\n",
+ __func__,
+ frame.croplen,
+ sync->croplen);
+ mutex_unlock(&sync->lock);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)frame.cropinfo,
+ sync->cropinfo,
+ sync->croplen)) {
+ ERR_COPY_TO_USER();
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+ }
+
+ if (sync->fdroiinfo.info) {
+ if (copy_to_user((void *)frame.roi_info.info,
+ sync->fdroiinfo.info,
+ sync->fdroiinfo.info_len)) {
+ ERR_COPY_TO_USER();
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+ }
+
+ if (sync->stereocam_enabled) {
+ frame.stcam_conv_value = sync->stcam_conv_value;
+ frame.stcam_quality_ind = sync->stcam_quality_ind;
+ }
+
+ if (copy_to_user((void *)arg,
+ &frame, sizeof(struct msm_frame))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ }
+
+ mutex_unlock(&sync->lock);
+ CDBG("%s: got frame\n", __func__);
+
+ 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);
+
+ 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);
+
+ return rc;
+}
+
+static struct msm_queue_cmd *__msm_control(struct msm_sync *sync,
+ struct msm_device_queue *queue,
+ struct msm_queue_cmd *qcmd,
+ int timeout)
+{
+ int rc;
+
+ CDBG("Inside __msm_control\n");
+ if (sync->event_q.len <= 100 && sync->frame_q.len <= 100) {
+ /* wake up config thread */
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ } else {
+ pr_err("%s, Error Queue limit exceeded e_q = %d, f_q = %d\n",
+ __func__, sync->event_q.len, sync->frame_q.len);
+ free_qcmd(qcmd);
+ return NULL;
+ }
+ if (!queue)
+ return NULL;
+
+ /* wait for config status */
+ CDBG("Waiting for config status \n");
+ rc = wait_event_interruptible_timeout(
+ queue->wait,
+ !list_empty_careful(&queue->list),
+ timeout);
+ CDBG("Waiting over for config status\n");
+ if (list_empty_careful(&queue->list)) {
+ if (!rc) {
+ rc = -ETIMEDOUT;
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ return ERR_PTR(rc);
+ } else if (rc < 0) {
+ pr_err("%s: wait_event error %d\n", __func__, rc);
+ if (msm_delete_entry(&sync->event_q,
+ list_config, qcmd)) {
+ sync->ignore_qcmd = true;
+ sync->ignore_qcmd_type =
+ (int16_t)((struct msm_ctrl_cmd *)
+ (qcmd->command))->type;
+ }
+ return ERR_PTR(rc);
+ }
+ }
+ qcmd = msm_dequeue(queue, list_control);
+ BUG_ON(!qcmd);
+ CDBG("__msm_control done \n");
+ return qcmd;
+}
+
+static struct msm_queue_cmd *__msm_control_nb(struct msm_sync *sync,
+ struct msm_queue_cmd *qcmd_to_copy)
+{
+ /* Since this is a non-blocking command, we cannot use qcmd_to_copy and
+ * its data, since they are on the stack. We replicate them on the heap
+ * and mark them on_heap so that they get freed when the config thread
+ * dequeues them.
+ */
+
+ struct msm_ctrl_cmd *udata;
+ struct msm_ctrl_cmd *udata_to_copy = qcmd_to_copy->command;
+
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(*qcmd_to_copy) +
+ sizeof(*udata_to_copy) +
+ udata_to_copy->length,
+ GFP_KERNEL);
+ if (!qcmd) {
+ pr_err("%s: out of memory\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+ *qcmd = *qcmd_to_copy;
+ udata = qcmd->command = qcmd + 1;
+ memcpy(udata, udata_to_copy, sizeof(*udata));
+ udata->value = udata + 1;
+ memcpy(udata->value, udata_to_copy->value, udata_to_copy->length);
+
+ atomic_set(&qcmd->on_heap, 1);
+
+ /* qcmd_resp will be set to NULL */
+ return __msm_control(sync, NULL, qcmd, 0);
+}
+
+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;
+ void __user *uptr;
+ struct msm_ctrl_cmd udata_resp;
+ struct msm_queue_cmd *qcmd_resp = NULL;
+ uint8_t data[max_control_command_size];
+ struct msm_ctrl_cmd *udata;
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(struct msm_queue_cmd) +
+ sizeof(struct msm_ctrl_cmd), GFP_ATOMIC);
+ if (!qcmd) {
+ pr_err("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ udata = (struct msm_ctrl_cmd *)(qcmd + 1);
+ atomic_set(&(qcmd->on_heap), 1);
+ CDBG("Inside msm_control\n");
+ if (copy_from_user(udata, arg, sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+
+ uptr = udata->value;
+ udata->value = data;
+ qcmd->type = MSM_CAM_Q_CTRL;
+ qcmd->command = udata;
+
+ if (udata->length) {
+ if (udata->length > sizeof(data)) {
+ pr_err("%s: user data too large (%d, max is %d)\n",
+ __func__,
+ udata->length,
+ sizeof(data));
+ rc = -EIO;
+ goto end;
+ }
+ if (copy_from_user(udata->value, uptr, udata->length)) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+ if (unlikely(!block)) {
+ qcmd_resp = __msm_control_nb(sync, qcmd);
+ goto end;
+ }
+
+ qcmd_resp = __msm_control(sync,
+ &ctrl_pmsm->ctrl_q,
+ qcmd, msecs_to_jiffies(10000));
+
+ /* ownership of qcmd will be transfered to event queue */
+ qcmd = NULL;
+
+ if (!qcmd_resp || IS_ERR(qcmd_resp)) {
+ /* Do not free qcmd_resp here. If the config thread read it,
+ * then it has already been freed, and we timed out because
+ * we did not receive a MSM_CAM_IOCTL_CTRL_CMD_DONE. If the
+ * config thread itself is blocked and not dequeueing commands,
+ * then it will either eventually unblock and process them,
+ * or when it is killed, qcmd will be freed in
+ * msm_release_config.
+ */
+ rc = PTR_ERR(qcmd_resp);
+ qcmd_resp = NULL;
+ goto end;
+ }
+
+ if (qcmd_resp->command) {
+ udata_resp = *(struct msm_ctrl_cmd *)qcmd_resp->command;
+ if (udata_resp.length > 0) {
+ if (copy_to_user(uptr,
+ udata_resp.value,
+ udata_resp.length)) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+ udata_resp.value = uptr;
+
+ if (copy_to_user((void *)arg, &udata_resp,
+ sizeof(struct msm_ctrl_cmd))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ goto end;
+ }
+ }
+
+end:
+ free_qcmd(qcmd);
+ CDBG("%s: done rc = %d\n", __func__, rc);
+ return rc;
+}
+
+/* Divert frames for post-processing by delivering them to the config thread;
+ * when post-processing is done, it will return the frame to the frame thread.
+ */
+static int msm_divert_frame(struct msm_sync *sync,
+ struct msm_vfe_resp *data,
+ struct msm_stats_event_ctrl *se)
+{
+ struct msm_pmem_info pinfo;
+ struct msm_postproc buf;
+ int rc;
+
+ CDBG("%s: Frame PP sync->pp_mask %d\n", __func__, sync->pp_mask);
+
+ if (!(sync->pp_mask & PP_PREV) && !(sync->pp_mask & PP_SNAP)) {
+ pr_err("%s: diverting frame, not in PP_PREV or PP_SNAP!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_pmem_frame_ptov_lookup(sync, data->phy.y_phy,
+ data->phy.cbcr_phy, &pinfo,
+ 0); /* do not clear the active flag */
+
+ if (rc < 0) {
+ pr_err("%s: msm_pmem_frame_ptov_lookup failed\n", __func__);
+ return rc;
+ }
+
+ buf.fmain.buffer = (unsigned long)pinfo.vaddr;
+ buf.fmain.y_off = pinfo.y_off;
+ buf.fmain.cbcr_off = pinfo.cbcr_off;
+ buf.fmain.fd = pinfo.fd;
+
+ CDBG("%s: buf 0x%x fd %d\n", __func__, (unsigned int)buf.fmain.buffer,
+ buf.fmain.fd);
+ if (copy_to_user((void *)(se->stats_event.data),
+ &(buf.fmain), sizeof(struct msm_frame))) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/* Divert stereo frames for post-processing by delivering
+ * them to the config thread.
+ */
+static int msm_divert_st_frame(struct msm_sync *sync,
+ struct msm_vfe_resp *data, struct msm_stats_event_ctrl *se, int path)
+{
+ struct msm_pmem_info pinfo;
+ struct msm_st_frame buf;
+ struct video_crop_t *crop = NULL;
+ int rc = 0;
+
+ if (se->stats_event.msg_id == OUTPUT_TYPE_ST_L) {
+ buf.type = OUTPUT_TYPE_ST_L;
+ } else if (se->stats_event.msg_id == OUTPUT_TYPE_ST_R) {
+ buf.type = OUTPUT_TYPE_ST_R;
+ } else {
+ if (se->resptype == MSM_CAM_RESP_STEREO_OP_1) {
+ rc = msm_pmem_frame_ptov_lookup(sync, data->phy.y_phy,
+ data->phy.cbcr_phy, &pinfo,
+ 1); /* do clear the active flag */
+ buf.buf_info.path = path;
+ } else if (se->resptype == MSM_CAM_RESP_STEREO_OP_2) {
+ rc = msm_pmem_frame_ptov_lookup(sync, data->phy.y_phy,
+ data->phy.cbcr_phy, &pinfo,
+ 0); /* do not clear the active flag */
+ buf.buf_info.path = path;
+ } else
+ CDBG("%s: Invalid resptype = %d\n", __func__,
+ se->resptype);
+
+ if (rc < 0) {
+ CDBG("%s: msm_pmem_frame_ptov_lookup failed\n",
+ __func__);
+ return rc;
+ }
+
+ buf.type = OUTPUT_TYPE_ST_D;
+
+ if (sync->cropinfo != NULL) {
+ crop = sync->cropinfo;
+ switch (path) {
+ case OUTPUT_TYPE_P:
+ case OUTPUT_TYPE_T: {
+ buf.L.stCropInfo.in_w = crop->in1_w;
+ buf.L.stCropInfo.in_h = crop->in1_h;
+ buf.L.stCropInfo.out_w = crop->out1_w;
+ buf.L.stCropInfo.out_h = crop->out1_h;
+ buf.R.stCropInfo = buf.L.stCropInfo;
+ break;
+ }
+
+ case OUTPUT_TYPE_V:
+ case OUTPUT_TYPE_S: {
+ buf.L.stCropInfo.in_w = crop->in2_w;
+ buf.L.stCropInfo.in_h = crop->in2_h;
+ buf.L.stCropInfo.out_w = crop->out2_w;
+ buf.L.stCropInfo.out_h = crop->out2_h;
+ buf.R.stCropInfo = buf.L.stCropInfo;
+ break;
+ }
+ default: {
+ pr_warning("%s: invalid frame path %d\n",
+ __func__, path);
+ break;
+ }
+ }
+ } else {
+ buf.L.stCropInfo.in_w = 0;
+ buf.L.stCropInfo.in_h = 0;
+ buf.L.stCropInfo.out_w = 0;
+ buf.L.stCropInfo.out_h = 0;
+ buf.R.stCropInfo = buf.L.stCropInfo;
+ }
+
+ /* hardcode for now. */
+ if ((path == OUTPUT_TYPE_S) || (path == OUTPUT_TYPE_T))
+ buf.packing = sync->sctrl.s_snap_packing;
+ else
+ buf.packing = sync->sctrl.s_video_packing;
+
+ buf.buf_info.buffer = (unsigned long)pinfo.vaddr;
+ buf.buf_info.phy_offset = pinfo.offset;
+ buf.buf_info.y_off = pinfo.y_off;
+ buf.buf_info.cbcr_off = pinfo.cbcr_off;
+ buf.buf_info.fd = pinfo.fd;
+
+ CDBG("%s: buf 0x%x fd %d\n", __func__,
+ (unsigned int)buf.buf_info.buffer, buf.buf_info.fd);
+ }
+
+ if (copy_to_user((void *)(se->stats_event.data),
+ &buf, sizeof(struct msm_st_frame))) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int msm_get_stats(struct msm_sync *sync, void __user *arg)
+{
+ 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_vpe_resp *vpe_data = NULL;
+ struct msm_stats_buf stats;
+
+ if (copy_from_user(&se, arg,
+ sizeof(struct msm_stats_event_ctrl))) {
+ ERR_COPY_FROM_USER();
+ pr_err("%s, ERR_COPY_FROM_USER\n", __func__);
+ return -EFAULT;
+ }
+
+ rc = 0;
+
+ qcmd = msm_dequeue(&sync->event_q, list_config);
+ if (!qcmd) {
+ /* Should be associated with wait_event
+ error -512 from __msm_control*/
+ pr_err("%s, qcmd is Null\n", __func__);
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+
+ CDBG("%s: received from DSP %d\n", __func__, qcmd->type);
+
+ switch (qcmd->type) {
+ case MSM_CAM_Q_VPE_MSG:
+ /* Complete VPE response. */
+ vpe_data = (struct msm_vpe_resp *)(qcmd->command);
+ se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+ se.stats_event.type = vpe_data->evt_msg.type;
+ se.stats_event.msg_id = vpe_data->evt_msg.msg_id;
+ se.stats_event.len = vpe_data->evt_msg.len;
+
+ if (vpe_data->type == VPE_MSG_OUTPUT_ST_L) {
+ CDBG("%s: Change msg_id to OUTPUT_TYPE_ST_L\n",
+ __func__);
+ se.stats_event.msg_id = OUTPUT_TYPE_ST_L;
+ rc = msm_divert_st_frame(sync, data, &se,
+ OUTPUT_TYPE_V);
+ } else if (vpe_data->type == VPE_MSG_OUTPUT_ST_R) {
+ CDBG("%s: Change msg_id to OUTPUT_TYPE_ST_R\n",
+ __func__);
+ se.stats_event.msg_id = OUTPUT_TYPE_ST_R;
+ rc = msm_divert_st_frame(sync, data, &se,
+ OUTPUT_TYPE_V);
+ } else {
+ pr_warning("%s: invalid vpe_data->type = %d\n",
+ __func__, vpe_data->type);
+ }
+ break;
+
+ 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;
+ se.stats_event.frame_id = data->evt_msg.frame_id;
+
+ CDBG("%s: qcmd->type %d length %d msd_id %d\n", __func__,
+ qcmd->type,
+ se.stats_event.len,
+ se.stats_event.msg_id);
+
+ if (data->type == VFE_MSG_COMMON) {
+ stats.status_bits = data->stats_msg.status_bits;
+ if (data->stats_msg.aec_buff) {
+ stats.aec.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.aec_buff,
+ &(stats.aec.fd));
+ if (!stats.aec.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ } else {
+ stats.aec.buff = 0;
+ }
+ if (data->stats_msg.awb_buff) {
+ stats.awb.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.awb_buff,
+ &(stats.awb.fd));
+ if (!stats.awb.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ } else {
+ stats.awb.buff = 0;
+ }
+ if (data->stats_msg.af_buff) {
+ stats.af.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.af_buff,
+ &(stats.af.fd));
+ if (!stats.af.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ } else {
+ stats.af.buff = 0;
+ }
+ if (data->stats_msg.ihist_buff) {
+ stats.ihist.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.ihist_buff,
+ &(stats.ihist.fd));
+ if (!stats.ihist.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ } else {
+ stats.ihist.buff = 0;
+ }
+
+ if (data->stats_msg.rs_buff) {
+ stats.rs.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.rs_buff,
+ &(stats.rs.fd));
+ if (!stats.rs.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+
+ } else {
+ stats.rs.buff = 0;
+ }
+
+ if (data->stats_msg.cs_buff) {
+ stats.cs.buff =
+ msm_pmem_stats_ptov_lookup(sync,
+ data->stats_msg.cs_buff,
+ &(stats.cs.fd));
+ if (!stats.cs.buff) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+ } else {
+ stats.cs.buff = 0;
+ }
+
+ se.stats_event.frame_id = data->phy.frame_id;
+ 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->type >= VFE_MSG_STATS_AEC) &&
+ (data->type <= VFE_MSG_STATS_WE)) {
+ /* the check above includes all stats type. */
+ 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",
+ __func__);
+ rc = -EINVAL;
+ goto failure;
+ }
+ se.stats_event.frame_id = data->phy.frame_id;
+ 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;
+ goto failure;
+ }
+ } else {
+ if (sync->stereocam_enabled) {
+ if (data->type == VFE_MSG_OUTPUT_P) {
+ CDBG("%s: Preview mark as st op 1\n",
+ __func__);
+ se.resptype = MSM_CAM_RESP_STEREO_OP_1;
+ rc = msm_divert_st_frame(sync, data,
+ &se, OUTPUT_TYPE_P);
+ break;
+ } else if (data->type == VFE_MSG_OUTPUT_V) {
+ CDBG("%s: Video mark as st op 2\n",
+ __func__);
+ se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+ rc = msm_divert_st_frame(sync, data,
+ &se, OUTPUT_TYPE_V);
+ break;
+ } else if (data->type == VFE_MSG_OUTPUT_S) {
+ CDBG("%s: Main img mark as st op 2\n",
+ __func__);
+ se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+ rc = msm_divert_st_frame(sync, data,
+ &se, OUTPUT_TYPE_S);
+ break;
+ } else if (data->type == VFE_MSG_OUTPUT_T) {
+ CDBG("%s: Thumb img mark as st op 2\n",
+ __func__);
+ se.resptype = MSM_CAM_RESP_STEREO_OP_2;
+ rc = msm_divert_st_frame(sync, data,
+ &se, OUTPUT_TYPE_T);
+ break;
+ } else
+ CDBG("%s: VFE_MSG Fall Through\n",
+ __func__);
+ }
+ if ((sync->pp_frame_avail == 1) &&
+ (sync->pp_mask & PP_PREV) &&
+ (data->type == VFE_MSG_OUTPUT_P)) {
+ CDBG("%s:%d:preiew PP\n",
+ __func__, __LINE__);
+ se.stats_event.frame_id =
+ data->phy.frame_id;
+ rc = msm_divert_frame(sync, data, &se);
+ sync->pp_frame_avail = 0;
+ } else {
+ if ((sync->pp_mask & PP_PREV) &&
+ (data->type == VFE_MSG_OUTPUT_P)) {
+ se.stats_event.frame_id =
+ data->phy.frame_id;
+ free_qcmd(qcmd);
+ return 0;
+ } else
+ CDBG("%s:indication type is %d\n",
+ __func__, data->type);
+ }
+ if (sync->pp_mask & PP_SNAP)
+ if (data->type == VFE_MSG_OUTPUT_S ||
+ data->type == VFE_MSG_OUTPUT_T)
+ rc = msm_divert_frame(sync, data, &se);
+ }
+ break;
+
+ case MSM_CAM_Q_CTRL:
+ /* control command from control thread */
+ ctrl = (struct msm_ctrl_cmd *)(qcmd->command);
+
+ CDBG("%s: qcmd->type %d length %d\n", __func__,
+ qcmd->type, 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);
+ 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;
+ goto failure;
+ }
+
+failure:
+ free_qcmd(qcmd);
+
+ CDBG("%s: %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm,
+ void __user *arg)
+{
+ void __user *uptr;
+ struct msm_queue_cmd *qcmd = &ctrl_pmsm->qcmd;
+ struct msm_ctrl_cmd *command = &ctrl_pmsm->ctrl;
+
+ if (copy_from_user(command, arg, sizeof(*command))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ atomic_set(&qcmd->on_heap, 0);
+ qcmd->command = command;
+ uptr = command->value;
+
+ if (command->length > 0) {
+ command->value = ctrl_pmsm->ctrl_data;
+ if (command->length > sizeof(ctrl_pmsm->ctrl_data)) {
+ pr_err("%s: user data %d is too big (max %d)\n",
+ __func__, command->length,
+ sizeof(ctrl_pmsm->ctrl_data));
+ return -EINVAL;
+ }
+
+ if (copy_from_user(command->value,
+ uptr,
+ command->length)) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ } else
+ command->value = NULL;
+
+ /* Ignore the command if the ctrl cmd has
+ return back due to signaling */
+ /* Should be associated with wait_event
+ error -512 from __msm_control*/
+ if (ctrl_pmsm->pmsm->sync->ignore_qcmd == true &&
+ ctrl_pmsm->pmsm->sync->ignore_qcmd_type == (int16_t)command->type) {
+ ctrl_pmsm->pmsm->sync->ignore_qcmd = false;
+ ctrl_pmsm->pmsm->sync->ignore_qcmd_type = -1;
+ } else /* wake up control thread */
+ msm_enqueue(&ctrl_pmsm->ctrl_q, &qcmd->list_control);
+
+ return 0;
+}
+
+static int msm_config_vpe(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_vpe_cfg_cmd cfgcmd;
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ CDBG("%s: cmd_type %s\n", __func__, vfe_config_cmd[cfgcmd.cmd_type]);
+ switch (cfgcmd.cmd_type) {
+ case CMD_VPE:
+ return sync->vpefn.vpe_config(&cfgcmd, NULL);
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd.cmd_type);
+ }
+ return -EINVAL;
+}
+
+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;
+
+ if (!sync->vfefn.vfe_config) {
+ pr_err("%s: no vfe_config!\n", __func__);
+ return -EIO;
+ }
+
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ memset(&axi_data, 0, sizeof(axi_data));
+ CDBG("%s: cmd_type %s\n", __func__, vfe_config_cmd[cfgcmd.cmd_type]);
+ switch (cfgcmd.cmd_type) {
+ case CMD_STATS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AF, ®ion[axi_data.bufnum1],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1 || !axi_data.bufnum2) {
+ pr_err("%s: pmem region lookup error\n", __func__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AF_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AF, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AEC_AWB_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AEC_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+ case CMD_STATS_AWB_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+
+ case CMD_STATS_IHIST_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_IHIST, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_STATS_RS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_RS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_STATS_CS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_CS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return sync->vfefn.vfe_config(&cfgcmd, &axi_data);
+
+ case CMD_GENERAL:
+ case CMD_STATS_DISABLE:
+ return sync->vfefn.vfe_config(&cfgcmd, NULL);
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd.cmd_type);
+ }
+
+ return -EINVAL;
+}
+static int msm_vpe_frame_cfg(struct msm_sync *sync,
+ void *cfgcmdin)
+{
+ int rc = -EIO;
+ struct axidata axi_data;
+ void *data = &axi_data;
+ struct msm_pmem_region region[8];
+ int pmem_type;
+
+ struct msm_vpe_cfg_cmd *cfgcmd;
+ cfgcmd = (struct msm_vpe_cfg_cmd *)cfgcmdin;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+ CDBG("In vpe_frame_cfg cfgcmd->cmd_type = %s\n",
+ vfe_config_cmd[cfgcmd->cmd_type]);
+ switch (cfgcmd->cmd_type) {
+ case CMD_AXI_CFG_VPE:
+ pmem_type = MSM_PMEM_VIDEO_VPE;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup_2(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8, &sync->pmem_frame_spinlock);
+ CDBG("axi_data.bufnum1 = %d\n", axi_data.bufnum1);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ pmem_type = MSM_PMEM_VIDEO;
+ break;
+ case CMD_AXI_CFG_SNAP_THUMB_VPE:
+ CDBG("%s: CMD_AXI_CFG_SNAP_THUMB_VPE", __func__);
+ pmem_type = MSM_PMEM_THUMBNAIL_VPE;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8, &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s: THUMBNAIL_VPE pmem region lookup error\n",
+ __func__);
+ return -EINVAL;
+ }
+ break;
+ case CMD_AXI_CFG_SNAP_VPE:
+ CDBG("%s: CMD_AXI_CFG_SNAP_VPE", __func__);
+ pmem_type = MSM_PMEM_MAINIMG_VPE;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8, &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s: MAINIMG_VPE pmem region lookup error\n",
+ __func__);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ break;
+ }
+ axi_data.region = ®ion[0];
+ CDBG("out vpe_frame_cfg cfgcmd->cmd_type = %s\n",
+ vfe_config_cmd[cfgcmd->cmd_type]);
+ /* send the AXI configuration command to driver */
+ if (sync->vpefn.vpe_config)
+ rc = sync->vpefn.vpe_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[MAX_PMEM_CFG_BUFFERS];
+ int pmem_type;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+
+ switch (cfgcmd->cmd_type) {
+
+ case CMD_AXI_CFG_PREVIEW:
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], MAX_PMEM_CFG_BUFFERS,
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error (empty %d)\n",
+ __func__, __LINE__,
+ hlist_empty(&sync->pmem_frames));
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO:
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], MAX_PMEM_CFG_BUFFERS,
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_VIDEO;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP:
+ CDBG("%s, CMD_AXI_CFG_SNAP, type=%d\n", __func__,
+ cfgcmd->cmd_type);
+ pmem_type = MSM_PMEM_THUMBNAIL;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], MAX_PMEM_CFG_BUFFERS,
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_AXI_CFG_ZSL:
+ CDBG("%s, CMD_AXI_CFG_ZSL, type = %d\n", __func__,
+ cfgcmd->cmd_type);
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], MAX_PMEM_CFG_BUFFERS,
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_THUMBNAIL;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1],
+ (MAX_PMEM_CFG_BUFFERS-(axi_data.bufnum1)),
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pmem_type = MSM_PMEM_MAINIMG;
+ axi_data.bufnum3 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[axi_data.bufnum1 + axi_data.bufnum2],
+ (MAX_PMEM_CFG_BUFFERS - axi_data.bufnum1 -
+ axi_data.bufnum2), &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum3) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_RAW_PICT_AXI_CFG:
+ pmem_type = MSM_PMEM_RAW_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup(&sync->pmem_frames, pmem_type,
+ ®ion[0], MAX_PMEM_CFG_BUFFERS,
+ &sync->pmem_frame_spinlock);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_GENERAL:
+ data = NULL;
+ break;
+
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ axi_data.region = ®ion[0];
+
+ /* send the AXI configuration command to driver */
+ if (sync->vfefn.vfe_config)
+ rc = sync->vfefn.vfe_config(cfgcmd, data);
+
+ return rc;
+}
+
+static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg)
+{
+ int rc = 0;
+ struct msm_camsensor_info info;
+ struct msm_camera_sensor_info *sdata;
+
+ if (copy_from_user(&info,
+ arg,
+ sizeof(struct msm_camsensor_info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ sdata = sync->pdev->dev.platform_data;
+ if (sync->sctrl.s_camera_type == BACK_CAMERA_3D)
+ info.support_3d = true;
+ else
+ info.support_3d = false;
+ memcpy(&info.name[0],
+ sdata->sensor_name,
+ MAX_SENSOR_NAME);
+ info.flash_enabled = sdata->flash_data->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_get_camera_info(void __user *arg)
+{
+ int rc = 0;
+ int i = 0;
+ struct msm_camera_info info;
+
+ if (copy_from_user(&info, arg, sizeof(struct msm_camera_info))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ CDBG("%s: camera_node %d\n", __func__, camera_node);
+ info.num_cameras = camera_node;
+
+ for (i = 0; i < camera_node; i++) {
+ info.has_3d_support[i] = 0;
+ info.is_internal_cam[i] = 0;
+ info.s_mount_angle[i] = sensor_mount_angle[i];
+ switch (camera_type[i]) {
+ case FRONT_CAMERA_2D:
+ info.is_internal_cam[i] = 1;
+ break;
+ case BACK_CAMERA_3D:
+ info.has_3d_support[i] = 1;
+ break;
+ case BACK_CAMERA_2D:
+ default:
+ break;
+ }
+ }
+ /* copy back to user space */
+ if (copy_to_user((void *)arg, &info, sizeof(struct msm_camera_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;
+
+ /* Change the active flag. */
+ pphy = msm_pmem_frame_vtop_lookup(sync,
+ pb->buffer,
+ pb->y_off, pb->cbcr_off, pb->fd, 1);
+
+ if (pphy != 0) {
+ CDBG("%s: rel: vaddr %lx, paddr %lx\n",
+ __func__,
+ 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",
+ __func__);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+static int __msm_put_pic_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, 1);
+
+ if (pphy != 0) {
+ CDBG("%s: rel: vaddr %lx, paddr %lx\n",
+ __func__,
+ pb->buffer, pphy);
+ cfgcmd.cmd_type = CMD_SNAP_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",
+ __func__);
+ 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_put_pic_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_pic_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_VIDEO:
+ case MSM_PMEM_PREVIEW:
+ case MSM_PMEM_THUMBNAIL:
+ case MSM_PMEM_MAINIMG:
+ case MSM_PMEM_RAW_MAINIMG:
+ case MSM_PMEM_VIDEO_VPE:
+ case MSM_PMEM_C2D:
+ case MSM_PMEM_MAINIMG_VPE:
+ case MSM_PMEM_THUMBNAIL_VPE:
+ rc = msm_pmem_table_add(&sync->pmem_frames, pinfo,
+ &sync->pmem_frame_spinlock, sync);
+ break;
+
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ case MSM_PMEM_AEC:
+ case MSM_PMEM_AWB:
+ case MSM_PMEM_RS:
+ case MSM_PMEM_CS:
+ case MSM_PMEM_IHIST:
+ case MSM_PMEM_SKIN:
+
+ rc = msm_pmem_table_add(&sync->pmem_stats, pinfo,
+ &sync->pmem_stats_spinlock, sync);
+ 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",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ if (cfgcmd->cmd_type != CMD_GENERAL) {
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats, pmem_type,
+ ®ion[0], NUM_STAT_OUTPUT_BUFFERS,
+ &sync->pmem_stats_spinlock);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ }
+
+ /* send the AEC/AWB STATS configuration command to driver */
+ if (sync->vfefn.vfe_config)
+ rc = sync->vfefn.vfe_config(cfgcmd, &axi_data);
+
+ return rc;
+}
+
+static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg)
+{
+ int rc = -EIO;
+
+ struct msm_stats_buf buf;
+ unsigned long pphy;
+ struct msm_vfe_cfg_cmd cfgcmd;
+
+ if (copy_from_user(&buf, arg,
+ sizeof(struct msm_stats_buf))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ CDBG("%s\n", __func__);
+ 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 if (buf.type == STAT_AEC)
+ cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE;
+ else if (buf.type == STAT_AWB)
+ cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE;
+ else if (buf.type == STAT_IHIST)
+ cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE;
+ else if (buf.type == STAT_RS)
+ cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE;
+ else if (buf.type == STAT_CS)
+ cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
+
+ else {
+ pr_err("%s: invalid buf type %d\n",
+ __func__,
+ 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("%s: vfe_config error %d\n",
+ __func__, rc);
+ } else
+ pr_err("%s: vfe_config is NULL\n", __func__);
+ } else {
+ pr_err("%s: NULL physical address\n", __func__);
+ 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_VIDEO:
+ case CMD_AXI_CFG_PREVIEW:
+ case CMD_AXI_CFG_SNAP:
+ case CMD_RAW_PICT_AXI_CFG:
+ case CMD_AXI_CFG_ZSL:
+ CDBG("%s, cfgcmd.cmd_type = %d\n", __func__, cfgcmd.cmd_type);
+ return msm_frame_axi_cfg(sync, &cfgcmd);
+
+ case CMD_AXI_CFG_VPE:
+ case CMD_AXI_CFG_SNAP_VPE:
+ case CMD_AXI_CFG_SNAP_THUMB_VPE:
+ return msm_vpe_frame_cfg(sync, (void *)&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",
+ __func__,
+ cfgcmd.cmd_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __msm_get_pic(struct msm_sync *sync,
+ struct msm_frame *frame)
+{
+
+ int rc = 0;
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_vfe_resp *vdata;
+ struct msm_vfe_phy_info *pphy;
+ struct msm_pmem_info pmem_info;
+ struct msm_frame *pframe;
+
+ qcmd = msm_dequeue(&sync->pict_q, list_pict);
+
+ if (!qcmd) {
+ pr_err("%s: no pic frame.\n", __func__);
+ return -EAGAIN;
+ }
+
+ if (MSM_CAM_Q_PP_MSG != qcmd->type) {
+ vdata = (struct msm_vfe_resp *)(qcmd->command);
+ pphy = &vdata->phy;
+
+ rc = msm_pmem_frame_ptov_lookup2(sync,
+ pphy->y_phy,
+ &pmem_info,
+ 1); /* mark pic frame in use */
+
+ if (rc < 0) {
+ pr_err("%s: cannot get pic frame, invalid lookup"
+ " address y %x cbcr %x\n",
+ __func__, pphy->y_phy, pphy->cbcr_phy);
+ goto err;
+ }
+
+ frame->ts = qcmd->ts;
+ frame->buffer = (unsigned long)pmem_info.vaddr;
+ frame->y_off = pmem_info.y_off;
+ frame->cbcr_off = pmem_info.cbcr_off;
+ frame->fd = pmem_info.fd;
+ if (sync->stereocam_enabled &&
+ sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+ if (pmem_info.type == MSM_PMEM_THUMBNAIL_VPE)
+ frame->path = OUTPUT_TYPE_T;
+ else
+ frame->path = OUTPUT_TYPE_S;
+ } else
+ frame->path = vdata->phy.output_id;
+
+ CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n",
+ __func__, pphy->y_phy,
+ pphy->cbcr_phy, (int) qcmd, (int) frame->buffer);
+ } else { /* PP */
+ pframe = (struct msm_frame *)(qcmd->command);
+ frame->ts = qcmd->ts;
+ frame->buffer = pframe->buffer;
+ frame->y_off = pframe->y_off;
+ frame->cbcr_off = pframe->cbcr_off;
+ frame->fd = pframe->fd;
+ frame->path = pframe->path;
+ CDBG("%s: PP y_off %x, cbcr_off %x, path %d vaddr 0x%x\n",
+ __func__, frame->y_off, frame->cbcr_off, frame->path,
+ (int) frame->buffer);
+ }
+
+err:
+ free_qcmd(qcmd);
+
+ return rc;
+}
+
+static int msm_get_pic(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_pic(sync, &frame);
+ if (rc < 0)
+ return rc;
+
+ if (sync->croplen && (!sync->stereocam_enabled)) {
+ if (frame.croplen != sync->croplen) {
+ pr_err("%s: invalid frame croplen %d,"
+ "expecting %d\n",
+ __func__,
+ frame.croplen,
+ sync->croplen);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)frame.cropinfo,
+ sync->cropinfo,
+ sync->croplen)) {
+ ERR_COPY_TO_USER();
+ return -EFAULT;
+ }
+ }
+ CDBG("%s: copy snapshot frame to user\n", __func__);
+ if (copy_to_user((void *)arg,
+ &frame, sizeof(struct msm_frame))) {
+ ERR_COPY_TO_USER();
+ rc = -EFAULT;
+ }
+
+ CDBG("%s: got pic frame\n", __func__);
+
+ return rc;
+}
+
+static int msm_set_crop(struct msm_sync *sync, void __user *arg)
+{
+ struct crop_info crop;
+
+ mutex_lock(&sync->lock);
+ if (copy_from_user(&crop,
+ arg,
+ sizeof(struct crop_info))) {
+ ERR_COPY_FROM_USER();
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+
+ if (crop.len != CROP_LEN) {
+ mutex_unlock(&sync->lock);
+ return -EINVAL;
+ }
+
+ if (!sync->croplen) {
+ sync->cropinfo = kmalloc(crop.len, GFP_KERNEL);
+ if (!sync->cropinfo) {
+ mutex_unlock(&sync->lock);
+ return -ENOMEM;
+ }
+ }
+
+ if (copy_from_user(sync->cropinfo,
+ crop.info,
+ crop.len)) {
+ ERR_COPY_FROM_USER();
+ sync->croplen = 0;
+ kfree(sync->cropinfo);
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+
+ sync->croplen = crop.len;
+
+ mutex_unlock(&sync->lock);
+ return 0;
+}
+
+static int msm_error_config(struct msm_sync *sync, void __user *arg)
+{
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+
+ qcmd->command = NULL;
+
+ if (qcmd)
+ atomic_set(&(qcmd->on_heap), 1);
+
+ if (copy_from_user(&(qcmd->error_code), arg, sizeof(uint32_t))) {
+ ERR_COPY_FROM_USER();
+ free_qcmd(qcmd);
+ return -EFAULT;
+ }
+
+ pr_err("%s: Enqueue Fake Frame with error code = %d\n", __func__,
+ qcmd->error_code);
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ return 0;
+}
+
+static int msm_set_fd_roi(struct msm_sync *sync, void __user *arg)
+{
+ struct fd_roi_info fd_roi;
+
+ mutex_lock(&sync->lock);
+ if (copy_from_user(&fd_roi,
+ arg,
+ sizeof(struct fd_roi_info))) {
+ ERR_COPY_FROM_USER();
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+ if (fd_roi.info_len <= 0) {
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+
+ if (!sync->fdroiinfo.info) {
+ sync->fdroiinfo.info = kmalloc(fd_roi.info_len, GFP_KERNEL);
+ if (!sync->fdroiinfo.info) {
+ mutex_unlock(&sync->lock);
+ return -ENOMEM;
+ }
+ sync->fdroiinfo.info_len = fd_roi.info_len;
+ } else if (sync->fdroiinfo.info_len < fd_roi.info_len) {
+ mutex_unlock(&sync->lock);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(sync->fdroiinfo.info,
+ fd_roi.info,
+ fd_roi.info_len)) {
+ ERR_COPY_FROM_USER();
+ kfree(sync->fdroiinfo.info);
+ sync->fdroiinfo.info = NULL;
+ mutex_unlock(&sync->lock);
+ return -EFAULT;
+ }
+ mutex_unlock(&sync->lock);
+ return 0;
+}
+
+static int msm_pp_grab(struct msm_sync *sync, void __user *arg)
+{
+ uint32_t enable;
+ if (copy_from_user(&enable, arg, sizeof(enable))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ } else {
+ enable &= PP_MASK;
+ if (enable & (enable - 1)) {
+ pr_err("%s: error: more than one PP request!\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (sync->pp_mask) {
+ if (enable) {
+ pr_err("%s: postproc %x is already enabled\n",
+ __func__, sync->pp_mask & enable);
+ return -EINVAL;
+ } else {
+ sync->pp_mask &= enable;
+ CDBG("%s: sync->pp_mask %d enable %d\n",
+ __func__, sync->pp_mask, enable);
+ return 0;
+ }
+ }
+
+ CDBG("%s: sync->pp_mask %d enable %d\n", __func__,
+ sync->pp_mask, enable);
+ sync->pp_mask |= enable;
+ }
+
+ return 0;
+}
+
+static int msm_put_st_frame(struct msm_sync *sync, void __user *arg)
+{
+ unsigned long flags;
+ unsigned long st_pphy;
+ if (sync->stereocam_enabled) {
+ /* Make stereo frame ready for VPE. */
+ struct msm_st_frame stereo_frame_half;
+
+ if (copy_from_user(&stereo_frame_half, arg,
+ sizeof(stereo_frame_half))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ if (stereo_frame_half.type == OUTPUT_TYPE_ST_L) {
+ struct msm_vfe_resp *vfe_rp;
+ struct msm_queue_cmd *qcmd;
+
+ spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+ if (!sync->pp_stereocam) {
+ pr_warning("%s: no stereo frame to deliver!\n",
+ __func__);
+ spin_unlock_irqrestore(&pp_stereocam_spinlock,
+ flags);
+ return -EINVAL;
+ }
+ CDBG("%s: delivering left frame to VPE\n", __func__);
+
+ qcmd = sync->pp_stereocam;
+ sync->pp_stereocam = NULL;
+ spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+ vfe_rp = (struct msm_vfe_resp *)qcmd->command;
+
+ CDBG("%s: Left Py = 0x%x y_off = %d cbcr_off = %d\n",
+ __func__, vfe_rp->phy.y_phy,
+ stereo_frame_half.L.buf_y_off,
+ stereo_frame_half.L.buf_cbcr_off);
+
+ sync->vpefn.vpe_cfg_offset(stereo_frame_half.packing,
+ vfe_rp->phy.y_phy + stereo_frame_half.L.buf_y_off,
+ vfe_rp->phy.y_phy + stereo_frame_half.L.buf_cbcr_off,
+ &(qcmd->ts), OUTPUT_TYPE_ST_L, stereo_frame_half.L,
+ stereo_frame_half.frame_id);
+
+ free_qcmd(qcmd);
+ } else if (stereo_frame_half.type == OUTPUT_TYPE_ST_R) {
+ CDBG("%s: delivering right frame to VPE\n", __func__);
+ spin_lock_irqsave(&st_frame_spinlock, flags);
+
+ sync->stcam_conv_value =
+ stereo_frame_half.buf_info.stcam_conv_value;
+ sync->stcam_quality_ind =
+ stereo_frame_half.buf_info.stcam_quality_ind;
+
+ st_pphy = msm_pmem_frame_vtop_lookup(sync,
+ stereo_frame_half.buf_info.buffer,
+ stereo_frame_half.buf_info.y_off,
+ stereo_frame_half.buf_info.cbcr_off,
+ stereo_frame_half.buf_info.fd,
+ 0); /* Do not change the active flag. */
+
+ sync->vpefn.vpe_cfg_offset(stereo_frame_half.packing,
+ st_pphy + stereo_frame_half.R.buf_y_off,
+ st_pphy + stereo_frame_half.R.buf_cbcr_off,
+ NULL, OUTPUT_TYPE_ST_R, stereo_frame_half.R,
+ stereo_frame_half.frame_id);
+
+ spin_unlock_irqrestore(&st_frame_spinlock, flags);
+ } else {
+ CDBG("%s: Invalid Msg\n", __func__);
+ }
+ }
+
+ return 0;
+}
+
+static struct msm_queue_cmd *msm_get_pp_qcmd(struct msm_frame* frame)
+{
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(struct msm_queue_cmd) +
+ sizeof(struct msm_frame), GFP_ATOMIC);
+ qcmd->command = (struct msm_frame *)(qcmd + 1);
+
+ qcmd->type = MSM_CAM_Q_PP_MSG;
+
+ ktime_get_ts(&(qcmd->ts));
+ memcpy(qcmd->command, frame, sizeof(struct msm_frame));
+ atomic_set(&(qcmd->on_heap), 1);
+ return qcmd;
+}
+
+static int msm_pp_release(struct msm_sync *sync, void __user *arg)
+{
+ unsigned long flags;
+
+ if (!sync->pp_mask) {
+ pr_warning("%s: pp not in progress for\n", __func__);
+ return -EINVAL;
+ }
+ if (sync->pp_mask & PP_PREV) {
+
+
+ spin_lock_irqsave(&pp_prev_spinlock, flags);
+ if (!sync->pp_prev) {
+ pr_err("%s: no preview frame to deliver!\n",
+ __func__);
+ spin_unlock_irqrestore(&pp_prev_spinlock,
+ flags);
+ return -EINVAL;
+ }
+ CDBG("%s: delivering pp_prev\n", __func__);
+
+ if (sync->frame_q.len <= 100 &&
+ sync->event_q.len <= 100) {
+ msm_enqueue(&sync->frame_q,
+ &sync->pp_prev->list_frame);
+ } else {
+ pr_err("%s, Error Queue limit exceeded f_q=%d,\
+ e_q = %d\n",
+ __func__, sync->frame_q.len,
+ sync->event_q.len);
+ free_qcmd(sync->pp_prev);
+ goto done;
+ }
+
+ sync->pp_prev = NULL;
+ spin_unlock_irqrestore(&pp_prev_spinlock, flags);
+ goto done;
+ }
+
+ if ((sync->pp_mask & PP_SNAP) ||
+ (sync->pp_mask & PP_RAW_SNAP)) {
+ struct msm_frame frame;
+ struct msm_queue_cmd *qcmd;
+
+ if (copy_from_user(&frame,
+ arg,
+ sizeof(struct msm_frame))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+ qcmd = msm_get_pp_qcmd(&frame);
+ if (!qcmd) {
+ pr_err("%s: no snapshot to deliver!\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s: delivering pp snap\n", __func__);
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ }
+
+done:
+ return 0;
+}
+
+static long msm_ioctl_common(struct msm_cam_device *pmsm,
+ unsigned int cmd,
+ void __user *argp)
+{
+ switch (cmd) {
+ case MSM_CAM_IOCTL_REGISTER_PMEM:
+ CDBG("%s cmd = MSM_CAM_IOCTL_REGISTER_PMEM\n", __func__);
+ return msm_register_pmem(pmsm->sync, argp);
+ case MSM_CAM_IOCTL_UNREGISTER_PMEM:
+ CDBG("%s cmd = MSM_CAM_IOCTL_UNREGISTER_PMEM\n", __func__);
+ return msm_pmem_table_del(pmsm->sync, argp);
+ case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER:
+ CDBG("%s cmd = MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER\n", __func__);
+ return msm_put_frame_buffer(pmsm->sync, argp);
+ break;
+ default:
+ CDBG("%s cmd invalid\n", __func__);
+ 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_cam_device *pmsm = filep->private_data;
+
+ CDBG("%s: cmd %d\n", __func__, _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_CONFIG_VPE:
+ /* Coming from config thread for update */
+ rc = msm_config_vpe(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:
+ case MSM_CAM_IOCTL_AXI_VPE_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_SET_FD_ROI:
+ rc = msm_set_fd_roi(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_PICT_PP:
+ /* Grab one preview frame or one snapshot
+ * frame.
+ */
+ rc = msm_pp_grab(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_PICT_PP_DONE:
+ /* Release the preview of snapshot frame
+ * that was grabbed.
+ */
+ rc = msm_pp_release(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_PUT_ST_FRAME:
+ /* Release the left or right frame
+ * that was sent for stereo processing.
+ */
+ rc = msm_put_st_frame(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(pmsm->sync->
+ sdata->flash_data, led_state);
+ break;
+ }
+
+ case MSM_CAM_IOCTL_STROBE_FLASH_CFG: {
+ uint32_t flash_type;
+ if (copy_from_user(&flash_type, argp, sizeof(flash_type))) {
+ pr_err("msm_strobe_flash_init failed");
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else {
+ CDBG("msm_strobe_flash_init enter");
+ rc = msm_strobe_flash_init(pmsm->sync, flash_type);
+ }
+ break;
+ }
+
+ case MSM_CAM_IOCTL_STROBE_FLASH_RELEASE:
+ if (pmsm->sync->sdata->strobe_flash_data) {
+ rc = pmsm->sync->sfctrl.strobe_flash_release(
+ pmsm->sync->sdata->strobe_flash_data, 0);
+ }
+ break;
+
+ case MSM_CAM_IOCTL_STROBE_FLASH_CHARGE: {
+ uint32_t charge_en;
+ if (copy_from_user(&charge_en, argp, sizeof(charge_en))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else
+ rc = pmsm->sync->sfctrl.strobe_flash_charge(
+ pmsm->sync->sdata->strobe_flash_data->flash_charge,
+ charge_en, pmsm->sync->sdata->strobe_flash_data->
+ flash_recharge_duration);
+ break;
+ }
+
+ case MSM_CAM_IOCTL_FLASH_CTRL: {
+ struct flash_ctrl_data flash_info;
+ if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else
+ rc = msm_flash_ctrl(pmsm->sync->sdata, &flash_info);
+
+ break;
+ }
+
+ case MSM_CAM_IOCTL_ERROR_CONFIG:
+ rc = msm_error_config(pmsm->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_ABORT_CAPTURE: {
+ unsigned long flags = 0;
+ CDBG("get_pic:MSM_CAM_IOCTL_ABORT_CAPTURE\n");
+ spin_lock_irqsave(&pmsm->sync->abort_pict_lock, flags);
+ pmsm->sync->get_pic_abort = 1;
+ spin_unlock_irqrestore(&pmsm->sync->abort_pict_lock, flags);
+ wake_up(&(pmsm->sync->pict_q.wait));
+ rc = 0;
+ break;
+ }
+
+ default:
+ rc = msm_ioctl_common(pmsm, cmd, argp);
+ break;
+ }
+
+ CDBG("%s: cmd %d DONE\n", __func__, _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_cam_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 int msm_unblock_poll_pic(struct msm_sync *sync);
+static long msm_ioctl_pic(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct msm_cam_device *pmsm = filep->private_data;
+
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_GET_PICTURE:
+ rc = msm_get_pic(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_RELEASE_PIC_BUFFER:
+ rc = msm_put_pic_buffer(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_UNBLOCK_POLL_PIC_FRAME:
+ rc = msm_unblock_poll_pic(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_cam_device *pmsm = ctrl_pmsm->pmsm;
+
+ switch (cmd) {
+ case MSM_CAM_IOCTL_CTRL_COMMAND:
+ /* Coming from control thread, may need to wait for
+ * command status */
+ CDBG("calling msm_control kernel msm_ioctl_control\n");
+ mutex_lock(&ctrl_cmd_lock);
+ rc = msm_control(ctrl_pmsm, 1, argp);
+ mutex_unlock(&ctrl_cmd_lock);
+ 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_SENSOR_INFO:
+ rc = msm_get_sensor_info(pmsm->sync, argp);
+ break;
+ case MSM_CAM_IOCTL_GET_CAMERA_INFO:
+ rc = msm_get_camera_info(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--;
+ pr_info("%s, open count =%d\n", __func__, sync->opencnt);
+ if (!sync->opencnt) {
+ /* need to clean up system resource */
+ pr_info("%s, release VFE\n", __func__);
+ if (sync->core_powered_on) {
+ if (sync->vfefn.vfe_release)
+ sync->vfefn.vfe_release(sync->pdev);
+ /*sensor release */
+ pr_info("%s, release Sensor\n", __func__);
+ sync->sctrl.s_release();
+ CDBG("%s, msm_camio_sensor_clk_off\n", __func__);
+ msm_camio_sensor_clk_off(sync->pdev);
+ if (sync->sfctrl.strobe_flash_release) {
+ CDBG("%s, strobe_flash_release\n", __func__);
+ sync->sfctrl.strobe_flash_release(
+ sync->sdata->strobe_flash_data, 1);
+ }
+ }
+ kfree(sync->cropinfo);
+ sync->cropinfo = NULL;
+ sync->croplen = 0;
+ CDBG("%s, free frame pmem region\n", __func__);
+ hlist_for_each_entry_safe(region, hnode, n,
+ &sync->pmem_frames, list) {
+ hlist_del(hnode);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ CDBG("%s, free stats pmem region\n", __func__);
+ hlist_for_each_entry_safe(region, hnode, n,
+ &sync->pmem_stats, list) {
+ hlist_del(hnode);
+ put_pmem_file(region->file);
+ kfree(region);
+ }
+ msm_queue_drain(&sync->pict_q, list_pict);
+ msm_queue_drain(&sync->event_q, list_config);
+
+ wake_unlock(&sync->wake_lock);
+ sync->apps_id = NULL;
+ sync->core_powered_on = 0;
+ }
+ mutex_unlock(&sync->lock);
+
+ return 0;
+}
+
+static int msm_release_config(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_cam_device *pmsm = filep->private_data;
+ pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&pmsm->sync->event_q, list_config);
+ 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_cam_device *pmsm = ctrl_pmsm->pmsm;
+ pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ g_v4l2_opencnt--;
+ mutex_lock(&pmsm->sync->lock);
+ if (pmsm->sync->core_powered_on && pmsm->sync->vfefn.vfe_stop) {
+ pr_info("%s, stop vfe if active\n", __func__);
+ pmsm->sync->vfefn.vfe_stop();
+ }
+ mutex_unlock(&pmsm->sync->lock);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control);
+ kfree(ctrl_pmsm);
+ }
+ return rc;
+}
+
+static int msm_release_frame(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_cam_device *pmsm = filep->private_data;
+ pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&pmsm->sync->frame_q, list_frame);
+ atomic_set(&pmsm->opened, 0);
+ }
+ return rc;
+}
+
+
+static int msm_release_pic(struct inode *node, struct file *filep)
+{
+ int rc;
+ struct msm_cam_device *pmsm = filep->private_data;
+ CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name);
+ rc = __msm_release(pmsm->sync);
+ if (!rc) {
+ msm_queue_drain(&pmsm->sync->pict_q, list_pict);
+ atomic_set(&pmsm->opened, 0);
+ }
+ return rc;
+}
+
+static int msm_unblock_poll_pic(struct msm_sync *sync)
+{
+ unsigned long flags;
+ CDBG("%s\n", __func__);
+ spin_lock_irqsave(&sync->pict_q.lock, flags);
+ sync->unblock_poll_pic_frame = 1;
+ wake_up(&sync->pict_q.wait);
+ spin_unlock_irqrestore(&sync->pict_q.lock, flags);
+ return 0;
+}
+
+static int msm_unblock_poll_frame(struct msm_sync *sync)
+{
+ unsigned long flags;
+ CDBG("%s\n", __func__);
+ spin_lock_irqsave(&sync->frame_q.lock, flags);
+ sync->unblock_poll_frame = 1;
+ wake_up(&sync->frame_q.wait);
+ spin_unlock_irqrestore(&sync->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->frame_q.wait, pll_table);
+
+ spin_lock_irqsave(&sync->frame_q.lock, flags);
+ if (!list_empty_careful(&sync->frame_q.list))
+ /* 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->frame_q.lock, flags);
+
+ return rc;
+}
+
+static unsigned int __msm_poll_pic(struct msm_sync *sync,
+ struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ poll_wait(filep, &sync->pict_q.wait , pll_table);
+ spin_lock_irqsave(&sync->abort_pict_lock, flags);
+ if (sync->get_pic_abort == 1) {
+ /* TODO: need to pass an error case */
+ sync->get_pic_abort = 0;
+ }
+ spin_unlock_irqrestore(&sync->abort_pict_lock, flags);
+
+ spin_lock_irqsave(&sync->pict_q.lock, flags);
+ if (!list_empty_careful(&sync->pict_q.list))
+ /* frame ready */
+ rc = POLLIN | POLLRDNORM;
+ if (sync->unblock_poll_pic_frame) {
+ CDBG("%s: sync->unblock_poll_pic_frame is true\n", __func__);
+ rc |= POLLPRI;
+ sync->unblock_poll_pic_frame = 0;
+ }
+ spin_unlock_irqrestore(&sync->pict_q.lock, flags);
+
+ return rc;
+}
+
+static unsigned int msm_poll_frame(struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ struct msm_cam_device *pmsm = filep->private_data;
+ return __msm_poll_frame(pmsm->sync, filep, pll_table);
+}
+
+static unsigned int msm_poll_pic(struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ struct msm_cam_device *pmsm = filep->private_data;
+ return __msm_poll_pic(pmsm->sync, filep, pll_table);
+}
+
+static unsigned int __msm_poll_config(struct msm_sync *sync,
+ struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ poll_wait(filep, &sync->event_q.wait, pll_table);
+
+ spin_lock_irqsave(&sync->event_q.lock, flags);
+ if (!list_empty_careful(&sync->event_q.list))
+ /* event ready */
+ rc = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&sync->event_q.lock, flags);
+
+ return rc;
+}
+
+static unsigned int msm_poll_config(struct file *filep,
+ struct poll_table_struct *pll_table)
+{
+ struct msm_cam_device *pmsm = filep->private_data;
+ return __msm_poll_config(pmsm->sync, filep, pll_table);
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+static void *msm_vfe_sync_alloc(int size,
+ void *syncdata __attribute__((unused)),
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd =
+ kzalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+ if (qcmd) {
+ atomic_set(&qcmd->on_heap, 1);
+ return qcmd + 1;
+ }
+ return NULL;
+}
+
+static void *msm_vpe_sync_alloc(int size,
+ void *syncdata __attribute__((unused)),
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd =
+ kzalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+ if (qcmd) {
+ atomic_set(&qcmd->on_heap, 1);
+ return qcmd + 1;
+ }
+ return NULL;
+}
+
+static void msm_vfe_sync_free(void *ptr)
+{
+ if (ptr) {
+ struct msm_queue_cmd *qcmd =
+ (struct msm_queue_cmd *)ptr;
+ qcmd--;
+ if (atomic_read(&qcmd->on_heap))
+ kfree(qcmd);
+ }
+}
+
+static void msm_vpe_sync_free(void *ptr)
+{
+ if (ptr) {
+ struct msm_queue_cmd *qcmd =
+ (struct msm_queue_cmd *)ptr;
+ qcmd--;
+ if (atomic_read(&qcmd->on_heap))
+ kfree(qcmd);
+ }
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+static void msm_vfe_sync(struct msm_vfe_resp *vdata,
+ enum msm_queue qtype, void *syncdata,
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_sync *sync = (struct msm_sync *)syncdata;
+ unsigned long flags;
+
+ if (!sync) {
+ pr_err("%s: no context in dsp callback.\n", __func__);
+ return;
+ }
+
+ qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+ qcmd->type = qtype;
+ qcmd->command = vdata;
+
+ ktime_get_ts(&(qcmd->ts));
+
+ if (qtype != MSM_CAM_Q_VFE_MSG)
+ goto vfe_for_config;
+
+ CDBG("%s: vdata->type %d\n", __func__, vdata->type);
+
+ switch (vdata->type) {
+ case VFE_MSG_OUTPUT_P:
+ if (sync->pp_mask & PP_PREV) {
+ CDBG("%s: PP_PREV in progress: phy_y %x phy_cbcr %x\n",
+ __func__,
+ vdata->phy.y_phy,
+ vdata->phy.cbcr_phy);
+ spin_lock_irqsave(&pp_prev_spinlock, flags);
+ if (sync->pp_prev)
+ CDBG("%s: overwriting pp_prev!\n",
+ __func__);
+ CDBG("%s: sending preview to config\n", __func__);
+ sync->pp_prev = qcmd;
+ spin_unlock_irqrestore(&pp_prev_spinlock, flags);
+ sync->pp_frame_avail = 1;
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ break;
+ }
+ CDBG("%s: msm_enqueue frame_q\n", __func__);
+ if (sync->stereocam_enabled)
+ CDBG("%s: Enqueue VFE_MSG_OUTPUT_P to event_q for "
+ "stereo processing\n", __func__);
+ else {
+ if (sync->frame_q.len <= 100 &&
+ sync->event_q.len <= 100) {
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ } else {
+ pr_err("%s, Error Queue limit exceeded "
+ "f_q = %d, e_q = %d\n", __func__,
+ sync->frame_q.len, sync->event_q.len);
+ free_qcmd(qcmd);
+ return;
+ }
+ }
+ break;
+
+ case VFE_MSG_OUTPUT_T:
+ if (sync->stereocam_enabled) {
+ spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+ /* if out1/2 is currently in progress, save the qcmd
+ and issue only ionce the 1st one completes the 3D
+ pipeline */
+ if (STEREO_SNAP_BUFFER1_PROCESSING ==
+ sync->stereo_state) {
+ sync->pp_stereocam2 = qcmd;
+ spin_unlock_irqrestore(&pp_stereocam_spinlock,
+ flags);
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: snapshot stereo in progress\n",
+ __func__);
+ return;
+ }
+
+ if (sync->pp_stereocam)
+ CDBG("%s: overwriting pp_stereocam!\n",
+ __func__);
+
+ CDBG("%s: sending stereo frame to config\n", __func__);
+ sync->pp_stereocam = qcmd;
+ sync->stereo_state =
+ STEREO_SNAP_BUFFER1_PROCESSING;
+
+ spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+ /* Increament on_heap by one because the same qcmd will
+ be used for VPE in msm_pp_release. */
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: Enqueue VFE_MSG_OUTPUT_T to event_q for "
+ "stereo processing.\n", __func__);
+ break;
+ }
+ if (sync->pp_mask & PP_SNAP) {
+ CDBG("%s: pp sending thumbnail to config\n",
+ __func__);
+ } else {
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ return;
+ }
+
+ case VFE_MSG_OUTPUT_S:
+ if (sync->stereocam_enabled &&
+ sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+ spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+ /* if out1/2 is currently in progress, save the qcmd
+ and issue only once the 1st one completes the 3D
+ pipeline */
+ if (STEREO_SNAP_BUFFER1_PROCESSING ==
+ sync->stereo_state) {
+ sync->pp_stereocam2 = qcmd;
+ spin_unlock_irqrestore(&pp_stereocam_spinlock,
+ flags);
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: snapshot stereo in progress\n",
+ __func__);
+ return;
+ }
+ if (sync->pp_stereocam)
+ CDBG("%s: overwriting pp_stereocam!\n",
+ __func__);
+
+ CDBG("%s: sending stereo frame to config\n", __func__);
+ sync->pp_stereocam = qcmd;
+ sync->stereo_state =
+ STEREO_SNAP_BUFFER1_PROCESSING;
+
+ spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+ /* Increament on_heap by one because the same qcmd will
+ be used for VPE in msm_pp_release. */
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: Enqueue VFE_MSG_OUTPUT_S to event_q for "
+ "stereo processing.\n", __func__);
+ break;
+ }
+ if (sync->pp_mask & PP_SNAP) {
+ CDBG("%s: pp sending main image to config\n",
+ __func__);
+ } else {
+ CDBG("%s: enqueue to picture queue\n", __func__);
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ return;
+ }
+ break;
+
+ case VFE_MSG_OUTPUT_V:
+ if (sync->stereocam_enabled) {
+ spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+
+ if (sync->pp_stereocam)
+ CDBG("%s: overwriting pp_stereocam!\n",
+ __func__);
+
+ CDBG("%s: sending stereo frame to config\n", __func__);
+ sync->pp_stereocam = qcmd;
+
+ spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+
+ /* Increament on_heap by one because the same qcmd will
+ be used for VPE in msm_pp_release. */
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: Enqueue VFE_MSG_OUTPUT_V to event_q for "
+ "stereo processing.\n", __func__);
+ break;
+ }
+ if (sync->vpefn.vpe_cfg_update) {
+ CDBG("dis_en = %d\n", *sync->vpefn.dis);
+ if (*(sync->vpefn.dis)) {
+ memset(&(vdata->vpe_bf), 0,
+ sizeof(vdata->vpe_bf));
+
+ if (sync->cropinfo != NULL)
+ vdata->vpe_bf.vpe_crop =
+ *(struct video_crop_t *)(sync->cropinfo);
+
+ vdata->vpe_bf.y_phy = vdata->phy.y_phy;
+ vdata->vpe_bf.cbcr_phy = vdata->phy.cbcr_phy;
+ vdata->vpe_bf.ts = (qcmd->ts);
+ vdata->vpe_bf.frame_id = vdata->phy.frame_id;
+ qcmd->command = vdata;
+ msm_enqueue_vpe(&sync->vpe_q,
+ &qcmd->list_vpe_frame);
+ return;
+ } else if (sync->vpefn.vpe_cfg_update(sync->cropinfo)) {
+ CDBG("%s: msm_enqueue video frame to vpe time "
+ "= %ld\n", __func__, qcmd->ts.tv_nsec);
+
+ sync->vpefn.send_frame_to_vpe(
+ vdata->phy.y_phy,
+ vdata->phy.cbcr_phy,
+ &(qcmd->ts), OUTPUT_TYPE_V);
+
+ free_qcmd(qcmd);
+ return;
+ } else {
+ CDBG("%s: msm_enqueue video frame_q\n",
+ __func__);
+ if (sync->liveshot_enabled) {
+ CDBG("%s: msm_enqueue liveshot\n",
+ __func__);
+ vdata->phy.output_id |= OUTPUT_TYPE_L;
+ sync->liveshot_enabled = false;
+ }
+ if (sync->frame_q.len <= 100 &&
+ sync->event_q.len <= 100) {
+ msm_enqueue(&sync->frame_q,
+ &qcmd->list_frame);
+ } else {
+ pr_err("%s, Error Queue limit exceeded\
+ f_q = %d, e_q = %d\n",
+ __func__, sync->frame_q.len,
+ sync->event_q.len);
+ free_qcmd(qcmd);
+ }
+
+ return;
+ }
+ } else {
+ CDBG("%s: msm_enqueue video frame_q\n", __func__);
+ if (sync->frame_q.len <= 100 &&
+ sync->event_q.len <= 100) {
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ } else {
+ pr_err("%s, Error Queue limit exceeded\
+ f_q = %d, e_q = %d\n",
+ __func__, sync->frame_q.len,
+ sync->event_q.len);
+ free_qcmd(qcmd);
+ }
+
+ return;
+ }
+
+ case VFE_MSG_SNAPSHOT:
+ if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) {
+ CDBG("%s: PP_SNAP in progress: pp_mask %x\n",
+ __func__, sync->pp_mask);
+ } else {
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ CDBG("%s: VFE_MSG_SNAPSHOT store\n",
+ __func__);
+ if (sync->stereocam_enabled &&
+ sync->stereo_state != STEREO_RAW_SNAP_STARTED) {
+ sync->pp_stereosnap = qcmd;
+ return;
+ }
+ }
+ break;
+
+ case VFE_MSG_COMMON:
+ CDBG("%s: qtype %d, comp stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_GENERAL:
+ CDBG("%s: qtype %d, general msg, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ default:
+ CDBG("%s: qtype %d not handled\n", __func__, vdata->type);
+ /* fall through, send to config. */
+ }
+
+vfe_for_config:
+ CDBG("%s: msm_enqueue event_q\n", __func__);
+ if (sync->frame_q.len <= 100 && sync->event_q.len <= 100) {
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ } else {
+ pr_err("%s, Error Queue limit exceeded f_q = %d, e_q = %d\n",
+ __func__, sync->frame_q.len, sync->event_q.len);
+ free_qcmd(qcmd);
+ }
+
+}
+
+static void msm_vpe_sync(struct msm_vpe_resp *vdata,
+ enum msm_queue qtype, void *syncdata, void *ts, gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd = NULL;
+ unsigned long flags;
+
+ struct msm_sync *sync = (struct msm_sync *)syncdata;
+ if (!sync) {
+ pr_err("%s: no context in dsp callback.\n", __func__);
+ return;
+ }
+
+ qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+ qcmd->type = qtype;
+ qcmd->command = vdata;
+ qcmd->ts = *((struct timespec *)ts);
+
+ if (qtype != MSM_CAM_Q_VPE_MSG) {
+ pr_err("%s: Invalid qcmd type = %d.\n", __func__, qcmd->type);
+ free_qcmd(qcmd);
+ return;
+ }
+
+ CDBG("%s: vdata->type %d\n", __func__, vdata->type);
+ switch (vdata->type) {
+ case VPE_MSG_OUTPUT_V:
+ if (sync->liveshot_enabled) {
+ CDBG("%s: msm_enqueue liveshot %d\n", __func__,
+ sync->liveshot_enabled);
+ vdata->phy.output_id |= OUTPUT_TYPE_L;
+ sync->liveshot_enabled = false;
+ }
+ if (sync->frame_q.len <= 100 && sync->event_q.len <= 100) {
+ CDBG("%s: enqueue to frame_q from VPE\n", __func__);
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ } else {
+ pr_err("%s, Error Queue limit exceeded f_q = %d, "
+ "e_q = %d\n", __func__, sync->frame_q.len,
+ sync->event_q.len);
+ free_qcmd(qcmd);
+ }
+ return;
+
+ case VPE_MSG_OUTPUT_ST_L:
+ CDBG("%s: enqueue left frame done msg to event_q from VPE\n",
+ __func__);
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ return;
+
+ case VPE_MSG_OUTPUT_ST_R:
+ spin_lock_irqsave(&pp_stereocam_spinlock, flags);
+ CDBG("%s: received VPE_MSG_OUTPUT_ST_R state %d\n", __func__,
+ sync->stereo_state);
+
+ if (STEREO_SNAP_BUFFER1_PROCESSING == sync->stereo_state) {
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ qcmd = sync->pp_stereocam2;
+ sync->pp_stereocam = sync->pp_stereocam2;
+ sync->pp_stereocam2 = NULL;
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ sync->stereo_state =
+ STEREO_SNAP_BUFFER2_PROCESSING;
+ } else if (STEREO_SNAP_BUFFER2_PROCESSING ==
+ sync->stereo_state) {
+ sync->stereo_state = STEREO_SNAP_IDLE;
+ /* Send snapshot DONE */
+ msm_enqueue(&sync->pict_q, &qcmd->list_pict);
+ qcmd = sync->pp_stereosnap;
+ sync->pp_stereosnap = NULL;
+ CDBG("%s: send SNAPSHOT_DONE message\n", __func__);
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ } else {
+ if (atomic_read(&qcmd->on_heap))
+ atomic_add(1, &qcmd->on_heap);
+ msm_enqueue(&sync->event_q, &qcmd->list_config);
+ if (sync->stereo_state == STEREO_VIDEO_ACTIVE) {
+ CDBG("%s: st frame to frame_q from VPE\n",
+ __func__);
+ msm_enqueue(&sync->frame_q, &qcmd->list_frame);
+ }
+ }
+ spin_unlock_irqrestore(&pp_stereocam_spinlock, flags);
+ return;
+
+ default:
+ pr_err("%s: qtype %d not handled\n", __func__, vdata->type);
+ }
+ pr_err("%s: Should not come here. Error.\n", __func__);
+}
+
+static struct msm_vpe_callback msm_vpe_s = {
+ .vpe_resp = msm_vpe_sync,
+ .vpe_alloc = msm_vpe_sync_alloc,
+ .vpe_free = msm_vpe_sync_free,
+};
+
+static struct msm_vfe_callback msm_vfe_s = {
+ .vfe_resp = msm_vfe_sync,
+ .vfe_alloc = msm_vfe_sync_alloc,
+ .vfe_free = msm_vfe_sync_free,
+};
+
+static int __msm_open(struct msm_cam_device *pmsm, const char *const apps_id,
+ int is_controlnode)
+{
+ int rc = 0;
+ struct msm_sync *sync = pmsm->sync;
+
+ mutex_lock(&sync->lock);
+ if (sync->apps_id && strcmp(sync->apps_id, apps_id)
+ && (!strcmp(MSM_APPS_ID_V4L2, apps_id))) {
+ pr_err("%s(%s): sensor %s is already opened for %s\n",
+ __func__,
+ apps_id,
+ sync->sdata->sensor_name,
+ sync->apps_id);
+ rc = -EBUSY;
+ goto msm_open_done;
+ }
+
+ sync->apps_id = apps_id;
+
+ if (!sync->core_powered_on && !is_controlnode) {
+ wake_lock(&sync->wake_lock);
+
+ msm_camvfe_fn_init(&sync->vfefn, sync);
+ if (sync->vfefn.vfe_init) {
+ sync->pp_frame_avail = 0;
+ sync->get_pic_abort = 0;
+ rc = msm_camio_sensor_clk_on(sync->pdev);
+ if (rc < 0) {
+ pr_err("%s: setting sensor clocks failed: %d\n",
+ __func__, rc);
+ goto msm_open_err;
+ }
+ rc = sync->sctrl.s_init(sync->sdata);
+ if (rc < 0) {
+ pr_err("%s: sensor init failed: %d\n",
+ __func__, rc);
+ msm_camio_sensor_clk_off(sync->pdev);
+ goto msm_open_err;
+ }
+ rc = sync->vfefn.vfe_init(&msm_vfe_s,
+ sync->pdev);
+ if (rc < 0) {
+ pr_err("%s: vfe_init failed at %d\n",
+ __func__, rc);
+ sync->sctrl.s_release();
+ msm_camio_sensor_clk_off(sync->pdev);
+ goto msm_open_err;
+ }
+ } else {
+ pr_err("%s: no sensor init func\n", __func__);
+ rc = -ENODEV;
+ goto msm_open_err;
+ }
+ msm_camvpe_fn_init(&sync->vpefn, sync);
+
+ spin_lock_init(&sync->abort_pict_lock);
+ spin_lock_init(&pp_prev_spinlock);
+ spin_lock_init(&pp_stereocam_spinlock);
+ spin_lock_init(&st_frame_spinlock);
+ if (rc >= 0) {
+ msm_region_init(sync);
+ if (sync->vpefn.vpe_reg)
+ sync->vpefn.vpe_reg(&msm_vpe_s);
+ sync->unblock_poll_frame = 0;
+ sync->unblock_poll_pic_frame = 0;
+ }
+ sync->core_powered_on = 1;
+ }
+ sync->opencnt++;
+
+msm_open_done:
+ mutex_unlock(&sync->lock);
+ return rc;
+
+msm_open_err:
+ atomic_set(&pmsm->opened, 0);
+ mutex_unlock(&sync->lock);
+ return rc;
+}
+
+static int msm_open_common(struct inode *inode, struct file *filep,
+ int once, int is_controlnode)
+{
+ int rc;
+ struct msm_cam_device *pmsm =
+ container_of(inode->i_cdev, struct msm_cam_device, cdev);
+
+ CDBG("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name);
+
+ if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) {
+ pr_err("%s: %s is already opened.\n",
+ __func__,
+ filep->f_path.dentry->d_name.name);
+ return -EBUSY;
+ }
+
+ rc = nonseekable_open(inode, filep);
+ if (rc < 0) {
+ pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = __msm_open(pmsm, MSM_APPS_ID_PROP, is_controlnode);
+ if (rc < 0)
+ return rc;
+ filep->private_data = pmsm;
+ CDBG("%s: rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_open(struct inode *inode, struct file *filep)
+{
+ return msm_open_common(inode, filep, 1, 0);
+}
+
+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, 1);
+ if (rc < 0) {
+ kfree(ctrl_pmsm);
+ return rc;
+ }
+ ctrl_pmsm->pmsm = filep->private_data;
+ filep->private_data = ctrl_pmsm;
+
+ msm_queue_init(&ctrl_pmsm->ctrl_q, "control");
+
+ if (!g_v4l2_opencnt)
+ g_v4l2_control_device = ctrl_pmsm;
+ g_v4l2_opencnt++;
+ CDBG("%s: rc %d\n", __func__, 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,
+ .poll = msm_poll_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 const struct file_operations msm_fops_pic = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .unlocked_ioctl = msm_ioctl_pic,
+ .release = msm_release_pic,
+ .poll = msm_poll_pic,
+};
+
+static int msm_setup_cdev(struct msm_cam_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("%s: error creating device: %d\n", __func__, 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("%s: error adding cdev: %d\n", __func__, rc);
+ device_destroy(msm_class, devno);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_tear_down_cdev(struct msm_cam_device *msm, dev_t devno)
+{
+ cdev_del(&msm->cdev);
+ device_destroy(msm_class, devno);
+ return 0;
+}
+
+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;
+
+ msm_queue_init(&sync->event_q, "event");
+ msm_queue_init(&sync->frame_q, "frame");
+ msm_queue_init(&sync->pict_q, "pict");
+ msm_queue_init(&sync->vpe_q, "vpe");
+
+ wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera");
+
+ rc = msm_camio_probe_on(pdev);
+ if (rc < 0) {
+ wake_lock_destroy(&sync->wake_lock);
+ 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("%s: failed to initialize %s\n",
+ __func__,
+ sync->sdata->sensor_name);
+ wake_lock_destroy(&sync->wake_lock);
+ return rc;
+ }
+
+ sync->opencnt = 0;
+ sync->core_powered_on = 0;
+ sync->ignore_qcmd = false;
+ sync->ignore_qcmd_type = -1;
+ mutex_init(&sync->lock);
+ if (sync->sdata->strobe_flash_data) {
+ sync->sdata->strobe_flash_data->state = 0;
+ spin_lock_init(&sync->sdata->strobe_flash_data->spin_lock);
+ }
+ CDBG("%s: initialized %s\n", __func__, 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_cam_device *pmsm,
+ struct msm_sync *sync,
+ int node)
+{
+ int dev_num = 4 * node;
+ int rc = msm_setup_cdev(pmsm, node,
+ MKDEV(MAJOR(msm_devno), dev_num),
+ "control", &msm_fops_control);
+ if (rc < 0) {
+ pr_err("%s: error creating control node: %d\n", __func__, 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("%s: error creating config node: %d\n", __func__, 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("%s: error creating frame node: %d\n", __func__, 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;
+ }
+
+ rc = msm_setup_cdev(pmsm + 3, node,
+ MKDEV(MAJOR(msm_devno), dev_num + 3),
+ "pic", &msm_fops_pic);
+ if (rc < 0) {
+ pr_err("%s: error creating pic node: %d\n", __func__, 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));
+ msm_tear_down_cdev(pmsm + 2,
+ MKDEV(MAJOR(msm_devno), dev_num + 2));
+ return rc;
+ }
+
+
+ atomic_set(&pmsm[0].opened, 0);
+ atomic_set(&pmsm[1].opened, 0);
+ atomic_set(&pmsm[2].opened, 0);
+ atomic_set(&pmsm[3].opened, 0);
+
+ pmsm[0].sync = sync;
+ pmsm[1].sync = sync;
+ pmsm[2].sync = sync;
+ pmsm[3].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_cam_device *pmsm = NULL;
+ struct msm_sync *sync;
+ int rc = -ENODEV;
+
+ if (camera_node >= MSM_MAX_CAMERA_SENSORS) {
+ pr_err("%s: too many camera sensors\n", __func__);
+ return rc;
+ }
+
+ if (!msm_class) {
+ /* There are three device nodes per sensor */
+ rc = alloc_chrdev_region(&msm_devno, 0,
+ 4 * MSM_MAX_CAMERA_SENSORS,
+ "msm_camera");
+ if (rc < 0) {
+ pr_err("%s: failed to allocate chrdev: %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ msm_class = class_create(THIS_MODULE, "msm_camera");
+ if (IS_ERR(msm_class)) {
+ rc = PTR_ERR(msm_class);
+ pr_err("%s: create device class failed: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ pmsm = kzalloc(sizeof(struct msm_cam_device) * 4 +
+ sizeof(struct msm_sync), GFP_ATOMIC);
+ if (!pmsm)
+ return -ENOMEM;
+ sync = (struct msm_sync *)(pmsm + 4);
+
+ rc = msm_sync_init(sync, dev, sensor_probe);
+ if (rc < 0) {
+ kfree(pmsm);
+ return rc;
+ }
+
+ CDBG("%s: setting camera node %d\n", __func__, camera_node);
+ rc = msm_device_init(pmsm, sync, camera_node);
+ if (rc < 0) {
+ msm_sync_destroy(sync);
+ kfree(pmsm);
+ return rc;
+ }
+
+ camera_type[camera_node] = sync->sctrl.s_camera_type;
+ sensor_mount_angle[camera_node] = sync->sctrl.s_mount_angle;
+ camera_node++;
+
+ list_add(&sync->list, &msm_sensors);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_drv_start);
diff --git a/drivers/media/video/msm/msm_gemini_common.h b/drivers/media/video/msm/msm_gemini_common.h
new file mode 100644
index 0000000..0ddedc5
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_common.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_COMMON_H
+#define MSM_GEMINI_COMMON_H
+
+#define MSM_GEMINI_DEBUG
+#ifdef MSM_GEMINI_DEBUG
+#define GMN_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define GMN_DBG(fmt, args...) do { } while (0)
+#endif
+
+#define GMN_PR_ERR pr_err
+
+enum GEMINI_MODE {
+ GEMINI_MODE_DISABLE,
+ GEMINI_MODE_OFFLINE,
+ GEMINI_MODE_REALTIME,
+ GEMINI_MODE_REALTIME_ROTATION
+};
+
+enum GEMINI_ROTATION {
+ GEMINI_ROTATION_0,
+ GEMINI_ROTATION_90,
+ GEMINI_ROTATION_180,
+ GEMINI_ROTATION_270
+};
+
+#endif /* MSM_GEMINI_COMMON_H */
diff --git a/drivers/media/video/msm/msm_gemini_core.c b/drivers/media/video/msm/msm_gemini_core.c
new file mode 100644
index 0000000..58c2e7c
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_core.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static struct msm_gemini_hw_pingpong fe_pingpong_buf;
+static struct msm_gemini_hw_pingpong we_pingpong_buf;
+static int we_pingpong_index;
+static int reset_done_ack;
+static spinlock_t reset_lock;
+static wait_queue_head_t reset_wait;
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size)
+{
+ unsigned long flags;
+ int rc = 0;
+ int tm = 500; /*500ms*/
+ memset(&fe_pingpong_buf, 0, sizeof(fe_pingpong_buf));
+ fe_pingpong_buf.is_fe = 1;
+ we_pingpong_index = 0;
+ memset(&we_pingpong_buf, 0, sizeof(we_pingpong_buf));
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ msm_gemini_hw_reset(base, size);
+ spin_unlock_irqrestore(&reset_lock, flags);
+ rc = wait_event_interruptible_timeout(
+ reset_wait,
+ reset_done_ack,
+ msecs_to_jiffies(tm));
+
+ if (!reset_done_ack) {
+ GMN_DBG("%s: reset ACK failed %d", __func__, rc);
+ return -EBUSY;
+ }
+
+ GMN_DBG("%s: reset_done_ack rc %d", __func__, rc);
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ spin_unlock_irqrestore(&reset_lock, flags);
+
+ if (op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(1);
+ } else {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(0);
+ }
+
+ /* @todo wait for reset done irq */
+
+ return 0;
+}
+
+void msm_gemini_core_release(int release_buf)
+{
+ int i = 0;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf_status[i] && release_buf)
+ msm_gemini_platform_p2v(we_pingpong_buf.buf[i].file);
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+}
+
+void msm_gemini_core_init(void)
+{
+ init_waitqueue_head(&reset_wait);
+ spin_lock_init(&reset_lock);
+}
+
+int msm_gemini_core_fe_start(void)
+{
+ msm_gemini_hw_fe_start();
+ return 0;
+}
+
+/* fetch engine */
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf)
+{
+ GMN_DBG("%s:%d] 0x%08x %d 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, buf->y_len,
+ (int) buf->cbcr_buffer_addr, buf->cbcr_len);
+ return msm_gemini_hw_pingpong_update(&fe_pingpong_buf, buf);
+}
+
+void *msm_gemini_core_fe_pingpong_irq(int gemini_irq_status, void *context)
+{
+ return msm_gemini_hw_pingpong_irq(&fe_pingpong_buf);
+}
+
+/* write engine */
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf)
+{
+ int rc;
+ GMN_DBG("%s:%d] 0x%08x 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, (int) buf->cbcr_buffer_addr,
+ buf->y_len);
+ we_pingpong_buf.buf_status[we_pingpong_index] = 0;
+ we_pingpong_index = (we_pingpong_index + 1)%2;
+ rc = msm_gemini_hw_pingpong_update(&we_pingpong_buf, buf);
+ return 0;
+}
+
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf)
+{
+ int i = 0;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf[i].y_buffer_addr
+ == buf->y_buffer_addr)
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+ return 0;
+}
+
+void *msm_gemini_core_we_pingpong_irq(int gemini_irq_status, void *context)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ return msm_gemini_hw_pingpong_irq(&we_pingpong_buf);
+}
+
+void *msm_gemini_core_framedone_irq(int gemini_irq_status, void *context)
+{
+ struct msm_gemini_hw_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
+ if (buf_p) {
+ buf_p->framedone_len = msm_gemini_hw_encode_output_size();
+ GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+ buf_p->framedone_len);
+ }
+
+ return buf_p;
+}
+
+void *msm_gemini_core_reset_ack_irq(int gemini_irq_status, void *context)
+{
+ /* @todo return the status back to msm_gemini_core_reset */
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return NULL;
+}
+
+void *msm_gemini_core_err_irq(int gemini_irq_status, void *context)
+{
+ GMN_PR_ERR("%s:%d]\n", __func__, gemini_irq_status);
+ return NULL;
+}
+
+static int (*msm_gemini_irq_handler) (int, void *, void *);
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context)
+{
+ void *data = NULL;
+ unsigned long flags;
+ int gemini_irq_status;
+
+ GMN_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 1;
+ spin_unlock_irqrestore(&reset_lock, flags);
+ gemini_irq_status = msm_gemini_hw_irq_get_status();
+
+ GMN_DBG("%s:%d] gemini_irq_status = %0x\n", __func__, __LINE__,
+ gemini_irq_status);
+
+ /*For reset and framedone IRQs, clear all bits*/
+ if (gemini_irq_status & 0x400) {
+ wake_up(&reset_wait);
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else if (gemini_irq_status & 0x1) {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ gemini_irq_status);
+ }
+
+ if (msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_framedone_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_FRAMEDONE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status)) {
+ data = msm_gemini_core_fe_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_FE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) &&
+ !msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_we_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_WE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_reset_ack(gemini_irq_status)) {
+ data = msm_gemini_core_reset_ack_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_RESET_ACK,
+ context, data);
+ }
+
+ /* Unexpected/unintended HW interrupt */
+ if (msm_gemini_hw_irq_is_err(gemini_irq_status)) {
+ data = msm_gemini_core_err_irq(gemini_irq_status, context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_ERR,
+ context, data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *))
+{
+ msm_gemini_irq_handler = irq_handler;
+}
+
+void msm_gemini_core_irq_remove(void)
+{
+ msm_gemini_irq_handler = NULL;
+}
diff --git a/drivers/media/video/msm/msm_gemini_core.h b/drivers/media/video/msm/msm_gemini_core.h
new file mode 100644
index 0000000..f240505
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_core.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_CORE_H
+#define MSM_GEMINI_CORE_H
+
+#include <linux/interrupt.h>
+#include "msm_gemini_hw.h"
+
+#define msm_gemini_core_buf msm_gemini_hw_buf
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context);
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *));
+void msm_gemini_core_irq_remove(void);
+
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf);
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size);
+int msm_gemini_core_fe_start(void);
+
+void msm_gemini_core_release(int);
+void msm_gemini_core_init(void);
+#endif /* MSM_GEMINI_CORE_H */
diff --git a/drivers/media/video/msm/msm_gemini_dev.c b/drivers/media/video/msm/msm_gemini_dev.c
new file mode 100644
index 0000000..1156bb0
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_dev.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <mach/board.h>
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+
+#include <media/msm_gemini.h>
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+
+#define MSM_GEMINI_NAME "gemini"
+
+static int msm_gemini_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = container_of(inode->i_cdev,
+ struct msm_gemini_device, cdev);
+ filp->private_data = pgmn_dev;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_open(pgmn_dev);
+
+ GMN_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+
+ return rc;
+}
+
+static int msm_gemini_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG(KERN_INFO "%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_release(pgmn_dev);
+
+ GMN_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+ return rc;
+}
+
+static long msm_gemini_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG(KERN_INFO "%s:%d] cmd = %d\n", __func__, __LINE__,
+ _IOC_NR(cmd));
+
+ rc = __msm_gemini_ioctl(pgmn_dev, cmd, arg);
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct file_operations msm_gemini_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_gemini_open,
+ .release = msm_gemini_release,
+ .unlocked_ioctl = msm_gemini_ioctl,
+};
+
+static struct class *msm_gemini_class;
+static dev_t msm_gemini_devno;
+static struct msm_gemini_device *msm_gemini_device_p;
+
+static int msm_gemini_init(struct platform_device *pdev)
+{
+ int rc = -1;
+ struct device *dev;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ msm_gemini_device_p = __msm_gemini_init(pdev);
+ if (msm_gemini_device_p == NULL) {
+ GMN_PR_ERR("%s: initialization failed\n", __func__);
+ goto fail;
+ }
+
+ rc = alloc_chrdev_region(&msm_gemini_devno, 0, 1, MSM_GEMINI_NAME);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: failed to allocate chrdev\n", __func__);
+ goto fail_1;
+ }
+
+ if (!msm_gemini_class) {
+ msm_gemini_class = class_create(THIS_MODULE, MSM_GEMINI_NAME);
+ if (IS_ERR(msm_gemini_class)) {
+ rc = PTR_ERR(msm_gemini_class);
+ GMN_PR_ERR("%s: create device class failed\n",
+ __func__);
+ goto fail_2;
+ }
+ }
+
+ dev = device_create(msm_gemini_class, NULL,
+ MKDEV(MAJOR(msm_gemini_devno), MINOR(msm_gemini_devno)), NULL,
+ "%s%d", MSM_GEMINI_NAME, 0);
+
+ if (IS_ERR(dev)) {
+ GMN_PR_ERR("%s: error creating device\n", __func__);
+ rc = -ENODEV;
+ goto fail_3;
+ }
+
+ cdev_init(&msm_gemini_device_p->cdev, &msm_gemini_fops);
+ msm_gemini_device_p->cdev.owner = THIS_MODULE;
+ msm_gemini_device_p->cdev.ops =
+ (const struct file_operations *) &msm_gemini_fops;
+ rc = cdev_add(&msm_gemini_device_p->cdev, msm_gemini_devno, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: error adding cdev\n", __func__);
+ rc = -ENODEV;
+ goto fail_4;
+ }
+
+ GMN_DBG("%s %s: success\n", __func__, MSM_GEMINI_NAME);
+
+ return rc;
+
+fail_4:
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+
+fail_3:
+ class_destroy(msm_gemini_class);
+
+fail_2:
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+fail_1:
+ __msm_gemini_exit(msm_gemini_device_p);
+
+fail:
+ return rc;
+}
+
+static void msm_gemini_exit(void)
+{
+ cdev_del(&msm_gemini_device_p->cdev);
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+ class_destroy(msm_gemini_class);
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+ __msm_gemini_exit(msm_gemini_device_p);
+}
+
+static int __msm_gemini_probe(struct platform_device *pdev)
+{
+ int rc;
+ rc = msm_gemini_init(pdev);
+ return rc;
+}
+
+static int __msm_gemini_remove(struct platform_device *pdev)
+{
+ msm_gemini_exit();
+ return 0;
+}
+
+static struct platform_driver msm_gemini_driver = {
+ .probe = __msm_gemini_probe,
+ .remove = __msm_gemini_remove,
+ .driver = {
+ .name = "msm_gemini",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_gemini_driver_init(void)
+{
+ int rc;
+ rc = platform_driver_register(&msm_gemini_driver);
+ return rc;
+}
+
+static void __exit msm_gemini_driver_exit(void)
+{
+ platform_driver_unregister(&msm_gemini_driver);
+}
+
+MODULE_DESCRIPTION("msm gemini jpeg driver");
+MODULE_VERSION("msm gemini 0.1");
+
+module_init(msm_gemini_driver_init);
+module_exit(msm_gemini_driver_exit);
+
diff --git a/drivers/media/video/msm/msm_gemini_hw.c b/drivers/media/video/msm/msm_gemini_hw.c
new file mode 100644
index 0000000..ba8f353
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_hw.c
@@ -0,0 +1,525 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_common.h"
+
+#include <linux/io.h>
+
+static void *gemini_region_base;
+static uint32_t gemini_region_size;
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf)
+{
+ int buf_free_index = -1;
+
+ if (!pingpong_hw->buf_status[0]) {
+ buf_free_index = 0;
+ } else if (!pingpong_hw->buf_status[1]) {
+ buf_free_index = 1;
+ } else {
+ GMN_PR_ERR("%s:%d: pingpong buffer busy\n", __func__, __LINE__);
+ return -1;
+ }
+
+ pingpong_hw->buf[buf_free_index] = *buf;
+ pingpong_hw->buf_status[buf_free_index] = 1;
+
+ if (pingpong_hw->is_fe) {
+ /* it is fe */
+ msm_gemini_hw_fe_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ } else {
+ /* it is we */
+ msm_gemini_hw_we_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ }
+ return 0;
+}
+
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index]) {
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+ pingpong_hw->buf_status[pingpong_hw->buf_active_index] = 0;
+ }
+
+ pingpong_hw->buf_active_index = !pingpong_hw->buf_active_index;
+
+ return (void *) buf_p;
+}
+
+void *msm_gemini_hw_pingpong_active_buffer(
+ struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index])
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+
+ return (void *) buf_p;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_get_status[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1, HWIO_JPEG_IRQ_STATUS_ADDR,
+ HWIO_JPEG_IRQ_STATUS_RMSK, {0} },
+};
+
+int msm_gemini_hw_irq_get_status(void)
+{
+ uint32_t n_irq_status = 0;
+ rmb();
+ n_irq_status = msm_gemini_hw_read(&hw_cmd_irq_get_status[0]);
+ rmb();
+ return n_irq_status;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_encode_output_size[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK, {0} },
+};
+
+long msm_gemini_hw_encode_output_size(void)
+{
+ uint32_t encode_output_size = 0;
+
+ encode_output_size = msm_gemini_hw_read(&hw_cmd_encode_output_size[0]);
+
+ return encode_output_size;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_clear[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_CLEAR_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+};
+
+void msm_gemini_hw_irq_clear(uint32_t mask, uint32_t data)
+{
+ GMN_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+ hw_cmd_irq_clear[0].mask = mask;
+ hw_cmd_irq_clear[0].data = data;
+ msm_gemini_hw_write(&hw_cmd_irq_clear[0]);
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_fe_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_fe_ping_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_fe_pong_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_start[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_OFFLINE_CMD_START} },
+};
+
+void msm_gemini_hw_fe_start(void)
+{
+ msm_gemini_hw_write(&hw_cmd_fe_start[0]);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_buffer_cfg[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_Y_THRESHOLD_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_UB_CFG_ADDR,
+ HWIO_JPEG_WE_Y_UB_CFG_RMSK, {JPEG_WE_YUB_ENCODE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK, {0} },
+};
+
+/* first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_Y_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+/* first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_CBCR_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p = &hw_cmd_we_buffer_cfg[0];
+
+ n_reg_val = (((GEMINI_WE_Y_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_Y_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ /* @todo maybe not for realtime? */
+ n_reg_val = (((GEMINI_WE_CBCR_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_CBCR_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PING_ADDR_RMSK, {0} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_we_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PONG_ADDR_RMSK, {0} },
+};
+
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_we_ping_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_we_pong_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_reset[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_DISABLE_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_RESET_CMD_ADDR,
+ HWIO_JPEG_RESET_CMD_RMSK, {JPEG_RESET_DEFAULT} },
+};
+
+void msm_gemini_hw_init(void *base, int size)
+{
+ gemini_region_base = base;
+ gemini_region_size = size;
+}
+
+void msm_gemini_hw_reset(void *base, int size)
+{
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ hw_cmd_p = &hw_cmd_reset[0];
+
+ wmb();
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p);
+ wmb();
+
+ return;
+}
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t data;
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ data = readl(paddr);
+ data &= hw_cmd_p->mask;
+
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, data);
+ return data;
+}
+
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t old_data, new_data;
+
+ /* type, repeat n times, offset, mask, data or pdata */
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, hw_cmd_p->data);
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ if (hw_cmd_p->mask == 0xffffffff) {
+ old_data = 0;
+ } else {
+ old_data = readl(paddr);
+ old_data &= ~hw_cmd_p->mask;
+ }
+
+ new_data = hw_cmd_p->data & hw_cmd_p->mask;
+ new_data |= old_data;
+ writel(new_data, paddr);
+}
+
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ uint32_t data;
+ uint32_t wait_data = hw_cmd_p->data & hw_cmd_p->mask;
+
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data != wait_data) {
+ while (tm) {
+ udelay(m_us);
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data == wait_data)
+ break;
+ tm--;
+ }
+ }
+ hw_cmd_p->data = data;
+ return tm;
+}
+
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ while (tm) {
+ udelay(m_us);
+ tm--;
+ }
+}
+
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds)
+{
+ int is_copy_to_user = -1;
+ uint32_t data;
+
+ while (m_cmds--) {
+ if (hw_cmd_p->offset > gemini_region_size) {
+ GMN_PR_ERR("%s:%d] %d exceed hw region %d\n", __func__,
+ __LINE__, hw_cmd_p->offset, gemini_region_size);
+ return -EFAULT;
+ }
+
+ switch (hw_cmd_p->type) {
+ case MSM_GEMINI_HW_CMD_TYPE_READ:
+ hw_cmd_p->data = msm_gemini_hw_read(hw_cmd_p);
+ is_copy_to_user = 1;
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE:
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE_OR:
+ data = msm_gemini_hw_read(hw_cmd_p);
+ hw_cmd_p->data = (hw_cmd_p->data & hw_cmd_p->mask) |
+ data;
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1000);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UDELAY:
+ msm_gemini_hw_delay(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MDELAY:
+ msm_gemini_hw_delay(hw_cmd_p, 1000);
+ break;
+
+ default:
+ GMN_PR_ERR("wrong hw command type\n");
+ break;
+ }
+
+ hw_cmd_p++;
+ }
+ return is_copy_to_user;
+}
+
+void msm_gemini_hw_region_dump(int size)
+{
+ uint32_t *p;
+ uint8_t *p8;
+
+ if (size > gemini_region_size)
+ GMN_PR_ERR("%s:%d] wrong region dump size\n",
+ __func__, __LINE__);
+
+ p = (uint32_t *) gemini_region_base;
+ while (size >= 16) {
+ GMN_DBG("0x%08X] %08X %08X %08X %08X\n",
+ gemini_region_size - size,
+ readl(p), readl(p+1), readl(p+2), readl(p+3));
+ p += 4;
+ size -= 16;
+ }
+
+ if (size > 0) {
+ uint32_t d;
+ GMN_DBG("0x%08X] ", gemini_region_size - size);
+ while (size >= 4) {
+ GMN_DBG("%08X ", readl(p++));
+ size -= 4;
+ }
+
+ d = readl(p);
+ p8 = (uint8_t *) &d;
+ while (size) {
+ GMN_DBG("%02X", *p8++);
+ size--;
+ }
+
+ GMN_DBG("\n");
+ }
+}
+
diff --git a/drivers/media/video/msm/msm_gemini_hw.h b/drivers/media/video/msm/msm_gemini_hw.h
new file mode 100644
index 0000000..ee1eac3
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_hw.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_HW_H
+#define MSM_GEMINI_HW_H
+
+#include <media/msm_gemini.h>
+#include "msm_gemini_hw_reg.h"
+
+struct msm_gemini_hw_buf {
+ struct msm_gemini_buf vbuf;
+ struct file *file;
+ uint32_t framedone_len;
+ uint32_t y_buffer_addr;
+ uint32_t y_len;
+ uint32_t cbcr_buffer_addr;
+ uint32_t cbcr_len;
+ uint32_t num_of_mcu_rows;
+};
+
+struct msm_gemini_hw_pingpong {
+ uint8_t is_fe; /* 1: fe; 0: we */
+ struct msm_gemini_hw_buf buf[2];
+ int buf_status[2];
+ int buf_active_index;
+};
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf);
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw);
+void *msm_gemini_hw_pingpong_active_buffer(struct msm_gemini_hw_pingpong
+ *pingpong_hw);
+
+void msm_gemini_hw_irq_clear(uint32_t, uint32_t);
+int msm_gemini_hw_irq_get_status(void);
+long msm_gemini_hw_encode_output_size(void);
+#define MSM_GEMINI_HW_MASK_COMP_FRAMEDONE \
+ MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_FE \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_WE \
+ (MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK)
+#define MSM_GEMINI_HW_MASK_COMP_RESET_ACK \
+ MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK
+#define MSM_GEMINI_HW_MASK_COMP_ERR \
+ (MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK)
+
+#define msm_gemini_hw_irq_is_frame_done(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FRAMEDONE)
+#define msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FE)
+#define msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_WE)
+#define msm_gemini_hw_irq_is_reset_ack(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_RESET_ACK)
+#define msm_gemini_hw_irq_is_err(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_ERR)
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime);
+
+void msm_gemini_hw_fe_start(void);
+void msm_gemini_hw_clk_cfg(void);
+
+void msm_gemini_hw_reset(void *base, int size);
+void msm_gemini_hw_irq_cfg(void);
+void msm_gemini_hw_init(void *base, int size);
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p);
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p);
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
+void msm_gemini_hw_region_dump(int size);
+
+#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP 128MHz */
+#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP 140MHz */
+#define MSM_GEMINI_PIPELINE_CLK_200MHZ 153 /* 12MP 153MHz */
+
+#endif /* MSM_GEMINI_HW_H */
diff --git a/drivers/media/video/msm/msm_gemini_hw_reg.h b/drivers/media/video/msm/msm_gemini_hw_reg.h
new file mode 100644
index 0000000..4bddfbb
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_hw_reg.h
@@ -0,0 +1,176 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_HW_REG_H
+#define MSM_GEMINI_HW_REG_H
+
+#define GEMINI_REG_BASE 0
+
+#define MSM_GEMINI_HW_IRQ_MASK_ADDR 0x00000014
+#define MSM_GEMINI_HW_IRQ_MASK_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_MASK_SHFT 0
+#define MSM_GEMINI_HW_IRQ_DISABLE 0
+#define MSM_GEMINI_HW_IRQ_ENABLE 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_CLEAR_ADDR 0x00000018
+#define MSM_GEMINI_HW_IRQ_CLEAR_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_CLEAR_SHFT 0
+#define MSM_GEMINI_HW_IRQ_CLEAR 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000002
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK 0x00000004
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_SHIFT 0x00000002
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK 0x00000008
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_SHIFT 0x00000003
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK 0x00000010
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_SHIFT 0x00000004
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK 0x00000020
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_SHIFT 0x00000005
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK 0x00000040
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_SHIFT 0x00000006
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK 0x00000080
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_SHIFT 0x00000007
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK 0x00000100
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_SHIFT 0x00000008
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK 0x00000200
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_SHIFT 0x00000009
+
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK 0x00000400
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK 0x00001000
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_SHIFT 0x0000000c
+
+#define JPEG_BUS_CMD_HALT_REQ 0x00000001
+
+#define JPEG_REALTIME_CMD_STOP_FB 0x00000000
+#define JPEG_REALTIME_CMD_STOP_IM 0x00000003
+#define JPEG_REALTIME_CMD_START 0x00000001
+
+#define JPEG_OFFLINE_CMD_START 0x00000003
+
+#define JPEG_DMI_CFG_DISABLE 0x00000000
+#define JPEG_DMI_ADDR_START 0x00000000
+
+#define JPEG_FE_CMD_BUFFERRELOAD 0x00000001
+
+#define JPEG_WE_YUB_ENCODE 0x01ff0000
+
+#define JPEG_RESET_DEFAULT 0x0004ffff /* cfff? */
+
+#define JPEG_IRQ_DISABLE_ALL 0x00000000
+#define JPEG_IRQ_CLEAR_ALL 0xffffffff
+#define JPEG_IRQ_ALLSOURCES_ENABLE 0xffffffff
+
+#define HWIO_JPEG_FE_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x00000080)
+#define HWIO_JPEG_FE_BUFFER_CFG_RMSK 0x1fff1fff
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x00000084)
+#define HWIO_JPEG_FE_Y_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000088)
+#define HWIO_JPEG_FE_Y_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x0000008c)
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000090)
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CMD_ADDR (GEMINI_REG_BASE + 0x00000094)
+#define HWIO_JPEG_FE_CMD_RMSK 0x3
+
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK 0x1fff0000
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT 0x10
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK 0x1fff
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c0)
+#define HWIO_JPEG_WE_Y_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c4)
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_UB_CFG_ADDR (GEMINI_REG_BASE + 0x000000e8)
+#define HWIO_JPEG_WE_Y_UB_CFG_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000c8)
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x000000d8)
+#define HWIO_JPEG_WE_Y_PING_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000cc)
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x000000dc)
+#define HWIO_JPEG_WE_Y_PONG_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_IRQ_MASK_ADDR (GEMINI_REG_BASE + 0x00000014)
+#define HWIO_JPEG_IRQ_MASK_RMSK 0xffffffff
+
+#define HWIO_JPEG_IRQ_CLEAR_ADDR (GEMINI_REG_BASE + 0x00000018)
+#define HWIO_JPEG_IRQ_CLEAR_RMSK 0xffffffff
+
+#define HWIO_JPEG_RESET_CMD_ADDR (GEMINI_REG_BASE + 0x00000004)
+#define HWIO_JPEG_RESET_CMD_RMSK 0xe004ffff
+
+#define HWIO_JPEG_IRQ_STATUS_ADDR (GEMINI_REG_BASE + 0x0000001c)
+#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
+
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffff
+
+#endif /* MSM_GEMINI_HW_REG_H */
diff --git a/drivers/media/video/msm/msm_gemini_platform.c b/drivers/media/video/msm/msm_gemini_platform.c
new file mode 100644
index 0000000..140d5d0
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_platform.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_qos_params.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <mach/msm_reqs.h>
+#include <mach/camera.h>
+
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+#include "msm_gemini_hw.h"
+
+#ifdef CONFIG_MSM_NPA_SYSTEM_BUS
+/* NPA Flow ID */
+#define MSM_SYSTEM_BUS_RATE MSM_AXI_FLOW_JPEG_12MP
+#else
+/* AXI rate in KHz */
+#define MSM_SYSTEM_BUS_RATE 160000
+#endif
+
+void msm_gemini_platform_p2v(struct file *file)
+{
+#ifdef CONFIG_ANDROID_PMEM
+ put_pmem_file(file);
+#endif
+}
+
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p)
+{
+ unsigned long paddr;
+ unsigned long size;
+ int rc;
+
+#ifdef CONFIG_ANDROID_PMEM
+ unsigned long kvstart;
+ rc = get_pmem_file(fd, &paddr, &kvstart, &size, file_p);
+#else
+ rc = 0;
+ paddr = 0;
+ size = 0;
+#endif
+ if (rc < 0) {
+ GMN_PR_ERR("%s: get_pmem_file fd %d error %d\n", __func__, fd,
+ rc);
+ return 0;
+ }
+
+ /* validate user input */
+ if (len > size) {
+ GMN_PR_ERR("%s: invalid offset + len\n", __func__);
+ return 0;
+ }
+
+ return paddr;
+}
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context)
+{
+ int rc = -1;
+ int gemini_irq;
+ struct resource *gemini_mem, *gemini_io, *gemini_irq_res;
+ void *gemini_base;
+
+ gemini_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!gemini_mem) {
+ GMN_PR_ERR("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ gemini_irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!gemini_irq_res) {
+ GMN_PR_ERR("no irq resource?\n");
+ return -ENODEV;
+ }
+ gemini_irq = gemini_irq_res->start;
+
+ gemini_io = request_mem_region(gemini_mem->start,
+ resource_size(gemini_mem), pdev->name);
+ if (!gemini_io) {
+ GMN_PR_ERR("%s: region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ gemini_base = ioremap(gemini_mem->start, resource_size(gemini_mem));
+ if (!gemini_base) {
+ rc = -ENOMEM;
+ GMN_PR_ERR("%s: ioremap failed\n", __func__);
+ goto fail1;
+ }
+
+ rc = msm_camio_jpeg_clk_enable();
+ if (rc) {
+ GMN_PR_ERR("%s: clk failed rc = %d\n", __func__, rc);
+ goto fail2;
+ }
+
+ msm_gemini_hw_init(gemini_base, resource_size(gemini_mem));
+ rc = request_irq(gemini_irq, handler, IRQF_TRIGGER_RISING, "gemini",
+ context);
+ if (rc) {
+ GMN_PR_ERR("%s: request_irq failed, %d\n", __func__,
+ gemini_irq);
+ goto fail3;
+ }
+
+ *mem = gemini_mem;
+ *base = gemini_base;
+ *irq = gemini_irq;
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+
+ return rc;
+
+fail3:
+ msm_camio_jpeg_clk_disable();
+fail2:
+ iounmap(gemini_base);
+fail1:
+ release_mem_region(gemini_mem->start, resource_size(gemini_mem));
+ GMN_DBG("%s:%d] fail\n", __func__, __LINE__);
+ return rc;
+}
+
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context)
+{
+ int result;
+
+ free_irq(irq, context);
+ result = msm_camio_jpeg_clk_disable();
+ iounmap(base);
+ release_mem_region(mem->start, resource_size(mem));
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return result;
+}
+
diff --git a/drivers/media/video/msm/msm_gemini_platform.h b/drivers/media/video/msm/msm_gemini_platform.h
new file mode 100644
index 0000000..49b1db6
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_platform.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_PLATFORM_H
+#define MSM_GEMINI_PLATFORM_H
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+void msm_gemini_platform_p2v(struct file *file);
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file);
+
+int msm_gemini_platform_clk_enable(void);
+int msm_gemini_platform_clk_disable(void);
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context);
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context);
+
+#endif /* MSM_GEMINI_PLATFORM_H */
diff --git a/drivers/media/video/msm/msm_gemini_sync.c b/drivers/media/video/msm/msm_gemini_sync.c
new file mode 100644
index 0000000..2ad0467
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_sync.c
@@ -0,0 +1,839 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <media/msm_gemini.h>
+#include "msm_gemini_sync.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static int release_buf;
+
+/*************** queue helper ****************/
+inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, name);
+ q_p->name = name;
+ spin_lock_init(&q_p->lck);
+ INIT_LIST_HEAD(&q_p->q);
+ init_waitqueue_head(&q_p->wait);
+ q_p->unblck = 0;
+}
+
+inline void *msm_gemini_q_out(struct msm_gemini_q *q_p)
+{
+ unsigned long flags;
+ struct msm_gemini_q_entry *q_entry_p = NULL;
+ void *data = NULL;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ spin_lock_irqsave(&q_p->lck, flags);
+ if (!list_empty(&q_p->q)) {
+ q_entry_p = list_first_entry(&q_p->q, struct msm_gemini_q_entry,
+ list);
+ list_del_init(&q_entry_p->list);
+ }
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ if (q_entry_p) {
+ data = q_entry_p->data;
+ kfree(q_entry_p);
+ } else {
+ GMN_DBG("%s:%d] %s no entry\n", __func__, __LINE__,
+ q_p->name);
+ }
+
+ return data;
+}
+
+inline int msm_gemini_q_in(struct msm_gemini_q *q_p, void *data)
+{
+ unsigned long flags;
+
+ struct msm_gemini_q_entry *q_entry_p;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+
+ q_entry_p = kmalloc(sizeof(struct msm_gemini_q_entry), GFP_ATOMIC);
+ if (!q_entry_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -1;
+ }
+ q_entry_p->data = data;
+
+ spin_lock_irqsave(&q_p->lck, flags);
+ list_add_tail(&q_entry_p->list, &q_p->q);
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ return 0;
+}
+
+inline int msm_gemini_q_in_buf(struct msm_gemini_q *q_p,
+ struct msm_gemini_core_buf *buf)
+{
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -1;
+ }
+
+ memcpy(buf_p, buf, sizeof(struct msm_gemini_core_buf));
+
+ msm_gemini_q_in(q_p, buf_p);
+ return 0;
+}
+
+inline int msm_gemini_q_wait(struct msm_gemini_q *q_p)
+{
+ int tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */
+ int rc;
+
+ GMN_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
+ rc = wait_event_interruptible_timeout(q_p->wait,
+ (!list_empty_careful(&q_p->q) || q_p->unblck),
+ msecs_to_jiffies(tm));
+ GMN_DBG("%s:%d] %s wait done\n", __func__, __LINE__, q_p->name);
+ if (list_empty_careful(&q_p->q)) {
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ GMN_PR_ERR("%s:%d] %s timeout\n", __func__, __LINE__,
+ q_p->name);
+ } else if (q_p->unblck) {
+ GMN_DBG("%s:%d] %s unblock is true\n", __func__,
+ __LINE__, q_p->name);
+ q_p->unblck = 0;
+ rc = -ECANCELED;
+ } else if (rc < 0) {
+ GMN_PR_ERR("%s:%d] %s rc %d\n", __func__, __LINE__,
+ q_p->name, rc);
+ }
+ }
+ return rc;
+}
+
+inline int msm_gemini_q_wakeup(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+inline int msm_gemini_q_unblock(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ q_p->unblck = 1;
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+inline void msm_gemini_outbuf_q_cleanup(struct msm_gemini_q *q_p)
+{
+ struct msm_gemini_core_buf *buf_p;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ buf_p = msm_gemini_q_out(q_p);
+ if (buf_p) {
+ msm_gemini_platform_p2v(buf_p->file);
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(buf_p);
+ }
+ } while (buf_p);
+ q_p->unblck = 0;
+}
+
+inline void msm_gemini_q_cleanup(struct msm_gemini_q *q_p)
+{
+ void *data;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ data = msm_gemini_q_out(q_p);
+ if (data) {
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(data);
+ }
+ } while (data);
+ q_p->unblck = 0;
+}
+
+/*************** event queue ****************/
+
+int msm_gemini_framedone_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ if (buf_in) {
+ buf_in->vbuf.framedone_len = buf_in->framedone_len;
+ buf_in->vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ GMN_DBG("%s:%d] 0x%08x %d framedone_len %d\n",
+ __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len,
+ buf_in->vbuf.framedone_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, buf_in);
+ } else {
+ GMN_PR_ERR("%s:%d] no output return buffer\n",
+ __func__, __LINE__);
+ rc = -1;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ return rc;
+}
+
+int msm_gemini_evt_get(struct msm_gemini_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->evt_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->evt_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no buffer\n", __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ ctrl_cmd.type = buf_p->vbuf.type;
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) ctrl_cmd.value, ctrl_cmd.len);
+
+ if (copy_to_user(to, &ctrl_cmd, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_evt_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->evt_q);
+ return 0;
+}
+
+void msm_gemini_reset_ack_irq(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+}
+
+void msm_gemini_err_irq(struct msm_gemini_device *pgmn_dev,
+ int event)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf buf;
+
+ GMN_PR_ERR("%s:%d] error: %d\n", __func__, __LINE__, event);
+
+ buf.vbuf.type = MSM_GEMINI_EVT_ERR;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, &buf);
+ if (!rc)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ if (!rc)
+ GMN_PR_ERR("%s:%d] err err\n", __func__, __LINE__);
+
+ return;
+}
+
+/*************** output queue ****************/
+
+int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf *buf_out;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no output return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_we_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ msm_gemini_core_we_buf_reset(buf_in);
+ GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_output_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->output_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->output_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no output buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ msm_gemini_platform_p2v(buf_p->file);
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_output_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->output_rtn_q);
+ return 0;
+}
+
+int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -1;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__, (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len, &buf_p->file);
+ if (!buf_p->y_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -1;
+ }
+ buf_p->y_len = buf_cmd.y_len;
+ buf_p->vbuf = buf_cmd;
+
+ msm_gemini_q_in(&pgmn_dev->output_buf_q, buf_p);
+ return 0;
+}
+
+/*************** input queue ****************/
+
+int msm_gemini_fe_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ struct msm_gemini_core_buf *buf_out;
+ int rc = 0;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->input_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no input return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ msm_gemini_core_fe_start();
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->input_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_input_get(struct msm_gemini_device *pgmn_dev, void __user * to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_wait(&pgmn_dev->input_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->input_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no input buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ msm_gemini_platform_p2v(buf_p->file);
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_input_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->input_rtn_q);
+ return 0;
+}
+
+int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -1;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file)
+ + buf_cmd.offset;
+ buf_p->y_len = buf_cmd.y_len;
+
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len;
+ buf_p->cbcr_len = buf_cmd.cbcr_len;
+
+ buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
+
+ if (!buf_p->y_buffer_addr || !buf_p->cbcr_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -1;
+ }
+ buf_p->vbuf = buf_cmd;
+
+ msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
+
+ return 0;
+}
+
+int msm_gemini_irq(int event, void *context, void *data)
+{
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ switch (event) {
+ case MSM_GEMINI_HW_MASK_COMP_FRAMEDONE:
+ msm_gemini_framedone_irq(pgmn_dev, data);
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_FE:
+ msm_gemini_fe_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_WE:
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_RESET_ACK:
+ msm_gemini_reset_ack_irq(pgmn_dev);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_ERR:
+ default:
+ msm_gemini_err_irq(pgmn_dev, event);
+ break;
+ }
+
+ return 0;
+}
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
+{
+ int rc;
+
+ mutex_lock(&pgmn_dev->lock);
+ if (pgmn_dev->open_count) {
+ /* only open once */
+ GMN_PR_ERR("%s:%d] busy\n", __func__, __LINE__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EBUSY;
+ }
+ pgmn_dev->open_count++;
+ mutex_unlock(&pgmn_dev->lock);
+
+ msm_gemini_core_irq_install(msm_gemini_irq);
+ rc = msm_gemini_platform_init(pgmn_dev->pdev,
+ &pgmn_dev->mem, &pgmn_dev->base,
+ &pgmn_dev->irq, msm_gemini_core_irq, pgmn_dev);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] platform_init fail %d\n", __func__,
+ __LINE__, rc);
+ return rc;
+ }
+
+ GMN_DBG("%s:%d] platform resources - mem %p, base %p, irq %d\n",
+ __func__, __LINE__,
+ pgmn_dev->mem, pgmn_dev->base, pgmn_dev->irq);
+
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
+ msm_gemini_core_init();
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return rc;
+}
+
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ mutex_lock(&pgmn_dev->lock);
+ if (!pgmn_dev->open_count) {
+ GMN_PR_ERR(KERN_ERR "%s: not opened\n", __func__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EINVAL;
+ }
+ pgmn_dev->open_count--;
+ mutex_unlock(&pgmn_dev->lock);
+
+ msm_gemini_core_release(release_buf);
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
+
+ if (pgmn_dev->open_count)
+ GMN_PR_ERR(KERN_ERR "%s: multiple opens\n", __func__);
+
+ msm_gemini_platform_release(pgmn_dev->mem, pgmn_dev->base,
+ pgmn_dev->irq, pgmn_dev);
+
+ return 0;
+}
+
+int msm_gemini_ioctl_hw_cmd(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ struct msm_gemini_hw_cmd hw_cmd;
+ int is_copy_to_user;
+
+ if (copy_from_user(&hw_cmd, arg, sizeof(struct msm_gemini_hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(&hw_cmd, 1);
+ GMN_DBG("%s:%d] type %d, n %d, offset %d, mask %x, data %x, pdata %x\n",
+ __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+ hw_cmd.mask, hw_cmd.data, (int) hw_cmd.pdata);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, &hw_cmd, sizeof(hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+int msm_gemini_ioctl_hw_cmds(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int is_copy_to_user;
+ int len;
+ uint32_t m;
+ struct msm_gemini_hw_cmds *hw_cmds_p;
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (copy_from_user(&m, arg, sizeof(m))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ len = sizeof(struct msm_gemini_hw_cmds) +
+ sizeof(struct msm_gemini_hw_cmd) * (m - 1);
+ hw_cmds_p = kmalloc(len, GFP_KERNEL);
+ if (!hw_cmds_p) {
+ GMN_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(hw_cmds_p, arg, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+
+ hw_cmd_p = (struct msm_gemini_hw_cmd *) &(hw_cmds_p->hw_cmd);
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(hw_cmd_p, m);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, hw_cmds_p, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+ }
+ kfree(hw_cmds_p);
+ return 0;
+}
+
+int msm_gemini_start(struct msm_gemini_device *pgmn_dev, void * __user arg)
+{
+ struct msm_gemini_core_buf *buf_out;
+ struct msm_gemini_core_buf *buf_out_free[2] = {NULL, NULL};
+ int i, rc;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ release_buf = 1;
+ for (i = 0; i < 2; i++) {
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ buf_out_free[i] = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out_free[i]) {
+ msm_gemini_core_we_buf_update(buf_out_free[i]);
+ } else if (i == 1) {
+ /* set the pong to same address as ping */
+ buf_out_free[0]->y_len >>= 1;
+ buf_out_free[0]->y_buffer_addr +=
+ buf_out_free[0]->y_len;
+ msm_gemini_core_we_buf_update(buf_out_free[0]);
+ /* since ping and pong are same buf release only once*/
+ release_buf = 0;
+ } else {
+ GMN_DBG("%s:%d] no output buffer\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ kfree(buf_out_free[i]);
+
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+int msm_gemini_ioctl_reset(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int rc;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&ctrl_cmd, arg, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ pgmn_dev->op_mode = ctrl_cmd.type;
+
+ rc = msm_gemini_core_reset(pgmn_dev->op_mode, pgmn_dev->base,
+ resource_size(pgmn_dev->mem));
+ return rc;
+}
+
+int msm_gemini_ioctl_test_dump_region(struct msm_gemini_device *pgmn_dev,
+ unsigned long arg)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_hw_region_dump(arg);
+ return 0;
+}
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ switch (cmd) {
+ case MSM_GMN_IOCTL_GET_HW_VERSION:
+ GMN_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_RESET:
+ rc = msm_gemini_ioctl_reset(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_STOP:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_START:
+ rc = msm_gemini_start(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_BUF_ENQUEUE:
+ rc = msm_gemini_input_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET:
+ rc = msm_gemini_input_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET_UNBLOCK:
+ rc = msm_gemini_input_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
+ rc = msm_gemini_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET:
+ rc = msm_gemini_output_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET_UNBLOCK:
+ rc = msm_gemini_output_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET:
+ rc = msm_gemini_evt_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET_UNBLOCK:
+ rc = msm_gemini_evt_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMD:
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMDS:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_TEST_DUMP_REGION:
+ rc = msm_gemini_ioctl_test_dump_region(pgmn_dev, arg);
+ break;
+
+ default:
+ GMN_PR_ERR(KERN_INFO "%s:%d] cmd = %d not supported\n",
+ __func__, __LINE__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev)
+{
+ struct msm_gemini_device *pgmn_dev;
+
+ pgmn_dev = kzalloc(sizeof(struct msm_gemini_device), GFP_ATOMIC);
+ if (!pgmn_dev) {
+ GMN_PR_ERR("%s:%d]no mem\n", __func__, __LINE__);
+ return NULL;
+ }
+
+ mutex_init(&pgmn_dev->lock);
+
+ pgmn_dev->pdev = pdev;
+
+ msm_gemini_q_init("evt_q", &pgmn_dev->evt_q);
+ msm_gemini_q_init("output_rtn_q", &pgmn_dev->output_rtn_q);
+ msm_gemini_q_init("output_buf_q", &pgmn_dev->output_buf_q);
+ msm_gemini_q_init("input_rtn_q", &pgmn_dev->input_rtn_q);
+ msm_gemini_q_init("input_buf_q", &pgmn_dev->input_buf_q);
+
+ return pgmn_dev;
+}
+
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev)
+{
+ mutex_destroy(&pgmn_dev->lock);
+ kfree(pgmn_dev);
+ return 0;
+}
+
diff --git a/drivers/media/video/msm/msm_gemini_sync.h b/drivers/media/video/msm/msm_gemini_sync.h
new file mode 100644
index 0000000..6c69a92
--- /dev/null
+++ b/drivers/media/video/msm/msm_gemini_sync.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_GEMINI_SYNC_H
+#define MSM_GEMINI_SYNC_H
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include "msm_gemini_core.h"
+
+struct msm_gemini_q {
+ char const *name;
+ struct list_head q;
+ spinlock_t lck;
+ wait_queue_head_t wait;
+ int unblck;
+};
+
+struct msm_gemini_q_entry {
+ struct list_head list;
+ void *data;
+};
+
+struct msm_gemini_device {
+ struct platform_device *pdev;
+ struct resource *mem;
+ int irq;
+ void *base;
+
+ struct device *device;
+ struct cdev cdev;
+ struct mutex lock;
+ char open_count;
+ uint8_t op_mode;
+
+ /* event queue including frame done & err indications
+ */
+ struct msm_gemini_q evt_q;
+
+ /* output return queue
+ */
+ struct msm_gemini_q output_rtn_q;
+
+ /* output buf queue
+ */
+ struct msm_gemini_q output_buf_q;
+
+ /* input return queue
+ */
+ struct msm_gemini_q input_rtn_q;
+
+ /* input buf queue
+ */
+ struct msm_gemini_q input_buf_q;
+};
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev);
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg);
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev);
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev);
+
+#endif /* MSM_GEMINI_SYNC_H */
diff --git a/drivers/media/video/msm/msm_io7x.c b/drivers/media/video/msm/msm_io7x.c
new file mode 100644
index 0000000..1befec6
--- /dev/null
+++ b/drivers/media/video/msm/msm_io7x.c
@@ -0,0 +1,318 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/clk.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 resource *appio, *mdcio;
+void __iomem *appbase, *mdcbase;
+
+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;
+ }
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ return 0;
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ int32_t rc = 0;
+ camio_ext = camdev->ioext;
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio)
+ rc = -EBUSY;
+ mdcbase = ioremap(camio_ext.mdcphy,
+ camio_ext.mdcsz);
+ if (!mdcbase) {
+ rc = -EINVAL;
+ goto mdc_no_mem;
+ }
+ camdev->camera_gpio_on();
+ return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ return rc;
+}
+
+int msm_camio_sensor_clk_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();
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+}
+
+void msm_disable_io_gpio_clk(struct platform_device *pdev)
+{
+ return;
+}
+
+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);
+ msleep(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);
+ msleep(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);
+ msleep(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+ msleep(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ uint32_t val;
+
+ /* do apps reset */
+ val = readl(appbase + 0x00000210);
+ val |= 0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000210);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000210);
+ mdelay(10);
+
+ /* do axi reset */
+ val = readl(appbase + 0x00000208);
+ val |= 0x1;
+ writel(val, appbase + 0x00000208);
+ mdelay(10);
+
+ val = readl(appbase + 0x00000208);
+ val &= ~0x1;
+ writel(val, appbase + 0x00000208);
+ 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/media/video/msm/msm_io8x.c b/drivers/media/video/msm/msm_io8x.c
new file mode 100644
index 0000000..6bc92b0
--- /dev/null
+++ b/drivers/media/video/msm/msm_io8x.c
@@ -0,0 +1,331 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/clk.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 0x00000214
+
+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;
+
+
+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;
+
+ 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;
+ }
+ msm_camio_clk_enable(CAMIO_MDC_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_AXI_CLK);
+ return 0;
+
+apps_no_mem:
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+enable_fail:
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ iounmap(appbase);
+ release_mem_region(camio_ext.appphy, camio_ext.appsz);
+ msm_camio_clk_disable(CAMIO_MDC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ int32_t rc = 0;
+ camio_ext = camdev->ioext;
+
+ mdcio = request_mem_region(camio_ext.mdcphy,
+ camio_ext.mdcsz, pdev->name);
+ if (!mdcio)
+ rc = -EBUSY;
+ mdcbase = ioremap(camio_ext.mdcphy,
+ camio_ext.mdcsz);
+ if (!mdcbase)
+ goto mdc_no_mem;
+ camdev->camera_gpio_on();
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_MDC_CLK);
+ return rc;
+
+
+mdc_no_mem:
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ return -EINVAL;
+}
+
+int msm_camio_sensor_clk_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();
+ iounmap(mdcbase);
+ release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK);
+
+}
+
+void msm_disable_io_gpio_clk(struct platform_device *pdev)
+{
+ return;
+}
+
+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);
+ msleep(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);
+ msleep(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);
+ msleep(10);
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL);
+
+ msleep(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 + APPS_RESET_OFFSET);
+ val |= 0x1;
+ writel(val, appbase + APPS_RESET_OFFSET);
+ mdelay(10);
+
+ val = readl(appbase + APPS_RESET_OFFSET);
+ val &= ~0x1;
+ writel(val, appbase + APPS_RESET_OFFSET);
+ 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/media/video/msm/msm_io_7x27a.c b/drivers/media/video/msm/msm_io_7x27a.c
new file mode 100644
index 0000000..c70cfca
--- /dev/null
+++ b/drivers/media/video/msm/msm_io_7x27a.c
@@ -0,0 +1,612 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_qos_params.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+/* MIPI CSI controller registers */
+#define MIPI_PHY_CONTROL 0x00000000
+#define MIPI_PROTOCOL_CONTROL 0x00000004
+#define MIPI_INTERRUPT_STATUS 0x00000008
+#define MIPI_INTERRUPT_MASK 0x0000000C
+#define MIPI_CAMERA_CNTL 0x00000024
+#define MIPI_CALIBRATION_CONTROL 0x00000018
+#define MIPI_PHY_D0_CONTROL2 0x00000038
+#define MIPI_PHY_D1_CONTROL2 0x0000003C
+#define MIPI_PHY_D2_CONTROL2 0x00000040
+#define MIPI_PHY_D3_CONTROL2 0x00000044
+#define MIPI_PHY_CL_CONTROL 0x00000048
+#define MIPI_PHY_D0_CONTROL 0x00000034
+#define MIPI_PHY_D1_CONTROL 0x00000020
+#define MIPI_PHY_D2_CONTROL 0x0000002C
+#define MIPI_PHY_D3_CONTROL 0x00000030
+#define MIPI_PWR_CNTL 0x00000054
+
+/*
+ * MIPI_PROTOCOL_CONTROL register bits to enable/disable the features of
+ * CSI Rx Block
+ */
+
+/* DPCM scheme */
+#define MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT 0x1e
+/* SW_RST to issue a SW reset to the CSI core */
+#define MIPI_PROTOCOL_CONTROL_SW_RST_BMSK 0x8000000
+/* To Capture Long packet Header Info in MIPI_PROTOCOL_STATUS register */
+#define MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK 0x200000
+/* Data format for unpacking purpose */
+#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT 0x13
+/* Enable decoding of payload based on data type filed of packet hdr */
+#define MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK 0x00000
+/* Enable error correction on packet headers */
+#define MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK 0x20000
+
+/*
+ * MIPI_CALIBRATION_CONTROL register contains control info for
+ * calibration impledence controller
+*/
+
+/* Enable bit for calibration pad */
+#define MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT 0x16
+/* With SWCAL_STRENGTH_OVERRIDE_EN, SW_CAL_EN and MANUAL_OVERRIDE_EN
+ * the hardware calibration circuitry associated with CAL_SW_HW_MODE
+ * is bypassed
+*/
+#define MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT 0x15
+/* To indicate the Calibration process is in the control of HW/SW */
+#define MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT 0x14
+/* When this is set the strength value of the data and clk lane impedence
+ * termination is updated with MANUAL_STRENGTH settings and calibration
+ * sensing logic is idle.
+*/
+#define MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT 0x7
+
+/* Data lane0 control */
+/* T-hs Settle count value for Rx */
+#define MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT 0x18
+/* Rx termination control */
+#define MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT 0x10
+/* LP Rx enable */
+#define MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT 0x4
+/*
+ * Enable for error in sync sequence
+ * 1 - one bit error in sync seq
+ * 0 - requires all 8 bit correct seq
+*/
+#define MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+
+/* Comments are same as D0 */
+#define MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+
+/* Comments are same as D0 */
+#define MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+
+/* Comments are same as D0 */
+#define MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+
+/* PHY_CL_CTRL programs the parameters of clk lane of CSIRXPHY */
+/* HS Rx termination control */
+#define MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT 0x18
+/* Start signal for T-hs delay */
+#define MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT 0x2
+
+/* PHY DATA lane 0 control */
+/*
+ * HS RX equalizer strength control
+ * 00 - 0db 01 - 3db 10 - 5db 11 - 7db
+*/
+#define MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT 0x1c
+
+/* PHY DATA lane 1 control */
+/* Shutdown signal for MIPI clk phy line */
+#define MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT 0x9
+/* Shutdown signal for MIPI data phy line */
+#define MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT 0x8
+
+#define MSM_AXI_QOS_PREVIEW 200000
+#define MSM_AXI_QOS_SNAPSHOT 200000
+#define MSM_AXI_QOS_RECORDING 200000
+
+#define MIPI_PWR_CNTL_ENA 0x07
+#define MIPI_PWR_CNTL_DIS 0x0
+
+static struct clk *camio_cam_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_csi_src_clk;
+static struct clk *camio_csi0_vfe_clk;
+static struct clk *camio_csi1_vfe_clk;
+static struct clk *camio_csi0_clk;
+static struct clk *camio_csi1_clk;
+static struct clk *camio_csi0_pclk;
+static struct clk *camio_csi1_pclk;
+
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct platform_device *camio_dev;
+void __iomem *csibase;
+void __iomem *appbase;
+
+
+
+void msm_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel((data), (addr));
+}
+
+u32 msm_io_r(void __iomem *addr)
+{
+ uint32_t data = readl(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_camio_vfe_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+ if (rate > clk_get_rate(clk))
+ clk_set_rate(clk, rate);
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_CAM_MCLK_CLK:
+ clk = clk_get(NULL, "cam_m_clk");
+ camio_cam_clk = clk;
+ msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+ break;
+ case CAMIO_VFE_CLK:
+ clk = clk_get(NULL, "vfe_clk");
+ camio_vfe_clk = clk;
+ msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+ break;
+ case CAMIO_CSI0_VFE_CLK:
+ clk = clk_get(&camio_dev->dev, "csi_vfe_clk");
+ camio_csi0_vfe_clk = clk;
+ break;
+ case CAMIO_CSI1_VFE_CLK:
+ clk = clk_get(NULL, "csi_vfe_clk");
+ camio_csi1_vfe_clk = clk;
+ break;
+ case CAMIO_CSI_SRC_CLK:
+ clk = clk_get(NULL, "csi_src_clk");
+ camio_csi_src_clk = clk;
+ break;
+ case CAMIO_CSI0_CLK:
+ clk = clk_get(&camio_dev->dev, "csi_clk");
+ camio_csi0_clk = clk;
+ msm_camio_clk_rate_set_2(clk, 400000000);
+ break;
+ case CAMIO_CSI1_CLK:
+ clk = clk_get(NULL, "csi_clk");
+ camio_csi1_clk = clk;
+ break;
+ case CAMIO_CSI0_PCLK:
+ clk = clk_get(&camio_dev->dev, "csi_pclk");
+ camio_csi0_pclk = clk;
+ break;
+ case CAMIO_CSI1_PCLK:
+ clk = clk_get(NULL, "csi_pclk");
+ camio_csi1_pclk = 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_CAM_MCLK_CLK:
+ clk = camio_cam_clk;
+ break;
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+ case CAMIO_CSI_SRC_CLK:
+ clk = camio_csi_src_clk;
+ break;
+ case CAMIO_CSI0_VFE_CLK:
+ clk = camio_csi0_vfe_clk;
+ break;
+ case CAMIO_CSI1_VFE_CLK:
+ clk = camio_csi1_vfe_clk;
+ break;
+ case CAMIO_CSI0_CLK:
+ clk = camio_csi0_clk;
+ break;
+ case CAMIO_CSI1_CLK:
+ clk = camio_csi1_clk;
+ break;
+ case CAMIO_CSI0_PCLK:
+ clk = camio_csi0_pclk;
+ break;
+ case CAMIO_CSI1_PCLK:
+ clk = camio_csi1_pclk;
+ 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_cam_clk;
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_set_min_rate(struct clk *clk, int rate)
+{
+ clk_set_min_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+
+ irq = msm_io_r(csibase + MIPI_INTERRUPT_STATUS);
+ CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+ msm_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+
+ /* TODO: Needs to send this info to upper layers */
+ if ((irq >> 19) & 0x1)
+ pr_info("Unsupported packet format is received\n");
+ return IRQ_HANDLED;
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ uint32_t val;
+
+ camio_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_CLK);
+ msm_camio_clk_enable(CAMIO_CSI1_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+ csibase = ioremap(camio_ext.csiphy, camio_ext.csisz);
+ if (!csibase) {
+ rc = -ENOMEM;
+ goto csi_busy;
+ }
+ rc = request_irq(camio_ext.csiirq, msm_io_csi_irq,
+ IRQF_TRIGGER_RISING, "csi", 0);
+ if (rc < 0)
+ goto csi_irq_fail;
+
+ msleep(20);
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+ appbase = ioremap(camio_ext.appphy,
+ camio_ext.appsz);
+ if (!appbase) {
+ rc = -ENOMEM;
+ goto csi_irq_fail;
+ }
+ return 0;
+
+csi_irq_fail:
+ iounmap(csibase);
+csi_busy:
+ msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+ camdev->camera_gpio_off();
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ uint32_t val;
+
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ msleep(20);
+
+ free_irq(camio_ext.csiirq, 0);
+ iounmap(csibase);
+ iounmap(appbase);
+ CDBG("disable clocks\n");
+
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+}
+
+int msm_camio_sensor_clk_on(struct platform_device *pdev)
+{
+ int rc = 0;
+ const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camio_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+ const 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_CAM_MCLK_CLK);
+
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ uint32_t val;
+
+ /* do apps reset */
+ val = readl_relaxed(appbase + 0x00000210);
+ val |= 0x1;
+ writel_relaxed(val, appbase + 0x00000210);
+ usleep_range(10000, 11000);
+
+ val = readl_relaxed(appbase + 0x00000210);
+ val &= ~0x1;
+ writel_relaxed(val, appbase + 0x00000210);
+ usleep_range(10000, 11000);
+
+ /* do axi reset */
+ val = readl_relaxed(appbase + 0x00000208);
+ val |= 0x1;
+ writel_relaxed(val, appbase + 0x00000208);
+ usleep_range(10000, 11000);
+
+ val = readl_relaxed(appbase + 0x00000208);
+ val &= ~0x1;
+ writel_relaxed(val, appbase + 0x00000208);
+ mb();
+ usleep_range(10000, 11000);
+ return;
+}
+
+int msm_camio_probe_on(struct platform_device *pdev)
+{
+ int rc = 0;
+ const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camio_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+
+ msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_probe_off(struct platform_device *pdev)
+{
+ const struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_off();
+
+ csibase = ioremap(camdev->ioext.csiphy, camdev->ioext.csisz);
+ if (!csibase) {
+ pr_err("ioremap failed for CSIBASE\n");
+ goto ioremap_fail;
+ }
+ msm_io_w(MIPI_PWR_CNTL_DIS, csibase + MIPI_PWR_CNTL);
+ iounmap(csibase);
+ioremap_fail:
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+
+ CDBG("msm_camio_csi_config\n");
+
+ /* Enable error correction for DATA lane. Applies to all data lanes */
+ msm_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+ msm_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+ csibase + MIPI_PROTOCOL_CONTROL);
+
+ val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+ MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+ MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+ val |= (uint32_t)(csi_params->data_format) <<
+ MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+ val |= csi_params->dpcm_scheme <<
+ MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+ CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+ val = (0x1 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+ (0x1 <<
+ MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+ CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+ val = (csi_params->settle_cnt <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+ val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+ val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+ (0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+ CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+ /* program number of lanes and lane mapping */
+ switch (csi_params->lane_cnt) {
+ case 1:
+ msm_io_w(csi_params->lane_assign << 8 | 0x4,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 2:
+ msm_io_w(csi_params->lane_assign << 8 | 0x5,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 3:
+ msm_io_w(csi_params->lane_assign << 8 | 0x6,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 4:
+ msm_io_w(csi_params->lane_assign << 8 | 0x7,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ }
+
+ msm_io_w(0xFFFFF3FF, csibase + MIPI_INTERRUPT_MASK);
+ /*clear IRQ bits - write 1 clears the status*/
+ msm_io_w(0xFFFFF3FF, csibase + MIPI_INTERRUPT_STATUS);
+
+ return rc;
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+ switch (perf_setting) {
+ case S_INIT:
+ add_axi_qos();
+ break;
+ case S_PREVIEW:
+ update_axi_qos(MSM_AXI_QOS_PREVIEW);
+ break;
+ case S_VIDEO:
+ update_axi_qos(MSM_AXI_QOS_RECORDING);
+ break;
+ case S_CAPTURE:
+ update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+ break;
+ case S_DEFAULT:
+ update_axi_qos(PM_QOS_DEFAULT_VALUE);
+ break;
+ case S_EXIT:
+ release_axi_qos();
+ break;
+ default:
+ CDBG("%s: INVALID CASE\n", __func__);
+ }
+}
diff --git a/drivers/media/video/msm/msm_io_8960.c b/drivers/media/video/msm/msm_io_8960.c
new file mode 100644
index 0000000..eb29d08
--- /dev/null
+++ b/drivers/media/video/msm/msm_io_8960.c
@@ -0,0 +1,1359 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include "msm_ispif.h"
+
+#define DBG_CSID 0
+#define DBG_CSIPHY 0
+
+/* MIPI CSI PHY registers */
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1E0
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1E8
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x0144
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x0180
+#define MIPI_CSIPHY_INTERRUPT_STATUS1_ADDR 0x0184
+#define MIPI_CSIPHY_INTERRUPT_STATUS2_ADDR 0x0188
+#define MIPI_CSIPHY_INTERRUPT_STATUS3_ADDR 0x018C
+#define MIPI_CSIPHY_INTERRUPT_STATUS4_ADDR 0x0190
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x01A0
+#define MIPI_CSIPHY_INTERRUPT_MASK1_ADDR 0x01A4
+#define MIPI_CSIPHY_INTERRUPT_MASK2_ADDR 0x01A8
+#define MIPI_CSIPHY_INTERRUPT_MASK3_ADDR 0x01AC
+#define MIPI_CSIPHY_INTERRUPT_MASK4_ADDR 0x01B0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x01C0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR1_ADDR 0x01C4
+#define MIPI_CSIPHY_INTERRUPT_CLEAR2_ADDR 0x01C8
+#define MIPI_CSIPHY_INTERRUPT_CLEAR3_ADDR 0x01CC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR4_ADDR 0x01D0
+
+/* MIPI CSID registers */
+#define CSID_CORE_CTRL_ADDR 0x4
+#define CSID_RST_CMD_ADDR 0x8
+#define CSID_CID_LUT_VC_0_ADDR 0xc
+#define CSID_CID_LUT_VC_1_ADDR 0x10
+#define CSID_CID_LUT_VC_2_ADDR 0x14
+#define CSID_CID_LUT_VC_3_ADDR 0x18
+#define CSID_CID_n_CFG_ADDR 0x1C
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x5c
+#define CSID_IRQ_MASK_ADDR 0x60
+#define CSID_IRQ_STATUS_ADDR 0x64
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x68
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x6c
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x70
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x78
+#define CSID_PIF_MISR_DL0_ADDR 0x7C
+#define CSID_PIF_MISR_DL1_ADDR 0x80
+#define CSID_PIF_MISR_DL2_ADDR 0x84
+#define CSID_PIF_MISR_DL3_ADDR 0x88
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x8C
+#define CSID_STATS_ECC_ADDR 0x90
+#define CSID_STATS_CRC_ADDR 0x94
+#define CSID_TG_CTRL_ADDR 0x9C
+#define CSID_TG_VC_CFG_ADDR 0xA0
+#define CSID_TG_DT_n_CFG_0_ADDR 0xA8
+#define CSID_TG_DT_n_CFG_1_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB0
+#define CSID_TG_DT_n_CFG_3_ADDR 0xD8
+
+/* Regulator Voltage and Current */
+
+#define CAM_VAF_MINUV 2800000
+#define CAM_VAF_MAXUV 2800000
+#define CAM_VDIG_MINUV 1200000
+#define CAM_VDIG_MAXUV 1200000
+#define CAM_VANA_MINUV 2800000
+#define CAM_VANA_MAXUV 2850000
+#define CAM_CSI_VDD_MINUV 1200000
+#define CAM_CSI_VDD_MAXUV 1200000
+
+#define CAM_VAF_LOAD_UA 300000
+#define CAM_VDIG_LOAD_UA 105000
+#define CAM_VANA_LOAD_UA 85600
+#define CAM_CSI_LOAD_UA 20000
+
+static struct clk *camio_cam_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_csi_src_clk;
+static struct clk *camio_csi1_src_clk;
+static struct clk *camio_csi0_vfe_clk;
+static struct clk *camio_csi0_clk;
+static struct clk *camio_csi0_pclk;
+static struct clk *camio_csi_pix_clk;
+static struct clk *camio_csi_rdi_clk;
+static struct clk *camio_csiphy0_timer_clk;
+static struct clk *camio_csiphy1_timer_clk;
+static struct clk *camio_vfe_axi_clk;
+static struct clk *camio_vfe_pclk;
+static struct clk *camio_csi0_phy_clk;
+static struct clk *camio_csiphy_timer_src_clk;
+
+/*static struct clk *camio_vfe_pclk;*/
+static struct clk *camio_jpeg_clk;
+static struct clk *camio_jpeg_pclk;
+static struct clk *camio_vpe_clk;
+static struct clk *camio_vpe_pclk;
+static struct regulator *fs_vfe;
+static struct regulator *fs_ijpeg;
+static struct regulator *fs_vpe;
+static struct regulator *cam_vana;
+static struct regulator *cam_vio;
+static struct regulator *cam_vdig;
+static struct regulator *cam_vaf;
+static struct regulator *mipi_csi_vdd;
+
+static struct msm_camera_io_clk camio_clk;
+static struct platform_device *camio_dev;
+static struct resource *csidio, *csiphyio;
+static struct resource *csid_mem, *csiphy_mem;
+static struct resource *csid_irq, *csiphy_irq;
+void __iomem *csidbase, *csiphybase;
+
+static struct msm_bus_vectors cam_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_VPE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_JPEG_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors cam_preview_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1521190000,
+ .ib = 1521190000,
+ },
+ {
+ .src = MSM_BUS_MASTER_VPE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_JPEG_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors cam_video_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1521190000,
+ .ib = 1521190000,
+ },
+ {
+ .src = MSM_BUS_MASTER_VPE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1521190000,
+ .ib = 1521190000,
+ },
+ {
+ .src = MSM_BUS_MASTER_JPEG_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors cam_snapshot_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1521190000,
+ .ib = 1521190000,
+ },
+ {
+ .src = MSM_BUS_MASTER_VPE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_JPEG_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1521190000,
+ .ib = 1521190000,
+ },
+};
+
+static struct msm_bus_paths cam_bus_client_config[] = {
+ {
+ ARRAY_SIZE(cam_init_vectors),
+ cam_init_vectors,
+ },
+ {
+ ARRAY_SIZE(cam_preview_vectors),
+ cam_preview_vectors,
+ },
+ {
+ ARRAY_SIZE(cam_video_vectors),
+ cam_video_vectors,
+ },
+ {
+ ARRAY_SIZE(cam_snapshot_vectors),
+ cam_snapshot_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata cam_bus_client_pdata = {
+ cam_bus_client_config,
+ ARRAY_SIZE(cam_bus_client_config),
+ .name = "msm_camera",
+};
+
+
+void msm_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+void msm_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ wmb();
+ writel_relaxed((data), (addr));
+ wmb();
+}
+
+u32 msm_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+u32 msm_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ rmb();
+ data = readl_relaxed(addr);
+ rmb();
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+ /* memcpy_toio does not work. Use writel_relaxed for now */
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+void msm_io_dump(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ sprintf(p_str, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ sprintf(p_str, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+void msm_io_memcpy(void __iomem *dest_addr, void __iomem *src_addr, u32 len)
+{
+ CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+ msm_io_memcpy_toio(dest_addr, src_addr, len / 4);
+ msm_io_dump(dest_addr, len);
+}
+
+static int msm_camera_vreg_enable(struct platform_device *pdev)
+{
+ if (mipi_csi_vdd == NULL) {
+ mipi_csi_vdd = regulator_get(&pdev->dev, "mipi_csi_vdd");
+ if (IS_ERR(mipi_csi_vdd)) {
+ CDBG("%s: VREG MIPI CSI VDD get failed\n", __func__);
+ mipi_csi_vdd = NULL;
+ return -ENODEV;
+ }
+ if (regulator_set_voltage(mipi_csi_vdd, CAM_CSI_VDD_MINUV,
+ CAM_CSI_VDD_MAXUV)) {
+ CDBG("%s: VREG MIPI CSI VDD set voltage failed\n",
+ __func__);
+ goto mipi_csi_vdd_put;
+ }
+ if (regulator_set_optimum_mode(mipi_csi_vdd,
+ CAM_CSI_LOAD_UA) < 0) {
+ CDBG("%s: VREG MIPI CSI set optimum mode failed\n",
+ __func__);
+ goto mipi_csi_vdd_release;
+ }
+ if (regulator_enable(mipi_csi_vdd)) {
+ CDBG("%s: VREG MIPI CSI VDD enable failed\n",
+ __func__);
+ goto mipi_csi_vdd_disable;
+ }
+ }
+ if (cam_vana == NULL) {
+ cam_vana = regulator_get(&pdev->dev, "cam_vana");
+ if (IS_ERR(cam_vana)) {
+ CDBG("%s: VREG CAM VANA get failed\n", __func__);
+ cam_vana = NULL;
+ goto mipi_csi_vdd_disable;
+ }
+ if (regulator_set_voltage(cam_vana, CAM_VANA_MINUV,
+ CAM_VANA_MAXUV)) {
+ CDBG("%s: VREG CAM VANA set voltage failed\n",
+ __func__);
+ goto cam_vana_put;
+ }
+ if (regulator_set_optimum_mode(cam_vana,
+ CAM_VANA_LOAD_UA) < 0) {
+ CDBG("%s: VREG CAM VANA set optimum mode failed\n",
+ __func__);
+ goto cam_vana_release;
+ }
+ if (regulator_enable(cam_vana)) {
+ CDBG("%s: VREG CAM VANA enable failed\n", __func__);
+ goto cam_vana_disable;
+ }
+ }
+ if (cam_vio == NULL) {
+ cam_vio = regulator_get(&pdev->dev, "cam_vio");
+ if (IS_ERR(cam_vio)) {
+ CDBG("%s: VREG VIO get failed\n", __func__);
+ cam_vio = NULL;
+ goto cam_vana_disable;
+ }
+ if (regulator_enable(cam_vio)) {
+ CDBG("%s: VREG VIO enable failed\n", __func__);
+ goto cam_vio_put;
+ }
+ }
+ if (cam_vdig == NULL) {
+ cam_vdig = regulator_get(&pdev->dev, "cam_vdig");
+ if (IS_ERR(cam_vdig)) {
+ CDBG("%s: VREG CAM VDIG get failed\n", __func__);
+ cam_vdig = NULL;
+ goto cam_vio_disable;
+ }
+ if (regulator_set_voltage(cam_vdig, CAM_VDIG_MINUV,
+ CAM_VDIG_MAXUV)) {
+ CDBG("%s: VREG CAM VDIG set voltage failed\n",
+ __func__);
+ goto cam_vdig_put;
+ }
+ if (regulator_set_optimum_mode(cam_vdig,
+ CAM_VDIG_LOAD_UA) < 0) {
+ CDBG("%s: VREG CAM VDIG set optimum mode failed\n",
+ __func__);
+ goto cam_vdig_release;
+ }
+ if (regulator_enable(cam_vdig)) {
+ CDBG("%s: VREG CAM VDIG enable failed\n", __func__);
+ goto cam_vdig_disable;
+ }
+ }
+ if (cam_vaf == NULL) {
+ cam_vaf = regulator_get(&pdev->dev, "cam_vaf");
+ if (IS_ERR(cam_vaf)) {
+ CDBG("%s: VREG CAM VAF get failed\n", __func__);
+ cam_vaf = NULL;
+ goto cam_vdig_disable;
+ }
+ if (regulator_set_voltage(cam_vaf, CAM_VAF_MINUV,
+ CAM_VAF_MAXUV)) {
+ CDBG("%s: VREG CAM VAF set voltage failed\n",
+ __func__);
+ goto cam_vaf_put;
+ }
+ if (regulator_set_optimum_mode(cam_vaf,
+ CAM_VAF_LOAD_UA) < 0) {
+ CDBG("%s: VREG CAM VAF set optimum mode failed\n",
+ __func__);
+ goto cam_vaf_release;
+ }
+ if (regulator_enable(cam_vaf)) {
+ CDBG("%s: VREG CAM VAF enable failed\n", __func__);
+ goto cam_vaf_disable;
+ }
+ }
+ if (fs_vfe == NULL) {
+ fs_vfe = regulator_get(&pdev->dev, "fs_vfe");
+ if (IS_ERR(fs_vfe)) {
+ CDBG("%s: Regulator FS_VFE get failed %ld\n", __func__,
+ PTR_ERR(fs_vfe));
+ fs_vfe = NULL;
+ } else if (regulator_enable(fs_vfe)) {
+ CDBG("%s: Regulator FS_VFE enable failed\n", __func__);
+ regulator_put(fs_vfe);
+ }
+ }
+ return 0;
+
+cam_vaf_disable:
+ regulator_set_optimum_mode(cam_vaf, 0);
+cam_vaf_release:
+ regulator_set_voltage(cam_vaf, 0, CAM_VAF_MAXUV);
+ regulator_disable(cam_vaf);
+cam_vaf_put:
+ regulator_put(cam_vaf);
+ cam_vaf = NULL;
+cam_vdig_disable:
+ regulator_set_optimum_mode(cam_vdig, 0);
+cam_vdig_release:
+ regulator_set_voltage(cam_vdig, 0, CAM_VDIG_MAXUV);
+ regulator_disable(cam_vdig);
+cam_vdig_put:
+ regulator_put(cam_vdig);
+ cam_vdig = NULL;
+cam_vio_disable:
+ regulator_disable(cam_vio);
+cam_vio_put:
+ regulator_put(cam_vio);
+ cam_vio = NULL;
+cam_vana_disable:
+ regulator_set_optimum_mode(cam_vana, 0);
+cam_vana_release:
+ regulator_set_voltage(cam_vana, 0, CAM_VANA_MAXUV);
+ regulator_disable(cam_vana);
+cam_vana_put:
+ regulator_put(cam_vana);
+ cam_vana = NULL;
+mipi_csi_vdd_disable:
+ regulator_set_optimum_mode(mipi_csi_vdd, 0);
+mipi_csi_vdd_release:
+ regulator_set_voltage(mipi_csi_vdd, 0, CAM_CSI_VDD_MAXUV);
+ regulator_disable(mipi_csi_vdd);
+
+mipi_csi_vdd_put:
+ regulator_put(mipi_csi_vdd);
+ mipi_csi_vdd = NULL;
+ return -ENODEV;
+}
+
+static void msm_camera_vreg_disable(void)
+{
+ if (mipi_csi_vdd) {
+ regulator_set_voltage(mipi_csi_vdd, 0, CAM_CSI_VDD_MAXUV);
+ regulator_set_optimum_mode(mipi_csi_vdd, 0);
+ regulator_disable(mipi_csi_vdd);
+ regulator_put(mipi_csi_vdd);
+ mipi_csi_vdd = NULL;
+ }
+
+ if (cam_vana) {
+ regulator_set_voltage(cam_vana, 0, CAM_VANA_MAXUV);
+ regulator_set_optimum_mode(cam_vana, 0);
+ regulator_disable(cam_vana);
+ regulator_put(cam_vana);
+ cam_vana = NULL;
+ }
+
+ if (cam_vio) {
+ regulator_disable(cam_vio);
+ regulator_put(cam_vio);
+ cam_vio = NULL;
+ }
+
+ if (cam_vdig) {
+ regulator_set_voltage(cam_vdig, 0, CAM_VDIG_MAXUV);
+ regulator_set_optimum_mode(cam_vdig, 0);
+ regulator_disable(cam_vdig);
+ regulator_put(cam_vdig);
+ cam_vdig = NULL;
+ }
+
+ if (cam_vaf) {
+ regulator_set_voltage(cam_vaf, 0, CAM_VAF_MAXUV);
+ regulator_set_optimum_mode(cam_vaf, 0);
+ regulator_disable(cam_vaf);
+ regulator_put(cam_vaf);
+ cam_vaf = NULL;
+ }
+
+ if (fs_vfe) {
+ regulator_disable(fs_vfe);
+ regulator_put(fs_vfe);
+ fs_vfe = NULL;
+ }
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+ struct msm_camera_sensor_info *sinfo = camio_dev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ uint8_t csid_core = camdev->csid_core;
+
+ switch (clktype) {
+ case CAMIO_CAM_MCLK_CLK:
+ camio_cam_clk =
+ clk = clk_get(&camio_dev->dev, "cam_clk");
+ msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+ break;
+
+ case CAMIO_VFE_CLK:
+ camio_vfe_clk =
+ clk = clk_get(NULL, "vfe_clk");
+ msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ camio_vfe_axi_clk =
+ clk = clk_get(NULL, "vfe_axi_clk");
+ break;
+
+ case CAMIO_VFE_PCLK:
+ camio_vfe_pclk =
+ clk = clk_get(NULL, "vfe_pclk");
+ break;
+
+ case CAMIO_CSI0_VFE_CLK:
+ camio_csi0_vfe_clk =
+ clk = clk_get(NULL, "csi_vfe_clk");
+ break;
+/*
+ case CAMIO_CSI1_VFE_CLK:
+ camio_csi1_vfe_clk =
+ clk = clk_get(&camio_dev->dev, "csi_vfe_clk");
+ break;
+*/
+ case CAMIO_CSI_SRC_CLK:
+ camio_csi_src_clk =
+ clk = clk_get(NULL, "csi_src_clk");
+ msm_camio_clk_rate_set_2(clk, 177780000);
+ break;
+
+ case CAMIO_CSI1_SRC_CLK:
+ camio_csi1_src_clk =
+ clk = clk_get(&camio_dev->dev, "csi_src_clk");
+ msm_camio_clk_rate_set_2(clk, 177780000);
+ break;
+
+ case CAMIO_CSI0_CLK:
+ camio_csi0_clk =
+ clk = clk_get(&camio_dev->dev, "csi_clk");
+ break;
+
+ case CAMIO_CSI0_PHY_CLK:
+ camio_csi0_phy_clk =
+ clk = clk_get(&camio_dev->dev, "csi_phy_clk");
+ break;
+
+ case CAMIO_CSI_PIX_CLK:
+ camio_csi_pix_clk =
+ clk = clk_get(NULL, "csi_pix_clk");
+ /* mux to select between csid0 and csid1 */
+ msm_camio_clk_rate_set_2(clk, csid_core);
+ break;
+
+ case CAMIO_CSI_RDI_CLK:
+ camio_csi_rdi_clk =
+ clk = clk_get(NULL, "csi_rdi_clk");
+ /* mux to select between csid0 and csid1 */
+ msm_camio_clk_rate_set_2(clk, csid_core);
+ break;
+
+ case CAMIO_CSIPHY0_TIMER_CLK:
+ camio_csiphy0_timer_clk =
+ clk = clk_get(NULL, "csi0phy_timer_clk");
+ break;
+
+ case CAMIO_CSIPHY1_TIMER_CLK:
+ camio_csiphy1_timer_clk =
+ clk = clk_get(NULL, "csi1phy_timer_clk");
+ break;
+
+ case CAMIO_CSIPHY_TIMER_SRC_CLK:
+ camio_csiphy_timer_src_clk =
+ clk = clk_get(NULL, "csiphy_timer_src_clk");
+ msm_camio_clk_rate_set_2(clk, 177780000);
+ break;
+
+ case CAMIO_CSI0_PCLK:
+ camio_csi0_pclk =
+ clk = clk_get(NULL, "csi_pclk");
+ break;
+
+ case CAMIO_JPEG_CLK:
+ camio_jpeg_clk =
+ clk = clk_get(NULL, "ijpeg_clk");
+ clk_set_min_rate(clk, 144000000);
+ break;
+
+ case CAMIO_JPEG_PCLK:
+ camio_jpeg_pclk =
+ clk = clk_get(NULL, "ijpeg_pclk");
+ break;
+
+ case CAMIO_VPE_CLK:
+ camio_vpe_clk =
+ clk = clk_get(NULL, "vpe_clk");
+ msm_camio_clk_set_min_rate(clk, 150000000);
+ break;
+
+ case CAMIO_VPE_PCLK:
+ camio_vpe_pclk =
+ clk = clk_get(NULL, "vpe_pclk");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk))
+ rc = clk_enable(clk);
+ else
+ rc = PTR_ERR(clk);
+
+ if (rc < 0)
+ pr_err("%s(%d) failed %d\n", __func__, clktype, rc);
+
+ return rc;
+}
+
+int msm_camio_clk_disable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_CAM_MCLK_CLK:
+ clk = camio_cam_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ case CAMIO_VFE_AXI_CLK:
+ clk = camio_vfe_axi_clk;
+ break;
+
+ case CAMIO_VFE_PCLK:
+ clk = camio_vfe_pclk;
+ break;
+
+ case CAMIO_CSI0_VFE_CLK:
+ clk = camio_csi0_vfe_clk;
+ break;
+
+ case CAMIO_CSI_SRC_CLK:
+ clk = camio_csi_src_clk;
+ break;
+
+ case CAMIO_CSI0_CLK:
+ clk = camio_csi0_clk;
+ break;
+
+ case CAMIO_CSI0_PHY_CLK:
+ clk = camio_csi0_phy_clk;
+ break;
+
+ case CAMIO_CSI_PIX_CLK:
+ clk = camio_csi_pix_clk;
+ break;
+
+ case CAMIO_CSI_RDI_CLK:
+ clk = camio_csi_rdi_clk;
+ break;
+
+ case CAMIO_CSIPHY0_TIMER_CLK:
+ clk = camio_csiphy0_timer_clk;
+ break;
+
+ case CAMIO_CSIPHY_TIMER_SRC_CLK:
+ clk = camio_csiphy_timer_src_clk;
+ break;
+
+ case CAMIO_CSI0_PCLK:
+ clk = camio_csi0_pclk;
+ break;
+
+ case CAMIO_JPEG_CLK:
+ clk = camio_jpeg_clk;
+ break;
+
+ case CAMIO_JPEG_PCLK:
+ clk = camio_jpeg_pclk;
+ break;
+
+ case CAMIO_VPE_CLK:
+ clk = camio_vpe_clk;
+ break;
+
+ case CAMIO_VPE_PCLK:
+ clk = camio_vpe_pclk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ } else
+ rc = PTR_ERR(clk);
+
+ if (rc < 0)
+ pr_err("%s(%d) failed %d\n", __func__, clktype, rc);
+
+ return rc;
+}
+
+void msm_camio_vfe_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+ if (rate > clk_get_rate(clk))
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_cam_clk;
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_set_min_rate(struct clk *clk, int rate)
+{
+ clk_set_min_rate(clk, rate);
+}
+
+#if DBG_CSID
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ irq = msm_io_r(csidbase + CSID_IRQ_STATUS_ADDR);
+ CDBG("%s CSID_IRQ_STATUS_ADDR = 0x%x\n", __func__, irq);
+ msm_io_w(irq, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+ irq = msm_io_r(csidbase + 0x7C);
+ CDBG("%s CSID_PIF_MISR_DL0 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csidbase + 0x80);
+ CDBG("%s CSID_PIF_MISR_DL1 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csidbase + 0x84);
+ CDBG("%s CSID_PIF_MISR_DL2 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csidbase + 0x88);
+ CDBG("%s CSID_PIF_MISR_DL3 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csidbase + 0x8C);
+ CDBG("%s PACKET Count = %d\n", __func__, irq);
+ return IRQ_HANDLED;
+}
+#endif
+/*
+void msm_io_read_interrupt(void)
+{
+ uint32_t irq;
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS0 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS0 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS1_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS1 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS2_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS2 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS3_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS3 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS4_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS4 = 0x%x\n", __func__, irq);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR1_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR2_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR3_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR4_ADDR);
+ msm_io_w(0x1, csiphybase + 0x164);
+ msm_io_w(0x0, csiphybase + 0x164);
+ return;
+}
+*/
+#if DBG_CSIPHY
+static irqreturn_t msm_io_csiphy_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS0 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS1_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR1_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS1 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS2_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR2_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS2 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS3_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR3_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS3 = 0x%x\n", __func__, irq);
+ irq = msm_io_r(csiphybase + MIPI_CSIPHY_INTERRUPT_STATUS4_ADDR);
+ msm_io_w(irq, csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR4_ADDR);
+ CDBG("%s MIPI_CSIPHY_INTERRUPT_STATUS4 = 0x%x\n", __func__, irq);
+ msm_io_w(0x1, csiphybase + 0x164);
+ msm_io_w(0x0, csiphybase + 0x164);
+ return IRQ_HANDLED;
+}
+#endif
+static int msm_camio_enable_all_clks(uint8_t csid_core)
+{
+ int rc = 0;
+
+ rc = msm_camio_clk_enable(CAMIO_CSI_SRC_CLK);
+ if (rc < 0)
+ goto csi_src_fail;
+ if (csid_core == 1) {
+ rc = msm_camio_clk_enable(CAMIO_CSI1_SRC_CLK);
+ if (rc < 0)
+ goto csi1_src_fail;
+ }
+ rc = msm_camio_clk_enable(CAMIO_CSI0_CLK);
+ if (rc < 0)
+ goto csi0_fail;
+ rc = msm_camio_clk_enable(CAMIO_CSI0_PHY_CLK);
+ if (rc < 0)
+ goto csi0_phy_fail;
+ rc = msm_camio_clk_enable(CAMIO_CSIPHY_TIMER_SRC_CLK);
+ if (rc < 0)
+ goto csiphy_timer_src_fail;
+ if (csid_core == 0) {
+ rc = msm_camio_clk_enable(CAMIO_CSIPHY0_TIMER_CLK);
+ if (rc < 0)
+ goto csiphy0_timer_fail;
+ } else if (csid_core == 1) {
+ rc = msm_camio_clk_enable(CAMIO_CSIPHY1_TIMER_CLK);
+ if (rc < 0)
+ goto csiphy1_timer_fail;
+ }
+ rc = msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+ if (rc < 0)
+ goto csi0p_fail;
+
+ rc = msm_camio_clk_enable(CAMIO_VFE_CLK);
+ if (rc < 0)
+ goto vfe_fail;
+ rc = msm_camio_clk_enable(CAMIO_VFE_AXI_CLK);
+ if (rc < 0)
+ goto axi_fail;
+ rc = msm_camio_clk_enable(CAMIO_VFE_PCLK);
+ if (rc < 0)
+ goto vfep_fail;
+
+ rc = msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+ if (rc < 0)
+ goto csi0_vfe_fail;
+ rc = msm_camio_clk_enable(CAMIO_CSI_PIX_CLK);
+ if (rc < 0)
+ goto csi_pix_fail;
+ rc = msm_camio_clk_enable(CAMIO_CSI_RDI_CLK);
+ if (rc < 0)
+ goto csi_rdi_fail;
+ return rc;
+
+csi_rdi_fail:
+ msm_camio_clk_disable(CAMIO_CSI_PIX_CLK);
+csi_pix_fail:
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+csi0_vfe_fail:
+ msm_camio_clk_disable(CAMIO_VFE_PCLK);
+vfep_fail:
+ msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+axi_fail:
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+vfe_fail:
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+csi0p_fail:
+ msm_camio_clk_disable(CAMIO_CSIPHY0_TIMER_CLK);
+csiphy1_timer_fail:
+ msm_camio_clk_disable(CAMIO_CSIPHY1_TIMER_CLK);
+csiphy0_timer_fail:
+ msm_camio_clk_disable(CAMIO_CSIPHY_TIMER_SRC_CLK);
+csiphy_timer_src_fail:
+ msm_camio_clk_disable(CAMIO_CSI0_PHY_CLK);
+csi0_phy_fail:
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+csi0_fail:
+ msm_camio_clk_disable(CAMIO_CSI1_SRC_CLK);
+csi1_src_fail:
+ msm_camio_clk_disable(CAMIO_CSI_SRC_CLK);
+csi_src_fail:
+ return rc;
+}
+
+static void msm_camio_disable_all_clks(uint8_t csid_core)
+{
+ msm_camio_clk_disable(CAMIO_CSI_RDI_CLK);
+ msm_camio_clk_disable(CAMIO_CSI_PIX_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_PCLK);
+ msm_camio_clk_disable(CAMIO_VFE_AXI_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ if (csid_core == 0)
+ msm_camio_clk_disable(CAMIO_CSIPHY0_TIMER_CLK);
+ else if (csid_core == 1)
+ msm_camio_clk_disable(CAMIO_CSIPHY1_TIMER_CLK);
+ msm_camio_clk_disable(CAMIO_CSIPHY_TIMER_SRC_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PHY_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ if (csid_core == 1)
+ msm_camio_clk_disable(CAMIO_CSI1_SRC_CLK);
+ msm_camio_clk_disable(CAMIO_CSI_SRC_CLK);
+}
+
+int msm_camio_jpeg_clk_disable(void)
+{
+ int rc = 0;
+ if (fs_ijpeg) {
+ rc = regulator_disable(fs_ijpeg);
+ if (rc < 0) {
+ CDBG("%s: Regulator disable failed %d\n", __func__, rc);
+ return rc;
+ }
+ regulator_put(fs_ijpeg);
+ }
+ rc = msm_camio_clk_disable(CAMIO_JPEG_PCLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_disable(CAMIO_JPEG_CLK);
+ CDBG("%s: exit %d\n", __func__, rc);
+ return rc;
+}
+
+int msm_camio_jpeg_clk_enable(void)
+{
+ int rc = 0;
+ rc = msm_camio_clk_enable(CAMIO_JPEG_CLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_enable(CAMIO_JPEG_PCLK);
+ if (rc < 0)
+ return rc;
+ fs_ijpeg = regulator_get(NULL, "fs_ijpeg");
+ if (IS_ERR(fs_ijpeg)) {
+ CDBG("%s: Regulator FS_IJPEG get failed %ld\n", __func__,
+ PTR_ERR(fs_ijpeg));
+ fs_ijpeg = NULL;
+ } else if (regulator_enable(fs_ijpeg)) {
+ CDBG("%s: Regulator FS_IJPEG enable failed\n", __func__);
+ regulator_put(fs_ijpeg);
+ }
+ CDBG("%s: exit %d\n", __func__, rc);
+ return rc;
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+ int rc = 0;
+ if (fs_vpe) {
+ regulator_disable(fs_vpe);
+ regulator_put(fs_vpe);
+ }
+
+ rc = msm_camio_clk_disable(CAMIO_VPE_CLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_disable(CAMIO_VPE_PCLK);
+ return rc;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+ int rc = 0;
+ (void)clk_rate;
+ fs_vpe = regulator_get(NULL, "fs_vpe");
+ if (IS_ERR(fs_vpe)) {
+ CDBG("%s: Regulator FS_VPE get failed %ld\n", __func__,
+ PTR_ERR(fs_vpe));
+ fs_vpe = NULL;
+ } else if (regulator_enable(fs_vpe)) {
+ CDBG("%s: Regulator FS_VPE enable failed\n", __func__);
+ regulator_put(fs_vpe);
+ }
+
+ rc = msm_camio_clk_enable(CAMIO_VPE_CLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_enable(CAMIO_VPE_PCLK);
+ return rc;
+}
+
+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;
+ uint8_t csid_core = camdev->csid_core;
+ char csid[] = "csid0";
+ char csiphy[] = "csiphy0";
+ if (csid_core < 0 || csid_core > 2)
+ return -ENODEV;
+
+ csid[4] = '0' + csid_core;
+ csiphy[6] = '0' + csid_core;
+
+ camio_dev = pdev;
+ camio_clk = camdev->ioclk;
+
+ rc = msm_camio_enable_all_clks(csid_core);
+ if (rc < 0)
+ return rc;
+
+ csid_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, csid);
+ if (!csid_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+ csid_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, csid);
+ if (!csid_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+ csiphy_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, csiphy);
+ if (!csiphy_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+ csiphy_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, csiphy);
+ if (!csiphy_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ csidio = request_mem_region(csid_mem->start,
+ resource_size(csid_mem), pdev->name);
+ if (!csidio) {
+ rc = -EBUSY;
+ goto common_fail;
+ }
+ csidbase = ioremap(csid_mem->start,
+ resource_size(csid_mem));
+ if (!csidbase) {
+ rc = -ENOMEM;
+ goto csi_busy;
+ }
+#if DBG_CSID
+ rc = request_irq(csid_irq->start, msm_io_csi_irq,
+ IRQF_TRIGGER_RISING, "csid", 0);
+ if (rc < 0)
+ goto csi_irq_fail;
+#endif
+ csiphyio = request_mem_region(csiphy_mem->start,
+ resource_size(csiphy_mem), pdev->name);
+ if (!csidio) {
+ rc = -EBUSY;
+ goto csi_irq_fail;
+ }
+ csiphybase = ioremap(csiphy_mem->start,
+ resource_size(csiphy_mem));
+ if (!csiphybase) {
+ rc = -ENOMEM;
+ goto csiphy_busy;
+ }
+#if DBG_CSIPHY
+ rc = request_irq(csiphy_irq->start, msm_io_csiphy_irq,
+ IRQF_TRIGGER_RISING, "csiphy", 0);
+ if (rc < 0)
+ goto csiphy_irq_fail;
+#endif
+ rc = msm_ispif_init(pdev);
+ if (rc < 0)
+ goto csiphy_irq_fail;
+ CDBG("camio enable done\n");
+ return 0;
+csiphy_irq_fail:
+ iounmap(csiphybase);
+csiphy_busy:
+ release_mem_region(csiphy_mem->start, resource_size(csiphy_mem));
+csi_irq_fail:
+ iounmap(csidbase);
+csi_busy:
+ release_mem_region(csid_mem->start, resource_size(csid_mem));
+common_fail:
+ msm_camio_disable_all_clks(csid_core);
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ 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;
+ uint8_t csid_core = camdev->csid_core;
+#if DBG_CSIPHY
+ free_irq(csiphy_irq->start, 0);
+#endif
+ iounmap(csiphybase);
+ release_mem_region(csiphy_mem->start, resource_size(csiphy_mem));
+
+#if DBG_CSID
+ free_irq(csid_irq, 0);
+#endif
+ iounmap(csidbase);
+ release_mem_region(csid_mem->start, resource_size(csid_mem));
+
+ msm_camio_disable_all_clks(csid_core);
+ msm_ispif_release(pdev);
+}
+
+int msm_camio_sensor_clk_on(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_dev = pdev;
+ camio_clk = camdev->ioclk;
+
+ msm_camera_vreg_enable(pdev);
+ msleep(20);
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ return;
+}
+
+int msm_camio_probe_on(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_dev = pdev;
+ camio_clk = camdev->ioclk;
+
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ msm_camera_vreg_enable(pdev);
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_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;
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_csid_cid_lut(struct msm_camera_csid_lut_params *csid_lut_params)
+{
+ int rc = 0, i = 0;
+ uint32_t val = 0;
+
+ for (i = 0; i < csid_lut_params->num_cid && i < 4; i++) {
+ if (csid_lut_params->vc_cfg[i].dt < 0x12 ||
+ csid_lut_params->vc_cfg[i].dt > 0x37) {
+ CDBG("%s: unsupported data type 0x%x\n",
+ __func__, csid_lut_params->vc_cfg[i].dt);
+ return rc;
+ }
+ val = msm_io_r(csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i].cid >> 2) * 4)
+ & ~(0xFF << csid_lut_params->vc_cfg[i].cid * 8);
+ val |= csid_lut_params->vc_cfg[i].dt <<
+ csid_lut_params->vc_cfg[i].cid * 8;
+ msm_io_w(val, csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i].cid >> 2) * 4);
+ val = csid_lut_params->vc_cfg[i].decode_format << 4 | 0x3;
+ msm_io_w(val, csidbase + CSID_CID_n_CFG_ADDR +
+ (csid_lut_params->vc_cfg[i].cid * 4));
+ }
+ return rc;
+}
+
+int msm_camio_csid_config(struct msm_camera_csid_params *csid_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ val = csid_params->lane_cnt - 1;
+ val |= csid_params->lane_assign << 2;
+ val |= 0x1 << 10;
+ val |= 0x1 << 11;
+ val |= 0x1 << 12;
+ val |= 0x1 << 28;
+ msm_io_w(val, csidbase + CSID_CORE_CTRL_ADDR);
+
+ rc = msm_camio_csid_cid_lut(&csid_params->lut_params);
+ if (rc < 0)
+ return rc;
+
+ msm_io_w(0xFFFFFFFF, csidbase + CSID_IRQ_MASK_ADDR);
+ msm_io_w(0xFFFFFFFF, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+
+ msleep(20);
+ return rc;
+}
+
+int msm_camio_csiphy_config(struct msm_camera_csiphy_params *csiphy_params)
+{
+ int rc = 0;
+ int i = 0;
+ uint32_t val = 0;
+ if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
+ CDBG("%s: unsupported lane cnt %d\n",
+ __func__, csiphy_params->lane_cnt);
+ return rc;
+ }
+
+ val = 0x3;
+ msm_io_w((((1 << csiphy_params->lane_cnt) - 1) << 2) | val,
+ csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+ msm_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
+ msm_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
+
+ for (i = 0; i < csiphy_params->lane_cnt; i++) {
+ msm_io_w(0x10, csiphybase + MIPI_CSIPHY_LNn_CFG1_ADDR + 0x40*i);
+ msm_io_w(0x5F, csiphybase + MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ msm_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNn_CFG3_ADDR + 0x40*i);
+ msm_io_w(0x00000052,
+ csiphybase + MIPI_CSIPHY_LNn_CFG5_ADDR + 0x40*i);
+ }
+
+ msm_io_w(0x00000000, csiphybase + MIPI_CSIPHY_LNCK_CFG1_ADDR);
+ msm_io_w(0x5F, csiphybase + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNCK_CFG3_ADDR);
+ msm_io_w(0x5, csiphybase + MIPI_CSIPHY_LNCK_CFG4_ADDR);
+ msm_io_w(0x2, csiphybase + MIPI_CSIPHY_LNCK_CFG5_ADDR);
+ msm_io_w(0x0, csiphybase + 0x128);
+
+ for (i = 0; i <= csiphy_params->lane_cnt; i++) {
+ msm_io_w(0xFFFFFFFF,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR + 0x4*i);
+ msm_io_w(0xFFFFFFFF,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
+ }
+ return rc;
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+ static uint32_t bus_perf_client;
+ int rc = 0;
+ switch (perf_setting) {
+ case S_INIT:
+ bus_perf_client =
+ msm_bus_scale_register_client(&cam_bus_client_pdata);
+ if (!bus_perf_client) {
+ CDBG("%s: Registration Failed!!!\n", __func__);
+ bus_perf_client = 0;
+ return;
+ }
+ CDBG("%s: S_INIT rc = %u\n", __func__, bus_perf_client);
+ break;
+ case S_EXIT:
+ if (bus_perf_client) {
+ CDBG("%s: S_EXIT\n", __func__);
+ msm_bus_scale_unregister_client(bus_perf_client);
+ } else
+ CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_PREVIEW:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 1);
+ CDBG("%s: S_PREVIEW rc = %d\n", __func__, rc);
+ } else
+ CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_VIDEO:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 2);
+ CDBG("%s: S_VIDEO rc = %d\n", __func__, rc);
+ } else
+ CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_CAPTURE:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 3);
+ CDBG("%s: S_CAPTURE rc = %d\n", __func__, rc);
+ } else
+ CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_DEFAULT:
+ break;
+ default:
+ pr_warning("%s: INVALID CASE\n", __func__);
+ }
+}
diff --git a/drivers/media/video/msm/msm_io_8x60.c b/drivers/media/video/msm/msm_io_8x60.c
new file mode 100644
index 0000000..845777d
--- /dev/null
+++ b/drivers/media/video/msm/msm_io_8x60.c
@@ -0,0 +1,900 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+/* MIPI CSI controller registers */
+#define MIPI_PHY_CONTROL 0x00000000
+#define MIPI_PROTOCOL_CONTROL 0x00000004
+#define MIPI_INTERRUPT_STATUS 0x00000008
+#define MIPI_INTERRUPT_MASK 0x0000000C
+#define MIPI_CAMERA_CNTL 0x00000024
+#define MIPI_CALIBRATION_CONTROL 0x00000018
+#define MIPI_PHY_D0_CONTROL2 0x00000038
+#define MIPI_PHY_D1_CONTROL2 0x0000003C
+#define MIPI_PHY_D2_CONTROL2 0x00000040
+#define MIPI_PHY_D3_CONTROL2 0x00000044
+#define MIPI_PHY_CL_CONTROL 0x00000048
+#define MIPI_PHY_D0_CONTROL 0x00000034
+#define MIPI_PHY_D1_CONTROL 0x00000020
+#define MIPI_PHY_D2_CONTROL 0x0000002C
+#define MIPI_PHY_D3_CONTROL 0x00000030
+#define MIPI_PROTOCOL_CONTROL_SW_RST_BMSK 0x8000000
+#define MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK 0x200000
+#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_BMSK 0x180000
+#define MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK 0x40000
+#define MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK 0x20000
+#define MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT 0x16
+#define MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT 0x15
+#define MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT 0x14
+#define MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT 0x7
+#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT 0x13
+#define MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT 0x1e
+#define MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT 0x18
+#define MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT 0x2
+#define MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT 0x1c
+#define MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT 0x9
+#define MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT 0x8
+
+static struct clk *camio_cam_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_csi_src_clk;
+static struct clk *camio_csi0_vfe_clk;
+static struct clk *camio_csi1_vfe_clk;
+static struct clk *camio_csi0_clk;
+static struct clk *camio_csi1_clk;
+static struct clk *camio_csi0_pclk;
+static struct clk *camio_csi1_pclk;
+static struct clk *camio_vfe_pclk;
+static struct clk *camio_jpeg_clk;
+static struct clk *camio_jpeg_pclk;
+static struct clk *camio_vpe_clk;
+static struct clk *camio_vpe_pclk;
+static struct regulator *fs_vfe;
+static struct regulator *fs_ijpeg;
+static struct regulator *fs_vpe;
+static struct regulator *ldo15;
+static struct regulator *lvs0;
+static struct regulator *ldo25;
+
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct platform_device *camio_dev;
+static struct resource *csiio;
+void __iomem *csibase;
+static int vpe_clk_rate;
+struct msm_bus_scale_pdata *cam_bus_scale_table;
+
+void msm_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+void msm_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ wmb();
+ writel_relaxed((data), (addr));
+ wmb();
+}
+
+u32 msm_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+u32 msm_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ rmb();
+ data = readl_relaxed(addr);
+ rmb();
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+ /* memcpy_toio does not work. Use writel for now */
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+void msm_io_dump(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ sprintf(p_str, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ sprintf(p_str, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+void msm_io_memcpy(void __iomem *dest_addr, void __iomem *src_addr, u32 len)
+{
+ CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+ msm_io_memcpy_toio(dest_addr, src_addr, len / 4);
+ msm_io_dump(dest_addr, len);
+}
+
+static void msm_camera_vreg_enable(void)
+{
+ ldo15 = regulator_get(NULL, "8058_l15");
+ if (IS_ERR(ldo15)) {
+ pr_err("%s: VREG LDO15 get failed\n", __func__);
+ ldo15 = NULL;
+ return;
+ }
+ if (regulator_set_voltage(ldo15, 2850000, 2850000)) {
+ pr_err("%s: VREG LDO15 set voltage failed\n", __func__);
+ goto ldo15_disable;
+ }
+ if (regulator_enable(ldo15)) {
+ pr_err("%s: VREG LDO15 enable failed\n", __func__);
+ goto ldo15_put;
+ }
+
+ lvs0 = regulator_get(NULL, "8058_lvs0");
+ if (IS_ERR(lvs0)) {
+ pr_err("%s: VREG LVS0 get failed\n", __func__);
+ lvs0 = NULL;
+ goto ldo15_disable;
+ }
+ if (regulator_enable(lvs0)) {
+ pr_err("%s: VREG LVS0 enable failed\n", __func__);
+ goto lvs0_put;
+ }
+
+ ldo25 = regulator_get(NULL, "8058_l25");
+ if (IS_ERR(ldo25)) {
+ pr_err("%s: VREG LDO25 get failed\n", __func__);
+ ldo25 = NULL;
+ goto lvs0_disable;
+ }
+ if (regulator_set_voltage(ldo25, 1200000, 1200000)) {
+ pr_err("%s: VREG LDO25 set voltage failed\n", __func__);
+ goto ldo25_disable;
+ }
+ if (regulator_enable(ldo25)) {
+ pr_err("%s: VREG LDO25 enable failed\n", __func__);
+ goto ldo25_put;
+ }
+
+ fs_vfe = regulator_get(NULL, "fs_vfe");
+ if (IS_ERR(fs_vfe)) {
+ CDBG("%s: Regulator FS_VFE get failed %ld\n", __func__,
+ PTR_ERR(fs_vfe));
+ fs_vfe = NULL;
+ } else if (regulator_enable(fs_vfe)) {
+ CDBG("%s: Regulator FS_VFE enable failed\n", __func__);
+ regulator_put(fs_vfe);
+ }
+ return;
+
+ldo25_disable:
+ regulator_disable(ldo25);
+ldo25_put:
+ regulator_put(ldo25);
+lvs0_disable:
+ regulator_disable(lvs0);
+lvs0_put:
+ regulator_put(lvs0);
+ldo15_disable:
+ regulator_disable(ldo15);
+ldo15_put:
+ regulator_put(ldo15);
+}
+
+static void msm_camera_vreg_disable(void)
+{
+ if (ldo15) {
+ regulator_disable(ldo15);
+ regulator_put(ldo15);
+ }
+
+ if (lvs0) {
+ regulator_disable(lvs0);
+ regulator_put(lvs0);
+ }
+
+ if (ldo25) {
+ regulator_disable(ldo25);
+ regulator_put(ldo25);
+ }
+
+ if (fs_vfe) {
+ regulator_disable(fs_vfe);
+ regulator_put(fs_vfe);
+ }
+}
+
+int msm_camio_clk_enable(enum msm_camio_clk_type clktype)
+{
+ int rc = 0;
+ struct clk *clk = NULL;
+
+ switch (clktype) {
+ case CAMIO_CAM_MCLK_CLK:
+ camio_cam_clk =
+ clk = clk_get(NULL, "cam_clk");
+ msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+ break;
+
+ case CAMIO_VFE_CLK:
+ camio_vfe_clk =
+ clk = clk_get(NULL, "vfe_clk");
+ msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+ break;
+
+ case CAMIO_CSI0_VFE_CLK:
+ camio_csi0_vfe_clk =
+ clk = clk_get(NULL, "csi_vfe_clk");
+ break;
+
+ case CAMIO_CSI1_VFE_CLK:
+ camio_csi1_vfe_clk =
+ clk = clk_get(&camio_dev->dev, "csi_vfe_clk");
+ break;
+
+ case CAMIO_CSI_SRC_CLK:
+ camio_csi_src_clk =
+ clk = clk_get(NULL, "csi_src_clk");
+ msm_camio_clk_rate_set_2(clk, 384000000);
+ break;
+
+ case CAMIO_CSI0_CLK:
+ camio_csi0_clk =
+ clk = clk_get(NULL, "csi_clk");
+ break;
+
+ case CAMIO_CSI1_CLK:
+ camio_csi1_clk =
+ clk = clk_get(&camio_dev->dev, "csi_clk");
+ break;
+
+ case CAMIO_VFE_PCLK:
+ camio_vfe_pclk =
+ clk = clk_get(NULL, "vfe_pclk");
+ break;
+
+ case CAMIO_CSI0_PCLK:
+ camio_csi0_pclk =
+ clk = clk_get(NULL, "csi_pclk");
+ break;
+
+ case CAMIO_CSI1_PCLK:
+ camio_csi1_pclk =
+ clk = clk_get(&camio_dev->dev, "csi_pclk");
+ break;
+
+ case CAMIO_JPEG_CLK:
+ camio_jpeg_clk =
+ clk = clk_get(NULL, "ijpeg_clk");
+ msm_camio_clk_rate_set_2(clk, 228571000);
+ break;
+
+ case CAMIO_JPEG_PCLK:
+ camio_jpeg_pclk =
+ clk = clk_get(NULL, "ijpeg_pclk");
+ break;
+
+ case CAMIO_VPE_CLK:
+ camio_vpe_clk =
+ clk = clk_get(NULL, "vpe_clk");
+ msm_camio_clk_set_min_rate(camio_vpe_clk, vpe_clk_rate);
+ break;
+
+ case CAMIO_VPE_PCLK:
+ camio_vpe_pclk =
+ clk = clk_get(NULL, "vpe_pclk");
+ 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_CAM_MCLK_CLK:
+ clk = camio_cam_clk;
+ break;
+
+ case CAMIO_VFE_CLK:
+ clk = camio_vfe_clk;
+ break;
+
+ case CAMIO_CSI_SRC_CLK:
+ clk = camio_csi_src_clk;
+ break;
+
+ case CAMIO_CSI0_VFE_CLK:
+ clk = camio_csi0_vfe_clk;
+ break;
+
+ case CAMIO_CSI1_VFE_CLK:
+ clk = camio_csi1_vfe_clk;
+ break;
+
+ case CAMIO_CSI0_CLK:
+ clk = camio_csi0_clk;
+ break;
+
+ case CAMIO_CSI1_CLK:
+ clk = camio_csi1_clk;
+ break;
+
+ case CAMIO_VFE_PCLK:
+ clk = camio_vfe_pclk;
+ break;
+
+ case CAMIO_CSI0_PCLK:
+ clk = camio_csi0_pclk;
+ break;
+
+ case CAMIO_CSI1_PCLK:
+ clk = camio_csi1_pclk;
+ break;
+
+ case CAMIO_JPEG_CLK:
+ clk = camio_jpeg_clk;
+ break;
+
+ case CAMIO_JPEG_PCLK:
+ clk = camio_jpeg_pclk;
+ break;
+
+ case CAMIO_VPE_CLK:
+ clk = camio_vpe_clk;
+ break;
+
+ case CAMIO_VPE_PCLK:
+ clk = camio_vpe_pclk;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ } else
+ rc = -1;
+ return rc;
+}
+
+void msm_camio_vfe_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+ if (rate > clk_get_rate(clk))
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_cam_clk;
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_set_min_rate(struct clk *clk, int rate)
+{
+ clk_set_min_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ irq = msm_io_r(csibase + MIPI_INTERRUPT_STATUS);
+ CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+ msm_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+ return IRQ_HANDLED;
+}
+
+int msm_camio_jpeg_clk_disable(void)
+{
+ int rc = 0;
+ if (fs_ijpeg) {
+ rc = regulator_disable(fs_ijpeg);
+ if (rc < 0) {
+ CDBG("%s: Regulator disable failed %d\n", __func__, rc);
+ return rc;
+ }
+ regulator_put(fs_ijpeg);
+ }
+ rc = msm_camio_clk_disable(CAMIO_JPEG_PCLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_disable(CAMIO_JPEG_CLK);
+ CDBG("%s: exit %d\n", __func__, rc);
+ return rc;
+}
+
+int msm_camio_jpeg_clk_enable(void)
+{
+ int rc = 0;
+ rc = msm_camio_clk_enable(CAMIO_JPEG_CLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_enable(CAMIO_JPEG_PCLK);
+ if (rc < 0)
+ return rc;
+ fs_ijpeg = regulator_get(NULL, "fs_ijpeg");
+ if (IS_ERR(fs_ijpeg)) {
+ CDBG("%s: Regulator FS_IJPEG get failed %ld\n", __func__,
+ PTR_ERR(fs_ijpeg));
+ fs_ijpeg = NULL;
+ } else if (regulator_enable(fs_ijpeg)) {
+ CDBG("%s: Regulator FS_IJPEG enable failed\n", __func__);
+ regulator_put(fs_ijpeg);
+ }
+ CDBG("%s: exit %d\n", __func__, rc);
+ return rc;
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+ int rc = 0;
+ if (fs_vpe) {
+ regulator_disable(fs_vpe);
+ regulator_put(fs_vpe);
+ }
+
+ rc = msm_camio_clk_disable(CAMIO_VPE_CLK);
+ if (rc < 0)
+ return rc;
+ rc = msm_camio_clk_disable(CAMIO_VPE_PCLK);
+ return rc;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+ int rc = 0;
+ fs_vpe = regulator_get(NULL, "fs_vpe");
+ if (IS_ERR(fs_vpe)) {
+ CDBG("%s: Regulator FS_VPE get failed %ld\n", __func__,
+ PTR_ERR(fs_vpe));
+ fs_vpe = NULL;
+ } else if (regulator_enable(fs_vpe)) {
+ CDBG("%s: Regulator FS_VPE enable failed\n", __func__);
+ regulator_put(fs_vpe);
+ }
+
+ vpe_clk_rate = clk_rate;
+ rc = msm_camio_clk_enable(CAMIO_VPE_CLK);
+ if (rc < 0)
+ return rc;
+
+ rc = msm_camio_clk_enable(CAMIO_VPE_PCLK);
+ return rc;
+}
+
+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;
+ uint32_t val;
+
+ camio_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+ cam_bus_scale_table = camdev->cam_bus_scale_table;
+
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI_SRC_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_CLK);
+ msm_camio_clk_enable(CAMIO_CSI1_CLK);
+ msm_camio_clk_enable(CAMIO_VFE_PCLK);
+ msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_enable(CAMIO_CSI1_PCLK);
+
+ csiio = request_mem_region(camio_ext.csiphy,
+ camio_ext.csisz, pdev->name);
+ if (!csiio) {
+ rc = -EBUSY;
+ goto common_fail;
+ }
+ csibase = ioremap(camio_ext.csiphy,
+ camio_ext.csisz);
+ if (!csibase) {
+ rc = -ENOMEM;
+ goto csi_busy;
+ }
+ rc = request_irq(camio_ext.csiirq, msm_io_csi_irq,
+ IRQF_TRIGGER_RISING, "csi", 0);
+ if (rc < 0)
+ goto csi_irq_fail;
+
+ msleep(10);
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ return 0;
+
+csi_irq_fail:
+ iounmap(csibase);
+csi_busy:
+ release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+common_fail:
+ msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ uint32_t val;
+ val = (0x0 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+ (0x0 <<
+ MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+ (0x0 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+ (0x0 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+ CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ msleep(10);
+
+ val = msm_io_r(csibase + MIPI_PHY_D1_CONTROL);
+ val &= ~((0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+ (0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT));
+ CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+ usleep_range(5000, 6000);
+ free_irq(camio_ext.csiirq, 0);
+ iounmap(csibase);
+ release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+ CDBG("disable clocks\n");
+
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI1_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI1_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI_SRC_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+}
+
+int msm_camio_sensor_clk_on(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_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+
+ msm_camera_vreg_enable();
+ msleep(10);
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ return;
+}
+
+int msm_camio_probe_on(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_dev = pdev;
+ camio_ext = camdev->ioext;
+ camio_clk = camdev->ioclk;
+
+ rc = camdev->camera_gpio_on();
+ if (rc < 0)
+ return rc;
+ msm_camera_vreg_enable();
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_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;
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+
+ CDBG("msm_camio_csi_config \n");
+
+ /* SOT_ECC_EN enable error correction for SYNC (data-lane) */
+ msm_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+ /* SW_RST to the CSI core */
+ msm_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+ csibase + MIPI_PROTOCOL_CONTROL);
+
+ /* PROTOCOL CONTROL */
+ val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+ MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+ MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+ val |= (uint32_t)(csi_params->data_format) <<
+ MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+ val |= csi_params->dpcm_scheme <<
+ MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+ CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+ /* SW CAL EN */
+ val = (0x1 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+ (0x1 <<
+ MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+ CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+ /* settle_cnt is very sensitive to speed!
+ increase this value to run at higher speeds */
+ val = (csi_params->settle_cnt <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+ val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+ val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+ (0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+ CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+ /* halcyon only supports 1 or 2 lane */
+ switch (csi_params->lane_cnt) {
+ case 1:
+ msm_io_w(csi_params->lane_assign << 8 | 0x4,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 2:
+ msm_io_w(csi_params->lane_assign << 8 | 0x5,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 3:
+ msm_io_w(csi_params->lane_assign << 8 | 0x6,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 4:
+ msm_io_w(csi_params->lane_assign << 8 | 0x7,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ }
+
+ /* mask out ID_ERROR[19], DATA_CMM_ERR[11]
+ and CLK_CMM_ERR[10] - de-featured */
+ msm_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_MASK);
+ /*clear IRQ bits*/
+ msm_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_STATUS);
+
+ return rc;
+}
+
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+ static uint32_t bus_perf_client;
+ int rc = 0;
+ switch (perf_setting) {
+ case S_INIT:
+ bus_perf_client =
+ msm_bus_scale_register_client(cam_bus_scale_table);
+ if (!bus_perf_client) {
+ pr_err("%s: Registration Failed!!!\n", __func__);
+ bus_perf_client = 0;
+ return;
+ }
+ CDBG("%s: S_INIT rc = %u\n", __func__, bus_perf_client);
+ break;
+ case S_EXIT:
+ if (bus_perf_client) {
+ CDBG("%s: S_EXIT\n", __func__);
+ msm_bus_scale_unregister_client(bus_perf_client);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_PREVIEW:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 1);
+ CDBG("%s: S_PREVIEW rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_VIDEO:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 2);
+ CDBG("%s: S_VIDEO rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_CAPTURE:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 3);
+ CDBG("%s: S_CAPTURE rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+
+ case S_ZSL:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 4);
+ CDBG("%s: S_ZSL rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_STEREO_VIDEO:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 5);
+ CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_STEREO_CAPTURE:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 6);
+ CDBG("%s: S_STEREO_VIDEO rc = %d\n", __func__, rc);
+ } else
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
+ case S_DEFAULT:
+ break;
+ default:
+ pr_warning("%s: INVALID CASE\n", __func__);
+ }
+}
diff --git a/drivers/media/video/msm/msm_io_vfe31.c b/drivers/media/video/msm/msm_io_vfe31.c
new file mode 100644
index 0000000..6279b36
--- /dev/null
+++ b/drivers/media/video/msm/msm_io_vfe31.c
@@ -0,0 +1,924 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pm_qos_params.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/clk.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
+
+/* MIPI CSI controller registers */
+#define MIPI_PHY_CONTROL 0x00000000
+#define MIPI_PROTOCOL_CONTROL 0x00000004
+#define MIPI_INTERRUPT_STATUS 0x00000008
+#define MIPI_INTERRUPT_MASK 0x0000000C
+#define MIPI_CAMERA_CNTL 0x00000024
+#define MIPI_CALIBRATION_CONTROL 0x00000018
+#define MIPI_PHY_D0_CONTROL2 0x00000038
+#define MIPI_PHY_D1_CONTROL2 0x0000003C
+#define MIPI_PHY_D2_CONTROL2 0x00000040
+#define MIPI_PHY_D3_CONTROL2 0x00000044
+#define MIPI_PHY_CL_CONTROL 0x00000048
+#define MIPI_PHY_D0_CONTROL 0x00000034
+#define MIPI_PHY_D1_CONTROL 0x00000020
+#define MIPI_PHY_D2_CONTROL 0x0000002C
+#define MIPI_PHY_D3_CONTROL 0x00000030
+#define MIPI_PROTOCOL_CONTROL_SW_RST_BMSK 0x8000000
+#define MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK 0x200000
+#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_BMSK 0x180000
+#define MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK 0x40000
+#define MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK 0x20000
+#define MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT 0x16
+#define MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT 0x15
+#define MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT 0x14
+#define MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT 0x7
+#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT 0x13
+#define MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT 0x1e
+#define MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT 0x18
+#define MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT 0x10
+#define MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT 0x4
+#define MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
+#define MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT 0x18
+#define MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT 0x2
+#define MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT 0x1c
+#define MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT 0x9
+#define MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT 0x8
+
+#define CAMIO_VFE_CLK_SNAP 122880000
+#define CAMIO_VFE_CLK_PREV 122880000
+
+#ifdef CONFIG_MSM_NPA_SYSTEM_BUS
+/* NPA Flow IDs */
+#define MSM_AXI_QOS_PREVIEW MSM_AXI_FLOW_CAMERA_PREVIEW_HIGH
+#define MSM_AXI_QOS_SNAPSHOT MSM_AXI_FLOW_CAMERA_SNAPSHOT_12MP
+#define MSM_AXI_QOS_RECORDING MSM_AXI_FLOW_CAMERA_RECORDING_720P
+#else
+/* AXI rates in KHz */
+#define MSM_AXI_QOS_PREVIEW 192000
+#define MSM_AXI_QOS_SNAPSHOT 192000
+#define MSM_AXI_QOS_RECORDING 192000
+#endif
+
+static struct clk *camio_vfe_mdc_clk;
+static struct clk *camio_mdc_clk;
+static struct clk *camio_vfe_clk;
+static struct clk *camio_vfe_camif_clk;
+static struct clk *camio_vfe_pbdg_clk;
+static struct clk *camio_cam_m_clk;
+static struct clk *camio_camif_pad_pbdg_clk;
+static struct clk *camio_csi_clk;
+static struct clk *camio_csi_pclk;
+static struct clk *camio_csi_vfe_clk;
+static struct clk *camio_jpeg_clk;
+static struct clk *camio_jpeg_pclk;
+static struct clk *camio_vpe_clk;
+static struct vreg *vreg_gp2;
+static struct vreg *vreg_lvsw1;
+static struct vreg *vreg_gp6;
+static struct vreg *vreg_gp16;
+static struct regulator *fs_vfe;
+static struct regulator *fs_vpe;
+static struct msm_camera_io_ext camio_ext;
+static struct msm_camera_io_clk camio_clk;
+static struct resource *camifpadio, *csiio;
+void __iomem *camifpadbase, *csibase;
+static uint32_t vpe_clk_rate;
+
+void msm_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+void msm_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ wmb();
+ writel_relaxed((data), (addr));
+ wmb();
+}
+
+u32 msm_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+u32 msm_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ rmb();
+ data = readl_relaxed(addr);
+ rmb();
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+ /* memcpy_toio does not work. Use writel for now */
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+void msm_io_dump(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ sprintf(p_str, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ sprintf(p_str, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+void msm_io_memcpy(void __iomem *dest_addr, void __iomem *src_addr, u32 len)
+{
+ CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+ msm_io_memcpy_toio(dest_addr, src_addr, len / 4);
+ msm_io_dump(dest_addr, len);
+}
+
+static void msm_camera_vreg_enable(struct platform_device *pdev)
+{
+ vreg_gp2 = vreg_get(NULL, "gp2");
+ if (IS_ERR(vreg_gp2)) {
+ pr_err("%s: VREG GP2 get failed %ld\n", __func__,
+ PTR_ERR(vreg_gp2));
+ vreg_gp2 = NULL;
+ return;
+ }
+
+ if (vreg_set_level(vreg_gp2, 2600)) {
+ pr_err("%s: VREG GP2 set failed\n", __func__);
+ goto gp2_put;
+ }
+
+ if (vreg_enable(vreg_gp2)) {
+ pr_err("%s: VREG GP2 enable failed\n", __func__);
+ goto gp2_put;
+ }
+
+ vreg_lvsw1 = vreg_get(NULL, "lvsw1");
+ if (IS_ERR(vreg_lvsw1)) {
+ pr_err("%s: VREG LVSW1 get failed %ld\n", __func__,
+ PTR_ERR(vreg_lvsw1));
+ vreg_lvsw1 = NULL;
+ goto gp2_disable;
+ }
+ if (vreg_set_level(vreg_lvsw1, 1800)) {
+ pr_err("%s: VREG LVSW1 set failed\n", __func__);
+ goto lvsw1_put;
+ }
+ if (vreg_enable(vreg_lvsw1)) {
+ pr_err("%s: VREG LVSW1 enable failed\n", __func__);
+ goto lvsw1_put;
+ }
+
+ if (!strcmp(pdev->name, "msm_camera_sn12m0pz")) {
+ vreg_gp6 = vreg_get(NULL, "gp6");
+ if (IS_ERR(vreg_gp6)) {
+ pr_err("%s: VREG GP6 get failed %ld\n", __func__,
+ PTR_ERR(vreg_gp6));
+ vreg_gp6 = NULL;
+ goto lvsw1_disable;
+ }
+
+ if (vreg_set_level(vreg_gp6, 3050)) {
+ pr_err("%s: VREG GP6 set failed\n", __func__);
+ goto gp6_put;
+ }
+
+ if (vreg_enable(vreg_gp6)) {
+ pr_err("%s: VREG GP6 enable failed\n", __func__);
+ goto gp6_put;
+ }
+ vreg_gp16 = vreg_get(NULL, "gp16");
+ if (IS_ERR(vreg_gp16)) {
+ pr_err("%s: VREG GP16 get failed %ld\n", __func__,
+ PTR_ERR(vreg_gp16));
+ vreg_gp16 = NULL;
+ goto gp6_disable;
+ }
+
+ if (vreg_set_level(vreg_gp16, 1200)) {
+ pr_err("%s: VREG GP16 set failed\n", __func__);
+ goto gp16_put;
+ }
+
+ if (vreg_enable(vreg_gp16)) {
+ pr_err("%s: VREG GP16 enable failed\n", __func__);
+ goto gp16_put;
+ }
+ }
+
+ fs_vfe = regulator_get(NULL, "fs_vfe");
+ if (IS_ERR(fs_vfe)) {
+ pr_err("%s: Regulator FS_VFE get failed %ld\n", __func__,
+ PTR_ERR(fs_vfe));
+ fs_vfe = NULL;
+ } else if (regulator_enable(fs_vfe)) {
+ pr_err("%s: Regulator FS_VFE enable failed\n", __func__);
+ regulator_put(fs_vfe);
+ }
+
+ return;
+
+gp16_put:
+ vreg_put(vreg_gp16);
+ vreg_gp16 = NULL;
+gp6_disable:
+ vreg_disable(vreg_gp6);
+gp6_put:
+ vreg_put(vreg_gp6);
+ vreg_gp6 = NULL;
+lvsw1_disable:
+ vreg_disable(vreg_lvsw1);
+lvsw1_put:
+ vreg_put(vreg_lvsw1);
+ vreg_lvsw1 = NULL;
+gp2_disable:
+ vreg_disable(vreg_gp2);
+gp2_put:
+ vreg_put(vreg_gp2);
+ vreg_gp2 = NULL;
+}
+
+static void msm_camera_vreg_disable(void)
+{
+ if (vreg_gp2) {
+ vreg_disable(vreg_gp2);
+ vreg_put(vreg_gp2);
+ vreg_gp2 = NULL;
+ }
+ if (vreg_lvsw1) {
+ vreg_disable(vreg_lvsw1);
+ vreg_put(vreg_lvsw1);
+ vreg_lvsw1 = NULL;
+ }
+ if (vreg_gp6) {
+ vreg_disable(vreg_gp6);
+ vreg_put(vreg_gp6);
+ vreg_gp6 = NULL;
+ }
+ if (vreg_gp16) {
+ vreg_disable(vreg_gp16);
+ vreg_put(vreg_gp16);
+ vreg_gp16 = NULL;
+ }
+ if (fs_vfe) {
+ regulator_disable(fs_vfe);
+ regulator_put(fs_vfe);
+ }
+}
+
+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");
+ msm_camio_clk_rate_set_2(clk, camio_clk.vfe_clk_rate);
+ break;
+
+ case CAMIO_VFE_CAMIF_CLK:
+ camio_vfe_camif_clk =
+ clk = clk_get(NULL, "vfe_camif_clk");
+ break;
+
+ case CAMIO_VFE_PBDG_CLK:
+ camio_vfe_pbdg_clk =
+ clk = clk_get(NULL, "vfe_pclk");
+ break;
+
+ case CAMIO_CAM_MCLK_CLK:
+ camio_cam_m_clk =
+ clk = clk_get(NULL, "cam_m_clk");
+ msm_camio_clk_rate_set_2(clk, camio_clk.mclk_clk_rate);
+ break;
+
+ case CAMIO_CAMIF_PAD_PBDG_CLK:
+ camio_camif_pad_pbdg_clk =
+ clk = clk_get(NULL, "camif_pad_pclk");
+ break;
+
+ case CAMIO_CSI0_CLK:
+ camio_csi_clk =
+ clk = clk_get(NULL, "csi_clk");
+ msm_camio_clk_rate_set_2(clk, 153600000);
+ break;
+ case CAMIO_CSI0_VFE_CLK:
+ camio_csi_vfe_clk =
+ clk = clk_get(NULL, "csi_vfe_clk");
+ break;
+ case CAMIO_CSI0_PCLK:
+ camio_csi_pclk =
+ clk = clk_get(NULL, "csi_pclk");
+ break;
+
+ case CAMIO_JPEG_CLK:
+ camio_jpeg_clk =
+ clk = clk_get(NULL, "jpeg_clk");
+ clk_set_min_rate(clk, 144000000);
+ break;
+ case CAMIO_JPEG_PCLK:
+ camio_jpeg_pclk =
+ clk = clk_get(NULL, "jpeg_pclk");
+ break;
+ case CAMIO_VPE_CLK:
+ camio_vpe_clk =
+ clk = clk_get(NULL, "vpe_clk");
+ msm_camio_clk_set_min_rate(clk, vpe_clk_rate);
+ 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_CAMIF_CLK:
+ clk = camio_vfe_camif_clk;
+ break;
+
+ case CAMIO_VFE_PBDG_CLK:
+ clk = camio_vfe_pbdg_clk;
+ break;
+
+ case CAMIO_CAM_MCLK_CLK:
+ clk = camio_cam_m_clk;
+ break;
+
+ case CAMIO_CAMIF_PAD_PBDG_CLK:
+ clk = camio_camif_pad_pbdg_clk;
+ break;
+ case CAMIO_CSI0_CLK:
+ clk = camio_csi_clk;
+ break;
+ case CAMIO_CSI0_VFE_CLK:
+ clk = camio_csi_vfe_clk;
+ break;
+ case CAMIO_CSI0_PCLK:
+ clk = camio_csi_pclk;
+ break;
+ case CAMIO_JPEG_CLK:
+ clk = camio_jpeg_clk;
+ break;
+ case CAMIO_JPEG_PCLK:
+ clk = camio_jpeg_pclk;
+ break;
+ case CAMIO_VPE_CLK:
+ clk = camio_vpe_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_cam_m_clk;
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_vfe_clk_rate_set(int rate)
+{
+ struct clk *clk = camio_vfe_clk;
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_rate_set_2(struct clk *clk, int rate)
+{
+ clk_set_rate(clk, rate);
+}
+
+void msm_camio_clk_set_min_rate(struct clk *clk, int rate)
+{
+ clk_set_min_rate(clk, rate);
+}
+
+static irqreturn_t msm_io_csi_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ irq = msm_io_r(csibase + MIPI_INTERRUPT_STATUS);
+ CDBG("%s MIPI_INTERRUPT_STATUS = 0x%x\n", __func__, irq);
+ msm_io_w(irq, csibase + MIPI_INTERRUPT_STATUS);
+ return IRQ_HANDLED;
+}
+
+int msm_camio_jpeg_clk_disable(void)
+{
+ msm_camio_clk_disable(CAMIO_JPEG_CLK);
+ msm_camio_clk_disable(CAMIO_JPEG_PCLK);
+ /* Need to add the code for remove PM QOS requirement */
+ return 0;
+}
+
+
+int msm_camio_jpeg_clk_enable(void)
+{
+ msm_camio_clk_enable(CAMIO_JPEG_CLK);
+ msm_camio_clk_enable(CAMIO_JPEG_PCLK);
+ return 0;
+}
+
+int msm_camio_vpe_clk_disable(void)
+{
+ msm_camio_clk_disable(CAMIO_VPE_CLK);
+
+ if (fs_vpe) {
+ regulator_disable(fs_vpe);
+ regulator_put(fs_vpe);
+ }
+
+ return 0;
+}
+
+int msm_camio_vpe_clk_enable(uint32_t clk_rate)
+{
+ fs_vpe = regulator_get(NULL, "fs_vpe");
+ if (IS_ERR(fs_vpe)) {
+ pr_err("%s: Regulator FS_VPE get failed %ld\n", __func__,
+ PTR_ERR(fs_vpe));
+ fs_vpe = NULL;
+ } else if (regulator_enable(fs_vpe)) {
+ pr_err("%s: Regulator FS_VPE enable failed\n", __func__);
+ regulator_put(fs_vpe);
+ }
+
+ vpe_clk_rate = clk_rate;
+ msm_camio_clk_enable(CAMIO_VPE_CLK);
+ return 0;
+}
+
+int msm_camio_enable(struct platform_device *pdev)
+{
+ int rc = 0;
+ uint32_t val;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK);
+ if (!sinfo->csi_if)
+ msm_camio_clk_enable(CAMIO_VFE_CAMIF_CLK);
+ else {
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ csiio = request_mem_region(camio_ext.csiphy,
+ camio_ext.csisz, pdev->name);
+ if (!csiio) {
+ rc = -EBUSY;
+ goto common_fail;
+ }
+ csibase = ioremap(camio_ext.csiphy,
+ camio_ext.csisz);
+ if (!csibase) {
+ rc = -ENOMEM;
+ goto csi_busy;
+ }
+ rc = request_irq(camio_ext.csiirq, msm_io_csi_irq,
+ IRQF_TRIGGER_RISING, "csi", 0);
+ if (rc < 0)
+ goto csi_irq_fail;
+ /* enable required clocks for CSI */
+ msm_camio_clk_enable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_enable(CAMIO_CSI0_CLK);
+
+ msleep(10);
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ }
+ return 0;
+csi_irq_fail:
+ iounmap(csibase);
+csi_busy:
+ release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+common_fail:
+ msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ return rc;
+}
+
+void msm_camio_disable(struct platform_device *pdev)
+{
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ uint32_t val;
+ if (!sinfo->csi_if) {
+ msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK);
+ } else {
+ val = (0x0 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+ (0x0<<MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT)|
+ (0x0 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+ (0x0 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+ CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+ val = (20 <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ msleep(10);
+ free_irq(camio_ext.csiirq, 0);
+ msm_camio_clk_disable(CAMIO_CSI0_PCLK);
+ msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CSI0_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ iounmap(csibase);
+ release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+ }
+ msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
+}
+
+void msm_camio_camif_pad_reg_reset(void)
+{
+ uint32_t reg;
+
+ msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL);
+ msleep(10);
+
+ reg = (msm_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+ reg |= 0x3;
+ msm_io_w(reg, camifpadbase);
+ msleep(10);
+
+ reg = (msm_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+ reg |= 0x10;
+ msm_io_w(reg, camifpadbase);
+ msleep(10);
+
+ reg = (msm_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+ /* Need to be uninverted*/
+ reg &= 0x03;
+ msm_io_w(reg, camifpadbase);
+ msleep(10);
+}
+
+void msm_camio_vfe_blk_reset(void)
+{
+ return;
+
+
+}
+
+void msm_camio_camif_pad_reg_reset_2(void)
+{
+ uint32_t reg;
+ uint32_t mask, value;
+ reg = (msm_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 1 << CAM_PAD_REG_SW_RESET_SHFT;
+ msm_io_w((reg & (~mask)) | (value & mask), camifpadbase);
+ mdelay(10);
+ reg = (msm_io_r(camifpadbase)) & CAMIF_CFG_RMSK;
+ mask = CAM_PAD_REG_SW_RESET_BMSK;
+ value = 0 << CAM_PAD_REG_SW_RESET_SHFT;
+ msm_io_w((reg & (~mask)) | (value & mask), camifpadbase);
+ 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;
+ }
+ }
+}
+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;
+ camio_clk = camdev->ioclk;
+ camio_ext = camdev->ioext;
+ camdev->camera_gpio_on();
+ msm_camera_vreg_enable(pdev);
+ return msm_camio_clk_enable(CAMIO_CAM_MCLK_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;
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+}
+
+int msm_camio_sensor_clk_on(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_clk = camdev->ioclk;
+ camio_ext = camdev->ioext;
+ camdev->camera_gpio_on();
+ msm_camera_vreg_enable(pdev);
+ msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_enable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ if (!sinfo->csi_if) {
+ camifpadio = request_mem_region(camio_ext.camifpadphy,
+ camio_ext.camifpadsz, pdev->name);
+ msm_camio_clk_enable(CAMIO_VFE_CLK);
+ if (!camifpadio) {
+ rc = -EBUSY;
+ goto common_fail;
+ }
+ camifpadbase = ioremap(camio_ext.camifpadphy,
+ camio_ext.camifpadsz);
+ if (!camifpadbase) {
+ CDBG("msm_camio_sensor_clk_on fail\n");
+ rc = -ENOMEM;
+ goto parallel_busy;
+ }
+ }
+ return rc;
+parallel_busy:
+ release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+ goto common_fail;
+common_fail:
+ msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ msm_camio_clk_disable(CAMIO_VFE_CLK);
+ msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ msm_camera_vreg_disable();
+ camdev->camera_gpio_off();
+ return rc;
+}
+
+int msm_camio_sensor_clk_off(struct platform_device *pdev)
+{
+ uint32_t rc = 0;
+ struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ camdev->camera_gpio_off();
+ msm_camera_vreg_disable();
+ rc = msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK);
+ rc = msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK);
+ if (!sinfo->csi_if) {
+ iounmap(camifpadbase);
+ release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz);
+ rc = msm_camio_clk_disable(CAMIO_VFE_CLK);
+ }
+ return rc;
+}
+
+int msm_camio_csi_config(struct msm_camera_csi_params *csi_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+
+ CDBG("msm_camio_csi_config \n");
+
+ /* SOT_ECC_EN enable error correction for SYNC (data-lane) */
+ msm_io_w(0x4, csibase + MIPI_PHY_CONTROL);
+
+ /* SW_RST to the CSI core */
+ msm_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
+ csibase + MIPI_PROTOCOL_CONTROL);
+
+ /* PROTOCOL CONTROL */
+ val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
+ MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
+ MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
+ val |= (uint32_t)(csi_params->data_format) <<
+ MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
+ val |= csi_params->dpcm_scheme <<
+ MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
+ CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PROTOCOL_CONTROL);
+
+ /* SW CAL EN */
+ val = (0x1 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
+ (0x1 <<
+ MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
+ (0x1 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
+ CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
+
+ /* settle_cnt is very sensitive to speed!
+ increase this value to run at higher speeds */
+ val = (csi_params->settle_cnt <<
+ MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
+ (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
+ (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+
+ val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
+ (0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+
+ val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL);
+
+ val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
+ (0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
+ CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL);
+
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D2_CONTROL);
+ msm_io_w(0x00000000, csibase + MIPI_PHY_D3_CONTROL);
+
+ /* halcyon only supports 1 or 2 lane */
+ switch (csi_params->lane_cnt) {
+ case 1:
+ msm_io_w(csi_params->lane_assign << 8 | 0x4,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 2:
+ msm_io_w(csi_params->lane_assign << 8 | 0x5,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 3:
+ msm_io_w(csi_params->lane_assign << 8 | 0x6,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ case 4:
+ msm_io_w(csi_params->lane_assign << 8 | 0x7,
+ csibase + MIPI_CAMERA_CNTL);
+ break;
+ }
+
+ /* mask out ID_ERROR[19], DATA_CMM_ERR[11]
+ and CLK_CMM_ERR[10] - de-featured */
+ msm_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_MASK);
+ /*clear IRQ bits*/
+ msm_io_w(0xFFF7F3FF, csibase + MIPI_INTERRUPT_STATUS);
+
+ return rc;
+}
+void msm_camio_set_perf_lvl(enum msm_bus_perf_setting perf_setting)
+{
+ switch (perf_setting) {
+ case S_INIT:
+ add_axi_qos();
+ break;
+ case S_PREVIEW:
+ update_axi_qos(MSM_AXI_QOS_PREVIEW);
+ break;
+ case S_VIDEO:
+ update_axi_qos(MSM_AXI_QOS_RECORDING);
+ break;
+ case S_CAPTURE:
+ update_axi_qos(MSM_AXI_QOS_SNAPSHOT);
+ break;
+ case S_DEFAULT:
+ update_axi_qos(PM_QOS_DEFAULT_VALUE);
+ break;
+ case S_EXIT:
+ release_axi_qos();
+ break;
+ default:
+ CDBG("%s: INVALID CASE\n", __func__);
+ }
+}
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
new file mode 100644
index 0000000..480c626
--- /dev/null
+++ b/drivers/media/video/msm/msm_isp.c
@@ -0,0 +1,814 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 <linux/vmalloc.h>
+#include <linux/android_pmem.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include "msm.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_isp: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+#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)
+
+#define MSM_FRAME_AXI_MAX_BUF 16
+/* This will enqueue ISP events or signal buffer completion */
+static int msm_isp_enqueue(struct msm_cam_media_controller *pmctl,
+ struct msm_vfe_resp *data,
+ enum msm_queue qtype)
+{
+ struct v4l2_event v4l2_evt;
+
+ struct msm_stats_buf stats;
+ struct msm_isp_stats_event_ctrl *isp_event;
+ isp_event = (struct msm_isp_stats_event_ctrl *)v4l2_evt.u.data;
+ if (!data) {
+ pr_err("%s !!!!data = 0x%p\n", __func__, data);
+ return -EINVAL;
+ }
+
+ D("%s data->type = %d\n", __func__, data->type);
+
+ switch (qtype) {
+ case MSM_CAM_Q_VFE_EVT:
+ case MSM_CAM_Q_VFE_MSG:
+ /* adsp event and message */
+ v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+ MSM_CAM_RESP_STAT_EVT_MSG;
+
+ isp_event->resptype = MSM_CAM_RESP_STAT_EVT_MSG;
+
+ /* 0 - msg from aDSP, 1 - event from mARM */
+ isp_event->isp_data.isp_msg.type = data->evt_msg.type;
+ isp_event->isp_data.isp_msg.msg_id = data->evt_msg.msg_id;
+ isp_event->isp_data.isp_msg.len = 0;
+
+ D("%s: qtype %d length %d msd_id %d\n", __func__,
+ qtype,
+ isp_event->isp_data.isp_msg.len,
+ isp_event->isp_data.isp_msg.msg_id);
+
+ if ((data->type >= VFE_MSG_STATS_AEC) &&
+ (data->type <= VFE_MSG_STATS_WE)) {
+
+ D("%s data->phy.sbuf_phy = 0x%x\n", __func__,
+ data->phy.sbuf_phy);
+ stats.buffer = msm_pmem_stats_ptov_lookup(&pmctl->sync,
+ data->phy.sbuf_phy,
+ &(stats.fd));
+ if (!stats.buffer) {
+ pr_err("%s: msm_pmem_stats_ptov_lookup error\n",
+ __func__);
+ isp_event->isp_data.isp_msg.len = 0;
+ } else {
+ struct msm_stats_buf *stats_buf =
+ kmalloc(sizeof(struct msm_stats_buf),
+ GFP_ATOMIC);
+ if (!stats_buf) {
+ pr_err("%s: out of memory.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ *stats_buf = stats;
+ isp_event->isp_data.isp_msg.len =
+ sizeof(struct msm_stats_buf);
+ isp_event->isp_data.isp_msg.data = stats_buf;
+ }
+
+ } else if ((data->evt_msg.len > 0) &&
+ (data->type == VFE_MSG_GENERAL)) {
+ isp_event->isp_data.isp_msg.data =
+ kmalloc(data->evt_msg.len, GFP_ATOMIC);
+ if (!isp_event->isp_data.isp_msg.data) {
+ pr_err("%s: out of memory.\n", __func__);
+ return -ENOMEM;
+ }
+ memcpy(isp_event->isp_data.isp_msg.data,
+ data->evt_msg.data,
+ data->evt_msg.len);
+ } else if (data->type == VFE_MSG_OUTPUT_P ||
+ data->type == VFE_MSG_OUTPUT_V ||
+ data->type == VFE_MSG_OUTPUT_S ||
+ data->type == VFE_MSG_OUTPUT_T) {
+ msm_mctl_buf_done(pmctl, data->type,
+ (u32)data->phy.y_phy);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* now queue the event */
+ v4l2_event_queue(pmctl->config_device->config_stat_event_queue.pvdev,
+ &v4l2_evt);
+ return 0;
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+
+void *msm_isp_sync_alloc(int size,
+ void *syncdata __attribute__((unused)),
+ gfp_t gfp)
+{
+ struct msm_queue_cmd *qcmd =
+ kmalloc(sizeof(struct msm_queue_cmd) + size, gfp);
+
+ if (qcmd) {
+ atomic_set(&qcmd->on_heap, 1);
+ return qcmd + 1;
+ }
+ return NULL;
+}
+
+void msm_isp_sync_free(void *ptr)
+{
+ if (ptr) {
+ struct msm_queue_cmd *qcmd =
+ (struct msm_queue_cmd *)ptr;
+ qcmd--;
+ if (atomic_read(&qcmd->on_heap))
+ kfree(qcmd);
+ }
+}
+
+/*
+ * This function executes in interrupt context.
+ */
+static int msm_isp_notify(struct v4l2_subdev *sd, void *arg)
+{
+ int rc = -EINVAL;
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_sync *sync =
+ (struct msm_sync *)v4l2_get_subdev_hostdata(sd);
+ struct msm_vfe_resp *vdata = (struct msm_vfe_resp *)arg;
+
+ if (!sync) {
+ pr_err("%s: no context in dsp callback.\n", __func__);
+ return rc;
+ }
+
+ qcmd = ((struct msm_queue_cmd *)vdata) - 1;
+ qcmd->type = MSM_CAM_Q_VFE_MSG;
+ qcmd->command = vdata;
+
+ D("%s: vdata->type %d\n", __func__, vdata->type);
+ switch (vdata->type) {
+ case VFE_MSG_STATS_AWB:
+ D("%s: qtype %d, AWB stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_AEC:
+ D("%s: qtype %d, AEC stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_IHIST:
+ D("%s: qtype %d, ihist stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_RS:
+ D("%s: qtype %d, rs stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_STATS_CS:
+ D("%s: qtype %d, cs stats, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+
+ case VFE_MSG_GENERAL:
+ D("%s: qtype %d, general msg, enqueue event_q.\n",
+ __func__, vdata->type);
+ break;
+ default:
+ D("%s: qtype %d not handled\n", __func__, vdata->type);
+ /* fall through, send to config. */
+ }
+
+ D("%s: msm_enqueue event_q\n", __func__);
+ rc = msm_isp_enqueue(&sync->pcam_sync->mctl, vdata, MSM_CAM_Q_VFE_MSG);
+
+ msm_isp_sync_free(vdata);
+
+ return rc;
+}
+
+/* This function is called by open() function, so we need to init HW*/
+static int msm_isp_open(struct v4l2_subdev *sd, struct msm_sync *sync)
+{
+ /* init vfe and senor, register sync callbacks for init*/
+ int rc = 0;
+ D("%s\n", __func__);
+ if (!sync) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_vfe_subdev_init(sd, sync, sync->pdev);
+ if (rc < 0) {
+ pr_err("%s: vfe_init failed at %d\n",
+ __func__, rc);
+ }
+
+ return rc;
+}
+
+static void msm_isp_release(struct msm_sync *psync)
+{
+ D("%s\n", __func__);
+ msm_vfe_subdev_release(psync->pdev);
+}
+
+static int msm_config_vfe(struct v4l2_subdev *sd,
+ struct msm_sync *sync, void __user *arg)
+{
+ struct msm_vfe_cfg_cmd cfgcmd;
+ struct msm_pmem_region region[8];
+ struct axidata axi_data;
+
+ if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) {
+ ERR_COPY_FROM_USER();
+ return -EFAULT;
+ }
+
+ memset(&axi_data, 0, sizeof(axi_data));
+ CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
+ switch (cfgcmd.cmd_type) {
+ case CMD_STATS_AF_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AF, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_STATS_AEC_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AEC, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_STATS_AWB_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_AWB, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_STATS_IHIST_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_IHIST, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_STATS_RS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_RS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_STATS_CS_ENABLE:
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats,
+ MSM_PMEM_CS, ®ion[0],
+ NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ case CMD_GENERAL:
+ case CMD_STATS_DISABLE:
+ return msm_isp_subdev_ioctl(sd, &cfgcmd,
+ &axi_data);
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd.cmd_type);
+ }
+
+ return -EINVAL;
+}
+
+static int msm_vpe_frame_cfg(struct msm_sync *sync,
+ void *cfgcmdin)
+{
+ int rc = -EIO;
+ struct axidata axi_data;
+ void *data = &axi_data;
+ struct msm_pmem_region region[8];
+ int pmem_type;
+
+ struct msm_vpe_cfg_cmd *cfgcmd;
+ cfgcmd = (struct msm_vpe_cfg_cmd *)cfgcmdin;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+ CDBG("In vpe_frame_cfg cfgcmd->cmd_type = %d\n",
+ cfgcmd->cmd_type);
+ switch (cfgcmd->cmd_type) {
+ case CMD_AXI_CFG_VPE:
+ pmem_type = MSM_PMEM_VIDEO_VPE;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup_2(&sync->pmem_frames, pmem_type,
+ ®ion[0], 8);
+ CDBG("axi_data.bufnum1 = %d\n", axi_data.bufnum1);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ pmem_type = MSM_PMEM_VIDEO;
+ break;
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ break;
+ }
+ axi_data.region = ®ion[0];
+ CDBG("out vpe_frame_cfg cfgcmd->cmd_type = %d\n",
+ cfgcmd->cmd_type);
+ /* send the AXI configuration command to driver */
+ if (sync->vpefn.vpe_config)
+ rc = sync->vpefn.vpe_config(cfgcmd, data);
+ return rc;
+}
+
+static int msm_stats_axi_cfg(struct v4l2_subdev *sd,
+ 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",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ if (cfgcmd->cmd_type != CMD_GENERAL) {
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup(&sync->pmem_stats, pmem_type,
+ ®ion[0], NUM_STAT_OUTPUT_BUFFERS);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ axi_data.region = ®ion[0];
+ }
+
+ /* send the AEC/AWB STATS configuration command to driver */
+ rc = msm_isp_subdev_ioctl(sd, cfgcmd, data);
+ return rc;
+}
+
+static int msm_frame_axi_cfg(struct v4l2_subdev *sd,
+ 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[MSM_FRAME_AXI_MAX_BUF];
+ int pmem_type;
+ int i = 0;
+ int idx = 0;
+ struct msm_cam_v4l2_device *pcam = sync->pcam_sync;
+ struct msm_cam_v4l2_dev_inst *pcam_inst;
+
+ memset(&axi_data, 0, sizeof(axi_data));
+
+ switch (cfgcmd->cmd_type) {
+
+ case CMD_AXI_CFG_PREVIEW:
+ pcam_inst =
+ pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ else
+ return rc;
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[0], pmem_type);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region 3 lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ D("%s __func__ axi_data.bufnum2 = %d\n", __func__,
+ axi_data.bufnum2);
+ break;
+
+ case CMD_AXI_CFG_VIDEO:
+ pcam_inst =
+ pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ else
+ return rc;
+ pmem_type = MSM_PMEM_PREVIEW;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[0], pmem_type);
+ D("%s bufnum1 = %d\n", __func__, axi_data.bufnum1);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pcam_inst
+ = pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_VIDEO];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ pmem_type = MSM_PMEM_VIDEO;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[axi_data.bufnum1], pmem_type);
+ D("%s bufnum2 = %d\n", __func__, axi_data.bufnum2);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+
+ case CMD_AXI_CFG_SNAP:
+ pcam_inst
+ = pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ else
+ return rc;
+ pmem_type = MSM_PMEM_THUMBNAIL;
+ axi_data.bufnum1 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[0], pmem_type);
+ if (!axi_data.bufnum1) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pcam_inst
+ = pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_MAIN];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ else
+ return rc;
+ pmem_type = MSM_PMEM_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[axi_data.bufnum1], pmem_type);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_RAW_PICT_AXI_CFG:
+ pcam_inst
+ = pcam->dev_inst_map[MSM_V4L2_EXT_CAPTURE_MODE_MAIN];
+ if (pcam_inst)
+ idx = pcam_inst->my_index;
+ else
+ return rc;
+ pmem_type = MSM_PMEM_RAW_MAINIMG;
+ axi_data.bufnum2 =
+ msm_pmem_region_lookup_3(sync->pcam_sync, idx,
+ ®ion[0], pmem_type);
+ if (!axi_data.bufnum2) {
+ pr_err("%s %d: pmem region lookup error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+
+ case CMD_GENERAL:
+ data = NULL;
+ break;
+
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__, cfgcmd->cmd_type);
+ return -EINVAL;
+ }
+
+ axi_data.region = ®ion[0];
+ D("%s bufnum1 = %d, bufnum2 = %d\n", __func__,
+ axi_data.bufnum1, axi_data.bufnum2);
+ for (i = 0; i < MSM_FRAME_AXI_MAX_BUF; i++) {
+ D("%s region %d paddr = 0x%p\n", __func__, i,
+ (void *)region[i].paddr);
+ D("%s region y_off = %d cbcr_off = %d\n", __func__,
+ region[i].info.y_off, region[i].info.cbcr_off);
+ }
+ /* send the AXI configuration command to driver */
+ rc = msm_isp_subdev_ioctl(sd, cfgcmd, data);
+ return rc;
+}
+
+static int msm_axi_config(struct v4l2_subdev *sd,
+ 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_VIDEO:
+ case CMD_AXI_CFG_PREVIEW:
+ case CMD_AXI_CFG_SNAP:
+ case CMD_RAW_PICT_AXI_CFG:
+ return msm_frame_axi_cfg(sd, sync, &cfgcmd);
+ case CMD_AXI_CFG_VPE:
+ return 0;
+ return msm_vpe_frame_cfg(sync, (void *)&cfgcmd);
+
+ case CMD_STATS_AXI_CFG:
+ case CMD_STATS_AF_AXI_CFG:
+ return msm_stats_axi_cfg(sd, sync, &cfgcmd);
+
+ default:
+ pr_err("%s: unknown command type %d\n",
+ __func__,
+ cfgcmd.cmd_type);
+ return -EINVAL;
+ }
+
+ 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_put_stats_buffer(struct v4l2_subdev *sd,
+ 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("%s\n", __func__);
+ pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd);
+
+ if (pphy != 0) {
+ if (buf.type == STAT_AF)
+ cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE;
+ else if (buf.type == STAT_AEC)
+ cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE;
+ else if (buf.type == STAT_AWB)
+ cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE;
+ else if (buf.type == STAT_IHIST)
+ cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE;
+ else if (buf.type == STAT_RS)
+ cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE;
+ else if (buf.type == STAT_CS)
+ cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
+
+ else {
+ pr_err("%s: invalid buf type %d\n",
+ __func__,
+ buf.type);
+ rc = -EINVAL;
+ goto put_done;
+ }
+
+ cfgcmd.value = (void *)&buf;
+
+ rc = msm_isp_subdev_ioctl(sd, &cfgcmd, &pphy);
+ } else {
+ pr_err("%s: NULL physical address\n", __func__);
+ rc = -EINVAL;
+ }
+
+put_done:
+ return rc;
+}
+
+/* config function simliar to origanl msm_ioctl_config*/
+static int msm_isp_config(struct msm_cam_media_controller *pmctl,
+ unsigned int cmd, unsigned long arg)
+{
+
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ struct v4l2_subdev *sd = &pmctl->isp_sdev->sd;
+
+ D("%s: cmd %d\n", __func__, _IOC_NR(cmd));
+ switch (cmd) {
+ case MSM_CAM_IOCTL_PICT_PP_DONE:
+ /* Release the preview of snapshot frame
+ * that was grabbed.
+ */
+ /*rc = msm_pp_release(pmsm->sync, arg);*/
+ break;
+
+ case MSM_CAM_IOCTL_CONFIG_VFE:
+ /* Coming from config thread for update */
+ rc = msm_config_vfe(sd, &pmctl->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_CONFIG_VPE:
+ /* Coming from config thread for update */
+ /*rc = msm_config_vpe(pmsm->sync, argp);*/
+ rc = 0;
+ break;
+
+ case MSM_CAM_IOCTL_AXI_CONFIG:
+ case MSM_CAM_IOCTL_AXI_VPE_CONFIG:
+ D("Received MSM_CAM_IOCTL_AXI_CONFIG\n");
+ rc = msm_axi_config(sd, &pmctl->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_SET_CROP:
+ rc = msm_set_crop(&pmctl->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER:
+ rc = msm_put_stats_buffer(sd, &pmctl->sync, argp);
+ break;
+
+ default:
+ break;
+ }
+
+ D("%s: cmd %d DONE\n", __func__, _IOC_NR(cmd));
+
+ return rc;
+}
+
+static struct msm_isp_ops isp_subdev[MSM_MAX_CAMERA_CONFIGS];
+
+/**/
+int msm_isp_init_module(int g_num_config_nodes)
+{
+ int i = 0;
+
+ for (i = 0; i < g_num_config_nodes; i++) {
+ isp_subdev[i].isp_open = msm_isp_open;
+ isp_subdev[i].isp_config = msm_isp_config;
+ isp_subdev[i].isp_release = msm_isp_release;
+ isp_subdev[i].isp_enqueue = msm_isp_enqueue;
+ isp_subdev[i].isp_notify = msm_isp_notify;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(msm_isp_init_module);
+
+/*
+*/
+int msm_isp_register(struct msm_cam_server_dev *psvr)
+{
+ int i = 0;
+
+ D("%s\n", __func__);
+
+ BUG_ON(!psvr);
+
+ /* Initialize notify function for v4l2_dev */
+ for (i = 0; i < psvr->config_info.num_config_nodes; i++)
+ psvr->isp_subdev[i] = &(isp_subdev[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_isp_register);
+
+/**/
+void msm_isp_unregister(struct msm_cam_server_dev *psvr)
+{
+ int i = 0;
+ for (i = 0; i < psvr->config_info.num_config_nodes; i++)
+ psvr->isp_subdev[i] = NULL;
+}
+
+int msm_isp_subdev_ioctl(struct v4l2_subdev *isp_subdev,
+ struct msm_vfe_cfg_cmd *cfgcmd, void *data)
+{
+ struct msm_camvfe_params vfe_params;
+ vfe_params.vfe_cfg = cfgcmd;
+ vfe_params.data = data;
+ return v4l2_subdev_call(isp_subdev, core, ioctl, 0, &vfe_params);
+}
diff --git a/drivers/media/video/msm/msm_ispif.c b/drivers/media/video/msm/msm_ispif.c
new file mode 100644
index 0000000..4611d06
--- /dev/null
+++ b/drivers/media/video/msm/msm_ispif.c
@@ -0,0 +1,397 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include "msm_ispif.h"
+#include "msm.h"
+
+#define DBG_ISPIF 0
+/* ISPIF registers */
+
+#define ISPIF_RST_CMD_ADDR 0X00
+#define ISPIF_INTF_CMD_ADDR 0X04
+#define ISPIF_CTRL_ADDR 0X08
+#define ISPIF_INPUT_SEL_ADDR 0X0C
+#define ISPIF_PIX_INTF_CID_MASK_ADDR 0X10
+#define ISPIF_RDI_INTF_CID_MASK_ADDR 0X14
+#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0X38
+#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0X3C
+#define ISPIF_PIX_STATUS_ADDR 0X24
+#define ISPIF_RDI_STATUS_ADDR 0X28
+#define ISPIF_RDI_1_STATUS_ADDR 0X64
+#define ISPIF_IRQ_MASK_ADDR 0X0100
+#define ISPIF_IRQ_CLEAR_ADDR 0X0104
+#define ISPIF_IRQ_STATUS_ADDR 0X0108
+#define ISPIF_IRQ_MASK_1_ADDR 0X010C
+#define ISPIF_IRQ_CLEAR_1_ADDR 0X0110
+#define ISPIF_IRQ_STATUS_1_ADDR 0X0114
+
+/*ISPIF RESET BITS*/
+
+#define VFE_CLK_DOMAIN_RST 31
+#define RDI_CLK_DOMAIN_RST 30
+#define PIX_CLK_DOMAIN_RST 29
+#define AHB_CLK_DOMAIN_RST 28
+#define RDI_1_CLK_DOMAIN_RST 27
+#define RDI_1_VFE_RST_STB 13
+#define RDI_1_CSID_RST_STB 12
+#define RDI_VFE_RST_STB 7
+#define RDI_CSID_RST_STB 6
+#define PIX_VFE_RST_STB 4
+#define PIX_CSID_RST_STB 3
+#define SW_REG_RST_STB 2
+#define MISC_LOGIC_RST_STB 1
+#define STROBED_RST_EN 0
+
+#define PIX_INTF_0_OVERFLOW_IRQ 12
+#define RAW_INTF_0_OVERFLOW_IRQ 25
+#define RAW_INTF_1_OVERFLOW_IRQ 25
+#define RESET_DONE_IRQ 27
+
+#define MAX_CID 15
+DEFINE_MUTEX(msm_ispif_mut);
+
+static struct resource *ispif_mem;
+static struct resource *ispif_irq;
+static struct resource *ispifio;
+void __iomem *ispifbase;
+static uint32_t global_intf_cmd_mask = 0xFFFFFFFF;
+#if DBG_ISPIF
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out)
+{
+ uint32_t *temp;
+ memset(out, 0, sizeof(struct ispif_irq_status));
+ temp = (uint32_t *)(ispifbase + ISPIF_IRQ_STATUS_ADDR);
+ out->ispifIrqStatus0 = msm_io_r(temp);
+ pr_err("ispif_irq: Irq_status0 = 0x%x\n",
+ out->ispifIrqStatus0);
+ msm_io_w(out->ispifIrqStatus0, ispifbase + ISPIF_IRQ_CLEAR_ADDR);
+}
+
+static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
+{
+ struct ispif_irq_status irq;
+ msm_ispif_read_irq_status(&irq);
+ return IRQ_HANDLED;
+}
+#endif
+int msm_ispif_init(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_ispif_fns ispif_fns;
+
+ ispif_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ispif");
+ if (!ispif_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+ ispif_irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "ispif");
+ if (!ispif_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ ispifio = request_mem_region(ispif_mem->start,
+ resource_size(ispif_mem), pdev->name);
+ if (!ispifio)
+ return -EBUSY;
+ ispifbase = ioremap(ispif_mem->start,
+ resource_size(ispif_mem));
+ if (!ispifbase) {
+ rc = -ENOMEM;
+ goto ispif_no_mem;
+ }
+#if DBG_ISPIF
+ rc = request_irq(ispif_irq->start, msm_io_ispif_irq,
+ IRQF_TRIGGER_RISING, "ispif", 0);
+ if (rc < 0)
+ goto ispif_irq_fail;
+#endif
+ global_intf_cmd_mask = 0xFFFFFFFF;
+ ispif_fns.ispif_config = msm_ispif_config;
+ ispif_fns.ispif_start_intf_transfer =
+ msm_ispif_start_intf_transfer;
+ rc = msm_ispif_register(&ispif_fns);
+ if (rc < 0)
+ goto ispif_irq_fail;
+
+ msm_ispif_reset();
+ return 0;
+
+ispif_irq_fail:
+ iounmap(ispifbase);
+ispif_no_mem:
+ release_mem_region(ispif_mem->start, resource_size(ispif_mem));
+ return rc;
+}
+
+void msm_ispif_release(struct platform_device *pdev)
+{
+ CDBG("%s, free_irq\n", __func__);
+#if DBG_ISPIF
+ free_irq(ispif_irq->start, 0);
+#endif
+ iounmap(ispifbase);
+ release_mem_region(ispif_mem->start, resource_size(ispif_mem));
+}
+
+void msm_ispif_intf_reset(uint8_t intftype)
+{
+ uint32_t data = 0x01 , data1 = 0x01;
+
+ msm_io_w(0x1<<STROBED_RST_EN, ispifbase + ISPIF_RST_CMD_ADDR);
+ switch (intftype) {
+ case PIX0:
+ data |= 0x1 << PIX_VFE_RST_STB;
+ msm_io_w(data, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ data1 |= 0x1 << PIX_CSID_RST_STB;
+ msm_io_w(data1, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ break;
+
+ case RDI0:
+ data |= 0x1 << RDI_VFE_RST_STB;
+ msm_io_w(data, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ data1 |= 0x1 << RDI_CSID_RST_STB;
+ msm_io_w(data1, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ break;
+
+ case RDI1:
+ data |= 0x1 << RDI_1_VFE_RST_STB;
+ msm_io_w(data, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ data1 |= 0x1 << RDI_1_CSID_RST_STB;
+ msm_io_w(data1, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void msm_ispif_swreg_misc_reset(void)
+{
+ uint32_t data = 0x01, data1 = 0x01;
+
+ data |= 0x1 << SW_REG_RST_STB;
+ msm_io_w(data, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+ data1 |= 0x1 << MISC_LOGIC_RST_STB;
+ msm_io_w(data1, ispifbase + ISPIF_RST_CMD_ADDR);
+ usleep_range(11000, 12000);
+}
+
+void msm_ispif_reset(void)
+{
+ msm_ispif_swreg_misc_reset();
+ msm_ispif_intf_reset(PIX0);
+ msm_ispif_intf_reset(RDI0);
+}
+
+void msm_ispif_sel_csid_core(uint8_t intftype, uint8_t csid)
+{
+ uint32_t data;
+ data = msm_io_r(ispifbase + ISPIF_INPUT_SEL_ADDR);
+ data |= csid<<(intftype*4);
+ msm_io_w(data, ispifbase + ISPIF_INPUT_SEL_ADDR);
+}
+
+static void
+msm_ispif_intf_cmd(uint8_t intftype, uint16_t cid_mask, uint8_t intf_cmd_mask)
+{
+ uint8_t vc = 0, val = 0;
+ while (cid_mask != 0) {
+ if ((cid_mask & 0xf) != 0x0) {
+ val = (intf_cmd_mask>>(vc*2)) & 0x3;
+ global_intf_cmd_mask &= ~((0x3 & ~val)
+ <<((vc*2)+(intftype*8)));
+ CDBG("intf cmd 0x%x\n", global_intf_cmd_mask);
+ msm_io_w(global_intf_cmd_mask,
+ ispifbase + ISPIF_INTF_CMD_ADDR);
+ }
+ vc++;
+ cid_mask >>= 4;
+ }
+}
+
+void msm_ispif_enable_intf_cids(uint8_t intftype, uint16_t cid_mask)
+{
+ uint32_t data;
+ mutex_lock(&msm_ispif_mut);
+ switch (intftype) {
+ case PIX0:
+ data = msm_io_r(ispifbase + ISPIF_PIX_INTF_CID_MASK_ADDR);
+ data |= cid_mask;
+ msm_io_w(data, ispifbase + ISPIF_PIX_INTF_CID_MASK_ADDR);
+ break;
+
+ case RDI0:
+ data = msm_io_r(ispifbase + ISPIF_RDI_INTF_CID_MASK_ADDR);
+ data |= cid_mask;
+ msm_io_w(data, ispifbase + ISPIF_RDI_INTF_CID_MASK_ADDR);
+ break;
+
+ case RDI1:
+ data = msm_io_r(ispifbase + ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+ data |= cid_mask;
+ msm_io_w(data, ispifbase + ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+ break;
+ }
+ mutex_unlock(&msm_ispif_mut);
+}
+
+int msm_ispif_abort_intf_transfer(struct msm_ispif_params *ispif_params)
+{
+ int rc = 0;
+ uint8_t intf_cmd_mask = 0xAA;
+
+ CDBG("abort stream request\n");
+ mutex_lock(&msm_ispif_mut);
+ msm_ispif_intf_cmd(ispif_params->intftype, ispif_params->cid_mask,
+ intf_cmd_mask);
+ msm_ispif_intf_reset(ispif_params->intftype);
+ global_intf_cmd_mask |= 0xFF<<(ispif_params->intftype * 8);
+ mutex_unlock(&msm_ispif_mut);
+ return rc;
+}
+
+int msm_ispif_start_intf_transfer(struct msm_ispif_params *ispif_params)
+{
+ uint32_t data;
+ uint8_t intf_cmd_mask = 0x55;
+ int rc = 0;
+
+ CDBG("start stream request\n");
+ mutex_lock(&msm_ispif_mut);
+ switch (ispif_params->intftype) {
+ case PIX0:
+ data = msm_io_r(ispifbase + ISPIF_PIX_STATUS_ADDR);
+ if ((data & 0xf) != 0xf) {
+ CDBG("interface is busy\n");
+ mutex_unlock(&msm_ispif_mut);
+ return -EBUSY;
+ }
+ break;
+
+ case RDI0:
+ data = msm_io_r(ispifbase + ISPIF_RDI_STATUS_ADDR);
+ break;
+
+ case RDI1:
+ data = msm_io_r(ispifbase + ISPIF_RDI_1_STATUS_ADDR);
+ break;
+ }
+ msm_ispif_intf_cmd(ispif_params->intftype,
+ ispif_params->cid_mask, intf_cmd_mask);
+ mutex_unlock(&msm_ispif_mut);
+ return rc;
+}
+
+int msm_ispif_stop_intf_transfer(struct msm_ispif_params *ispif_params)
+{
+ int rc = 0;
+ uint8_t intf_cmd_mask = 0x00;
+ CDBG("stop stream request\n");
+ mutex_lock(&msm_ispif_mut);
+ msm_ispif_intf_cmd(ispif_params->intftype,
+ ispif_params->cid_mask, intf_cmd_mask);
+ switch (ispif_params->intftype) {
+ case PIX0:
+ while ((msm_io_r(ispifbase + ISPIF_PIX_STATUS_ADDR) & 0xf)
+ != 0xf) {
+ CDBG("Wait for Idle\n");
+ }
+ break;
+
+ case RDI0:
+ while ((msm_io_r(ispifbase + ISPIF_RDI_STATUS_ADDR) & 0xf)
+ != 0xf) {
+ CDBG("Wait for Idle\n");
+ }
+ break;
+ default:
+ break;
+ }
+ global_intf_cmd_mask |= 0xFF<<(ispif_params->intftype * 8);
+ mutex_unlock(&msm_ispif_mut);
+ return rc;
+}
+
+int msm_ispif_config(struct msm_ispif_params *ispif_params, uint8_t num_of_intf)
+{
+ uint32_t data, data1;
+ int rc = 0, i = 0;
+ CDBG("Enable interface\n");
+ data = msm_io_r(ispifbase + ISPIF_PIX_STATUS_ADDR);
+ data1 = msm_io_r(ispifbase + ISPIF_RDI_STATUS_ADDR);
+ if (((data & 0xf) != 0xf) || ((data1 & 0xf) != 0xf))
+ return -EBUSY;
+ msm_io_w(0x00000000, ispifbase + ISPIF_IRQ_MASK_ADDR);
+ for (i = 0; i < num_of_intf; i++) {
+ msm_ispif_sel_csid_core(ispif_params[i].intftype,
+ ispif_params[i].csid);
+ msm_ispif_enable_intf_cids(ispif_params[i].intftype,
+ ispif_params[i].cid_mask);
+ }
+ msm_io_w(0x0BFFFFFF, ispifbase + ISPIF_IRQ_MASK_ADDR);
+ msm_io_w(0x0BFFFFFF, ispifbase + ISPIF_IRQ_CLEAR_ADDR);
+ return rc;
+}
+
+void msm_ispif_vfe_get_cid(uint8_t intftype, char *cids, int *num)
+{
+ uint32_t data = 0;
+ int i = 0, j = 0;
+ switch (intftype) {
+ case PIX0:
+ data = msm_io_r(ispifbase +
+ ISPIF_PIX_INTF_CID_MASK_ADDR);
+ break;
+
+ case RDI0:
+ data = msm_io_r(ispifbase +
+ ISPIF_RDI_INTF_CID_MASK_ADDR);
+ break;
+
+ case RDI1:
+ data = msm_io_r(ispifbase +
+ ISPIF_RDI_1_INTF_CID_MASK_ADDR);
+ break;
+
+ default:
+ break;
+ }
+ for (i = 0; i <= MAX_CID; i++) {
+ if ((data & 0x1) == 0x1) {
+ cids[j++] = i;
+ (*num)++;
+ }
+ data >>= 1;
+ }
+}
diff --git a/drivers/media/video/msm/msm_ispif.h b/drivers/media/video/msm/msm_ispif.h
new file mode 100644
index 0000000..e5e0c23
--- /dev/null
+++ b/drivers/media/video/msm/msm_ispif.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef MSM_ISPIF_H
+#define MSM_ISPIF_H
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <mach/camera.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+
+struct ispif_irq_status {
+ uint32_t ispifIrqStatus0;
+ uint32_t ispifIrqStatus1;
+};
+
+int msm_ispif_init(struct platform_device *pdev);
+void msm_ispif_release(struct platform_device *pdev);
+void msm_ispif_intf_reset(uint8_t intftype);
+void msm_ispif_swreg_misc_reset(void);
+void msm_ispif_reset(void);
+void msm_ispif_sel_csid_core(uint8_t intftype, uint8_t csid);
+void msm_ispif_enable_intf_cids(uint8_t intftype, uint16_t cid_mask);
+int msm_ispif_start_intf_transfer(struct msm_ispif_params *ispif_params);
+int msm_ispif_stop_intf_transfer(struct msm_ispif_params *ispif_params);
+int msm_ispif_abort_intf_transfer(struct msm_ispif_params *ispif_params);
+int msm_ispif_config(struct msm_ispif_params *ispif_params, \
+ uint8_t num_of_intf);
+void msm_ispif_vfe_get_cid(uint8_t intftype, char *cids, int *num);
+
+#endif
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
new file mode 100644
index 0000000..35cb68a
--- /dev/null
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -0,0 +1,838 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+#include "msm_ispif.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_mctl: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+#define MSM_V4L2_SWFI_LATENCY 3
+
+/* VFE required buffer number for streaming */
+static struct msm_isp_color_fmt msm_isp_formats[] = {
+ {
+ .name = "NV12YUV",
+ .depth = 12,
+ .bitsperpxl = 8,
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .pxlcode = V4L2_MBUS_FMT_YUYV8_2X8, /* YUV sensor */
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV21YUV",
+ .depth = 12,
+ .bitsperpxl = 8,
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .pxlcode = V4L2_MBUS_FMT_YUYV8_2X8, /* YUV sensor */
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV12BAYER",
+ .depth = 8,
+ .bitsperpxl = 8,
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .pxlcode = V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV21BAYER",
+ .depth = 8,
+ .bitsperpxl = 8,
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .pxlcode = V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "RAWBAYER",
+ .depth = 10,
+ .bitsperpxl = 10,
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .pxlcode = V4L2_MBUS_FMT_SBGGR10_1X10, /* Bayer sensor */
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+
+};
+/* master controller instance counter
+static atomic_t mctl_instance = ATOMIC_INIT(0);
+*/
+
+static int buffer_size(int width, int height, int pixelformat)
+{
+ int size;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12:
+ size = width * height * 3/2;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ size = width * height;
+ break;
+ default:
+ pr_err("%s: pixelformat %d not supported.\n",
+ __func__, pixelformat);
+ size = -EINVAL;
+ }
+
+ return size;
+}
+/*
+ * Videobuf operations
+ */
+
+static void free_buffer(struct videobuf_queue *vq,
+ struct msm_frame_buffer *buf)
+{
+ struct videobuf_buffer *vb = &buf->vidbuf;
+
+ BUG_ON(in_interrupt());
+
+ D("%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ /* This waits until this buffer is out of danger, i.e.,
+ * until it is no longer in STATE_QUEUED or STATE_ACTIVE */
+ videobuf_waiton(vq, vb, 0, 0);
+ videobuf_pmem_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* Setup # of buffers and size of each buffer for the videobuf_queue.
+ This is called when videobuf_reqbufs() is called, so this function
+ should tell how many buffer should be used and how big the size is.
+
+ The caller will allocate the real buffers, either in user space or
+ in kernel */
+static int msm_vidbuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ /* get the video device */
+ struct msm_cam_v4l2_dev_inst *pcam_inst = vq->priv_data;
+ struct msm_cam_v4l2_device *pcam = NULL;
+
+ pcam = pcam_inst->pcam;
+
+ D("%s\n", __func__);
+ if (!pcam || !count || !size) {
+ pr_err("%s error : invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ D("%s, inst=0x%x,idx=%d, width = %d\n", __func__,
+ (u32)pcam_inst, pcam_inst->my_index,
+ pcam_inst->vid_fmt.fmt.pix.width);
+ D("%s, inst=0x%x,idx=%d, height = %d\n", __func__,
+ (u32)pcam_inst, pcam_inst->my_index,
+ pcam_inst->vid_fmt.fmt.pix.height);
+ *size = buffer_size(pcam_inst->vid_fmt.fmt.pix.width,
+ pcam_inst->vid_fmt.fmt.pix.height,
+ pcam_inst->vid_fmt.fmt.pix.pixelformat);
+ D("%s:inst=0x%x,idx=%d,count=%d, size=%d\n", __func__,
+ (u32)pcam_inst, pcam_inst->my_index, *count, *size);
+ return 0;
+}
+
+/* Prepare the buffer before it is put into the videobuf_queue for streaming.
+ This is called when videobuf_qbuf() is called, so this function should
+ setup the video buffer to receieve the VFE output. */
+static int msm_vidbuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ int rc = 0;
+ /*struct msm_cam_v4l2_device *pcam = vq->priv_data;*/
+ struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+ struct msm_cam_v4l2_device *pcam = NULL;
+ struct msm_frame_buffer *buf = NULL;
+
+ D("%s\n", __func__);
+ if (!vb || !vq) {
+ pr_err("%s error : input is NULL\n", __func__);
+ return -EINVAL;
+ }
+ pcam_inst = vq->priv_data;
+ pcam = pcam_inst->pcam;
+ buf = container_of(vb, struct msm_frame_buffer, vidbuf);
+
+ if (!pcam || !buf) {
+ pr_err("%s error : pointer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ D("%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ /* by this time vid_fmt should be already set */
+ /* return error if it is not */
+ if ((pcam_inst->vid_fmt.fmt.pix.width == 0) ||
+ (pcam_inst->vid_fmt.fmt.pix.height == 0)) {
+ pr_err("%s error : pcam vid_fmt is not set\n", __func__);
+ return -EINVAL;
+ }
+
+ buf->inuse = 1;
+
+ D("buf->pxlcode=%d, pcam->sensor_pxlcode=%d, vb->width=%d,"
+ "pcam->vid_fmt.fmt.pix.width = %d, vb->height = %d,"
+ "pcam->vid_fmt.fmt.pix.height=%d, vb->field=%d, field=%d\n",
+ buf->pxlcode, pcam_inst->sensor_pxlcode, vb->width,
+ pcam_inst->vid_fmt.fmt.pix.width, vb->height,
+ pcam_inst->vid_fmt.fmt.pix.height, vb->field, field);
+
+ if (buf->pxlcode != pcam_inst->sensor_pxlcode ||
+ vb->width != pcam_inst->vid_fmt.fmt.pix.width ||
+ vb->height != pcam_inst->vid_fmt.fmt.pix.height ||
+ vb->field != field) {
+ buf->pxlcode = pcam_inst->sensor_pxlcode;
+ vb->width = pcam_inst->vid_fmt.fmt.pix.width;
+ vb->height = pcam_inst->vid_fmt.fmt.pix.height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ D("VIDEOBUF_NEEDS_INIT\n");
+ }
+
+ vb->size = buffer_size(pcam_inst->vid_fmt.fmt.pix.width, vb->height,
+ pcam_inst->vid_fmt.fmt.pix.pixelformat);
+
+ D("vb->size=%lu, vb->bsize=%u, vb->baddr=0x%x\n",
+ vb->size, vb->bsize, (uint32_t)vb->baddr);
+
+ if (0 != vb->baddr && vb->bsize < vb->size) {
+ pr_err("Something wrong vb->size=%lu, vb->bsize=%u,\
+ vb->baddr=0x%x\n",
+ vb->size, vb->bsize,
+ (uint32_t)vb->baddr);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, vb, NULL);
+ if (rc)
+ goto fail;
+ D("%s: setting buffer state to prepared\n", __func__);
+ vb->state = VIDEOBUF_PREPARED;
+ }
+
+ buf->inuse = 0;
+
+ /* finally if everything is oK, set the VIDEOBUF_PREPARED state*/
+ if (0 == rc)
+ vb->state = VIDEOBUF_PREPARED;
+ return rc;
+
+fail:
+ free_buffer(vq, buf);
+
+out:
+ buf->inuse = 0;
+ return rc;
+}
+
+/* Called under spin_lock_irqsave(q->irqlock, flags) in videobuf-core.c*/
+static void msm_vidbuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ /*struct msm_cam_v4l2_device *pcam = vq->priv_data;*/
+ struct msm_cam_v4l2_dev_inst *pcam_inst = NULL;
+ struct msm_cam_v4l2_device *pcam = NULL;
+ unsigned long phyaddr = 0;
+ int rc;
+
+ D("%s\n", __func__);
+ if (!vb || !vq) {
+ pr_err("%s error : input is NULL\n", __func__);
+ return ;
+ }
+ pcam_inst = vq->priv_data;
+ pcam = pcam_inst->pcam;
+ if (!pcam) {
+ pr_err("%s error : pcam is NULL\n", __func__);
+ return;
+ }
+ D("%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize);
+
+
+ vb->state = VIDEOBUF_QUEUED;
+ if (vq->streaming) {
+ struct msm_frame frame;
+ struct msm_vfe_cfg_cmd cfgcmd;
+ /* we are returning a buffer to the queue */
+ struct videobuf_contig_pmem *mem = vb->priv;
+ /* get the physcial address of the buffer */
+ phyaddr = (unsigned long) videobuf_to_pmem_contig(vb);
+
+ D("%s buffer type is %d\n", __func__, mem->buffer_type);
+ frame.path = pcam_inst->path;
+ frame.buffer = 0;
+ frame.y_off = mem->y_off;
+ frame.cbcr_off = mem->cbcr_off;
+
+ /* now release frame to vfe */
+ cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE;
+ cfgcmd.value = (void *)&frame;
+ /* yyan: later change to mctl APIs*/
+ rc = msm_isp_subdev_ioctl(&pcam->mctl.isp_sdev->sd,
+ &cfgcmd, &phyaddr);
+ }
+}
+
+/* This will be called when streamingoff is called. */
+static void msm_vidbuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct msm_cam_v4l2_dev_inst *pcam_inst = vq->priv_data;
+ struct msm_cam_v4l2_device *pcam = pcam_inst->pcam;
+ struct msm_frame_buffer *buf = container_of(vb, struct msm_frame_buffer,
+ vidbuf);
+
+ D("%s\n", __func__);
+ if (!pcam || !vb || !vq) {
+ pr_err("%s error : input is NULL\n", __func__);
+ return ;
+ }
+#ifdef DEBUG
+ D("%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ switch (vb->state) {
+ case VIDEOBUF_ACTIVE:
+ D("%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ D("%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ D("%s (prepared)\n", __func__);
+ break;
+ default:
+ D("%s (unknown) state = %d\n", __func__, vb->state);
+ break;
+ }
+#endif
+
+ /* free the buffer */
+ free_buffer(vq, buf);
+}
+
+
+static struct videobuf_queue_ops msm_vidbuf_ops = {
+ .buf_setup = msm_vidbuf_setup,
+ .buf_prepare = msm_vidbuf_prepare,
+ .buf_queue = msm_vidbuf_queue,
+ .buf_release = msm_vidbuf_release,
+};
+
+
+
+/* prepare a video buffer queue for a vl42 device*/
+static int msm_vidbuf_init(struct msm_cam_v4l2_dev_inst *pcam_inst,
+ struct videobuf_queue *q)
+{
+ int rc = 0;
+ struct resource *res;
+ struct platform_device *pdev = NULL;
+ struct msm_cam_v4l2_device *pcam = pcam_inst->pcam;
+ D("%s\n", __func__);
+ if (!pcam || !q) {
+ pr_err("%s error : input is NULL\n", __func__);
+ return -EINVAL;
+ } else
+ pdev = pcam->mctl.sync.pdev;
+
+ if (!pdev) {
+ pr_err("%s error : pdev is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (pcam->use_count == 1) {
+ /* first check if we have resources */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (res) {
+ D("res->start = 0x%x\n", (u32)res->start);
+ D("res->size = 0x%x\n", (u32)resource_size(res));
+ D("res->end = 0x%x\n", (u32)res->end);
+ rc = dma_declare_coherent_memory(&pdev->dev, res->start,
+ res->start,
+ resource_size(res),
+ DMA_MEMORY_MAP |
+ DMA_MEMORY_EXCLUSIVE);
+ if (!rc) {
+ pr_err("%s: Unable to declare coherent memory.\n",
+ __func__);
+ rc = -ENXIO;
+ return rc;
+ }
+
+ /*pcam->memsize = resource_size(res);*/
+ D("%s: found DMA capable resource\n", __func__);
+ } else {
+ pr_err("%s: no DMA capable resource\n", __func__);
+ return -ENOMEM;
+ }
+ }
+ spin_lock_init(&pcam_inst->vb_irqlock);
+
+ videobuf_queue_pmem_contig_init(q, &msm_vidbuf_ops, &pdev->dev,
+ &pcam_inst->vb_irqlock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct msm_frame_buffer), pcam_inst, NULL);
+
+
+ return 0;
+}
+
+/*
+ * V4l2 subdevice operations
+ */
+static int mctl_subdev_log_status(struct v4l2_subdev *sd)
+{
+ return -EINVAL;
+}
+
+static long mctl_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_cam_media_controller *pmctl = NULL;
+ if (!sd) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ } else
+ pmctl = (struct msm_cam_media_controller *)
+ v4l2_get_subdevdata(sd);
+
+
+ return -EINVAL;
+}
+
+
+static int mctl_subdev_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static struct v4l2_subdev_core_ops mctl_subdev_core_ops = {
+ .log_status = mctl_subdev_log_status,
+ .ioctl = mctl_subdev_ioctl,
+};
+
+static struct v4l2_subdev_video_ops mctl_subdev_video_ops = {
+ .g_mbus_fmt = mctl_subdev_g_mbus_fmt,
+};
+
+static struct v4l2_subdev_ops mctl_subdev_ops = {
+ .core = &mctl_subdev_core_ops,
+ .video = &mctl_subdev_video_ops,
+};
+
+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;
+ D("%s: sensor_name %s\n", __func__, sdata->sensor_name);
+
+ memcpy(&info.name[0], sdata->sensor_name, MAX_SENSOR_NAME);
+ info.flash_enabled = sdata->flash_data->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;
+}
+
+/* called by other subdev to notify any changes*/
+
+static int msm_mctl_notify(struct msm_cam_media_controller *p_mctl,
+ unsigned int notification, void *arg)
+{
+ int rc = -EINVAL;
+ struct msm_ispif_params ispif_params;
+ struct msm_camera_sensor_info *sinfo =
+ p_mctl->plat_dev->dev.platform_data;
+ struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+ uint8_t csid_core = camdev->csid_core;
+ switch (notification) {
+ case NOTIFY_CID_CHANGE:
+ /* reconfig the ISPIF*/
+ if (p_mctl->ispif_fns->ispif_config) {
+ ispif_params.intftype = PIX0;
+ ispif_params.cid_mask = 0x0001;
+ ispif_params.csid = csid_core;
+
+ rc = p_mctl->ispif_fns->ispif_config(&ispif_params, 1);
+ if (rc < 0)
+ return rc;
+ rc = p_mctl->ispif_fns->ispif_start_intf_transfer
+ (&ispif_params);
+ if (rc < 0)
+ return rc;
+ msleep(20);
+ }
+ break;
+ case NOTIFY_VFE_MSG_EVT:
+ if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_notify) {
+ rc = p_mctl->isp_sdev->isp_notify(
+ &p_mctl->isp_sdev->sd, arg);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/* called by the server or the config nodes to handle user space
+ commands*/
+static int msm_mctl_cmd(struct msm_cam_media_controller *p_mctl,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ if (!p_mctl) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ }
+ D("%s start cmd = %d\n", __func__, _IOC_NR(cmd));
+
+ /* ... call sensor, ISPIF or VEF subdev*/
+ switch (cmd) {
+ /* sensor config*/
+ case MSM_CAM_IOCTL_GET_SENSOR_INFO:
+ rc = msm_get_sensor_info(&p_mctl->sync, argp);
+ break;
+
+ case MSM_CAM_IOCTL_SENSOR_IO_CFG:
+ rc = p_mctl->sync.sctrl.s_config(argp);
+ break;
+
+ case MSM_CAM_IOCTL_FLASH_CTRL: {
+ struct flash_ctrl_data flash_info;
+ if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else {
+ rc = msm_flash_ctrl(p_mctl->sync.sdata, &flash_info);
+ }
+ break;
+ }
+
+ /* ISFIF config*/
+
+ default:
+ /* ISP config*/
+ rc = p_mctl->isp_sdev->isp_config(p_mctl, cmd, arg);
+ break;
+ }
+ D("%s: !!! cmd = %d, rc = %d\n", __func__, _IOC_NR(cmd), rc);
+ return rc;
+}
+
+static int msm_sync_init(struct msm_sync *sync,
+ struct platform_device *pdev, struct msm_sensor_ctrl *sctrl)
+{
+ int rc = 0;
+ if (!sync) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ }
+
+ sync->sdata = pdev->dev.platform_data;
+
+ wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera");
+
+ sync->pdev = pdev;
+ sync->sctrl = *sctrl;
+ sync->opencnt = 0;
+ mutex_init(&sync->lock);
+ D("%s: initialized %s\n", __func__, sync->sdata->sensor_name);
+ return rc;
+}
+
+static int msm_mctl_open(struct msm_cam_media_controller *p_mctl,
+ const char *const apps_id)
+{
+ int rc = 0;
+ struct msm_sync *sync = NULL;
+ D("%s\n", __func__);
+ if (!p_mctl) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ }
+
+ /* msm_sync_init() muct be called before*/
+ sync = &(p_mctl->sync);
+
+ mutex_lock(&sync->lock);
+ /* open sub devices - once only*/
+ if (!sync->opencnt) {
+ wake_lock(&sync->wake_lock);
+
+ /* turn on clock */
+ rc = msm_camio_sensor_clk_on(sync->pdev);
+ if (rc < 0) {
+ pr_err("%s: msm_camio_sensor_clk_on failed:%d\n",
+ __func__, rc);
+ goto msm_open_done;
+ }
+
+ /* ISP first*/
+ if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_open)
+ rc = p_mctl->isp_sdev->isp_open(
+ &p_mctl->isp_sdev->sd, sync);
+
+ if (rc < 0) {
+ pr_err("%s: isp init failed: %d\n", __func__, rc);
+ goto msm_open_done;
+ }
+
+ /* then sensor - move sub dev later*/
+ if (sync->sctrl.s_init)
+ rc = sync->sctrl.s_init(sync->sdata);
+
+ if (rc < 0) {
+ pr_err("%s: isp init failed: %d\n", __func__, rc);
+ goto msm_open_done;
+ }
+
+ pm_qos_add_request(&p_mctl->pm_qos_req_list,
+ PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ pm_qos_update_request(&p_mctl->pm_qos_req_list,
+ MSM_V4L2_SWFI_LATENCY);
+
+ sync->apps_id = apps_id;
+ sync->opencnt++;
+ }
+
+msm_open_done:
+ mutex_unlock(&sync->lock);
+ return rc;
+}
+
+static int msm_mctl_release(struct msm_cam_media_controller *p_mctl)
+{
+ struct msm_sync *sync = NULL;
+ int rc = 0;
+
+ sync = &(p_mctl->sync);
+
+ if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
+ p_mctl->isp_sdev->isp_release(&p_mctl->sync);
+
+ if (p_mctl->sync.sctrl.s_release)
+ p_mctl->sync.sctrl.s_release();
+
+ rc = msm_camio_sensor_clk_off(sync->pdev);
+ if (rc < 0)
+ pr_err("%s: msm_camio_sensor_clk_off failed:%d\n",
+ __func__, rc);
+
+ pm_qos_update_request(&p_mctl->pm_qos_req_list,
+ PM_QOS_DEFAULT_VALUE);
+ pm_qos_remove_request(&p_mctl->pm_qos_req_list);
+
+ return rc;
+}
+
+int msm_mctl_init_user_formats(struct msm_cam_v4l2_device *pcam)
+{
+ struct v4l2_subdev *sd = &pcam->sensor_sdev;
+ enum v4l2_mbus_pixelcode pxlcode;
+ int numfmt_sensor = 0;
+ int numfmt = 0;
+ int rc = 0;
+ int i, j;
+
+ D("%s\n", __func__);
+ while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, numfmt_sensor,
+ &pxlcode))
+ numfmt_sensor++;
+
+ D("%s, numfmt_sensor = %d\n", __func__, numfmt_sensor);
+ if (!numfmt_sensor)
+ return -ENXIO;
+
+ pcam->usr_fmts = vmalloc(numfmt_sensor * ARRAY_SIZE(msm_isp_formats) *
+ sizeof(struct msm_isp_color_fmt));
+ if (!pcam->usr_fmts)
+ return -ENOMEM;
+
+ /* from sensor to ISP.. fill the data structure */
+ for (i = 0; i < numfmt_sensor; i++) {
+ rc = v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &pxlcode);
+ D("rc is %d\n", rc);
+ if (rc < 0) {
+ vfree(pcam->usr_fmts);
+ return rc;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(msm_isp_formats); j++) {
+ /* find the corresponding format */
+ if (pxlcode == msm_isp_formats[j].pxlcode) {
+ pcam->usr_fmts[numfmt] = msm_isp_formats[j];
+ D("pcam->usr_fmts=0x%x\n", (u32)pcam->usr_fmts);
+ D("format pxlcode 0x%x (0x%x) found\n",
+ pcam->usr_fmts[numfmt].pxlcode,
+ pcam->usr_fmts[numfmt].fourcc);
+ numfmt++;
+ }
+ }
+ }
+
+ pcam->num_fmts = numfmt;
+
+ if (numfmt == 0) {
+ pr_err("%s: No supported formats.\n", __func__);
+ vfree(pcam->usr_fmts);
+ return -EINVAL;
+ }
+
+ D("Found %d supported formats.\n", pcam->num_fmts);
+ /* set the default pxlcode, in any case, it will be set through
+ * setfmt */
+ return 0;
+}
+
+/* this function plug in the implementation of a v4l2_subdev */
+int msm_mctl_init_module(struct msm_cam_v4l2_device *pcam)
+{
+
+ struct msm_cam_media_controller *pmctl = NULL;
+ D("%s\n", __func__);
+ if (!pcam) {
+ pr_err("%s: param is NULL", __func__);
+ return -EINVAL;
+ } else
+ pmctl = &pcam->mctl;
+
+ /* init module sync object*/
+ msm_sync_init(&pmctl->sync, pcam->pdev, &pcam->sctrl);
+
+ /* init module operations*/
+ pmctl->mctl_open = msm_mctl_open;
+ pmctl->mctl_cmd = msm_mctl_cmd;
+ pmctl->mctl_notify = msm_mctl_notify;
+ pmctl->mctl_vidbuf_init = msm_vidbuf_init;
+ pmctl->mctl_release = msm_mctl_release;
+
+ pmctl->plat_dev = pcam->pdev;
+ /* init sub device*/
+ v4l2_subdev_init(&(pmctl->mctl_sdev), &mctl_subdev_ops);
+ v4l2_set_subdevdata(&(pmctl->mctl_sdev), pmctl);
+
+ return 0;
+}
+static int msm_mctl_out_type_to_inst_index(struct msm_cam_v4l2_device *pcam,
+ int out_type)
+{
+ switch (out_type) {
+ case VFE_MSG_OUTPUT_P:
+ return pcam->dev_inst_map
+ [MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW]->my_index;
+ case VFE_MSG_OUTPUT_V:
+ return pcam->dev_inst_map
+ [MSM_V4L2_EXT_CAPTURE_MODE_VIDEO]->my_index;
+ case VFE_MSG_OUTPUT_S:
+ return pcam->dev_inst_map
+ [MSM_V4L2_EXT_CAPTURE_MODE_MAIN]->my_index;
+ case VFE_MSG_OUTPUT_T:
+ return pcam->dev_inst_map
+ [MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL]->my_index;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+int msm_mctl_buf_done(struct msm_cam_media_controller *pmctl,
+ int msg_type, uint32_t y_phy)
+{
+ struct videobuf_queue *q;
+ struct videobuf_buffer *buf = NULL;
+ uint32_t buf_phyaddr = 0;
+ int i, idx;
+ unsigned long flags = 0;
+
+ idx = msm_mctl_out_type_to_inst_index(pmctl->sync.pcam_sync, msg_type);
+ q = &(pmctl->sync.pcam_sync->dev_inst[idx]->vid_bufq);
+
+ D("q=0x%x\n", (u32)q);
+
+ /* find the videobuf which is done */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ buf = q->bufs[i];
+ buf_phyaddr = videobuf_to_pmem_contig(buf);
+ D("buf_phyaddr=0x%x\n", (u32)buf_phyaddr);
+ D("data->phy.y_phy=0x%x\n",
+ y_phy);
+ D("buf = 0x%x\n", (u32)buf);
+ if (buf_phyaddr == y_phy) {
+ /* signal that buffer is done */
+ /* get the buf lock first */
+ spin_lock_irqsave(q->irqlock, flags);
+ buf->state = VIDEOBUF_DONE;
+ D("queuedequeue video_buffer 0x%x,"
+ "phyaddr = 0x%x\n",
+ (u32)buf, y_phy);
+
+ do_gettimeofday(&buf->ts);
+ buf->field_count++;
+ wake_up(&buf->done);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
new file mode 100644
index 0000000..5232f7c
--- /dev/null
+++ b/drivers/media/video/msm/msm_mem.c
@@ -0,0 +1,403 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 <linux/vmalloc.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+#include <linux/android_pmem.h>
+
+#include "msm.h"
+#include "msm_vfe31.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm_isp: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+
+#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)
+
+
+#define PAD_TO_WORD(a) (((a) + 3) & ~3)
+
+#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; \
+})
+
+static DEFINE_MUTEX(hlist_mut);
+
+#ifdef CONFIG_ANDROID_PMEM
+static int check_pmem_info(struct msm_pmem_info *info, int len)
+{
+ if (info->offset < len &&
+ info->offset + info->len <= len &&
+ info->y_off < len &&
+ info->cbcr_off < len)
+ return 0;
+
+ pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n",
+ __func__,
+ info->offset,
+ info->len,
+ info->y_off,
+ info->cbcr_off,
+ len);
+ return -EINVAL;
+}
+#endif
+
+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)) {
+ CDBG(" 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 -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int msm_pmem_table_add(struct hlist_head *ptype,
+ struct msm_pmem_info *info)
+{
+ struct file *file;
+ unsigned long paddr;
+#ifdef CONFIG_ANDROID_PMEM
+ unsigned long kvstart;
+ int rc;
+#endif
+ unsigned long len;
+ struct msm_pmem_region *region;
+#ifdef CONFIG_ANDROID_PMEM
+ rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file);
+ if (rc < 0) {
+ pr_err("%s: get_pmem_file fd %d error %d\n",
+ __func__,
+ info->fd, rc);
+ return rc;
+ }
+ if (!info->len)
+ info->len = len;
+
+ rc = check_pmem_info(info, len);
+ if (rc < 0)
+ return rc;
+#else
+ paddr = 0;
+ file = NULL;
+#endif
+ paddr += info->offset;
+ len = info->len;
+
+ if (check_overlap(ptype, paddr, len) < 0)
+ return -EINVAL;
+
+ CDBG("%s: type %d, active flag %d, paddr 0x%lx, vaddr 0x%lx\n",
+ __func__, info->type, info->active, paddr,
+ (unsigned long)info->vaddr);
+
+ region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ INIT_HLIST_NODE(®ion->list);
+
+ region->paddr = paddr;
+ region->len = len;
+ region->file = file;
+ memcpy(®ion->info, info, sizeof(region->info));
+ D("%s Adding region to list with type %d\n", __func__,
+ region->info.type);
+ D("%s pmem_stats address is 0x%p\n", __func__, ptype);
+ hlist_add_head(&(region->list), ptype);
+
+ return 0;
+}
+
+static int __msm_register_pmem(struct hlist_head *ptype,
+ struct msm_pmem_info *pinfo)
+{
+ int rc = 0;
+
+ switch (pinfo->type) {
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ case MSM_PMEM_AEC:
+ case MSM_PMEM_AWB:
+ case MSM_PMEM_RS:
+ case MSM_PMEM_CS:
+ case MSM_PMEM_IHIST:
+ case MSM_PMEM_SKIN:
+ rc = msm_pmem_table_add(ptype, pinfo);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int __msm_pmem_table_del(struct hlist_head *ptype,
+ struct msm_pmem_info *pinfo)
+{
+ int rc = 0;
+ struct msm_pmem_region *region;
+ struct hlist_node *node, *n;
+
+ switch (pinfo->type) {
+ case MSM_PMEM_AEC_AWB:
+ case MSM_PMEM_AF:
+ hlist_for_each_entry_safe(region, node, n,
+ ptype, list) {
+
+ if (pinfo->type == region->info.type &&
+ pinfo->vaddr == region->info.vaddr &&
+ pinfo->fd == region->info.fd) {
+ hlist_del(node);
+#ifdef CONFIG_ANDROID_PMEM
+ put_pmem_file(region->file);
+#else
+
+#endif
+ kfree(region);
+ }
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/* return of 0 means failure */
+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;
+ D("%s\n", __func__);
+ regptr = reg;
+ mutex_lock(&hlist_mut);
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ if (region->info.type == pmem_type && region->info.active) {
+ *regptr = *region;
+ rc += 1;
+ if (rc >= maxcount)
+ break;
+ regptr++;
+ }
+ }
+ D("%s finished, rc=%d\n", __func__, rc);
+ mutex_unlock(&hlist_mut);
+ return rc;
+}
+
+uint8_t msm_pmem_region_lookup_2(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;
+ mutex_lock(&hlist_mut);
+ hlist_for_each_entry_safe(region, node, n, ptype, list) {
+ D("Mio: info.type=%d, pmem_type = %d,"
+ "info.active = %d\n",
+ region->info.type, pmem_type, region->info.active);
+
+ if (region->info.type == pmem_type && region->info.active) {
+ D("info.type=%d, pmem_type = %d,"
+ "info.active = %d,\n",
+ region->info.type, pmem_type,
+ region->info.active);
+ *regptr = *region;
+ region->info.type = MSM_PMEM_VIDEO;
+ rc += 1;
+ if (rc >= maxcount)
+ break;
+ regptr++;
+ }
+ }
+ mutex_unlock(&hlist_mut);
+ return rc;
+}
+
+uint8_t msm_pmem_region_lookup_3(struct msm_cam_v4l2_device *pcam, int idx,
+ struct msm_pmem_region *reg,
+ int mem_type)
+{
+ struct videobuf_contig_pmem *mem;
+ uint8_t rc = 0;
+ struct videobuf_queue *q = &pcam->dev_inst[idx]->vid_bufq;
+ struct videobuf_buffer *buf = NULL;
+
+ videobuf_queue_lock(q);
+ list_for_each_entry(buf, &q->stream, stream) {
+ mem = buf->priv;
+ reg->paddr = mem->phyaddr;
+ D("%s paddr for buf %d is 0x%p\n", __func__,
+ buf->i,
+ (void *)reg->paddr);
+ reg->len = sizeof(struct msm_pmem_info);
+ reg->file = NULL;
+ reg->info.len = mem->size;
+
+ reg->info.vaddr =
+ (void *)(buf->baddr);
+
+ reg->info.type = mem_type;
+
+ reg->info.offset = 0;
+ reg->info.y_off = mem->y_off;
+ reg->info.cbcr_off = PAD_TO_WORD(mem->cbcr_off);
+ D("%s y_off = %d, cbcr_off = %d\n", __func__,
+ reg->info.y_off, reg->info.cbcr_off);
+ rc += 1;
+ reg++;
+ }
+ videobuf_queue_unlock(q);
+
+ return rc;
+}
+
+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->pmem_stats, list) {
+ if (((unsigned long)(region->info.vaddr) == buffer) &&
+ (region->info.fd == fd) &&
+ region->info.active == 0) {
+ region->info.active = 1;
+ return region->paddr;
+ }
+ }
+
+ return 0;
+}
+
+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->pmem_stats, list) {
+ if (addr == region->paddr && region->info.active) {
+ /* offset since we could pass vaddr inside a
+ * registered pmem buffer */
+ *fd = region->info.fd;
+ region->info.active = 0;
+ return (unsigned long)(region->info.vaddr);
+ }
+ }
+
+ return 0;
+}
+
+int msm_register_pmem(struct hlist_head *ptype, 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(ptype, &info);
+}
+EXPORT_SYMBOL(msm_register_pmem);
+
+int msm_pmem_table_del(struct hlist_head *ptype, 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(ptype, &info);
+}
+EXPORT_SYMBOL(msm_pmem_table_del);
diff --git a/drivers/media/video/msm/msm_sensor.c b/drivers/media/video/msm/msm_sensor.c
new file mode 100644
index 0000000..83fe643
--- /dev/null
+++ b/drivers/media/video/msm/msm_sensor.c
@@ -0,0 +1,814 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <media/msm_camera.h>
+#include "msm_sensor.h"
+
+/*=============================================================*/
+
+int32_t msm_sensor_i2c_rxdata(struct msm_sensor_ctrl_t *s_ctrl,
+ unsigned char *rxdata, int length)
+{
+ uint16_t saddr = s_ctrl->msm_sensor_client->addr >> 1;
+ 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(s_ctrl->msm_sensor_client->adapter, msgs, 2) < 0) {
+ CDBG("msm_sensor_i2c_rxdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+int32_t msm_sensor_i2c_txdata(struct msm_sensor_ctrl_t *s_ctrl,
+ unsigned char *txdata, int length)
+{
+ uint16_t saddr = s_ctrl->msm_sensor_client->addr >> 1;
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+ if (i2c_transfer(s_ctrl->msm_sensor_client->adapter, msg, 1) < 0) {
+ CDBG("msm_sensor_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+int32_t msm_sensor_i2c_waddr_write_b(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("%s waddr = 0x%x, wdata = 0x%x\n", __func__, waddr, bdata);
+ rc = msm_sensor_i2c_txdata(s_ctrl, buf, 3);
+ if (rc < 0)
+ CDBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_sensor_i2c_waddr_write_w(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("%s waddr = 0x%x, wdata = 0x%x\n", __func__, waddr, wdata);
+ rc = msm_sensor_i2c_txdata(s_ctrl, buf, 4);
+ if (rc < 0)
+ CDBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_sensor_i2c_waddr_write_b_tbl(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_reg_conf const *reg_conf_tbl, uint8_t size)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < size; i++) {
+ rc = msm_sensor_i2c_waddr_write_b(
+ s_ctrl,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+int32_t msm_sensor_i2c_waddr_write_w_tbl(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_reg_conf const *reg_conf_tbl, uint8_t size)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < size; i++) {
+ rc = msm_sensor_i2c_waddr_write_w(
+ s_ctrl,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+int32_t msm_sensor_i2c_waddr_read_w(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint16_t *data)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!data)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ rc = msm_sensor_i2c_rxdata(s_ctrl, buf, 2);
+ if (rc < 0) {
+ CDBG("%s fail\n", __func__);
+ return rc;
+ }
+ *data = (buf[0] << 8 | buf[1]);
+ CDBG("%s waddr = 0x%x val = 0x%x!\n", __func__,
+ waddr, *data);
+ return rc;
+}
+
+int msm_sensor_write_b_conf_array(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_conf_array *array, uint16_t index)
+{
+ return msm_sensor_i2c_waddr_write_b_tbl(
+ s_ctrl, array[index].conf, array[index].size);
+}
+
+int msm_sensor_write_w_conf_array(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_conf_array *array, uint16_t index)
+{
+ return msm_sensor_i2c_waddr_write_w_tbl(
+ s_ctrl, array[index].conf, array[index].size);
+}
+
+int msm_sensor_write_b_init_settings(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0, i;
+ for (i = 0; i < s_ctrl->msm_sensor_reg.init_size; i++) {
+ rc = msm_sensor_write_b_conf_array(
+ s_ctrl, s_ctrl->msm_sensor_reg.init_settings, i);
+ msleep(s_ctrl->msm_sensor_reg.init_settings[i].delay);
+ if (rc < 0)
+ break;
+ }
+ return rc;
+}
+
+int msm_sensor_write_w_init_settings(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0, i;
+ for (i = 0; i < s_ctrl->msm_sensor_reg.init_size; i++) {
+ rc = msm_sensor_write_w_conf_array(
+ s_ctrl, s_ctrl->msm_sensor_reg.init_settings, i);
+ msleep(s_ctrl->msm_sensor_reg.init_settings[i].delay);
+ if (rc < 0)
+ break;
+ }
+ return rc;
+}
+
+int msm_sensor_write_b_res_settings(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t res)
+{
+ int rc = 0;
+ rc = msm_sensor_write_b_conf_array(
+ s_ctrl, s_ctrl->msm_sensor_reg.res_settings, res);
+ msleep(s_ctrl->msm_sensor_reg.res_settings[res].delay);
+ return rc;
+}
+
+int msm_sensor_write_w_res_settings(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t res)
+{
+ int rc = 0;
+ rc = msm_sensor_write_w_conf_array(
+ s_ctrl, s_ctrl->msm_sensor_reg.res_settings, res);
+ msleep(s_ctrl->msm_sensor_reg.res_settings[res].delay);
+ return rc;
+}
+
+uint16_t msm_sensor_read_b_conf_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ enum msm_sensor_resolution_t res, int8_t array_addr)
+{
+ return
+ s_ctrl->msm_sensor_reg.res_settings[res].
+ conf[array_addr].reg_data << 8 |
+ s_ctrl->msm_sensor_reg.res_settings[res].
+ conf[array_addr+1].reg_data;
+}
+
+uint16_t msm_sensor_read_w_conf_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ enum msm_sensor_resolution_t res, int8_t array_addr)
+{
+ return
+ s_ctrl->msm_sensor_reg.res_settings[res].
+ conf[array_addr].reg_data;
+}
+
+void msm_sensor_start_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ msm_sensor_i2c_waddr_write_b_tbl(s_ctrl,
+ s_ctrl->msm_sensor_reg.start_stream_conf,
+ s_ctrl->msm_sensor_reg.start_stream_conf_size);
+}
+
+void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ msm_sensor_i2c_waddr_write_b_tbl(s_ctrl,
+ s_ctrl->msm_sensor_reg.stop_stream_conf,
+ s_ctrl->msm_sensor_reg.stop_stream_conf_size);
+}
+
+void msm_sensor_group_hold_on(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ msm_sensor_i2c_waddr_write_b_tbl(s_ctrl,
+ s_ctrl->msm_sensor_reg.group_hold_on_conf,
+ s_ctrl->msm_sensor_reg.group_hold_on_conf_size);
+}
+
+void msm_sensor_group_hold_off(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ msm_sensor_i2c_waddr_write_b_tbl(s_ctrl,
+ s_ctrl->msm_sensor_reg.group_hold_off_conf,
+ s_ctrl->msm_sensor_reg.group_hold_off_conf_size);
+}
+
+uint16_t msm_sensor_get_prev_lines_pf(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ return s_ctrl->prev_frame_length_lines;
+}
+
+uint16_t msm_sensor_get_prev_pixels_pl(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ return s_ctrl->prev_line_length_pck;
+}
+
+uint16_t msm_sensor_get_pict_lines_pf(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ return s_ctrl->snap_frame_length_lines;
+}
+
+uint16_t msm_sensor_get_pict_pixels_pl(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ return s_ctrl->snap_line_length_pck;
+}
+
+uint32_t msm_sensor_get_pict_max_exp_lc(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ return s_ctrl->snap_frame_length_lines * 24;
+}
+
+void msm_sensor_get_pict_fps(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t fps, uint16_t *pfps)
+{
+ uint32_t divider, d1, d2;
+ d1 = s_ctrl->prev_frame_length_lines * Q10 /
+ s_ctrl->snap_frame_length_lines;
+ d2 = s_ctrl->prev_line_length_pck * Q10 /
+ s_ctrl->snap_line_length_pck;
+ divider = d1 * d2 / Q10;
+ *pfps = (uint16_t) (fps * divider / Q10);
+}
+
+int32_t msm_sensor_set_fps(struct msm_sensor_ctrl_t *s_ctrl,
+ struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ s_ctrl->fps_divider = fps->fps_div;
+
+ total_lines_per_frame = (uint16_t)
+ ((s_ctrl->prev_frame_length_lines) *
+ s_ctrl->fps_divider/Q10);
+
+ rc = msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->frame_length_lines_addr,
+ total_lines_per_frame);
+ return rc;
+}
+
+int32_t msm_sensor_write_exp_gain1(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines;
+ uint8_t offset;
+ fl_lines = s_ctrl->curr_frame_length_lines;
+ line = (line * s_ctrl->fps_divider) / Q10;
+ offset = s_ctrl->vert_offset;
+ if (line > (fl_lines - offset))
+ fl_lines = line + offset;
+
+ s_ctrl->func_tbl.sensor_group_hold_on(s_ctrl);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->frame_length_lines_addr, fl_lines);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->coarse_int_time_addr, line);
+ msm_sensor_i2c_waddr_write_w(s_ctrl, s_ctrl->global_gain_addr, gain);
+ s_ctrl->func_tbl.sensor_group_hold_off(s_ctrl);
+ return 0;
+}
+
+int32_t msm_sensor_write_exp_gain2(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines, ll_pclk, ll_ratio;
+ uint8_t offset;
+ fl_lines = s_ctrl->curr_frame_length_lines;
+ ll_pclk = s_ctrl->curr_line_length_pck;
+ line = (line * s_ctrl->fps_divider) / Q10;
+ offset = s_ctrl->vert_offset;
+ if (line > (fl_lines - offset)) {
+ ll_ratio = (line * Q10) / (fl_lines - offset);
+ ll_pclk = ll_pclk * ll_ratio;
+ line = fl_lines - offset;
+ }
+
+ s_ctrl->func_tbl.sensor_group_hold_on(s_ctrl);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->line_length_pck_addr, ll_pclk);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->coarse_int_time_addr, line);
+ msm_sensor_i2c_waddr_write_w(s_ctrl, s_ctrl->global_gain_addr, gain);
+ s_ctrl->func_tbl.sensor_group_hold_off(s_ctrl);
+ return 0;
+}
+
+int32_t msm_sensor_set_sensor_mode_b(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, int res)
+{
+ int32_t rc = 0;
+
+ if (s_ctrl->curr_res != res) {
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ s_ctrl->prev_res = res;
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ s_ctrl->pict_res = res;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ s_ctrl->curr_frame_length_lines =
+ msm_sensor_read_b_conf_wdata
+ (s_ctrl, res, s_ctrl->frame_length_lines_array_addr);
+ s_ctrl->curr_line_length_pck =
+ msm_sensor_read_b_conf_wdata
+ (s_ctrl, res, s_ctrl->line_length_pck_array_addr);
+
+ if (s_ctrl->func_tbl.sensor_setting
+ (s_ctrl, MSM_SENSOR_UPDATE_PERIODIC, res) < 0)
+ return rc;
+ }
+ s_ctrl->curr_res = res;
+ return rc;
+}
+
+int32_t msm_sensor_set_sensor_mode_w(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, int res)
+{
+ int32_t rc = 0;
+
+ if (s_ctrl->curr_res != res) {
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ s_ctrl->prev_res = res;
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ s_ctrl->pict_res = res;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ s_ctrl->curr_frame_length_lines =
+ msm_sensor_read_w_conf_wdata
+ (s_ctrl, res, s_ctrl->frame_length_lines_array_addr);
+ s_ctrl->curr_line_length_pck =
+ msm_sensor_read_w_conf_wdata
+ (s_ctrl, res, s_ctrl->line_length_pck_array_addr);
+
+ if (s_ctrl->func_tbl.sensor_setting
+ (s_ctrl, MSM_SENSOR_UPDATE_PERIODIC, res) < 0)
+ return rc;
+ }
+ s_ctrl->curr_res = res;
+ return rc;
+}
+
+int32_t msm_sensor_mode_init_bdata(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, struct sensor_init_cfg *init_info)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ if (mode != s_ctrl->cam_mode) {
+ if (init_info->prev_res >=
+ s_ctrl->msm_sensor_reg.num_conf ||
+ init_info->pict_res >=
+ s_ctrl->msm_sensor_reg.num_conf) {
+ CDBG("Resolution does not exist");
+ return -EINVAL;
+ }
+
+ s_ctrl->prev_res = init_info->prev_res;
+ s_ctrl->pict_res = init_info->pict_res;
+ s_ctrl->curr_res = MSM_SENSOR_INVALID_RES;
+ s_ctrl->cam_mode = mode;
+
+ s_ctrl->prev_frame_length_lines =
+ msm_sensor_read_b_conf_wdata(s_ctrl,
+ s_ctrl->prev_res,
+ s_ctrl->frame_length_lines_array_addr);
+ s_ctrl->prev_line_length_pck =
+ msm_sensor_read_b_conf_wdata(s_ctrl,
+ s_ctrl->prev_res,
+ s_ctrl->line_length_pck_array_addr);
+
+ s_ctrl->snap_frame_length_lines =
+ msm_sensor_read_b_conf_wdata(s_ctrl,
+ s_ctrl->pict_res,
+ s_ctrl->frame_length_lines_array_addr);
+
+ s_ctrl->snap_line_length_pck =
+ msm_sensor_read_b_conf_wdata(s_ctrl,
+ s_ctrl->pict_res,
+ s_ctrl->line_length_pck_array_addr);
+
+
+ rc = s_ctrl->func_tbl.sensor_setting(s_ctrl,
+ MSM_SENSOR_REG_INIT, s_ctrl->prev_res);
+ }
+ return rc;
+}
+
+int32_t msm_sensor_mode_init_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, struct sensor_init_cfg *init_info)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ if (mode != s_ctrl->cam_mode) {
+ if (init_info->prev_res >=
+ s_ctrl->msm_sensor_reg.num_conf ||
+ init_info->pict_res >=
+ s_ctrl->msm_sensor_reg.num_conf) {
+ CDBG("Resolution does not exist");
+ return -EINVAL;
+ }
+
+ s_ctrl->prev_res = init_info->prev_res;
+ s_ctrl->pict_res = init_info->pict_res;
+ s_ctrl->curr_res = MSM_SENSOR_INVALID_RES;
+ s_ctrl->cam_mode = mode;
+
+ s_ctrl->prev_frame_length_lines =
+ msm_sensor_read_w_conf_wdata(s_ctrl,
+ s_ctrl->prev_res,
+ s_ctrl->frame_length_lines_array_addr);
+ s_ctrl->prev_line_length_pck =
+ msm_sensor_read_w_conf_wdata(s_ctrl,
+ s_ctrl->prev_res,
+ s_ctrl->line_length_pck_array_addr);
+
+ s_ctrl->snap_frame_length_lines =
+ msm_sensor_read_w_conf_wdata(s_ctrl,
+ s_ctrl->pict_res,
+ s_ctrl->frame_length_lines_array_addr);
+
+ s_ctrl->snap_line_length_pck =
+ msm_sensor_read_w_conf_wdata(s_ctrl,
+ s_ctrl->pict_res,
+ s_ctrl->line_length_pck_array_addr);
+
+
+ rc = s_ctrl->func_tbl.sensor_setting(s_ctrl,
+ MSM_SENSOR_REG_INIT, s_ctrl->prev_res);
+ }
+ return rc;
+}
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, 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;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("msm_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ s_ctrl->func_tbl.
+ sensor_get_pict_fps(
+ s_ctrl,
+ 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 =
+ s_ctrl->func_tbl.
+ sensor_get_prev_lines_pf
+ (s_ctrl);
+
+ 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 =
+ s_ctrl->func_tbl.
+ sensor_get_prev_pixels_pl
+ (s_ctrl);
+
+ 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 =
+ s_ctrl->func_tbl.
+ sensor_get_pict_lines_pf
+ (s_ctrl);
+
+ 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 =
+ s_ctrl->func_tbl.
+ sensor_get_pict_pixels_pl
+ (s_ctrl);
+
+ 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 =
+ s_ctrl->func_tbl.
+ sensor_get_pict_max_exp_lc
+ (s_ctrl);
+
+ 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 = s_ctrl->func_tbl.
+ sensor_set_fps(
+ s_ctrl,
+ &(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ s_ctrl->func_tbl.
+ sensor_write_exp_gain(
+ s_ctrl,
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ s_ctrl->func_tbl.
+ sensor_write_exp_gain(
+ s_ctrl,
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = s_ctrl->func_tbl.
+ sensor_set_sensor_mode(
+ s_ctrl,
+ cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ break;
+
+ case CFG_MOVE_FOCUS:
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = 32;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ break;
+
+
+ case CFG_SEND_WB_INFO:
+ break;
+
+ case CFG_SENSOR_INIT:
+ rc = s_ctrl->func_tbl.
+ sensor_mode_init(
+ s_ctrl,
+ cdata.mode,
+ &(cdata.cfg.init_info));
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+int16_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0;
+ uint16_t chipid = 0;
+ rc = msm_sensor_i2c_waddr_read_w(s_ctrl,
+ s_ctrl->sensor_id_addr, &chipid);
+ CDBG("msm_sensor id: %d\n", chipid);
+ if (chipid != s_ctrl->sensor_id) {
+ CDBG("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *this_ctrl;
+ CDBG("%s_i2c_probe called\n", client->name);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ this_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
+ this_ctrl->msm_sensor_client = client;
+ return 0;
+
+probe_failure:
+ CDBG("%s_i2c_probe failed\n", client->name);
+ return rc;
+}
+
+int msm_sensor_probe(struct msm_sensor_ctrl_t *s_ctrl,
+ const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(s_ctrl->msm_sensor_i2c_driver);
+ if (rc < 0 || s_ctrl->msm_sensor_client == NULL) {
+ rc = -ENOTSUPP;
+ CDBG("I2C add driver failed");
+ goto probe_fail;
+ }
+
+ rc = s_ctrl->func_tbl.sensor_power_up(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = s_ctrl->func_tbl.sensor_open_init;
+ s->s_release = s_ctrl->func_tbl.sensor_release;
+ s->s_config = s_ctrl->func_tbl.sensor_config;
+ s->s_camera_type = s_ctrl->camera_type;
+ s->s_mount_angle = 0;
+ s_ctrl->func_tbl.sensor_power_down(info);
+ return rc;
+probe_fail:
+ return rc;
+}
+
+int msm_sensor_v4l2_probe(struct msm_sensor_ctrl_t *s_ctrl,
+ const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = s_ctrl->func_tbl.sensor_probe(s_ctrl, info, s);
+ if (rc < 0)
+ return rc;
+
+ s_ctrl->sensor_v4l2_subdev = sdev;
+ v4l2_i2c_subdev_init(s_ctrl->sensor_v4l2_subdev,
+ s_ctrl->msm_sensor_client, s_ctrl->sensor_v4l2_subdev_ops);
+ s_ctrl->sensor_v4l2_subdev->dev_priv = (void *) s_ctrl;
+ return rc;
+}
+
+int msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct msm_sensor_ctrl_t *s_ctrl =
+ (struct msm_sensor_ctrl_t *) sd->dev_priv;
+ if ((unsigned int)index >= s_ctrl->sensor_v4l2_subdev_info_size)
+ return -EINVAL;
+
+ *code = s_ctrl->sensor_v4l2_subdev_info[index].code;
+ return 0;
+}
+
+static int msm_sensor_debugfs_stream_s(void *data, u64 val)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *) data;
+ if (val)
+ s_ctrl->func_tbl.sensor_start_stream(s_ctrl);
+ else
+ s_ctrl->func_tbl.sensor_stop_stream(s_ctrl);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sensor_debugfs_stream, NULL,
+ msm_sensor_debugfs_stream_s, "%llu\n");
+
+static int msm_sensor_debugfs_test_s(void *data, u64 val)
+{
+ CDBG("val: %llu\n", val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sensor_debugfs_test, NULL,
+ msm_sensor_debugfs_test_s, "%llu\n");
+
+int msm_sensor_enable_debugfs(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ struct dentry *debugfs_base, *sensor_dir;
+ debugfs_base = debugfs_create_dir("msm_sensor", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ sensor_dir = debugfs_create_dir
+ (s_ctrl->sensordata->sensor_name, debugfs_base);
+ if (!sensor_dir)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, sensor_dir,
+ (void *) s_ctrl, &sensor_debugfs_stream))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("test", S_IRUGO | S_IWUSR, sensor_dir,
+ (void *) s_ctrl, &sensor_debugfs_test))
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/media/video/msm/msm_sensor.h b/drivers/media/video/msm/msm_sensor.h
new file mode 100644
index 0000000..d8224ce
--- /dev/null
+++ b/drivers/media/video/msm/msm_sensor.h
@@ -0,0 +1,246 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/debugfs.h>
+#include <mach/camera.h>
+#include <media/msm_camera.h>
+#include <media/v4l2-subdev.h>
+#define Q8 0x00000100
+#define Q10 0x00000400
+
+enum msm_sensor_resolution_t {
+ MSM_SENSOR_RES_0,
+ MSM_SENSOR_RES_1,
+ MSM_SENSOR_RES_2,
+ MSM_SENSOR_RES_3,
+ MSM_SENSOR_RES_4,
+ MSM_SENSOR_RES_5,
+ MSM_SENSOR_RES_6,
+ MSM_SENSOR_RES_7,
+ MSM_SENSOR_INVALID_RES,
+};
+
+#define MSM_SENSOR_MCLK_8HZ 8000000
+#define MSM_SENSOR_MCLK_16HZ 16000000
+#define MSM_SENSOR_MCLK_24HZ 24000000
+
+enum msm_sensor_reg_update {
+ /* Sensor egisters that need to be updated during initialization */
+ MSM_SENSOR_REG_INIT,
+ /* Sensor egisters that needs periodic I2C writes */
+ MSM_SENSOR_UPDATE_PERIODIC,
+ /* All the sensor Registers will be updated */
+ MSM_SENSOR_UPDATE_ALL,
+ /* Not valid update */
+ MSM_SENSOR_UPDATE_INVALID
+};
+
+enum msm_sensor_cam_mode_t {
+ MSM_SENSOR_MODE_2D_RIGHT,
+ MSM_SENSOR_MODE_2D_LEFT,
+ MSM_SENSOR_MODE_3D,
+ MSM_SENSOR_MODE_INVALID
+};
+
+struct msm_sensor_i2c_reg_conf {
+ unsigned short reg_addr;
+ unsigned short reg_data;
+};
+
+struct msm_sensor_i2c_conf_array {
+ struct msm_sensor_i2c_reg_conf *conf;
+ unsigned short size;
+ unsigned short delay;
+};
+
+struct msm_sensor_reg_t {
+ struct msm_sensor_i2c_reg_conf *start_stream_conf;
+ uint8_t start_stream_conf_size;
+ struct msm_sensor_i2c_reg_conf *stop_stream_conf;
+ uint8_t stop_stream_conf_size;
+ struct msm_sensor_i2c_reg_conf *group_hold_on_conf;
+ uint8_t group_hold_on_conf_size;
+ struct msm_sensor_i2c_reg_conf *group_hold_off_conf;
+ uint8_t group_hold_off_conf_size;
+ struct msm_sensor_i2c_conf_array *init_settings;
+ uint8_t init_size;
+ struct msm_sensor_i2c_conf_array *res_settings;
+ uint8_t num_conf;
+};
+
+struct v4l2_subdev_info {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ uint16_t fmt;
+ uint16_t order;
+};
+
+struct msm_sensor_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ struct i2c_client *msm_sensor_client;
+ struct i2c_driver *msm_sensor_i2c_driver;
+ struct msm_sensor_reg_t msm_sensor_reg;
+
+ uint16_t sensor_id_addr;
+ uint16_t sensor_id;
+ uint16_t frame_length_lines_addr;
+ uint16_t line_length_pck_addr;
+ uint16_t global_gain_addr;
+ uint16_t coarse_int_time_addr;
+
+ uint8_t frame_length_lines_array_addr;
+ uint8_t line_length_pck_array_addr;
+
+ uint16_t curr_line_length_pck;
+ uint16_t curr_frame_length_lines;
+ uint16_t prev_line_length_pck;
+ uint16_t prev_frame_length_lines;
+ uint16_t snap_line_length_pck;
+ uint16_t snap_frame_length_lines;
+ uint16_t vert_offset;
+
+ uint16_t fps;
+ uint32_t fps_divider;
+ enum msm_sensor_resolution_t prev_res;
+ enum msm_sensor_resolution_t pict_res;
+ enum msm_sensor_resolution_t curr_res;
+ enum msm_sensor_cam_mode_t cam_mode;
+ enum msm_camera_type camera_type;
+
+ struct mutex *msm_sensor_mutex;
+ bool config_csi_flag;
+ struct msm_camera_csi_params *csi_params;
+
+ /*To Do: Changing v4l2_subdev to a pointer according to yupeng*/
+ struct v4l2_subdev *sensor_v4l2_subdev;
+ struct v4l2_subdev_info *sensor_v4l2_subdev_info;
+ uint8_t sensor_v4l2_subdev_info_size;
+ struct v4l2_subdev_ops *sensor_v4l2_subdev_ops;
+
+ struct msm_sensor_fn_t {
+ void (*sensor_start_stream) (struct msm_sensor_ctrl_t *);
+ void (*sensor_stop_stream) (struct msm_sensor_ctrl_t *);
+ void (*sensor_group_hold_on) (struct msm_sensor_ctrl_t *);
+ void (*sensor_group_hold_off) (struct msm_sensor_ctrl_t *);
+
+ uint16_t (*sensor_get_prev_lines_pf)
+ (struct msm_sensor_ctrl_t *);
+ uint16_t (*sensor_get_prev_pixels_pl)
+ (struct msm_sensor_ctrl_t *);
+ uint16_t (*sensor_get_pict_lines_pf)
+ (struct msm_sensor_ctrl_t *);
+ uint16_t (*sensor_get_pict_pixels_pl)
+ (struct msm_sensor_ctrl_t *);
+ uint32_t (*sensor_get_pict_max_exp_lc)
+ (struct msm_sensor_ctrl_t *);
+ void (*sensor_get_pict_fps) (struct msm_sensor_ctrl_t *,
+ uint16_t, uint16_t *);
+ int32_t (*sensor_set_fps) (struct msm_sensor_ctrl_t *,
+ struct fps_cfg *);
+ int32_t (*sensor_write_exp_gain) (struct msm_sensor_ctrl_t *,
+ uint16_t, uint32_t);
+ int32_t (*sensor_setting) (struct msm_sensor_ctrl_t *,
+ int update_type, int rt);
+ int32_t (*sensor_set_sensor_mode)
+ (struct msm_sensor_ctrl_t *, int, int);
+ int32_t (*sensor_mode_init) (struct msm_sensor_ctrl_t *,
+ int, struct sensor_init_cfg *);
+ int (*sensor_config) (void __user *);
+ int (*sensor_open_init) (const struct msm_camera_sensor_info *);
+ int (*sensor_release) (void);
+ int (*sensor_power_down)
+ (const struct msm_camera_sensor_info *);
+ int (*sensor_power_up) (const struct msm_camera_sensor_info *);
+ int (*sensor_probe) (struct msm_sensor_ctrl_t *s_ctrl,
+ const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s);
+ } func_tbl;
+};
+
+int32_t msm_sensor_i2c_rxdata(struct msm_sensor_ctrl_t *s_ctrl,
+ unsigned char *rxdata, int length);
+
+int32_t msm_sensor_i2c_txdata(struct msm_sensor_ctrl_t *s_ctrl,
+ unsigned char *txdata, int length);
+
+int32_t msm_sensor_i2c_waddr_write_b(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint8_t bdata);
+
+int32_t msm_sensor_i2c_waddr_write_w(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint16_t wdata);
+
+int32_t msm_sensor_i2c_waddr_read_w(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t waddr, uint16_t *data);
+
+int32_t msm_sensor_i2c_waddr_write_b_tbl(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_reg_conf const *reg_conf_tbl, uint8_t size);
+
+int32_t msm_sensor_i2c_waddr_write_w_tbl(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_i2c_reg_conf const *reg_conf_tbl, uint8_t size);
+
+void msm_sensor_start_stream(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_group_hold_on(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_group_hold_off(struct msm_sensor_ctrl_t *s_ctrl);
+
+uint16_t msm_sensor_get_prev_lines_pf(struct msm_sensor_ctrl_t *s_ctrl);
+uint16_t msm_sensor_get_prev_pixels_pl(struct msm_sensor_ctrl_t *s_ctrl);
+uint16_t msm_sensor_get_pict_lines_pf(struct msm_sensor_ctrl_t *s_ctrl);
+uint16_t msm_sensor_get_pict_pixels_pl(struct msm_sensor_ctrl_t *s_ctrl);
+uint32_t msm_sensor_get_pict_max_exp_lc(struct msm_sensor_ctrl_t *s_ctrl);
+void msm_sensor_get_pict_fps(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t fps, uint16_t *pfps);
+int32_t msm_sensor_set_fps(struct msm_sensor_ctrl_t *s_ctrl,
+ struct fps_cfg *fps);
+int32_t msm_sensor_write_exp_gain1(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line);
+int32_t msm_sensor_write_exp_gain2(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line);
+int32_t msm_sensor_set_sensor_mode_b(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, int res);
+int32_t msm_sensor_set_sensor_mode_w(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, int res);
+int32_t msm_sensor_mode_init_bdata(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, struct sensor_init_cfg *init_info);
+int32_t msm_sensor_mode_init_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ int mode, struct sensor_init_cfg *init_info);
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp);
+int16_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
+uint16_t msm_sensor_read_b_conf_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ enum msm_sensor_resolution_t res, int8_t array_addr);
+uint16_t msm_sensor_read_w_conf_wdata(struct msm_sensor_ctrl_t *s_ctrl,
+ enum msm_sensor_resolution_t res, int8_t array_addr);
+
+int msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+
+int msm_sensor_probe(struct msm_sensor_ctrl_t *s_ctrl,
+ const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s);
+
+int msm_sensor_v4l2_probe(struct msm_sensor_ctrl_t *s_ctrl,
+ const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s);
+
+int msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code);
+
+int msm_sensor_write_b_init_settings(struct msm_sensor_ctrl_t *s_ctrl);
+int msm_sensor_write_w_init_settings(struct msm_sensor_ctrl_t *s_ctrl);
+int msm_sensor_write_b_res_settings
+ (struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
+int msm_sensor_write_w_res_settings
+ (struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
+
+int msm_sensor_enable_debugfs(struct msm_sensor_ctrl_t *s_ctrl);
diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c
new file mode 100644
index 0000000..aa35096
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.c
@@ -0,0 +1,3729 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/irqs.h>
+#include <mach/camera.h>
+#include <mach/msm_reqs.h>
+#include <asm/atomic.h>
+
+#include "msm_vfe31.h"
+#include "msm_vpe1.h"
+
+atomic_t irq_cnt;
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ cmd->length)) { \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+static struct vfe31_ctrl_type *vfe31_ctrl;
+static struct msm_camera_io_clk camio_clk;
+static void *vfe_syncdata;
+static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id);
+static void vfe31_reset_hist_cfg(void);
+
+struct vfe31_isr_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ uint32_t vfePingPongStatus;
+ struct vfe_frame_asf_info vfeAsfFrameInfo;
+ struct vfe_frame_bpc_info vfeBpcFrameInfo;
+ struct vfe_msg_camif_status vfeCamifStatusLocal;
+};
+
+static struct vfe31_cmd_type vfe31_cmd[] = {
+/* 0*/ {V31_DUMMY_0},
+ {V31_SET_CLK},
+ {V31_RESET},
+ {V31_START},
+ {V31_TEST_GEN_START},
+/* 5*/ {V31_OPERATION_CFG, V31_OPERATION_CFG_LEN},
+ {V31_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF},
+ {V31_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF},
+ {V31_AXI_INPUT_CFG},
+ {V31_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+ 0xFF},
+/*10*/ {V31_ROLL_OFF_CFG, V31_ROLL_OFF_CFG_LEN, V31_ROLL_OFF_CFG_OFF,
+ 0xFF},
+ {V31_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+ {V31_DEMOSAIC_0_CFG, V31_DEMOSAIC_0_LEN, V31_DEMOSAIC_0_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_1_CFG, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_2_CFG, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+ 0xFF},
+/*15*/ {V31_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+ {V31_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+ 0xFF},
+ {V31_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF},
+ {V31_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF},
+ {V31_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF},
+/*20*/ {V31_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF },
+ {V31_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF},
+ {V31_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+ 0xFF},
+ {V31_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+ {V31_SK_ENHAN_CFG, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+/*25*/ {V31_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF},
+ {V31_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+ {V31_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+ {V31_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF,
+ 0xFF},
+ {V31_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF,
+ 0xFF},
+/*30*/ {V31_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF,
+ 0xFF},
+ {V31_DUMMY_1},
+ {V31_DUMMY_2},
+ {V31_DUMMY_3},
+ {V31_UPDATE},
+/*35*/ {V31_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF,
+ 0xFF},
+ {V31_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF},
+ {V31_DEMOSAIC_1_UPDATE, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF,
+ 0xFF},
+ {V31_DEMOSAIC_2_UPDATE, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF,
+ 0xFF},
+ {V31_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF},
+/*40*/ {V31_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF,
+ 0xFF},
+ {V31_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF},
+ {V31_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF,
+ 0xFF},
+ {V31_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF},
+ {V31_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF },
+/*45*/ {V31_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF,
+ 0xFF},
+ {V31_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF,
+ 0xFF},
+ {V31_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF},
+ {V31_SK_ENHAN_UPDATE, V31_SCE_LEN, V31_SCE_OFF, 0xFF},
+ {V31_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF},
+/*50*/ {V31_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF},
+ {V31_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF},
+ {V31_FRAME_SKIP_UPDATE},
+ {V31_CAMIF_FRAME_UPDATE},
+ {V31_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*55*/ {V31_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+ {V31_STATS_AWB_UPDATE, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+ {V31_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+ {V31_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+ {V31_STATS_SKIN_UPDATE},
+/*60*/ {V31_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+ {V31_DUMMY_4},
+ {V31_EPOCH1_ACK},
+ {V31_EPOCH2_ACK},
+ {V31_START_RECORDING},
+/*65*/ {V31_STOP_RECORDING},
+ {V31_DUMMY_5},
+ {V31_DUMMY_6},
+ {V31_CAPTURE, V31_CAPTURE_LEN, 0xFF},
+ {V31_DUMMY_7},
+/*70*/ {V31_STOP},
+ {V31_GET_HW_VERSION},
+ {V31_GET_FRAME_SKIP_COUNTS},
+ {V31_OUTPUT1_BUFFER_ENQ},
+ {V31_OUTPUT2_BUFFER_ENQ},
+/*75*/ {V31_OUTPUT3_BUFFER_ENQ},
+ {V31_JPEG_OUT_BUF_ENQ},
+ {V31_RAW_OUT_BUF_ENQ},
+ {V31_RAW_IN_BUF_ENQ},
+ {V31_STATS_AF_ENQ},
+/*80*/ {V31_STATS_AE_ENQ},
+ {V31_STATS_AWB_ENQ},
+ {V31_STATS_RS_ENQ},
+ {V31_STATS_CS_ENQ},
+ {V31_STATS_SKIN_ENQ},
+/*85*/ {V31_STATS_IHIST_ENQ},
+ {V31_DUMMY_8},
+ {V31_JPEG_ENC_CFG},
+ {V31_DUMMY_9},
+ {V31_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF},
+/*90*/ {V31_STATS_AF_STOP},
+ {V31_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF},
+ {V31_STATS_AE_STOP},
+ {V31_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF},
+ {V31_STATS_AWB_STOP},
+/*95*/ {V31_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF},
+ {V31_STATS_RS_STOP},
+ {V31_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF},
+ {V31_STATS_CS_STOP},
+ {V31_STATS_SKIN_START},
+/*100*/ {V31_STATS_SKIN_STOP},
+ {V31_STATS_IHIST_START,
+ V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF},
+ {V31_STATS_IHIST_STOP},
+ {V31_DUMMY_10},
+ {V31_SYNC_TIMER_SETTING, V31_SYNC_TIMER_LEN,
+ V31_SYNC_TIMER_OFF},
+/*105*/ {V31_ASYNC_TIMER_SETTING, V31_ASYNC_TIMER_LEN, V31_ASYNC_TIMER_OFF},
+ {V31_LIVESHOT},
+ {V31_ZSL, V31_CAPTURE_LEN, 0xFF},
+ {V31_STEREOCAM},
+ {V31_LA_SETUP},
+/*110*/ {V31_XBAR_CFG, V31_XBAR_CFG_LEN, V31_XBAR_CFG_OFF},
+/*111*/ {V31_EZTUNE_CFG, V31_EZTUNE_CFG_LEN, V31_EZTUNE_CFG_OFF},
+};
+
+uint32_t vfe31_AXI_WM_CFG[] = {
+ 0x0000004C,
+ 0x00000064,
+ 0x0000007C,
+ 0x00000094,
+ 0x000000AC,
+ 0x000000C4,
+ 0x000000DC,
+};
+
+static const char *vfe31_general_cmd[] = {
+ "DUMMY_0", /* 0 */
+ "SET_CLK",
+ "RESET",
+ "START",
+ "TEST_GEN_START",
+ "OPERATION_CFG", /* 5 */
+ "AXI_OUT_CFG",
+ "CAMIF_CFG",
+ "AXI_INPUT_CFG",
+ "BLACK_LEVEL_CFG",
+ "ROLL_OFF_CFG", /* 10 */
+ "DEMUX_CFG",
+ "DEMOSAIC_0_CFG", /* general */
+ "DEMOSAIC_1_CFG", /* ABF */
+ "DEMOSAIC_2_CFG", /* BPC */
+ "FOV_CFG", /* 15 */
+ "MAIN_SCALER_CFG",
+ "WB_CFG",
+ "COLOR_COR_CFG",
+ "RGB_G_CFG",
+ "LA_CFG", /* 20 */
+ "CHROMA_EN_CFG",
+ "CHROMA_SUP_CFG",
+ "MCE_CFG",
+ "SK_ENHAN_CFG",
+ "ASF_CFG", /* 25 */
+ "S2Y_CFG",
+ "S2CbCr_CFG",
+ "CHROMA_SUBS_CFG",
+ "OUT_CLAMP_CFG",
+ "FRAME_SKIP_CFG", /* 30 */
+ "DUMMY_1",
+ "DUMMY_2",
+ "DUMMY_3",
+ "UPDATE",
+ "BL_LVL_UPDATE", /* 35 */
+ "DEMUX_UPDATE",
+ "DEMOSAIC_1_UPDATE", /* BPC */
+ "DEMOSAIC_2_UPDATE", /* ABF */
+ "FOV_UPDATE",
+ "MAIN_SCALER_UPDATE", /* 40 */
+ "WB_UPDATE",
+ "COLOR_COR_UPDATE",
+ "RGB_G_UPDATE",
+ "LA_UPDATE",
+ "CHROMA_EN_UPDATE", /* 45 */
+ "CHROMA_SUP_UPDATE",
+ "MCE_UPDATE",
+ "SK_ENHAN_UPDATE",
+ "S2CbCr_UPDATE",
+ "S2Y_UPDATE", /* 50 */
+ "ASF_UPDATE",
+ "FRAME_SKIP_UPDATE",
+ "CAMIF_FRAME_UPDATE",
+ "STATS_AF_UPDATE",
+ "STATS_AE_UPDATE", /* 55 */
+ "STATS_AWB_UPDATE",
+ "STATS_RS_UPDATE",
+ "STATS_CS_UPDATE",
+ "STATS_SKIN_UPDATE",
+ "STATS_IHIST_UPDATE", /* 60 */
+ "DUMMY_4",
+ "EPOCH1_ACK",
+ "EPOCH2_ACK",
+ "START_RECORDING",
+ "STOP_RECORDING", /* 65 */
+ "DUMMY_5",
+ "DUMMY_6",
+ "CAPTURE",
+ "DUMMY_7",
+ "STOP", /* 70 */
+ "GET_HW_VERSION",
+ "GET_FRAME_SKIP_COUNTS",
+ "OUTPUT1_BUFFER_ENQ",
+ "OUTPUT2_BUFFER_ENQ",
+ "OUTPUT3_BUFFER_ENQ", /* 75 */
+ "JPEG_OUT_BUF_ENQ",
+ "RAW_OUT_BUF_ENQ",
+ "RAW_IN_BUF_ENQ",
+ "STATS_AF_ENQ",
+ "STATS_AE_ENQ", /* 80 */
+ "STATS_AWB_ENQ",
+ "STATS_RS_ENQ",
+ "STATS_CS_ENQ",
+ "STATS_SKIN_ENQ",
+ "STATS_IHIST_ENQ", /* 85 */
+ "DUMMY_8",
+ "JPEG_ENC_CFG",
+ "DUMMY_9",
+ "STATS_AF_START",
+ "STATS_AF_STOP", /* 90 */
+ "STATS_AE_START",
+ "STATS_AE_STOP",
+ "STATS_AWB_START",
+ "STATS_AWB_STOP",
+ "STATS_RS_START", /* 95 */
+ "STATS_RS_STOP",
+ "STATS_CS_START",
+ "STATS_CS_STOP",
+ "STATS_SKIN_START",
+ "STATS_SKIN_STOP", /* 100 */
+ "STATS_IHIST_START",
+ "STATS_IHIST_STOP",
+ "DUMMY_10",
+ "SYNC_TIMER_SETTING",
+ "ASYNC_TIMER_SETTING", /* 105 */
+ "V31_LIVESHOT",
+ "V31_ZSL",
+ "V31_STEREOCAM",
+ "V31_LA_SETUP",
+ "V31_XBAR_CFG",
+};
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+ uint8_t outid;
+ switch (type) {
+ case VFE_MSG_OUTPUT_T:
+ case VFE_MSG_OUTPUT_P:
+ case VFE_MSG_OUTPUT_S:
+ case VFE_MSG_OUTPUT_V:
+ {
+ pinfo->output_id =
+ ((struct vfe_message *)data)->_u.msgOut.output_id;
+
+ switch (type) {
+ case VFE_MSG_OUTPUT_P:
+ outid = OUTPUT_TYPE_P;
+ break;
+ case VFE_MSG_OUTPUT_V:
+ outid = OUTPUT_TYPE_V;
+ break;
+ case VFE_MSG_OUTPUT_T:
+ outid = OUTPUT_TYPE_T;
+ break;
+ case VFE_MSG_OUTPUT_S:
+ outid = OUTPUT_TYPE_S;
+ break;
+ default:
+ outid = 0xff;
+ break;
+ }
+ pinfo->output_id = outid;
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOut.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOut.cbcrBuffer;
+
+ pinfo->frame_id =
+ ((struct vfe_message *)data)->_u.msgOut.frameCounter;
+
+ ((struct vfe_msg_output *)(vfe31_ctrl->extdata))->bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOut.bpcInfo;
+ ((struct vfe_msg_output *)(vfe31_ctrl->extdata))->asfInfo =
+ ((struct vfe_message *)data)->_u.msgOut.asfInfo;
+ ((struct vfe_msg_output *)(vfe31_ctrl->extdata))->frameCounter =
+ ((struct vfe_message *)data)->_u.msgOut.frameCounter;
+ *ext = vfe31_ctrl->extdata;
+ *elen = vfe31_ctrl->extlen;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+}
+
+
+static void vfe31_proc_ops(enum VFE31_MESSAGE_ID id, void *msg, size_t len)
+{
+ struct msm_vfe_resp *rp;
+
+ rp = vfe31_ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp),
+ vfe31_ctrl->syncdata, GFP_ATOMIC);
+ if (!rp) {
+ CDBG("rp: cannot allocate buffer\n");
+ return;
+ }
+ CDBG("vfe31_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 MSG_ID_SNAPSHOT_DONE:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_ID_OUTPUT_P:
+ rp->type = VFE_MSG_OUTPUT_P;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_T:
+ rp->type = VFE_MSG_OUTPUT_T;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_S:
+ rp->type = VFE_MSG_OUTPUT_S;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_V:
+ rp->type = VFE_MSG_OUTPUT_V;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_V,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_COMMON:
+ rp->type = VFE_MSG_COMMON;
+ rp->stats_msg.status_bits = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.status_bits;
+ rp->stats_msg.frame_id = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.frameCounter;
+
+ rp->stats_msg.aec_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.aec;
+ rp->stats_msg.awb_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.awb;
+ rp->stats_msg.af_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.af;
+ rp->stats_msg.ihist_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.ihist;
+ rp->stats_msg.rs_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.rs;
+ rp->stats_msg.cs_buff = ((struct vfe_message *)
+ rp->evt_msg.data)->_u.msgStats.buff.cs;
+ break;
+
+ case MSG_ID_SYNC_TIMER0_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER0;
+ break;
+
+ case MSG_ID_SYNC_TIMER1_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER1;
+ break;
+
+ case MSG_ID_SYNC_TIMER2_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER2;
+ break;
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+
+ /* save the frame id.*/
+ rp->evt_msg.frame_id = rp->phy.frame_id;
+
+ vfe31_ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe31_ctrl->syncdata,
+ GFP_ATOMIC);
+}
+
+static void vfe_send_outmsg(uint8_t msgid, uint32_t pyaddr,
+ uint32_t pcbcraddr)
+{
+ struct vfe_message msg;
+ uint8_t outid;
+
+ msg._d = msgid; /* now the output mode is redundnat. */
+ msg._u.msgOut.frameCounter = vfe31_ctrl->vfeFrameId;
+
+ switch (msgid) {
+ case MSG_ID_OUTPUT_P:
+ outid = OUTPUT_TYPE_P;
+ break;
+ case MSG_ID_OUTPUT_V:
+ outid = OUTPUT_TYPE_V;
+ break;
+ case MSG_ID_OUTPUT_T:
+ outid = OUTPUT_TYPE_T;
+ break;
+ case MSG_ID_OUTPUT_S:
+ outid = OUTPUT_TYPE_S;
+ break;
+ default:
+ outid = 0xff; /* -1 for error condition.*/
+ break;
+ }
+ msg._u.msgOut.output_id = msgid;
+ msg._u.msgOut.yBuffer = pyaddr;
+ msg._u.msgOut.cbcrBuffer = pcbcraddr;
+
+ vfe31_proc_ops(msgid, &msg, sizeof(struct vfe_message));
+ return;
+}
+static int vfe31_enable(struct camera_enable_cmd *enable)
+{
+ return 0;
+}
+
+static void vfe31_stop(void)
+{
+ atomic_set(&vfe31_ctrl->vstate, 0);
+ atomic_set(&vfe31_ctrl->stop_ack_pending, 1);
+
+ /* in either continuous or snapshot mode, stop command can be issued
+ * at any time. stop camif immediately. */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+ vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+ /* disable all interrupts. */
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1,
+ vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+ /* now enable only halt_irq & reset_irq */
+ msm_io_w(0xf0000000, /* this is for async timer. */
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_IMASK_AXI_HALT,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* then apply axi halt command. */
+ msm_io_w_mb(AXI_HALT,
+ vfe31_ctrl->vfebase + VFE_AXI_CMD);
+}
+
+static int vfe31_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev)
+{
+ msm_camio_set_perf_lvl(S_EXIT);
+ msm_camio_disable(dev);
+ return 0;
+}
+
+static int vfe31_add_free_buf2(struct vfe31_output_ch *outch,
+ uint32_t paddr, uint32_t y_off, uint32_t cbcr_off)
+{
+ struct vfe31_free_buf *free_buf = NULL;
+ unsigned long flags = 0;
+ free_buf = kmalloc(sizeof(struct vfe31_free_buf), GFP_KERNEL);
+ if (!free_buf)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ free_buf->paddr = paddr;
+ free_buf->y_off = y_off;
+ free_buf->cbcr_off = cbcr_off;
+ list_add_tail(&free_buf->node, &outch->free_buf_head);
+
+ CDBG("%s: free_buf paddr = 0x%x, y_off = %d, cbcr_off = %d\n",
+ __func__, free_buf->paddr, free_buf->y_off,
+ free_buf->cbcr_off);
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+ return 0;
+}
+
+#define vfe31_add_free_buf(outch, regptr) \
+ vfe31_add_free_buf2(outch, regptr->paddr, regptr->info.y_off, \
+ regptr->info.cbcr_off)
+
+#define vfe31_free_buf_available(outch) \
+ (!list_empty(&outch.free_buf_head))
+
+static inline struct vfe31_free_buf *vfe31_get_free_buf(
+ struct vfe31_output_ch *outch)
+{
+ unsigned long flags = 0;
+ struct vfe31_free_buf *free_buf = NULL;
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ if (!list_empty(&outch->free_buf_head)) {
+ free_buf = list_first_entry(&outch->free_buf_head,
+ struct vfe31_free_buf, node);
+ if (free_buf)
+ list_del_init(&free_buf->node);
+ }
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+ return free_buf;
+}
+
+static inline void vfe31_reset_free_buf_queue(
+ struct vfe31_output_ch *outch)
+{
+ unsigned long flags = 0;
+ struct vfe31_free_buf *free_buf = NULL;
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ while (!list_empty(&outch->free_buf_head)) {
+ free_buf = list_first_entry(&outch->free_buf_head,
+ struct vfe31_free_buf, node);
+ if (free_buf) {
+ list_del_init(&free_buf->node);
+ kfree(free_buf);
+ }
+ }
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+}
+
+#define vfe31_init_free_buf_queue() do { \
+ INIT_LIST_HEAD(&vfe31_ctrl->outpath.out0.free_buf_head); \
+ INIT_LIST_HEAD(&vfe31_ctrl->outpath.out1.free_buf_head); \
+ INIT_LIST_HEAD(&vfe31_ctrl->outpath.out2.free_buf_head); \
+ spin_lock_init(&vfe31_ctrl->outpath.out0.free_buf_lock); \
+ spin_lock_init(&vfe31_ctrl->outpath.out1.free_buf_lock); \
+ spin_lock_init(&vfe31_ctrl->outpath.out2.free_buf_lock); \
+} while (0)
+
+#define vfe31_reset_free_buf_queue_all() do { \
+ vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out0); \
+ vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out1); \
+ vfe31_reset_free_buf_queue(&vfe31_ctrl->outpath.out2); \
+} while (0)
+
+static int vfe31_config_axi(int mode, struct axidata *ad, uint32_t *ao)
+{
+ int i;
+ uint32_t *p, *p1, *p2, *p3;
+ int32_t *ch_info;
+ struct vfe31_output_ch *outp1, *outp2, *outp3;
+ struct msm_pmem_region *regp1 = NULL;
+ struct msm_pmem_region *regp2 = NULL;
+ struct msm_pmem_region *regp3 = NULL;
+ int ret;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+ outp1 = NULL;
+ outp2 = NULL;
+ outp3 = NULL;
+
+ p = ao + 2;
+
+ /* Update the corresponding write masters for each output*/
+ ch_info = ao + V31_AXI_CFG_LEN;
+ vfe31_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe31_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+
+ CDBG("vfe31_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d"
+ "bufnum3 = %d", mode, ad->bufnum1, ad->bufnum2, ad->bufnum3);
+
+ switch (mode) {
+
+ case OUTPUT_2: {
+ if (ad->bufnum2 != 3)
+ return -EINVAL;
+ regp1 = &(ad->region[ad->bufnum1]);
+ outp1 = &(vfe31_ctrl->outpath.out0);
+ vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_PT;
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 12 + i; /* wm1 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+ ret = vfe31_add_free_buf(outp1, regp1);
+ if (ret < 0)
+ return ret;
+ }
+ break;
+
+ case OUTPUT_1_AND_2:
+ /* use wm0& 4 for thumbnail, wm1&5 for main image.*/
+ if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1))
+ return -EINVAL;
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_S; /* main image.*/
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_PT; /* thumbnail. */
+
+ /* this is thumbnail buffer. */
+ regp1 = &(ad->region[ad->bufnum1-1]);
+ /* this is main image buffer. */
+ regp2 = &(ad->region[ad->bufnum1+ad->bufnum2-1]);
+
+ outp1 = &(vfe31_ctrl->outpath.out0);
+ outp2 = &(vfe31_ctrl->outpath.out1); /* snapshot */
+
+ /* Parse the buffers!!! */
+ if (ad->bufnum2 == 1) { /* assuming bufnum1 = bufnum2 */
+ p1 = ao + 6; /* wm0 ping */
+ *p1++ = (regp1->paddr + regp1->info.y_off);
+
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 30; /* wm4 ping */
+ *p1++ = (regp1->paddr + regp1->info.cbcr_off);
+ CDBG("%s: regp1->info.cbcr_off = 0x%x\n", __func__,
+ regp1->info.cbcr_off);
+
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+
+ p1 = ao + 12; /* wm1 ping */
+ *p1++ = (regp2->paddr + regp2->info.y_off);
+
+ /* pong = ping,*/
+ *p1 = (regp2->paddr + regp2->info.y_off);
+
+ p1 = ao + 36; /* wm5 */
+ *p1++ = (regp2->paddr + regp2->info.cbcr_off);
+ CDBG("%s: regp2->info.cbcr_off = 0x%x\n", __func__,
+ regp2->info.cbcr_off);
+
+ /* pong = ping,*/
+ *p1 = (regp2->paddr + regp2->info.cbcr_off);
+ } else { /* more than one snapshot */
+ /* first fill ping & pong */
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ p1 = ao + 30 + i; /* wm4 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1--;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm1 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+ p2 = ao + 36 + i; /* wm5 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2--;
+ }
+
+ for (i = 2; i < ad->bufnum1; i++) {
+ ret = vfe31_add_free_buf(outp1, regp1);
+ if (ret < 0)
+ return ret;
+ regp1--;
+ }
+
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe31_add_free_buf(outp2, regp2);
+ if (ret < 0)
+ return ret;
+ regp2--;
+ }
+ }
+ break;
+
+ case OUTPUT_1_2_AND_3:
+ CDBG("%s: OUTPUT_1_2_AND_3", __func__);
+ CDBG("%s: %d %d %d", __func__, ad->bufnum1, ad->bufnum2,
+ ad->bufnum3);
+ /* use wm0& 4 for postview, wm1&5 for preview.*/
+ /* use wm2& 6 for main img */
+ if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1) || (ad->bufnum3 < 1))
+ return -EINVAL;
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_S; /* main image.*/
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_P; /* preview. */
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_T; /* thumbnail. */
+
+ /* this is preview buffer. */
+ regp1 = &(ad->region[0]);
+ /* this is thumbnail buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);
+ /* this is main image buffer. */
+ regp3 = &(ad->region[ad->bufnum1+ad->bufnum2]);
+ outp1 = &(vfe31_ctrl->outpath.out0);
+ outp2 = &(vfe31_ctrl->outpath.out1);
+ outp3 = &(vfe31_ctrl->outpath.out2);
+
+ /* Parse the buffers!!! */
+ /* first fill ping & pong */
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ p1 = ao + 30 + i; /* wm4 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm1 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+ p2 = ao + 36 + i; /* wm5 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p3 = ao + 18 + i; /* wm2 for y */
+ *p3 = (regp3->paddr + regp3->info.y_off);
+ p3 = ao + 42 + i; /* wm6 for cbcr */
+ *p3 = (regp3->paddr + regp3->info.cbcr_off);
+ regp3++;
+ }
+
+ for (i = 2; i < ad->bufnum1; i++) {
+ ret = vfe31_add_free_buf(outp1, regp1);
+ if (ret < 0)
+ return ret;
+ regp1++;
+ }
+
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe31_add_free_buf(outp2, regp2);
+ if (ret < 0)
+ return ret;
+ regp2++;
+ }
+
+ for (i = 2; i < ad->bufnum3; i++) {
+ ret = vfe31_add_free_buf(outp3, regp3);
+ if (ret < 0)
+ return ret;
+ regp3++;
+ }
+ break;
+
+ case OUTPUT_1_AND_3: {
+ /* use wm0&4 for preview, wm1&5 for video.*/
+ if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2))
+ return -EINVAL;
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ *p++ = 0x1; /* xbar cfg0 */
+ *p = 0x1a03; /* xbar cfg1 */
+#endif
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_V; /* video*/
+ vfe31_ctrl->outpath.output_mode |=
+ VFE31_OUTPUT_MODE_PT; /* preview */
+
+ regp1 = &(ad->region[0]); /* this is preview buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */
+ outp1 = &(vfe31_ctrl->outpath.out0); /* preview */
+ outp2 = &(vfe31_ctrl->outpath.out2); /* video */
+
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 30 + i; /* wm4 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm1 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+
+ p2 = ao + 36 + i; /* wm5 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2++;
+ }
+ for (i = 2; i < ad->bufnum1; i++) {
+ ret = vfe31_add_free_buf(outp1, regp1);
+ if (ret < 0)
+ return ret;
+ regp1++;
+ }
+
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe31_add_free_buf(outp2, regp2);
+ if (ret < 0)
+ return ret;
+ regp2++;
+ }
+ }
+ break;
+ case CAMIF_TO_AXI_VIA_OUTPUT_2: { /* use wm0 only */
+ if (ad->bufnum2 < 1)
+ return -EINVAL;
+ CDBG("config axi for raw snapshot.\n");
+ vfe31_ctrl->outpath.out1.ch0 = 0; /* raw */
+ regp1 = &(ad->region[ad->bufnum1]);
+ vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_S;
+ p1 = ao + 6; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ if (p_sync->stereocam_enabled)
+ p_sync->stereo_state = STEREO_RAW_SNAP_IDLE;
+ }
+ break;
+ default:
+ break;
+ }
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[V31_AXI_OUT_CFG].offset,
+ ao, vfe31_cmd[V31_AXI_OUT_CFG].length - V31_AXI_CH_INF_LEN);
+
+ return 0;
+}
+
+static void vfe31_reset_internal_variables(void)
+{
+ unsigned long flags;
+ vfe31_ctrl->vfeImaskCompositePacked = 0;
+ /* state control variables */
+ vfe31_ctrl->start_ack_pending = FALSE;
+ atomic_set(&irq_cnt, 0);
+
+ spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+ vfe31_ctrl->xbar_update_pending = 0;
+ spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+
+ atomic_set(&vfe31_ctrl->stop_ack_pending, 0);
+ atomic_set(&vfe31_ctrl->vstate, 0);
+
+ vfe31_ctrl->aec_ack_pending = FALSE;
+ vfe31_ctrl->af_ack_pending = FALSE;
+ vfe31_ctrl->awb_ack_pending = FALSE;
+ vfe31_ctrl->ihist_ack_pending = FALSE;
+ vfe31_ctrl->rs_ack_pending = FALSE;
+ vfe31_ctrl->cs_ack_pending = FALSE;
+
+ vfe31_ctrl->reset_ack_pending = FALSE;
+
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ vfe31_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+
+ vfe31_ctrl->recording_state = VFE_REC_STATE_IDLE;
+
+ /* 0 for continuous mode, 1 for snapshot mode */
+ vfe31_ctrl->operation_mode = VFE_MODE_OF_OPERATION_CONTINUOUS;
+ vfe31_ctrl->outpath.output_mode = 0;
+ vfe31_ctrl->vfe_capture_count = 0;
+
+ /* this is unsigned 32 bit integer. */
+ vfe31_ctrl->vfeFrameId = 0;
+
+ vfe31_ctrl->output1Pattern = 0xffffffff;
+ vfe31_ctrl->output1Period = 31;
+ vfe31_ctrl->output2Pattern = 0xffffffff;
+ vfe31_ctrl->output2Period = 31;
+ vfe31_ctrl->vfeFrameSkipCount = 0;
+ vfe31_ctrl->vfeFrameSkipPeriod = 31;
+
+ /* Stats control variables. */
+ memset(&(vfe31_ctrl->afStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe31_ctrl->awbStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe31_ctrl->aecStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe31_ctrl->ihistStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe31_ctrl->rsStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe31_ctrl->csStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+}
+
+static void vfe31_reset(void)
+{
+ uint32_t vfe_version;
+ vfe31_reset_free_buf_queue_all();
+ vfe31_reset_internal_variables();
+
+ vfe31_reset_hist_cfg();
+ vfe_version = msm_io_r(vfe31_ctrl->vfebase);
+ CDBG("vfe_version = 0x%x\n", vfe_version);
+ /* disable all interrupts. vfeImaskLocal is also reset to 0
+ * to begin with. */
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_io_w(VFE_CLEAR_ALL_IRQS, vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(VFE_CLEAR_ALL_IRQS, vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+ /* enable reset_ack interrupt. */
+ msm_io_w(VFE_IMASK_RESET,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+ * is done, hardware interrupt will be generated. VFE ist processes
+ * the interrupt to complete the function call. Note that the reset
+ * function is synchronous. */
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(VFE_RESET_UPON_RESET_CMD,
+ vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe31_operation_config(uint32_t *cmd)
+{
+ uint32_t *p = cmd;
+
+ vfe31_ctrl->operation_mode = *p;
+ vpe_ctrl->pad_2k_bool = (vfe31_ctrl->operation_mode & 1) ?
+ FALSE : TRUE;
+
+ vfe31_ctrl->stats_comp = *(++p);
+ vfe31_ctrl->hfr_mode = *(++p);
+
+ msm_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CFG_OFF);
+ msm_io_w(*(++p), vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_w(*(++p), vfe31_ctrl->vfebase + VFE_REALIGN_BUF);
+ msm_io_w(*(++p), vfe31_ctrl->vfebase + VFE_CHROMA_UP);
+ msm_io_w(*(++p), vfe31_ctrl->vfebase + VFE_STATS_CFG);
+ wmb();
+ return 0;
+}
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PONG_ADDR);
+
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PONG_ADDR);
+
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static void vfe31_start_common(void)
+{
+ uint32_t irq_mask = 0x00E00021;
+ vfe31_ctrl->start_ack_pending = TRUE;
+ CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
+ vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+ /* Enable IRQ for comp stats, Image master, SOF & Reg Update*/
+ if (vfe31_ctrl->stats_comp)
+ irq_mask |= 0x01000000;
+ else /* Enable IRQ for Image masters, AF stats, SOF & Reg Update */
+ irq_mask |= 0x00004000;
+
+ /* Enable EOF for video mode */
+ if (VFE_MODE_OF_OPERATION_VIDEO == vfe31_ctrl->operation_mode)
+ irq_mask |= 0x4;
+
+ msm_io_w(irq_mask, vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+ msm_io_w(VFE_IMASK_RESET,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+ /* enable out of order option */
+ msm_io_w(0x80000000, vfe31_ctrl->vfebase + VFE_AXI_CFG);
+ /* enable performance monitor */
+ msm_io_w(1, vfe31_ctrl->vfebase + VFE_BUS_PM_CFG);
+ msm_io_w(1, vfe31_ctrl->vfebase + VFE_BUS_PM_CMD);
+
+
+ msm_io_dump(vfe31_ctrl->vfebase, 0x600);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ msm_io_w(1, vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+ wmb();
+
+ atomic_set(&vfe31_ctrl->vstate, 1);
+}
+
+static int vfe31_start_recording(void)
+{
+ msm_camio_set_perf_lvl(S_VIDEO);
+ usleep(1000);
+ vfe31_ctrl->recording_state = VFE_REC_STATE_START_REQUESTED;
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ return 0;
+}
+
+static int vfe31_stop_recording(void)
+{
+ vfe31_ctrl->recording_state = VFE_REC_STATE_STOP_REQUESTED;
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ msm_camio_set_perf_lvl(S_PREVIEW);
+ return 0;
+}
+
+static void vfe31_liveshot(void)
+{
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+ if (p_sync)
+ p_sync->liveshot_enabled = true;
+}
+
+static void vfe31_stereocam(uint32_t enable)
+{
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+ if (p_sync) {
+ CDBG("%s: Enable StereoCam %d!!!\n", __func__, enable);
+ p_sync->stereocam_enabled = enable;
+ }
+}
+
+static int vfe31_zsl(void)
+{
+ uint32_t irq_comp_mask = 0;
+ /* capture command is valid for both idle and active state. */
+ irq_comp_mask =
+ msm_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ CDBG("%s:op mode %d O/P Mode %d\n", __func__,
+ vfe31_ctrl->operation_mode, vfe31_ctrl->outpath.output_mode);
+ if ((vfe31_ctrl->operation_mode == VFE_MODE_OF_OPERATION_ZSL)) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_P) {
+ irq_comp_mask |=
+ ((0x1 << (vfe31_ctrl->outpath.out0.ch0)) |
+ (0x1 << (vfe31_ctrl->outpath.out0.ch1)));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_T) {
+ irq_comp_mask |=
+ ((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+ (0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ ((0x1 << (vfe31_ctrl->outpath.out2.ch0 + 8)) |
+ (0x1 << (vfe31_ctrl->outpath.out2.ch1 + 8)));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_P) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_T) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+ }
+ }
+ msm_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+ vfe31_start_common();
+ msm_camio_set_perf_lvl(S_ZSL);
+ usleep(1000);
+ /* for debug */
+ msm_io_w(1, vfe31_ctrl->vfebase + 0x18C);
+ msm_io_w(1, vfe31_ctrl->vfebase + 0x188);
+ return 0;
+}
+
+static int vfe31_capture(uint32_t num_frames_capture)
+{
+ uint32_t irq_comp_mask = 0;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+ /* capture command is valid for both idle and active state. */
+ vfe31_ctrl->vfe_capture_count = num_frames_capture;
+ if (p_sync) {
+ p_sync->snap_count = num_frames_capture;
+ p_sync->thumb_count = num_frames_capture;
+ }
+
+ irq_comp_mask =
+ msm_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ if ((vfe31_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT) ||
+ (vfe31_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_ZSL)){
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ irq_comp_mask |=
+ ((0x1 << (vfe31_ctrl->outpath.out0.ch0 + 8)) |
+ (0x1 << (vfe31_ctrl->outpath.out0.ch1 + 8)));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ ((0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)) |
+ (0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)));
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+ }
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch1]);
+ }
+ } else { /* this is raw snapshot mode. */
+ CDBG("config the comp imask for raw snapshot mode.\n");
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8));
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out1.ch0]);
+ }
+ }
+ msm_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+ if (p_sync->stereocam_enabled)
+ msm_camio_set_perf_lvl(S_STEREO_CAPTURE);
+ else
+ msm_camio_set_perf_lvl(S_CAPTURE);
+
+ usleep(1000);
+ vfe31_start_common();
+ return 0;
+}
+
+static int vfe31_start(void)
+{
+ uint32_t irq_comp_mask = 0;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+ /* start command now is only good for continuous mode. */
+ if ((vfe31_ctrl->operation_mode != VFE_MODE_OF_OPERATION_CONTINUOUS) &&
+ (vfe31_ctrl->operation_mode != VFE_MODE_OF_OPERATION_VIDEO))
+ return 0;
+ irq_comp_mask =
+ msm_io_r(vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 |
+ 0x1 << vfe31_ctrl->outpath.out0.ch1);
+ }
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out2.ch0 + 16)|
+ 0x1 << (vfe31_ctrl->outpath.out2.ch1 + 16));
+ }
+
+ msm_io_w(irq_comp_mask, vfe31_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out0.ch1]);
+ }
+ if (p_sync->stereocam_enabled)
+ msm_camio_set_perf_lvl(S_STEREO_VIDEO);
+ else
+ msm_camio_set_perf_lvl(S_PREVIEW);
+
+ usleep(1000);
+ vfe31_start_common();
+ return 0;
+}
+
+static void vfe31_update(void)
+{
+ unsigned long flags;
+ CDBG("vfe31_update\n");
+
+ if (vfe31_ctrl->update_gamma) {
+ if (!msm_io_r(vfe31_ctrl->vfebase + V31_GAMMA_CFG_OFF))
+ msm_io_w(7, vfe31_ctrl->vfebase+V31_GAMMA_CFG_OFF);
+ else
+ msm_io_w(0, vfe31_ctrl->vfebase+V31_GAMMA_CFG_OFF);
+ vfe31_ctrl->update_gamma = false;
+ }
+ if (vfe31_ctrl->update_luma) {
+ if (!msm_io_r(vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF))
+ msm_io_w(1, vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+ else
+ msm_io_w(0, vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+ vfe31_ctrl->update_luma = false;
+ }
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ vfe31_ctrl->update_ack_pending = TRUE;
+ spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags);
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ return;
+}
+
+static void vfe31_sync_timer_stop(void)
+{
+ uint32_t value = 0;
+ vfe31_ctrl->sync_timer_state = 0;
+ if (vfe31_ctrl->sync_timer_number == 0)
+ value = 0x10000;
+ else if (vfe31_ctrl->sync_timer_number == 1)
+ value = 0x20000;
+ else if (vfe31_ctrl->sync_timer_number == 2)
+ value = 0x40000;
+
+ /* Timer Stop */
+ msm_io_w_mb(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+}
+
+static void vfe31_sync_timer_start(const uint32_t *tbl)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = 1;
+ uint32_t val;
+
+ vfe31_ctrl->sync_timer_state = *tbl++;
+ vfe31_ctrl->sync_timer_repeat_count = *tbl++;
+ vfe31_ctrl->sync_timer_number = *tbl++;
+ CDBG("%s timer_state %d, repeat_cnt %d timer number %d\n",
+ __func__, vfe31_ctrl->sync_timer_state,
+ vfe31_ctrl->sync_timer_repeat_count,
+ vfe31_ctrl->sync_timer_number);
+
+ if (vfe31_ctrl->sync_timer_state) { /* Start Timer */
+ value = value << vfe31_ctrl->sync_timer_number;
+ } else { /* Stop Timer */
+ CDBG("Failed to Start timer\n");
+ return;
+ }
+
+ /* Timer Start */
+ msm_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF);
+ /* Sync Timer Line Start */
+ value = *tbl++;
+ msm_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+ 4 + ((vfe31_ctrl->sync_timer_number) * 12));
+ /* Sync Timer Pixel Start */
+ value = *tbl++;
+ msm_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+ 8 + ((vfe31_ctrl->sync_timer_number) * 12));
+ /* Sync Timer Pixel Duration */
+ value = *tbl++;
+ val = camio_clk.vfe_clk_rate / 10000;
+ val = 10000000 / val;
+ val = value * 10000 / val;
+ CDBG("%s: Pixel Clk Cycles!!! %d \n", __func__, val);
+ msm_io_w(val, vfe31_ctrl->vfebase + V31_SYNC_TIMER_OFF +
+ 12 + ((vfe31_ctrl->sync_timer_number) * 12));
+ /* Timer0 Active High/LOW */
+ value = *tbl++;
+ msm_io_w(value, vfe31_ctrl->vfebase + V31_SYNC_TIMER_POLARITY_OFF);
+ /* Selects sync timer 0 output to drive onto timer1 port */
+ value = 0;
+ msm_io_w(value, vfe31_ctrl->vfebase + V31_TIMER_SELECT_OFF);
+ wmb();
+}
+
+static void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = VFE_DMI_CFG_DEFAULT;
+ value += (uint32_t)bankSel;
+
+ msm_io_w_mb(value, vfe31_ctrl->vfebase + VFE_DMI_CFG);
+ /* by default, always starts with offset 0.*/
+ msm_io_w(0, vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+ wmb();
+}
+static void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+ const uint32_t *tbl)
+{
+ int i;
+ uint32_t value, value1, value2;
+ vfe31_program_dmi_cfg(channel_sel);
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) {
+ value = *tbl++;
+ value1 = value & 0x0000FFFF;
+ value2 = (value & 0xFFFF0000)>>16;
+ msm_io_w((value1), vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ msm_io_w((value2), vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_reset_hist_cfg()
+{
+ uint32_t i;
+ uint32_t value = 0;
+
+ vfe31_program_dmi_cfg(STATS_HIST_RAM);
+ for (i = 0 ; i < VFE31_HIST_TABLE_LENGTH ; i++)
+ msm_io_w(value, vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe31_write_la_cfg(enum VFE31_DMI_RAM_SEL channel_sel,
+ const uint32_t *tbl)
+{
+ uint32_t i;
+ uint32_t value, value1, value2;
+
+ vfe31_program_dmi_cfg(channel_sel);
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < (VFE31_LA_TABLE_LENGTH/2) ; i++) {
+ value = *tbl++;
+ value1 = value & 0x0000FFFF;
+ value2 = (value & 0xFFFF0000)>>16;
+ msm_io_w((value1), vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ msm_io_w((value2), vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static int vfe31_proc_general(struct msm_vfe31_cmd *cmd)
+{
+ int i , rc = 0;
+ uint32_t old_val = 0 , new_val = 0;
+ uint32_t *cmdp = NULL;
+ uint32_t *cmdp_local = NULL;
+ uint32_t snapshot_cnt = 0;
+ uint32_t stereo_cam_enable = 0;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+ CDBG("vfe31_proc_general: cmdID = %s, length = %d\n",
+ vfe31_general_cmd[cmd->id], cmd->length);
+ switch (cmd->id) {
+ case V31_RESET:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ vfe31_reset();
+ break;
+ case V31_START:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ rc = vfe31_start();
+ break;
+ case V31_UPDATE:
+ vfe31_update();
+ break;
+ case V31_ZSL:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ vfe31_zsl();
+ break;
+ case V31_CAPTURE:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+ sizeof(uint32_t))) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe31_capture(snapshot_cnt);
+ break;
+ case V31_START_RECORDING:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ rc = vfe31_start_recording();
+ if (p_sync->stereocam_enabled)
+ p_sync->stereo_state = STEREO_VIDEO_ACTIVE;
+ break;
+ case V31_STOP_RECORDING:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ rc = vfe31_stop_recording();
+ if (p_sync->stereocam_enabled)
+ p_sync->stereo_state = STEREO_VIDEO_IDLE;
+ break;
+ case V31_OPERATION_CFG: {
+ if (cmd->length != V31_OPERATION_CFG_LEN) {
+ rc = -EINVAL;
+ goto proc_general_done;
+ }
+ cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_ATOMIC);
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ V31_OPERATION_CFG_LEN)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe31_operation_config(cmdp);
+ }
+ break;
+
+ case V31_STATS_AE_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AE_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+ case V31_STATS_AF_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AF_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+ case V31_STATS_AWB_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AWB_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ case V31_STATS_IHIST_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= IHIST_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ case V31_XBAR_CFG: {
+ unsigned long flags = 0;
+ spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+ if ((cmd->length != V31_XBAR_CFG_LEN)
+ || vfe31_ctrl->xbar_update_pending) {
+ rc = -EINVAL;
+ spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+ goto proc_general_done;
+ }
+ spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+ vfe31_ctrl->xbar_cfg[0] = *cmdp;
+ vfe31_ctrl->xbar_cfg[1] = *(cmdp+1);
+ vfe31_ctrl->xbar_update_pending = 1;
+ spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+ CDBG("%s: xbar0 0x%x xbar1 0x%x", __func__,
+ vfe31_ctrl->xbar_cfg[0],
+ vfe31_ctrl->xbar_cfg[1]);
+ }
+ break;
+
+ case V31_STATS_RS_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /*
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= RS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ */
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ case V31_STATS_CS_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /*
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ */
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ case V31_MCE_UPDATE:
+ case V31_MCE_CFG:{
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ /* Incrementing with 4 so as to point to the 2nd Register as
+ the 2nd register has the mce_enable bit */
+ old_val = msm_io_r(vfe31_ctrl->vfebase +
+ V31_CHROMA_SUP_OFF + 4);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+ old_val &= MCE_EN_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 4,
+ &new_val, 4);
+ cmdp_local += 1;
+
+ old_val = msm_io_r(vfe31_ctrl->vfebase +
+ V31_CHROMA_SUP_OFF + 8);
+ new_val = *cmdp_local;
+ old_val &= MCE_Q_K_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_SUP_OFF + 8,
+ &new_val, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+ case V31_DEMOSAIC_2_UPDATE: /* 38 BPC update */
+ case V31_DEMOSAIC_2_CFG: { /* 14 BPC config */
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = msm_io_r(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF);
+ old_val &= BPC_MASK;
+
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+ cmdp_local, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+ case V31_DEMOSAIC_1_UPDATE:/* 37 ABF update */
+ case V31_DEMOSAIC_1_CFG: { /* 13 ABF config */
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = msm_io_r(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF);
+ old_val &= ABF_MASK;
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF,
+ cmdp_local, 4);
+
+ cmdp_local += 1;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+ case V31_ROLL_OFF_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value) , cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp_local, 16);
+ cmdp_local += 4;
+ vfe31_program_dmi_cfg(ROLLOFF_RAM);
+ /* for loop for extrcting init table. */
+ for (i = 0 ; i < (VFE31_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) {
+ msm_io_w(*cmdp_local ,
+ vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ CDBG("done writing init table \n");
+ /* by default, always starts with offset 0. */
+ msm_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET,
+ vfe31_ctrl->vfebase + VFE_DMI_ADDR);
+ /* for loop for extracting delta table. */
+ for (i = 0 ; i < (VFE31_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) {
+ msm_io_w(*cmdp_local,
+ vfe31_ctrl->vfebase + VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ vfe31_program_dmi_cfg(NO_MEM_SELECTED);
+ }
+ break;
+
+ case V31_LA_CFG:{
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /* Select Bank 0*/
+ *cmdp = 0;
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ cmdp += 1;
+ vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0 , cmdp);
+ cmdp -= 1;
+ }
+ break;
+
+ case V31_LA_UPDATE: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + V31_LUMA_CFG_OFF);
+ cmdp += 1;
+ if (old_val != 0x0)
+ vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0 , cmdp);
+ else
+ vfe31_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK1 , cmdp);
+ vfe31_ctrl->update_luma = true;
+ cmdp -= 1;
+ }
+ break;
+
+ case V31_SK_ENHAN_CFG:
+ case V31_SK_ENHAN_UPDATE:{
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_SCE_OFF,
+ cmdp, V31_SCE_LEN);
+ }
+ break;
+
+ case V31_LIVESHOT:
+ vfe31_liveshot();
+ break;
+
+ case V31_STEREOCAM:
+ if (copy_from_user(&stereo_cam_enable,
+ (void __user *)(cmd->value), sizeof(uint32_t))) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ vfe31_stereocam(stereo_cam_enable);
+ break;
+
+ case V31_RGB_G_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /* Select Bank 0*/
+ *cmdp = 0;
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF,
+ cmdp, 4);
+ cmdp += 1;
+ vfe31_write_gamma_cfg(RGBLUT_CHX_BANK0, cmdp);
+ cmdp -= 1;
+ }
+ break;
+
+ case V31_RGB_G_UPDATE: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe31_ctrl->vfebase + V31_GAMMA_CFG_OFF);
+ cmdp += 1;
+
+ if (!old_val) {
+ vfe31_write_gamma_cfg(RGBLUT_CHX_BANK1, cmdp);
+ } else {
+ vfe31_write_gamma_cfg(RGBLUT_CHX_BANK0, cmdp);
+ }
+ vfe31_ctrl->update_gamma = true;
+ cmdp -= 1;
+ }
+ break;
+
+ case V31_STATS_AWB_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AWB_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V31_STATS_AE_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AE_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V31_STATS_AF_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AF_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V31_STATS_IHIST_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~IHIST_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V31_STATS_RS_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~RS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V31_STATS_CS_STOP: {
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V31_STOP:
+ pr_info("vfe31_proc_general: cmdID = %s\n",
+ vfe31_general_cmd[cmd->id]);
+ vfe31_stop();
+ break;
+
+ case V31_SYNC_TIMER_SETTING:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ vfe31_sync_timer_start(cmdp);
+ break;
+
+ case V31_EZTUNE_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ *cmdp &= ~STATS_ENABLE_MASK;
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= STATS_ENABLE_MASK;
+ *cmdp |= old_val;
+
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ default: {
+ if (cmd->length != vfe31_cmd[cmd->id].length)
+ return -EINVAL;
+
+ cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+
+ CHECKED_COPY_FROM_USER(cmdp);
+ msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset,
+ cmdp, (vfe31_cmd[cmd->id].length));
+ }
+ break;
+
+ }
+
+proc_general_done:
+ kfree(cmdp);
+
+ return rc;
+}
+
+static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->af_ack_pending = FALSE;
+}
+
+static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->awb_ack_pending = FALSE;
+}
+
+static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->aec_ack_pending = FALSE;
+}
+
+static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->ihist_ack_pending = FALSE;
+}
+
+static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->rs_ack_pending = FALSE;
+}
+
+static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe31_ctrl->cs_ack_pending = FALSE;
+}
+
+static int vfe31_config(struct msm_vfe_cfg_cmd *cmd, void *data)
+{
+ struct msm_vfe31_cmd vfecmd;
+
+ long rc = 0;
+ uint32_t i = 0;
+ struct vfe_cmd_stats_buf *scfg = NULL;
+ struct msm_pmem_region *regptr = NULL;
+ struct vfe_cmd_stats_ack *sack = NULL;
+
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_SNAP_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n", __func__,
+ __LINE__);
+ return -EFAULT;
+ }
+ } else {
+ /* here eith stats release or frame release. */
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_SNAP_BUF_RELEASE) {
+ /* then must be stats release. */
+ if (!data)
+ return -EFAULT;
+ sack = kmalloc(sizeof(struct vfe_cmd_stats_ack),
+ GFP_ATOMIC);
+ if (!sack)
+ return -ENOMEM;
+
+ sack->nextStatsBuf = *(uint32_t *)data;
+ }
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+ if ((cmd->cmd_type == CMD_STATS_AF_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AWB_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_RS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_CS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AEC_ENABLE)) {
+ struct axidata *axid;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto vfe31_config_done;
+ }
+
+ scfg =
+ kmalloc(sizeof(struct vfe_cmd_stats_buf),
+ GFP_ATOMIC);
+ if (!scfg) {
+ rc = -ENOMEM;
+ goto vfe31_config_done;
+ }
+ regptr = axid->region;
+ if (axid->bufnum1 > 0) {
+ for (i = 0; i < axid->bufnum1; i++) {
+ scfg->statsBuf[i] =
+ (uint32_t)(regptr->paddr);
+ regptr++;
+ }
+ }
+ /* individual */
+ switch (cmd->cmd_type) {
+ case CMD_STATS_AEC_ENABLE:
+ rc = vfe_stats_aec_buf_init(scfg);
+ break;
+ case CMD_STATS_AF_ENABLE:
+ rc = vfe_stats_af_buf_init(scfg);
+ break;
+ case CMD_STATS_AWB_ENABLE:
+ rc = vfe_stats_awb_buf_init(scfg);
+ break;
+ case CMD_STATS_IHIST_ENABLE:
+ rc = vfe_stats_ihist_buf_init(scfg);
+ break;
+ case CMD_STATS_RS_ENABLE:
+ rc = vfe_stats_rs_buf_init(scfg);
+ break;
+ case CMD_STATS_CS_ENABLE:
+ rc = vfe_stats_cs_buf_init(scfg);
+ break;
+ }
+ }
+
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe31_proc_general(&vfecmd);
+ break;
+
+ case CMD_FRAME_BUF_RELEASE: {
+ struct msm_frame *b;
+ unsigned long p;
+ int ret;
+ struct vfe31_output_ch *outch = NULL;
+ if (!data) {
+ rc = -EFAULT;
+ break;
+ }
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path);
+
+ if (b->path & OUTPUT_TYPE_P) {
+ CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+ outch = &vfe31_ctrl->outpath.out0;
+ } else if (b->path & OUTPUT_TYPE_S) {
+ outch = &vfe31_ctrl->outpath.out1;
+ } else if (b->path & OUTPUT_TYPE_V) {
+ outch = &vfe31_ctrl->outpath.out2;
+ } else {
+ rc = -EFAULT;
+ break;
+ }
+
+ ret = vfe31_add_free_buf2(outch, p, b->y_off, b->cbcr_off);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ case CMD_SNAP_BUF_RELEASE: {
+ struct msm_frame *b;
+ unsigned long p;
+ int ret;
+ struct vfe31_output_ch *outch = NULL;
+ if (!data)
+ return -EFAULT;
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ CDBG("CMD_PIC_BUF_RELEASE b->path = %d\n", b->path);
+
+ if (b->path & OUTPUT_TYPE_T) {
+ CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+ outch = &vfe31_ctrl->outpath.out1;
+ } else if (b->path & OUTPUT_TYPE_S) {
+ outch = &vfe31_ctrl->outpath.out2;
+ } else
+ return -EFAULT;
+
+ ret = vfe31_add_free_buf2(outch, p, b->y_off, b->cbcr_off);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ case CMD_STATS_AEC_BUF_RELEASE:
+ vfe31_stats_aec_ack(sack);
+ break;
+
+ case CMD_STATS_AF_BUF_RELEASE:
+ vfe31_stats_af_ack(sack);
+ break;
+
+ case CMD_STATS_AWB_BUF_RELEASE:
+ vfe31_stats_awb_ack(sack);
+ break;
+
+ case CMD_STATS_IHIST_BUF_RELEASE:
+ vfe31_stats_ihist_ack(sack);
+ break;
+
+ case CMD_STATS_RS_BUF_RELEASE:
+ vfe31_stats_rs_ack(sack);
+ break;
+
+ case CMD_STATS_CS_BUF_RELEASE:
+ vfe31_stats_cs_ack(sack);
+ break;
+
+ case CMD_AXI_CFG_PREVIEW: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+ axio =
+ kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe31_config_axi(OUTPUT_2, axid, axio);
+ kfree(axio);
+ break;
+ }
+
+ case CMD_RAW_PICT_AXI_CFG: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+ axio =
+ kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe31_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio);
+ kfree(axio);
+ break;
+ }
+
+ case CMD_AXI_CFG_SNAP: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ CDBG("%s, CMD_AXI_CFG_SNAP\n", __func__);
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio =
+ kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe31_config_axi(OUTPUT_1_AND_2, axid, axio);
+ kfree(axio);
+ break;
+ }
+
+ case CMD_AXI_CFG_ZSL: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ CDBG("%s, CMD_AXI_CFG_ZSL\n", __func__);
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio =
+ kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe31_config_axi(OUTPUT_1_2_AND_3, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+
+ axio =
+ kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe31_cmd[V31_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe31_config_axi(OUTPUT_1_AND_3, axid, axio);
+ kfree(axio);
+ break;
+ }
+
+ default:
+ break;
+ }
+vfe31_config_done:
+ kfree(scfg);
+ kfree(sack);
+ CDBG("%s done: rc = %d\n", __func__, (int) rc);
+ return rc;
+}
+
+static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id)
+{
+ struct vfe_message msg;
+
+ CDBG("vfe31_send_msg_no_payload\n");
+ msg._d = id;
+ vfe31_proc_ops(id, &msg, 0);
+}
+
+static void vfe31_process_reg_update_irq(void)
+{
+ uint32_t temp, old_val;
+ unsigned long flags;
+ if (vfe31_ctrl->recording_state == VFE_REC_STATE_START_REQUESTED) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+ msm_io_w(1, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+ }
+ vfe31_ctrl->recording_state = VFE_REC_STATE_STARTED;
+ if (vpe_ctrl->dis_en) {
+ old_val = msm_io_r(
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= RS_CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ CDBG("start video triggered .\n");
+ } else if (vfe31_ctrl->recording_state
+ == VFE_REC_STATE_STOP_REQUESTED) {
+ if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) {
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch0]);
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->outpath.out2.ch1]);
+ }
+
+ /*disable rs& cs when stop recording. */
+ old_val = msm_io_r(vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= (~RS_CS_ENABLE_MASK);
+ msm_io_w(old_val,
+ vfe31_ctrl->vfebase + VFE_MODULE_CFG);
+ CDBG("stop video triggered\n");
+ }
+ if (vfe31_ctrl->start_ack_pending == TRUE) {
+ vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe31_ctrl->start_ack_pending = FALSE;
+ } else {
+ if (vfe31_ctrl->recording_state ==
+ VFE_REC_STATE_STOP_REQUESTED) {
+ vfe31_ctrl->recording_state = VFE_REC_STATE_STOPPED;
+ msm_io_w_mb(1, vfe31_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
+ } else if (vfe31_ctrl->recording_state ==
+ VFE_REC_STATE_STOPPED) {
+ CDBG("sent stop video rec ACK");
+ vfe31_send_msg_no_payload(MSG_ID_STOP_REC_ACK);
+ vfe31_ctrl->recording_state = VFE_REC_STATE_IDLE;
+ }
+ spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags);
+ if (vfe31_ctrl->update_ack_pending == TRUE) {
+ vfe31_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(
+ &vfe31_ctrl->update_ack_lock, flags);
+ vfe31_send_msg_no_payload(MSG_ID_UPDATE_ACK);
+ } else {
+ spin_unlock_irqrestore(
+ &vfe31_ctrl->update_ack_lock, flags);
+ }
+ }
+ /* in snapshot mode */
+ if (vfe31_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT) {
+ /* later we need to add check for live snapshot mode. */
+
+ if (vfe31_ctrl->vfe_capture_count)
+ vfe31_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ /* stop the bus output: write master enable = 0*/
+ if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_PT) {
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[
+ vfe31_ctrl->outpath.out0.ch0]);
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->
+ outpath.out0.ch1]);
+ }
+ if (vfe31_ctrl->outpath.output_mode &
+ VFE31_OUTPUT_MODE_S) {
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->
+ outpath.out1.ch0]);
+ msm_io_w(0, vfe31_ctrl->vfebase +
+ vfe31_AXI_WM_CFG[vfe31_ctrl->
+ outpath.out1.ch1]);
+ }
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+ /* Ensure the read order while reading
+ to the command register using the barrier */
+ temp = msm_io_r_mb(vfe31_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+ /* then do reg_update. */
+ msm_io_w_mb(1, vfe31_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
+ } /* if snapshot mode. */
+}
+
+static void vfe31_set_default_reg_values(void)
+{
+ msm_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_0);
+ msm_io_w(0x800080, vfe31_ctrl->vfebase + VFE_DEMUX_GAIN_1);
+ msm_io_w(0xFFFFF, vfe31_ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+ /* default frame drop period and pattern */
+ msm_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+ msm_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+ msm_io_w(0xFFFFFFFF, vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+ msm_io_w(0xFFFFFFFF,
+ vfe31_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+ msm_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y);
+ msm_io_w(0x1f, vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR);
+ msm_io_w(0xFFFFFFFF,
+ vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+ msm_io_w(0xFFFFFFFF,
+ vfe31_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+ msm_io_w(0, vfe31_ctrl->vfebase + VFE_CLAMP_MIN);
+ msm_io_w(0xFFFFFF, vfe31_ctrl->vfebase + VFE_CLAMP_MAX);
+
+ /* stats UB config */
+ msm_io_w(0x3980007, vfe31_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
+ msm_io_w(0x3A00007, vfe31_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
+ msm_io_w(0x3A8000F, vfe31_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
+ msm_io_w(0x3B80007, vfe31_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
+ msm_io_w(0x3C0001F, vfe31_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
+ msm_io_w(0x3E0001F, vfe31_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+}
+
+static void vfe31_process_reset_irq(void)
+{
+ atomic_set(&vfe31_ctrl->vstate, 0);
+ vfe31_ctrl->while_stopping_mask = VFE_IMASK_WHILE_STOPPING_1;
+ if (atomic_read(&vfe31_ctrl->stop_ack_pending)) {
+ /* this is from the stop command. */
+ atomic_set(&vfe31_ctrl->stop_ack_pending, 0);
+ vfe31_send_msg_no_payload(MSG_ID_STOP_ACK);
+ } else {
+ /* this is from reset command. */
+ vfe31_set_default_reg_values();
+
+ /* reload all write masters. (frame & line)*/
+ msm_io_w_mb(0x7FFF, vfe31_ctrl->vfebase + VFE_BUS_CMD);
+ vfe31_send_msg_no_payload(MSG_ID_RESET_ACK);
+ }
+}
+
+
+static void vfe31_process_axi_halt_irq(void)
+{
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(AXI_HALT_CLEAR,
+ vfe31_ctrl->vfebase + VFE_AXI_CMD);
+ vfe31_ctrl->while_stopping_mask = VFE_IMASK_RESET;
+
+ /* disable all interrupts. */
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1,
+ vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+ /* now enable only halt_irq & reset_irq */
+ msm_io_w(0xf0000000, /* this is for async timer. */
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_IMASK_RESET,
+ vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ CDBG("%s: about to reset vfe...\n", __func__);
+ msm_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+ vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
+
+}
+
+static void vfe31_process_camif_sof_irq(void)
+{
+ uint32_t temp;
+
+ /* in raw snapshot mode */
+ if (vfe31_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
+ if (vfe31_ctrl->start_ack_pending) {
+ vfe31_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe31_ctrl->start_ack_pending = FALSE;
+ }
+ if (vfe31_ctrl->vfe_capture_count)
+ vfe31_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ vfe31_ctrl->vfebase + VFE_CAMIF_COMMAND);
+ temp = msm_io_r_mb(vfe31_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+ } /* if raw snapshot mode. */
+
+ if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+ (vfe31_ctrl->operation_mode == VFE_MODE_OF_OPERATION_VIDEO) &&
+ (vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+ vfe31_ctrl->vfeFrameId++;
+ CDBG("Skip the SOF notification when HFR enabled\n");
+ return;
+ }
+ vfe31_send_msg_no_payload(MSG_ID_SOF_ACK);
+ vfe31_ctrl->vfeFrameId++;
+ CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId);
+
+ if (vfe31_ctrl->sync_timer_state) {
+ if (vfe31_ctrl->sync_timer_repeat_count == 0)
+ vfe31_sync_timer_stop();
+ else
+ vfe31_ctrl->sync_timer_repeat_count--;
+ }
+}
+
+static void vfe31_process_error_irq(uint32_t errStatus)
+{
+ uint32_t camifStatus, read_val;
+ uint32_t *temp;
+
+ if (errStatus & VFE31_IMASK_CAMIF_ERROR) {
+ pr_err("vfe31_irq: camif errors\n");
+ temp = (uint32_t *)(vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+ camifStatus = msm_io_r(temp);
+ pr_err("camifStatus = 0x%x\n", camifStatus);
+ vfe31_send_msg_no_payload(MSG_ID_CAMIF_ERROR);
+ }
+
+ if (errStatus & VFE31_IMASK_STATS_CS_OVWR)
+ pr_err("vfe31_irq: stats cs overwrite\n");
+
+ if (errStatus & VFE31_IMASK_STATS_IHIST_OVWR)
+ pr_err("vfe31_irq: stats ihist overwrite\n");
+
+ if (errStatus & VFE31_IMASK_REALIGN_BUF_Y_OVFL)
+ pr_err("vfe31_irq: realign bug Y overflow\n");
+
+ if (errStatus & VFE31_IMASK_REALIGN_BUF_CB_OVFL)
+ pr_err("vfe31_irq: realign bug CB overflow\n");
+
+ if (errStatus & VFE31_IMASK_REALIGN_BUF_CR_OVFL)
+ pr_err("vfe31_irq: realign bug CR overflow\n");
+
+ if (errStatus & VFE31_IMASK_VIOLATION)
+ pr_err("vfe31_irq: violation interrupt\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_0_BUS_OVFL)
+ pr_err("vfe31_irq: image master 0 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_1_BUS_OVFL)
+ pr_err("vfe31_irq: image master 1 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_2_BUS_OVFL)
+ pr_err("vfe31_irq: image master 2 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_3_BUS_OVFL)
+ pr_err("vfe31_irq: image master 3 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_4_BUS_OVFL)
+ pr_err("vfe31_irq: image master 4 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_5_BUS_OVFL)
+ pr_err("vfe31_irq: image master 5 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_IMG_MAST_6_BUS_OVFL)
+ pr_err("vfe31_irq: image master 6 bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_AE_BUS_OVFL)
+ pr_err("vfe31_irq: ae stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_AF_BUS_OVFL)
+ pr_err("vfe31_irq: af stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_AWB_BUS_OVFL)
+ pr_err("vfe31_irq: awb stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_RS_BUS_OVFL)
+ pr_err("vfe31_irq: rs stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_CS_BUS_OVFL)
+ pr_err("vfe31_irq: cs stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_IHIST_BUS_OVFL)
+ pr_err("vfe31_irq: ihist stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_STATS_SKIN_BUS_OVFL)
+ pr_err("vfe31_irq: skin stats bus overflow\n");
+
+ if (errStatus & VFE31_IMASK_AXI_ERROR) {
+ pr_err("vfe31_irq: axi error\n");
+ /* read status too when overflow happens.*/
+ read_val = msm_io_r(vfe31_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS);
+ pr_debug("VFE_BUS_PING_PONG_STATUS = 0x%x\n", read_val);
+ read_val = msm_io_r(vfe31_ctrl->vfebase +
+ VFE_BUS_OPERATION_STATUS);
+ pr_debug("VFE_BUS_OPERATION_STATUS = 0x%x\n", read_val);
+ read_val = msm_io_r(vfe31_ctrl->vfebase +
+ VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0);
+ pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0 = 0x%x\n",
+ read_val);
+ read_val = msm_io_r(vfe31_ctrl->vfebase +
+ VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1);
+ pr_debug("VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1 = 0x%x\n",
+ read_val);
+ read_val = msm_io_r(vfe31_ctrl->vfebase +
+ VFE_AXI_STATUS);
+ pr_debug("VFE_AXI_STATUS = 0x%x\n", read_val);
+ }
+}
+
+#define VFE31_AXI_OFFSET 0x0050
+#define vfe31_get_ch_ping_addr(chn) \
+ (msm_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_get_ch_pong_addr(chn) \
+ (msm_io_r(vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_get_ch_addr(ping_pong, chn) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn))
+
+#define vfe31_put_ch_ping_addr(chn, addr) \
+ (msm_io_w((addr), vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe31_put_ch_pong_addr(chn, addr) \
+ (msm_io_w((addr), vfe31_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe31_put_ch_addr(ping_pong, chn, addr) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe31_put_ch_pong_addr((chn), (addr)) : \
+ vfe31_put_ch_ping_addr((chn), (addr)))
+
+static void vfe31_process_output_path_irq_0(uint32_t ping_pong)
+{
+ uint32_t pyaddr, pcbcraddr;
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ struct vfe31_free_buf *free_buf = NULL;
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ */
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out0);
+
+ if (free_buf) {
+ /* Y channel */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1);
+
+ CDBG("output path 0, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+
+ kfree(free_buf);
+ /* if continuous mode, for display. (preview) */
+ vfe_send_outmsg(MSG_ID_OUTPUT_P, pyaddr, pcbcraddr);
+ } else {
+ vfe31_ctrl->outpath.out0.frame_drop_cnt++;
+ pr_warning("path_irq_0 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out0.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out0.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out0.ch0,
+ pyaddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out0.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out0.ch1,
+ pcbcraddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out0.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+}
+
+static void vfe31_process_snapshot_frame(uint32_t ping_pong)
+{
+ uint32_t pyaddr, pcbcraddr;
+ struct vfe31_free_buf *free_buf = NULL;
+ /* Y channel- Main Image */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel - TN Image */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1);
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+ CDBG("%s: snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ __func__, pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ }
+ vfe_send_outmsg(MSG_ID_OUTPUT_S, pyaddr, pcbcraddr);
+
+ /* Y channel- TN Image */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0);
+ /* Chroma channel - TN Image */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1);
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out0);
+ CDBG("%s: snapshot TN, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ __func__, pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out0.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ }
+
+ vfe_send_outmsg(MSG_ID_OUTPUT_T, pyaddr, pcbcraddr);
+
+ /* in snapshot mode if done then send
+ snapshot done message */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE);
+ /* Ensure the write order while writing
+ to the cmd register using barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+ vfe31_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+}
+
+static void vfe31_process_raw_snapshot_frame(uint32_t ping_pong)
+{
+ uint32_t pyaddr, pcbcraddr;
+ struct vfe31_free_buf *free_buf = NULL;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+
+ if (p_sync->stereocam_enabled)
+ p_sync->stereo_state = STEREO_RAW_SNAP_STARTED;
+
+ /* Y channel- Main Image */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel - Main Image */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1);
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+ CDBG("%s: snapshot raw, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ __func__, pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ }
+ vfe_send_outmsg(MSG_ID_OUTPUT_S, pyaddr, pcbcraddr);
+
+ /* in snapshot mode if done then send
+ snapshot done message */
+ if (vfe31_ctrl->vfe_capture_count == 0) {
+ vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE);
+ /* Ensure the write order while writing
+ to the cmd register using barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_IMMEDIATELY,
+ vfe31_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+}
+static void vfe31_process_zsl_frame(uint32_t ping_pong)
+{
+ uint32_t pyaddr, pcbcraddr;
+ struct vfe31_free_buf *free_buf = NULL;
+ /* Y channel- Main Image */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0);
+ /* Chroma channel - Main Image */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1);
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out2);
+ CDBG("%s: snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ __func__, pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ }
+ vfe_send_outmsg(MSG_ID_OUTPUT_S, pyaddr, pcbcraddr);
+
+ /* Y channel- TN Image */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel - TN Image */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1);
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out1);
+ CDBG("%s: snapshot TN, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ __func__, pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out1.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ }
+
+ vfe_send_outmsg(MSG_ID_OUTPUT_T, pyaddr, pcbcraddr);
+}
+
+static void vfe31_process_output_path_irq_1(uint32_t ping_pong)
+{
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ CDBG("%s, operation_mode = %d, cap_cnt = %d\n", __func__,
+ vfe31_ctrl->operation_mode, vfe31_ctrl->vfe_capture_count);
+
+ /* In Snapshot mode */
+ if ((VFE_MODE_OF_OPERATION_SNAPSHOT == vfe31_ctrl->operation_mode)
+ && ((vfe31_ctrl->vfe_capture_count <= 1)
+ || (vfe31_free_buf_available(vfe31_ctrl->outpath.out0) &&
+ vfe31_free_buf_available(vfe31_ctrl->outpath.out1)))) {
+ vfe31_process_snapshot_frame(ping_pong);
+ } else if ((VFE_MODE_OF_OPERATION_RAW_SNAPSHOT ==
+ vfe31_ctrl->operation_mode) &&
+ ((vfe31_ctrl->vfe_capture_count <= 1) ||
+ vfe31_free_buf_available(vfe31_ctrl->outpath.out1))) {
+ vfe31_process_raw_snapshot_frame(ping_pong);
+ } else if ((VFE_MODE_OF_OPERATION_ZSL == vfe31_ctrl->operation_mode)
+ && (vfe31_free_buf_available(vfe31_ctrl->outpath.out1)
+ && vfe31_free_buf_available(vfe31_ctrl->outpath.out2))) {
+ vfe31_process_zsl_frame(ping_pong);
+ } else {
+ vfe31_ctrl->outpath.out1.frame_drop_cnt++;
+ pr_info("path_irq_1 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out1.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out1.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out1.ch0,
+ pyaddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out1.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out1.ch1,
+ pcbcraddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out1.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+
+}
+
+static void vfe31_process_output_path_irq_2(uint32_t ping_pong)
+{
+ uint32_t pyaddr, pcbcraddr;
+ struct vfe31_free_buf *free_buf = NULL;
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ */
+ CDBG("%s, operation_mode = %d, state %d\n", __func__,
+ vfe31_ctrl->operation_mode,
+ vfe31_ctrl->recording_state);
+ /* Ensure that both wm1 and wm5 ping and pong buffers are active*/
+ if (!(((ping_pong & 0x22) == 0x22) ||
+ ((ping_pong & 0x22) == 0x0))) {
+ pr_err(" Irq_2 - skip the frame pp_status is not proper"
+ "PP_status = 0x%x\n", ping_pong);
+ return;
+ }
+ if (vfe31_ctrl->recording_state == VFE_REC_STATE_STOPPED) {
+ vfe31_ctrl->outpath.out2.frame_drop_cnt++;
+ pr_warning("path_irq_2 - recording stopped\n");
+ return;
+ }
+
+ free_buf = vfe31_get_free_buf(&vfe31_ctrl->outpath.out2);
+
+ if (free_buf) {
+ /* Y channel */
+ pyaddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe31_get_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1);
+
+ CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+
+ /* Y channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe31_put_ch_addr(ping_pong,
+ vfe31_ctrl->outpath.out2.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ kfree(free_buf);
+ vfe_send_outmsg(MSG_ID_OUTPUT_V, pyaddr, pcbcraddr);
+ } else {
+ vfe31_ctrl->outpath.out2.frame_drop_cnt++;
+ pr_warning("path_irq_2 - no free buffer!\n");
+
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe31_get_ch_ping_addr(
+ vfe31_ctrl->outpath.out2.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe31_get_ch_pong_addr(
+ vfe31_ctrl->outpath.out2.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out2.ch0,
+ pyaddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out2.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe31_put_ch_ping_addr(vfe31_ctrl->outpath.out2.ch1,
+ pcbcraddr_pong);
+ vfe31_put_ch_pong_addr(vfe31_ctrl->outpath.out2.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+}
+
+
+static uint32_t vfe31_process_stats_irq_common(uint32_t statsNum,
+ uint32_t newAddr) {
+
+ uint32_t pingpongStatus;
+ uint32_t returnAddr;
+ uint32_t pingpongAddr;
+
+ /* must be 0=ping, 1=pong */
+ pingpongStatus =
+ ((msm_io_r(vfe31_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS))
+ & ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+ /* stats bits starts at 7 */
+ CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+ pingpongAddr =
+ ((uint32_t)(vfe31_ctrl->vfebase +
+ VFE_BUS_STATS_PING_PONG_BASE)) +
+ (3*statsNum)*4 + (1-pingpongStatus)*4;
+ returnAddr = msm_io_r((uint32_t *)pingpongAddr);
+ msm_io_w(newAddr, (uint32_t *)pingpongAddr);
+ return returnAddr;
+}
+
+static void vfe_send_stats_msg(void)
+{
+ struct vfe_message msg;
+ /* fill message with right content. */
+ msg._u.msgStats.frameCounter = vfe31_ctrl->vfeFrameId;
+ msg._u.msgStats.status_bits = vfe31_ctrl->status_bits;
+ msg._d = MSG_ID_COMMON;
+
+ msg._u.msgStats.buff.aec = vfe31_ctrl->aecStatsControl.bufToRender;
+ msg._u.msgStats.buff.awb = vfe31_ctrl->awbStatsControl.bufToRender;
+ msg._u.msgStats.buff.af = vfe31_ctrl->afStatsControl.bufToRender;
+
+ msg._u.msgStats.buff.ihist = vfe31_ctrl->ihistStatsControl.bufToRender;
+ msg._u.msgStats.buff.rs = vfe31_ctrl->rsStatsControl.bufToRender;
+ msg._u.msgStats.buff.cs = vfe31_ctrl->csStatsControl.bufToRender;
+
+ vfe31_proc_ops(msg._d,
+ &msg, sizeof(struct vfe_message));
+ return;
+}
+
+static void vfe31_process_stats(void)
+{
+ int32_t process_stats = false;
+
+ CDBG("%s, stats = 0x%x\n", __func__, vfe31_ctrl->status_bits);
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+ if (!vfe31_ctrl->aec_ack_pending) {
+ vfe31_ctrl->aec_ack_pending = TRUE;
+ vfe31_ctrl->aecStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAeNum,
+ vfe31_ctrl->aecStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else{
+ vfe31_ctrl->aecStatsControl.bufToRender = 0;
+ vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++;
+ }
+ } else {
+ vfe31_ctrl->aecStatsControl.bufToRender = 0;
+ }
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
+ if (!vfe31_ctrl->awb_ack_pending) {
+ vfe31_ctrl->awb_ack_pending = TRUE;
+ vfe31_ctrl->awbStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAwbNum,
+ vfe31_ctrl->awbStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else{
+ vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++;
+ vfe31_ctrl->awbStatsControl.bufToRender = 0;
+ }
+ } else {
+ vfe31_ctrl->awbStatsControl.bufToRender = 0;
+ }
+
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+ if (!vfe31_ctrl->af_ack_pending) {
+ vfe31_ctrl->af_ack_pending = TRUE;
+ vfe31_ctrl->afStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsAfNum,
+ vfe31_ctrl->afStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else {
+ vfe31_ctrl->afStatsControl.bufToRender = 0;
+ vfe31_ctrl->afStatsControl.droppedStatsFrameCount++;
+ }
+ } else {
+ vfe31_ctrl->afStatsControl.bufToRender = 0;
+ }
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
+ if (!vfe31_ctrl->ihist_ack_pending) {
+ vfe31_ctrl->ihist_ack_pending = TRUE;
+ vfe31_ctrl->ihistStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsIhistNum,
+ vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else {
+ vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+ vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+ }
+ } else {
+ vfe31_ctrl->ihistStatsControl.bufToRender = 0;
+ }
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_RS) {
+ if (!vfe31_ctrl->rs_ack_pending) {
+ vfe31_ctrl->rs_ack_pending = TRUE;
+ vfe31_ctrl->rsStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsRsNum,
+ vfe31_ctrl->rsStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else {
+ vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++;
+ vfe31_ctrl->rsStatsControl.bufToRender = 0;
+ }
+ } else {
+ vfe31_ctrl->rsStatsControl.bufToRender = 0;
+ }
+
+
+ if (vfe31_ctrl->status_bits & VFE_IRQ_STATUS0_STATS_CS) {
+ if (!vfe31_ctrl->cs_ack_pending) {
+ vfe31_ctrl->cs_ack_pending = TRUE;
+ vfe31_ctrl->csStatsControl.bufToRender =
+ vfe31_process_stats_irq_common(statsCsNum,
+ vfe31_ctrl->csStatsControl.nextFrameAddrBuf);
+ process_stats = true;
+ } else {
+ vfe31_ctrl->csStatsControl.droppedStatsFrameCount++;
+ vfe31_ctrl->csStatsControl.bufToRender = 0;
+ }
+ } else {
+ vfe31_ctrl->csStatsControl.bufToRender = 0;
+ }
+
+ if (process_stats)
+ vfe_send_stats_msg();
+
+ return;
+}
+
+static void vfe31_process_stats_irq(uint32_t *irqstatus)
+{
+ /* Subsample the stats according to the hfr speed*/
+ if ((vfe31_ctrl->hfr_mode != HFR_MODE_OFF) &&
+ (vfe31_ctrl->vfeFrameId % vfe31_ctrl->hfr_mode != 0)) {
+ CDBG("Skip the stats when HFR enabled\n");
+ return;
+ }
+
+ vfe31_ctrl->status_bits = VFE_COM_STATUS & *irqstatus;
+ vfe31_process_stats();
+ return;
+}
+
+static void vfe31_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+
+ struct vfe31_isr_queue_cmd *qcmd = NULL;
+
+ CDBG("=== vfe31_do_tasklet start === \n");
+
+ while (atomic_read(&irq_cnt)) {
+ spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+ qcmd = list_first_entry(&vfe31_ctrl->tasklet_q,
+ struct vfe31_isr_queue_cmd, list);
+ atomic_sub(1, &irq_cnt);
+
+ if (!qcmd) {
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+ flags);
+ return;
+ }
+
+ list_del(&qcmd->list);
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock,
+ flags);
+
+ /* interrupt to be processed, *qcmd has the payload. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_REG_UPDATE_MASK) {
+ CDBG("irq regUpdateIrq\n");
+ vfe31_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus1 &
+ VFE_IMASK_RESET) {
+ CDBG("irq resetAckIrq\n");
+ vfe31_process_reset_irq();
+ }
+
+
+ if (qcmd->vfeInterruptStatus1 &
+ VFE_IMASK_AXI_HALT) {
+ CDBG("irq axi halt irq\n");
+ vfe31_process_axi_halt_irq();
+ }
+
+ if (atomic_read(&vfe31_ctrl->vstate)) {
+ if (qcmd->vfeInterruptStatus1 &
+ VFE31_IMASK_ERROR_ONLY_1) {
+ pr_err("irq errorIrq\n");
+ vfe31_process_error_irq(
+ qcmd->vfeInterruptStatus1 &
+ VFE31_IMASK_ERROR_ONLY_1);
+ }
+
+ /* irqs below are only valid when in active state. */
+ /* next, check output path related interrupts. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+ CDBG("Image composite done 0 irq occured.\n");
+ vfe31_process_output_path_irq_0(
+ qcmd->vfePingPongStatus);
+ }
+
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+ CDBG("Image composite done 1 irq occured.\n");
+ vfe31_process_output_path_irq_1(
+ qcmd->vfePingPongStatus);
+ }
+
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) {
+ CDBG("Image composite done 2 irq occured.\n");
+ vfe31_process_output_path_irq_2(
+ qcmd->vfePingPongStatus);
+ }
+
+ /* then process stats irq. */
+ if (vfe31_ctrl->stats_comp) {
+ /* process stats comb interrupt. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+ CDBG("Stats composite irq occured.\n");
+ vfe31_process_stats_irq(
+ &qcmd->vfeInterruptStatus0);
+ }
+ } else {
+ /* process individual stats interrupt. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_COM_STATUS) {
+ CDBG("VFE stats occured.\n");
+ vfe31_process_stats_irq(
+ &qcmd->vfeInterruptStatus0);
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER0) {
+ CDBG("SYNC_TIMER 0 irq occured.\n");
+ vfe31_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER0_DONE);
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER1) {
+ CDBG("SYNC_TIMER 1 irq occured.\n");
+ vfe31_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER1_DONE);
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER2) {
+ CDBG("SYNC_TIMER 2 irq occured.\n");
+ vfe31_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER2_DONE);
+ }
+ }
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_CAMIF_SOF_MASK) {
+ CDBG("irq camifSofIrq\n");
+ vfe31_process_camif_sof_irq();
+ }
+ kfree(qcmd);
+ }
+ CDBG("=== vfe31_do_tasklet end === \n");
+}
+
+DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0);
+
+static irqreturn_t vfe31_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct vfe31_irq_status irq;
+ struct vfe31_isr_queue_cmd *qcmd;
+ uint32_t *val;
+ CDBG("vfe_parse_irq\n");
+ memset(&irq, 0, sizeof(struct vfe31_irq_status));
+
+ val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_0);
+ irq.vfeIrqStatus0 = msm_io_r(val);
+
+ val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_IRQ_STATUS_1);
+ irq.vfeIrqStatus1 = msm_io_r(val);
+
+ if (irq.vfeIrqStatus1 & VFE_IMASK_AXI_HALT) {
+ msm_io_w(VFE_IMASK_RESET, vfe31_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ msm_io_w_mb(AXI_HALT_CLEAR,
+ vfe31_ctrl->vfebase + VFE_AXI_CMD);
+ }
+
+ val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_CAMIF_STATUS);
+ irq.camifStatus = msm_io_r(val);
+ CDBG("camifStatus = 0x%x\n", irq.camifStatus);
+
+ val = (uint32_t *)(vfe31_ctrl->vfebase + VFE_BUS_PING_PONG_STATUS);
+ irq.vfePingPongStatus = msm_io_r(val);
+
+ /* clear the pending interrupt of the same kind.*/
+ msm_io_w(irq.vfeIrqStatus0, vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(irq.vfeIrqStatus1, vfe31_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe31_ctrl->vfebase + VFE_IRQ_CMD);
+
+ if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+ CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+ return IRQ_HANDLED;
+ }
+
+ qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd),
+ GFP_ATOMIC);
+ if (!qcmd) {
+ pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+ return IRQ_HANDLED;
+ }
+
+ if (atomic_read(&vfe31_ctrl->stop_ack_pending)) {
+ irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+ irq.vfeIrqStatus1 &= vfe31_ctrl->while_stopping_mask;
+ }
+
+ spin_lock_irqsave(&vfe31_ctrl->xbar_lock, flags);
+ if ((irq.vfeIrqStatus0 &
+ VFE_IRQ_STATUS0_CAMIF_EOF_MASK) &&
+ vfe31_ctrl->xbar_update_pending) {
+ CDBG("irq camifEofIrq\n");
+ msm_io_memcpy(vfe31_ctrl->vfebase + V31_XBAR_CFG_OFF,
+ (void *)vfe31_ctrl->xbar_cfg, V31_XBAR_CFG_LEN);
+ vfe31_ctrl->xbar_update_pending = 0;
+ }
+ spin_unlock_irqrestore(&vfe31_ctrl->xbar_lock, flags);
+ CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+ irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+ qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+ qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+ qcmd->vfePingPongStatus = irq.vfePingPongStatus;
+
+ spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags);
+ list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q);
+
+ atomic_add(1, &irq_cnt);
+ spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags);
+ tasklet_schedule(&vfe31_tasklet);
+ return IRQ_HANDLED;
+}
+
+static void vfe31_release(struct platform_device *pdev)
+{
+ struct resource *vfemem, *vfeio;
+
+ vfe31_reset_free_buf_queue_all();
+ CDBG("%s, free_irq\n", __func__);
+ free_irq(vfe31_ctrl->vfeirq, 0);
+ tasklet_kill(&vfe31_tasklet);
+
+ if (atomic_read(&irq_cnt))
+ pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
+
+ vfemem = vfe31_ctrl->vfemem;
+ vfeio = vfe31_ctrl->vfeio;
+
+ msm_vpe_release();
+
+ kfree(vfe31_ctrl->extdata);
+ iounmap(vfe31_ctrl->vfebase);
+ kfree(vfe31_ctrl);
+ vfe31_ctrl = NULL;
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ CDBG("%s, msm_camio_disable\n", __func__);
+ msm_camio_disable(pdev);
+ msm_camio_set_perf_lvl(S_EXIT);
+
+ vfe_syncdata = NULL;
+}
+
+static int vfe31_resource_init(struct msm_vfe_callback *presp,
+ struct platform_device *pdev, void *sdata)
+{
+ struct resource *vfemem, *vfeirq, *vfeio;
+ int rc;
+ struct msm_camera_sensor_info *s_info;
+ s_info = pdev->dev.platform_data;
+
+ pdev->resource = s_info->resource;
+ pdev->num_resources = s_info->num_resources;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ pr_err("%s: VFE region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
+ if (!vfe31_ctrl) {
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+
+ vfe31_ctrl->vfeirq = vfeirq->start;
+
+ vfe31_ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!vfe31_ctrl->vfebase) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto cmd_init_failed2;
+ }
+
+ if (presp && presp->vfe_resp)
+ vfe31_ctrl->resp = presp;
+ else {
+ rc = -EINVAL;
+ goto cmd_init_failed3;
+ }
+
+ vfe31_ctrl->extdata =
+ kmalloc(sizeof(struct vfe31_frame_extra), GFP_KERNEL);
+ if (!vfe31_ctrl->extdata) {
+ rc = -ENOMEM;
+ goto cmd_init_failed3;
+ }
+
+ vfe31_ctrl->extlen = sizeof(struct vfe31_frame_extra);
+
+ spin_lock_init(&vfe31_ctrl->io_lock);
+ spin_lock_init(&vfe31_ctrl->update_ack_lock);
+ spin_lock_init(&vfe31_ctrl->tasklet_lock);
+
+ INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
+ vfe31_init_free_buf_queue();
+
+ vfe31_ctrl->syncdata = sdata;
+ vfe31_ctrl->vfemem = vfemem;
+ vfe31_ctrl->vfeio = vfeio;
+ vfe31_ctrl->update_gamma = false;
+ vfe31_ctrl->update_luma = false;
+ vfe31_ctrl->s_info = s_info;
+ vfe31_ctrl->stats_comp = 0;
+ vfe31_ctrl->hfr_mode = HFR_MODE_OFF;
+ return 0;
+
+cmd_init_failed3:
+ free_irq(vfe31_ctrl->vfeirq, 0);
+ iounmap(vfe31_ctrl->vfebase);
+cmd_init_failed2:
+ kfree(vfe31_ctrl);
+cmd_init_failed1:
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ return rc;
+}
+
+static int vfe31_init(struct msm_vfe_callback *presp,
+ 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_clk = camdev->ioclk;
+
+ rc = vfe31_resource_init(presp, pdev, vfe_syncdata);
+ if (rc < 0)
+ return rc;
+ /* Bring up all the required GPIOs and Clocks */
+ rc = msm_camio_enable(pdev);
+ msm_camio_set_perf_lvl(S_INIT);
+ if (msm_vpe_open() < 0)
+ CDBG("%s: vpe_open failed\n", __func__);
+
+ /* TO DO: Need to release the VFE resources */
+ rc = request_irq(vfe31_ctrl->vfeirq, vfe31_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ fptr->vfe_init = vfe31_init;
+ fptr->vfe_enable = vfe31_enable;
+ fptr->vfe_config = vfe31_config;
+ fptr->vfe_disable = vfe31_disable;
+ fptr->vfe_release = vfe31_release;
+ fptr->vfe_stop = vfe31_stop;
+ vfe_syncdata = data;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+ fptr->vpe_reg = msm_vpe_reg;
+ fptr->send_frame_to_vpe = msm_send_frame_to_vpe;
+ fptr->vpe_config = msm_vpe_config;
+ fptr->vpe_cfg_update = msm_vpe_cfg_update;
+ fptr->dis = &(vpe_ctrl->dis_en);
+ fptr->vpe_cfg_offset = msm_vpe_offset_update;
+ vpe_ctrl->syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe31.h b/drivers/media/video/msm/msm_vfe31.h
new file mode 100644
index 0000000..e3c06ee
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe31.h
@@ -0,0 +1,1113 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef __MSM_VFE31_H__
+#define __MSM_VFE31_H__
+
+#define TRUE 1
+#define FALSE 0
+
+/* 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
+
+/* clear axi_halt_irq */
+#define MASK_AXI_HALT_IRQ 0xFF7FFFFF
+
+/* 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
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK 0x00000001
+#define VFE_IRQ_STATUS0_CAMIF_EOF_MASK 0x00000004
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK 0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */
+
+#define VFE_IRQ_STATUS0_SYNC_TIMER0 0x2000000 /* bit 25 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER1 0x4000000 /* bit 26 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER2 0x8000000 /* bit 27 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER0 0x10000000 /* bit 28 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER1 0x20000000 /* bit 29 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER2 0x40000000 /* bit 30 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER3 0x80000000 /* bit 31 */
+
+/* imask for while waiting for stop ack, driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-31 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+ irq */
+#define VFE_IMASK_WHILE_STOPPING_0 0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1 0x00C00000
+#define VFE_IMASK_RESET 0x00400000
+#define VFE_IMASK_AXI_HALT 0x00800000
+
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1 0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For BPC bit 1 and 2 are set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF9
+
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AWB_ENABLE_MASK 0x00000080 /* bit 7 */
+#define AF_ENABLE_MASK 0x00000040 /* bit 6 */
+#define AE_ENABLE_MASK 0x00000020 /* bit 5 */
+#define IHIST_ENABLE_MASK 0x00008000 /* bit 15 */
+#define RS_ENABLE_MASK 0x00000100 /* bit 8 */
+#define CS_ENABLE_MASK 0x00000200 /* bit 9 */
+#define RS_CS_ENABLE_MASK 0x00000300 /* bit 8,9 */
+#define STATS_ENABLE_MASK 0x000483E0 /* bit 18,15,9,8,7,6,5*/
+
+#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_AE_PINGPONG_STATUS_BIT 0x80
+#define VFE_AF_PINGPONG_STATUS_BIT 0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT 0x200
+
+#define HFR_MODE_OFF 1
+
+enum VFE31_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_RAM = 0x8,
+ RGBLUT_CHX_BANK0 = 0x9,
+ RGBLUT_CHX_BANK1 = 0xa,
+ LUMA_ADAPT_LUT_RAM_BANK0 = 0xb,
+ LUMA_ADAPT_LUT_RAM_BANK1 = 0xc
+};
+
+enum VFE_STATE {
+ VFE_STATE_IDLE,
+ VFE_STATE_ACTIVE
+};
+
+enum vfe_recording_state {
+ VFE_REC_STATE_IDLE,
+ VFE_REC_STATE_START_REQUESTED,
+ VFE_REC_STATE_STARTED,
+ VFE_REC_STATE_STOP_REQUESTED,
+ VFE_REC_STATE_STOPPED,
+};
+
+#define V31_DUMMY_0 0
+#define V31_SET_CLK 1
+#define V31_RESET 2
+#define V31_START 3
+#define V31_TEST_GEN_START 4
+#define V31_OPERATION_CFG 5
+#define V31_AXI_OUT_CFG 6
+#define V31_CAMIF_CFG 7
+#define V31_AXI_INPUT_CFG 8
+#define V31_BLACK_LEVEL_CFG 9
+#define V31_ROLL_OFF_CFG 10
+#define V31_DEMUX_CFG 11
+#define V31_DEMOSAIC_0_CFG 12 /* general */
+#define V31_DEMOSAIC_1_CFG 13 /* ABF */
+#define V31_DEMOSAIC_2_CFG 14 /* BPC */
+#define V31_FOV_CFG 15
+#define V31_MAIN_SCALER_CFG 16
+#define V31_WB_CFG 17
+#define V31_COLOR_COR_CFG 18
+#define V31_RGB_G_CFG 19
+#define V31_LA_CFG 20
+#define V31_CHROMA_EN_CFG 21
+#define V31_CHROMA_SUP_CFG 22
+#define V31_MCE_CFG 23
+#define V31_SK_ENHAN_CFG 24
+#define V31_ASF_CFG 25
+#define V31_S2Y_CFG 26
+#define V31_S2CbCr_CFG 27
+#define V31_CHROMA_SUBS_CFG 28
+#define V31_OUT_CLAMP_CFG 29
+#define V31_FRAME_SKIP_CFG 30
+#define V31_DUMMY_1 31
+#define V31_DUMMY_2 32
+#define V31_DUMMY_3 33
+#define V31_UPDATE 34
+#define V31_BL_LVL_UPDATE 35
+#define V31_DEMUX_UPDATE 36
+#define V31_DEMOSAIC_1_UPDATE 37 /* BPC */
+#define V31_DEMOSAIC_2_UPDATE 38 /* ABF */
+#define V31_FOV_UPDATE 39
+#define V31_MAIN_SCALER_UPDATE 40
+#define V31_WB_UPDATE 41
+#define V31_COLOR_COR_UPDATE 42
+#define V31_RGB_G_UPDATE 43
+#define V31_LA_UPDATE 44
+#define V31_CHROMA_EN_UPDATE 45
+#define V31_CHROMA_SUP_UPDATE 46
+#define V31_MCE_UPDATE 47
+#define V31_SK_ENHAN_UPDATE 48
+#define V31_S2CbCr_UPDATE 49
+#define V31_S2Y_UPDATE 50
+#define V31_ASF_UPDATE 51
+#define V31_FRAME_SKIP_UPDATE 52
+#define V31_CAMIF_FRAME_UPDATE 53
+#define V31_STATS_AF_UPDATE 54
+#define V31_STATS_AE_UPDATE 55
+#define V31_STATS_AWB_UPDATE 56
+#define V31_STATS_RS_UPDATE 57
+#define V31_STATS_CS_UPDATE 58
+#define V31_STATS_SKIN_UPDATE 59
+#define V31_STATS_IHIST_UPDATE 60
+#define V31_DUMMY_4 61
+#define V31_EPOCH1_ACK 62
+#define V31_EPOCH2_ACK 63
+#define V31_START_RECORDING 64
+#define V31_STOP_RECORDING 65
+#define V31_DUMMY_5 66
+#define V31_DUMMY_6 67
+#define V31_CAPTURE 68
+#define V31_DUMMY_7 69
+#define V31_STOP 70
+#define V31_GET_HW_VERSION 71
+#define V31_GET_FRAME_SKIP_COUNTS 72
+#define V31_OUTPUT1_BUFFER_ENQ 73
+#define V31_OUTPUT2_BUFFER_ENQ 74
+#define V31_OUTPUT3_BUFFER_ENQ 75
+#define V31_JPEG_OUT_BUF_ENQ 76
+#define V31_RAW_OUT_BUF_ENQ 77
+#define V31_RAW_IN_BUF_ENQ 78
+#define V31_STATS_AF_ENQ 79
+#define V31_STATS_AE_ENQ 80
+#define V31_STATS_AWB_ENQ 81
+#define V31_STATS_RS_ENQ 82
+#define V31_STATS_CS_ENQ 83
+#define V31_STATS_SKIN_ENQ 84
+#define V31_STATS_IHIST_ENQ 85
+#define V31_DUMMY_8 86
+#define V31_JPEG_ENC_CFG 87
+#define V31_DUMMY_9 88
+#define V31_STATS_AF_START 89
+#define V31_STATS_AF_STOP 90
+#define V31_STATS_AE_START 91
+#define V31_STATS_AE_STOP 92
+#define V31_STATS_AWB_START 93
+#define V31_STATS_AWB_STOP 94
+#define V31_STATS_RS_START 95
+#define V31_STATS_RS_STOP 96
+#define V31_STATS_CS_START 97
+#define V31_STATS_CS_STOP 98
+#define V31_STATS_SKIN_START 99
+#define V31_STATS_SKIN_STOP 100
+#define V31_STATS_IHIST_START 101
+#define V31_STATS_IHIST_STOP 102
+#define V31_DUMMY_10 103
+#define V31_SYNC_TIMER_SETTING 104
+#define V31_ASYNC_TIMER_SETTING 105
+#define V31_LIVESHOT 106
+#define V31_ZSL 107
+#define V31_STEREOCAM 108
+#define V31_LA_SETUP 109
+#define V31_XBAR_CFG 110
+#define V31_EZTUNE_CFG 111
+
+#define V31_CAMIF_OFF 0x000001E4
+#define V31_CAMIF_LEN 32
+
+#define V31_DEMUX_OFF 0x00000284
+#define V31_DEMUX_LEN 20
+
+#define V31_DEMOSAIC_0_OFF 0x00000298
+#define V31_DEMOSAIC_0_LEN 4
+/* ABF */
+#define V31_DEMOSAIC_1_OFF 0x000002A4
+#define V31_DEMOSAIC_1_LEN 180
+/* BPC */
+#define V31_DEMOSAIC_2_OFF 0x0000029C
+#define V31_DEMOSAIC_2_LEN 8
+
+/* gamma VFE_LUT_BANK_SEL*/
+#define V31_GAMMA_CFG_OFF 0x000003BC
+#define V31_LUMA_CFG_OFF 0x000003C0
+
+#define V31_OUT_CLAMP_OFF 0x00000524
+#define V31_OUT_CLAMP_LEN 8
+
+#define V31_OPERATION_CFG_LEN 32
+
+#define V31_AXI_OUT_OFF 0x00000038
+#define V31_AXI_OUT_LEN 212
+#define V31_AXI_CH_INF_LEN 24
+#define V31_AXI_CFG_LEN 47
+
+#define V31_FRAME_SKIP_OFF 0x00000504
+#define V31_FRAME_SKIP_LEN 32
+
+#define V31_CHROMA_SUBS_OFF 0x000004F8
+#define V31_CHROMA_SUBS_LEN 12
+
+#define V31_FOV_OFF 0x00000360
+#define V31_FOV_LEN 8
+
+#define V31_MAIN_SCALER_OFF 0x00000368
+#define V31_MAIN_SCALER_LEN 28
+
+#define V31_S2Y_OFF 0x000004D0
+#define V31_S2Y_LEN 20
+
+#define V31_S2CbCr_OFF 0x000004E4
+#define V31_S2CbCr_LEN 20
+
+#define V31_CHROMA_EN_OFF 0x000003C4
+#define V31_CHROMA_EN_LEN 36
+
+#define V31_SYNC_TIMER_OFF 0x0000020C
+#define V31_SYNC_TIMER_POLARITY_OFF 0x00000234
+#define V31_TIMER_SELECT_OFF 0x0000025C
+#define V31_SYNC_TIMER_LEN 28
+
+#define V31_ASYNC_TIMER_OFF 0x00000238
+#define V31_ASYNC_TIMER_LEN 28
+
+#define V31_BLACK_LEVEL_OFF 0x00000264
+#define V31_BLACK_LEVEL_LEN 16
+
+#define V31_ROLL_OFF_CFG_OFF 0x00000274
+#define V31_ROLL_OFF_CFG_LEN 16
+
+#define V31_COLOR_COR_OFF 0x00000388
+#define V31_COLOR_COR_LEN 52
+
+#define V31_WB_OFF 0x00000384
+#define V31_WB_LEN 4
+
+#define V31_RGB_G_OFF 0x000003BC
+#define V31_RGB_G_LEN 4
+
+#define V31_LA_OFF 0x000003C0
+#define V31_LA_LEN 4
+
+#define V31_SCE_OFF 0x00000418
+#define V31_SCE_LEN 136
+
+#define V31_CHROMA_SUP_OFF 0x000003E8
+#define V31_CHROMA_SUP_LEN 12
+
+#define V31_MCE_OFF 0x000003F4
+#define V31_MCE_LEN 36
+#define V31_STATS_AF_OFF 0x0000053c
+#define V31_STATS_AF_LEN 16
+
+#define V31_STATS_AE_OFF 0x00000534
+#define V31_STATS_AE_LEN 8
+
+#define V31_STATS_AWB_OFF 0x0000054c
+#define V31_STATS_AWB_LEN 32
+
+#define V31_STATS_IHIST_OFF 0x0000057c
+#define V31_STATS_IHIST_LEN 8
+
+#define V31_STATS_RS_OFF 0x0000056c
+#define V31_STATS_RS_LEN 8
+
+#define V31_STATS_CS_OFF 0x00000574
+#define V31_STATS_CS_LEN 8
+
+#define V31_XBAR_CFG_OFF 0x00000040
+#define V31_XBAR_CFG_LEN 8
+
+#define V31_EZTUNE_CFG_OFF 0x00000010
+#define V31_EZTUNE_CFG_LEN 4
+
+#define V31_ASF_OFF 0x000004A0
+#define V31_ASF_LEN 48
+#define V31_ASF_UPDATE_LEN 36
+
+#define V31_CAPTURE_LEN 4
+
+struct vfe_cmd_hw_version {
+ uint32_t minorVersion;
+ uint32_t majorVersion;
+ uint32_t coreVersion;
+};
+
+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
+};
+
+
+#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_per_write_master {
+ 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_per_write_master firstWM;
+ struct vfe_cmds_per_write_master secondWM;
+};
+
+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_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_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_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
+};
+
+
+/* 13*1 */
+#define VFE31_ROLL_OFF_INIT_TABLE_SIZE 13
+/* 13*16 */
+#define VFE31_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+#define VFE31_GAMMA_NUM_ENTRIES 64
+
+#define VFE31_LA_TABLE_LENGTH 64
+
+#define VFE31_HIST_TABLE_LENGTH 256
+
+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 64
+
+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_bus_pm_start {
+ uint8_t output2YWrPmEnable;
+ uint8_t output2CbcrWrPmEnable;
+ uint8_t output1YWrPmEnable;
+ uint8_t output1CbcrWrPmEnable;
+};
+
+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
+};
+
+enum VFE31_MESSAGE_ID {
+ MSG_ID_RESET_ACK, /* 0 */
+ MSG_ID_START_ACK,
+ MSG_ID_STOP_ACK,
+ MSG_ID_UPDATE_ACK,
+ MSG_ID_OUTPUT_P,
+ MSG_ID_OUTPUT_T,
+ MSG_ID_OUTPUT_S,
+ MSG_ID_OUTPUT_V,
+ MSG_ID_SNAPSHOT_DONE,
+ MSG_ID_COMMON,
+ MSG_ID_EPOCH1, /* 10 */
+ MSG_ID_EPOCH2,
+ MSG_ID_SYNC_TIMER0_DONE,
+ MSG_ID_SYNC_TIMER1_DONE,
+ MSG_ID_SYNC_TIMER2_DONE,
+ MSG_ID_ASYNC_TIMER0_DONE,
+ MSG_ID_ASYNC_TIMER1_DONE,
+ MSG_ID_ASYNC_TIMER2_DONE,
+ MSG_ID_ASYNC_TIMER3_DONE,
+ MSG_ID_AE_OVERFLOW,
+ MSG_ID_AF_OVERFLOW, /* 20 */
+ MSG_ID_AWB_OVERFLOW,
+ MSG_ID_RS_OVERFLOW,
+ MSG_ID_CS_OVERFLOW,
+ MSG_ID_IHIST_OVERFLOW,
+ MSG_ID_SKIN_OVERFLOW,
+ MSG_ID_AXI_ERROR,
+ MSG_ID_CAMIF_OVERFLOW,
+ MSG_ID_VIOLATION,
+ MSG_ID_CAMIF_ERROR,
+ MSG_ID_BUS_OVERFLOW, /* 30 */
+ MSG_ID_SOF_ACK,
+ MSG_ID_STOP_REC_ACK,
+};
+
+struct stats_buffer {
+ uint32_t aec;
+ uint32_t awb;
+ uint32_t af;
+ uint32_t ihist;
+ uint32_t rs;
+ uint32_t cs;
+ uint32_t skin;
+};
+
+struct vfe_msg_stats {
+ struct stats_buffer buff;
+ uint32_t frameCounter;
+ uint32_t status_bits;
+};
+
+
+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 vfe31_irq_status {
+ uint32_t vfeIrqStatus0;
+ uint32_t vfeIrqStatus1;
+ uint32_t camifStatus;
+ uint32_t demosaicStatus;
+ uint32_t asfMaxEdge;
+ uint32_t vfePingPongStatus;
+};
+
+struct vfe_msg_output {
+ uint8_t output_id;
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+};
+
+struct vfe_message {
+ enum VFE31_MESSAGE_ID _d;
+ union {
+ struct vfe_msg_output msgOut;
+ struct vfe_msg_stats msgStats;
+ struct vfe_msg_camif_status msgCamifError;
+ } _u;
+};
+
+/* New one for 7x30 */
+struct msm_vfe31_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+#define V31_PREVIEW_AXI_FLAG 0x00000001
+#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe31_cmd_type {
+ uint16_t id;
+ uint32_t length;
+ uint32_t offset;
+ uint32_t flag;
+};
+
+struct vfe31_free_buf {
+ struct list_head node;
+ uint32_t paddr;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+};
+
+struct vfe31_output_ch {
+ struct list_head free_buf_head;
+ spinlock_t free_buf_lock;
+ uint16_t output_fmt;
+ int8_t ch0;
+ int8_t ch1;
+ int8_t ch2;
+ uint32_t frame_drop_cnt;
+};
+
+/* no error irq in mask 0 */
+#define VFE31_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE31_IMASK_ERROR_ONLY_1 0x003FFFFF
+#define VFE31_IMASK_CAMIF_ERROR (0x00000001<<0)
+#define VFE31_IMASK_STATS_CS_OVWR (0x00000001<<1)
+#define VFE31_IMASK_STATS_IHIST_OVWR (0x00000001<<2)
+#define VFE31_IMASK_REALIGN_BUF_Y_OVFL (0x00000001<<3)
+#define VFE31_IMASK_REALIGN_BUF_CB_OVFL (0x00000001<<4)
+#define VFE31_IMASK_REALIGN_BUF_CR_OVFL (0x00000001<<5)
+#define VFE31_IMASK_VIOLATION (0x00000001<<6)
+#define VFE31_IMASK_IMG_MAST_0_BUS_OVFL (0x00000001<<7)
+#define VFE31_IMASK_IMG_MAST_1_BUS_OVFL (0x00000001<<8)
+#define VFE31_IMASK_IMG_MAST_2_BUS_OVFL (0x00000001<<9)
+#define VFE31_IMASK_IMG_MAST_3_BUS_OVFL (0x00000001<<10)
+#define VFE31_IMASK_IMG_MAST_4_BUS_OVFL (0x00000001<<11)
+#define VFE31_IMASK_IMG_MAST_5_BUS_OVFL (0x00000001<<12)
+#define VFE31_IMASK_IMG_MAST_6_BUS_OVFL (0x00000001<<13)
+#define VFE31_IMASK_STATS_AE_BUS_OVFL (0x00000001<<14)
+#define VFE31_IMASK_STATS_AF_BUS_OVFL (0x00000001<<15)
+#define VFE31_IMASK_STATS_AWB_BUS_OVFL (0x00000001<<16)
+#define VFE31_IMASK_STATS_RS_BUS_OVFL (0x00000001<<17)
+#define VFE31_IMASK_STATS_CS_BUS_OVFL (0x00000001<<18)
+#define VFE31_IMASK_STATS_IHIST_BUS_OVFL (0x00000001<<19)
+#define VFE31_IMASK_STATS_SKIN_BUS_OVFL (0x00000001<<20)
+#define VFE31_IMASK_AXI_ERROR (0x00000001<<21)
+
+#define VFE_COM_STATUS 0x000FE000
+
+struct vfe31_output_path {
+ uint16_t output_mode; /* bitmask */
+
+ struct vfe31_output_ch out0; /* preview and thumbnail */
+ struct vfe31_output_ch out1; /* snapshot */
+ struct vfe31_output_ch out2; /* video */
+};
+
+struct vfe31_frame_extra {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+
+ uint32_t yWrPmStats0;
+ uint32_t yWrPmStats1;
+ uint32_t cbcrWrPmStats0;
+ uint32_t cbcrWrPmStats1;
+
+ uint32_t frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS 0
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+
+#define VFE_GLOBAL_RESET 0x00000004
+#define VFE_CGC_OVERRIDE 0x0000000C
+#define VFE_MODULE_CFG 0x00000010
+#define VFE_CFG_OFF 0x00000014
+#define VFE_IRQ_CMD 0x00000018
+#define VFE_IRQ_MASK_0 0x0000001C
+#define VFE_IRQ_MASK_1 0x00000020
+#define VFE_IRQ_CLEAR_0 0x00000024
+#define VFE_IRQ_CLEAR_1 0x00000028
+#define VFE_IRQ_STATUS_0 0x0000002C
+#define VFE_IRQ_STATUS_1 0x00000030
+#define VFE_IRQ_COMP_MASK 0x00000034
+#define VFE_BUS_CMD 0x00000038
+#define VFE_BUS_PING_PONG_STATUS 0x00000180
+#define VFE_BUS_OPERATION_STATUS 0x00000184
+
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_0 0x00000190
+#define VFE_BUS_IMAGE_MASTER_0_WR_PM_STATS_1 0x00000194
+
+#define VFE_AXI_CMD 0x000001D8
+#define VFE_AXI_STATUS 0x000001DC
+#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4
+
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG 0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR 0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR 0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG 0x00000120
+
+#define VFE_BUS_STATS_CS_WR_PING_ADDR 0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR 0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG 0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144
+#define VFE_BUS_PM_CMD 0x00000188
+#define VFE_BUS_PM_CFG 0x0000018C
+#define VFE_CAMIF_COMMAND 0x000001E0
+#define VFE_CAMIF_STATUS 0x00000204
+#define VFE_REG_UPDATE_CMD 0x00000260
+#define VFE_DEMUX_GAIN_0 0x00000288
+#define VFE_DEMUX_GAIN_1 0x0000028C
+#define VFE_CHROMA_UP 0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG 0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG 0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN 0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN 0x00000510
+#define VFE_FRAMEDROP_VIEW_Y 0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR 0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN 0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520
+#define VFE_CLAMP_MAX 0x00000524
+#define VFE_CLAMP_MIN 0x00000528
+#define VFE_REALIGN_BUF 0x0000052C
+#define VFE_STATS_CFG 0x00000530
+#define VFE_DMI_CFG 0x00000598
+#define VFE_DMI_ADDR 0x0000059C
+#define VFE_DMI_DATA_LO 0x000005A4
+#define VFE_AXI_CFG 0x00000600
+
+struct vfe_stats_control {
+ uint8_t ackPending;
+ uint32_t nextFrameAddrBuf;
+ uint32_t droppedStatsFrameCount;
+ uint32_t bufToRender;
+};
+
+struct vfe31_ctrl_type {
+ uint16_t operation_mode; /* streaming or snapshot */
+ struct vfe31_output_path outpath;
+
+ uint32_t vfeImaskCompositePacked;
+
+ spinlock_t update_ack_lock;
+ spinlock_t io_lock;
+
+ int8_t aec_ack_pending;
+ int8_t awb_ack_pending;
+ int8_t af_ack_pending;
+ int8_t ihist_ack_pending;
+ int8_t rs_ack_pending;
+ int8_t cs_ack_pending;
+
+ struct msm_vfe_callback *resp;
+ uint32_t extlen;
+ void *extdata;
+
+ int8_t start_ack_pending;
+ atomic_t stop_ack_pending;
+ int8_t reset_ack_pending;
+ int8_t update_ack_pending;
+ enum vfe_recording_state recording_state;
+ int8_t output0_available;
+ int8_t output1_available;
+ int8_t update_gamma;
+ int8_t update_luma;
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ int vfeirq;
+ void __iomem *vfebase;
+ void *syncdata;
+
+ struct resource *vfemem;
+ struct resource *vfeio;
+
+ uint32_t stats_comp;
+ uint32_t hfr_mode;
+ atomic_t vstate;
+ uint32_t vfe_capture_count;
+ uint32_t sync_timer_repeat_count;
+ uint32_t sync_timer_state;
+ uint32_t sync_timer_number;
+
+ uint32_t vfeFrameId;
+ uint32_t output1Pattern;
+ uint32_t output1Period;
+ uint32_t output2Pattern;
+ uint32_t output2Period;
+ uint32_t vfeFrameSkipCount;
+ uint32_t vfeFrameSkipPeriod;
+ uint32_t status_bits;
+ struct vfe_stats_control afStatsControl;
+ struct vfe_stats_control awbStatsControl;
+ struct vfe_stats_control aecStatsControl;
+ struct vfe_stats_control ihistStatsControl;
+ struct vfe_stats_control rsStatsControl;
+ struct vfe_stats_control csStatsControl;
+ struct msm_camera_sensor_info *s_info;
+ struct vfe_message vMsgHold_Snap;
+ struct vfe_message vMsgHold_Thumb;
+ int8_t xbar_update_pending;
+ uint32_t xbar_cfg[2];
+ spinlock_t xbar_lock;
+ uint32_t while_stopping_mask;
+};
+
+#define statsAeNum 0
+#define statsAfNum 1
+#define statsAwbNum 2
+#define statsRsNum 3
+#define statsCsNum 4
+#define statsIhistNum 5
+#define statsSkinNum 6
+
+struct vfe_cmd_stats_ack{
+ uint32_t nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_buf{
+ uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+#endif /* __MSM_VFE31_H__ */
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
new file mode 100644
index 0000000..4ca62ce
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -0,0 +1,3379 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <mach/irqs.h>
+#include <mach/camera.h>
+#include <mach/msm_reqs.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "msm.h"
+#include "msm_vfe32.h"
+#include "msm_vpe1.h"
+
+atomic_t irq_cnt;
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ cmd->length)) { \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+static struct vfe32_ctrl_type *vfe32_ctrl;
+static struct msm_camera_io_clk camio_clk;
+static void *vfe_syncdata;
+
+struct vfe32_isr_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct vfe_frame_asf_info vfeAsfFrameInfo;
+ struct vfe_frame_bpc_info vfeBpcFrameInfo;
+ struct vfe_msg_camif_status vfeCamifStatusLocal;
+};
+
+static struct vfe32_cmd_type vfe32_cmd[] = {
+/* 0*/ {V32_DUMMY_0},
+ {V32_SET_CLK},
+ {V32_RESET},
+ {V32_START},
+ {V32_TEST_GEN_START},
+/* 5*/ {V32_OPERATION_CFG, V32_OPERATION_CFG_LEN},
+ {V32_AXI_OUT_CFG, V32_AXI_OUT_LEN, V32_AXI_OUT_OFF, 0xFF},
+ {V32_CAMIF_CFG, V32_CAMIF_LEN, V32_CAMIF_OFF, 0xFF},
+ {V32_AXI_INPUT_CFG},
+ {V32_BLACK_LEVEL_CFG, V32_BLACK_LEVEL_LEN, V32_BLACK_LEVEL_OFF,
+ 0xFF},
+/*10*/ {V32_ROLL_OFF_CFG, V32_ROLL_OFF_CFG_LEN, V32_ROLL_OFF_CFG_OFF,
+ 0xFF},
+ {V32_DEMUX_CFG, V32_DEMUX_LEN, V32_DEMUX_OFF, 0xFF},
+ {V32_FOV_CFG, V32_FOV_LEN, V32_FOV_OFF, 0xFF},
+ {V32_MAIN_SCALER_CFG, V32_MAIN_SCALER_LEN, V32_MAIN_SCALER_OFF,
+ 0xFF},
+ {V32_WB_CFG, V32_WB_LEN, V32_WB_OFF, 0xFF},
+/*15*/ {V32_COLOR_COR_CFG, V32_COLOR_COR_LEN, V32_COLOR_COR_OFF, 0xFF},
+ {V32_RGB_G_CFG, V32_RGB_G_LEN, V32_RGB_G_OFF, 0xFF},
+ {V32_LA_CFG, V32_LA_LEN, V32_LA_OFF, 0xFF },
+ {V32_CHROMA_EN_CFG, V32_CHROMA_EN_LEN, V32_CHROMA_EN_OFF, 0xFF},
+ {V32_CHROMA_SUP_CFG, V32_CHROMA_SUP_LEN, V32_CHROMA_SUP_OFF,
+ 0xFF},
+/*20*/ {V32_MCE_CFG, V32_MCE_LEN, V32_MCE_OFF, 0xFF},
+ {V32_SK_ENHAN_CFG, V32_SCE_LEN, V32_SCE_OFF, 0xFF},
+ {V32_ASF_CFG, V32_ASF_LEN, V32_ASF_OFF, 0xFF},
+ {V32_S2Y_CFG, V32_S2Y_LEN, V32_S2Y_OFF, 0xFF},
+ {V32_S2CbCr_CFG, V32_S2CbCr_LEN, V32_S2CbCr_OFF, 0xFF},
+/*25*/ {V32_CHROMA_SUBS_CFG, V32_CHROMA_SUBS_LEN, V32_CHROMA_SUBS_OFF,
+ 0xFF},
+ {V32_OUT_CLAMP_CFG, V32_OUT_CLAMP_LEN, V32_OUT_CLAMP_OFF,
+ 0xFF},
+ {V32_FRAME_SKIP_CFG, V32_FRAME_SKIP_LEN, V32_FRAME_SKIP_OFF,
+ 0xFF},
+ {V32_DUMMY_1},
+ {V32_DUMMY_2},
+/*30*/ {V32_DUMMY_3},
+ {V32_UPDATE},
+ {V32_BL_LVL_UPDATE, V32_BLACK_LEVEL_LEN, V32_BLACK_LEVEL_OFF,
+ 0xFF},
+ {V32_DEMUX_UPDATE, V32_DEMUX_LEN, V32_DEMUX_OFF, 0xFF},
+ {V32_FOV_UPDATE, V32_FOV_LEN, V32_FOV_OFF, 0xFF},
+/*35*/ {V32_MAIN_SCALER_UPDATE, V32_MAIN_SCALER_LEN, V32_MAIN_SCALER_OFF,
+ 0xFF},
+ {V32_WB_UPDATE, V32_WB_LEN, V32_WB_OFF, 0xFF},
+ {V32_COLOR_COR_UPDATE, V32_COLOR_COR_LEN, V32_COLOR_COR_OFF,
+ 0xFF},
+ {V32_RGB_G_UPDATE, V32_RGB_G_LEN, V32_CHROMA_EN_OFF, 0xFF},
+ {V32_LA_UPDATE, V32_LA_LEN, V32_LA_OFF, 0xFF },
+/*40*/ {V32_CHROMA_EN_UPDATE, V32_CHROMA_EN_LEN, V32_CHROMA_EN_OFF,
+ 0xFF},
+ {V32_CHROMA_SUP_UPDATE, V32_CHROMA_SUP_LEN, V32_CHROMA_SUP_OFF,
+ 0xFF},
+ {V32_MCE_UPDATE, V32_MCE_LEN, V32_MCE_OFF, 0xFF},
+ {V32_SK_ENHAN_UPDATE, V32_SCE_LEN, V32_SCE_OFF, 0xFF},
+ {V32_S2CbCr_UPDATE, V32_S2CbCr_LEN, V32_S2CbCr_OFF, 0xFF},
+/*45*/ {V32_S2Y_UPDATE, V32_S2Y_LEN, V32_S2Y_OFF, 0xFF},
+ {V32_ASF_UPDATE, V32_ASF_UPDATE_LEN, V32_ASF_OFF, 0xFF},
+ {V32_FRAME_SKIP_UPDATE},
+ {V32_CAMIF_FRAME_UPDATE},
+ {V32_STATS_AF_UPDATE, V32_STATS_AF_LEN, V32_STATS_AF_OFF},
+/*50*/ {V32_STATS_AE_UPDATE, V32_STATS_AE_LEN, V32_STATS_AE_OFF},
+ {V32_STATS_AWB_UPDATE, V32_STATS_AWB_LEN, V32_STATS_AWB_OFF},
+ {V32_STATS_RS_UPDATE, V32_STATS_RS_LEN, V32_STATS_RS_OFF},
+ {V32_STATS_CS_UPDATE, V32_STATS_CS_LEN, V32_STATS_CS_OFF},
+ {V32_STATS_SKIN_UPDATE},
+/*55*/ {V32_STATS_IHIST_UPDATE, V32_STATS_IHIST_LEN, V32_STATS_IHIST_OFF},
+ {V32_DUMMY_4},
+ {V32_EPOCH1_ACK},
+ {V32_EPOCH2_ACK},
+ {V32_START_RECORDING},
+/*60*/ {V32_STOP_RECORDING},
+ {V32_DUMMY_5},
+ {V32_DUMMY_6},
+ {V32_CAPTURE, V32_CAPTURE_LEN, 0xFF},
+ {V32_DUMMY_7},
+/*65*/ {V32_STOP},
+ {V32_GET_HW_VERSION},
+ {V32_GET_FRAME_SKIP_COUNTS},
+ {V32_OUTPUT1_BUFFER_ENQ},
+ {V32_OUTPUT2_BUFFER_ENQ},
+/*70*/ {V32_OUTPUT3_BUFFER_ENQ},
+ {V32_JPEG_OUT_BUF_ENQ},
+ {V32_RAW_OUT_BUF_ENQ},
+ {V32_RAW_IN_BUF_ENQ},
+ {V32_STATS_AF_ENQ},
+/*75*/ {V32_STATS_AE_ENQ},
+ {V32_STATS_AWB_ENQ},
+ {V32_STATS_RS_ENQ},
+ {V32_STATS_CS_ENQ},
+ {V32_STATS_SKIN_ENQ},
+/*80*/ {V32_STATS_IHIST_ENQ},
+ {V32_DUMMY_8},
+ {V32_JPEG_ENC_CFG},
+ {V32_DUMMY_9},
+ {V32_STATS_AF_START, V32_STATS_AF_LEN, V32_STATS_AF_OFF},
+/*85*/ {V32_STATS_AF_STOP},
+ {V32_STATS_AE_START, V32_STATS_AE_LEN, V32_STATS_AE_OFF},
+ {V32_STATS_AE_STOP},
+ {V32_STATS_AWB_START, V32_STATS_AWB_LEN, V32_STATS_AWB_OFF},
+ {V32_STATS_AWB_STOP},
+/*90*/ {V32_STATS_RS_START, V32_STATS_RS_LEN, V32_STATS_RS_OFF},
+ {V32_STATS_RS_STOP},
+ {V32_STATS_CS_START, V32_STATS_CS_LEN, V32_STATS_CS_OFF},
+ {V32_STATS_CS_STOP},
+ {V32_STATS_SKIN_START},
+/*95*/ {V32_STATS_SKIN_STOP},
+ {V32_STATS_IHIST_START,
+ V32_STATS_IHIST_LEN, V32_STATS_IHIST_OFF},
+ {V32_STATS_IHIST_STOP},
+ {V32_DUMMY_10},
+ {V32_SYNC_TIMER_SETTING, V32_SYNC_TIMER_LEN,
+ V32_SYNC_TIMER_OFF},
+/*100*/ {V32_ASYNC_TIMER_SETTING, V32_ASYNC_TIMER_LEN, V32_ASYNC_TIMER_OFF},
+ {V32_LIVESHOT},
+ {V32_LA_SETUP},
+ {V32_LINEARIZATION, V32_LINEARIZATION_LEN1,
+ V32_LINEARIZATION_OFF1},
+ {V32_DEMOSAICV3},
+/*105*/ {V32_DEMOSAICV3_ABCC_CFG},
+ {V32_DEMOSAICV3_DBCC_CFG, V32_DEMOSAICV3_DBCC_LEN,
+ V32_DEMOSAICV3_DBCC_OFF},
+ {V32_DEMOSAICV3_DBPC_CFG},
+ {V32_DEMOSAICV3_ABF_CFG},
+ {V32_DEMOSAICV3_ABCC_UPDATE},
+ {V32_DEMOSAICV3_DBCC_UPDATE, V32_DEMOSAICV3_DBCC_LEN,
+ V32_DEMOSAICV3_DBCC_OFF},
+ {V32_DEMOSAICV3_DBPC_UPDATE},
+};
+
+uint32_t vfe32_AXI_WM_CFG[] = {
+ 0x0000004C,
+ 0x00000064,
+ 0x0000007C,
+ 0x00000094,
+ 0x000000AC,
+ 0x000000C4,
+ 0x000000DC,
+};
+
+static const char * const vfe32_general_cmd[] = {
+ "DUMMY_0", /* 0 */
+ "SET_CLK",
+ "RESET",
+ "START",
+ "TEST_GEN_START",
+ "OPERATION_CFG", /* 5 */
+ "AXI_OUT_CFG",
+ "CAMIF_CFG",
+ "AXI_INPUT_CFG",
+ "BLACK_LEVEL_CFG",
+ "ROLL_OFF_CFG", /* 10 */
+ "DEMUX_CFG",
+ "FOV_CFG",
+ "MAIN_SCALER_CFG",
+ "WB_CFG",
+ "COLOR_COR_CFG", /* 15 */
+ "RGB_G_CFG",
+ "LA_CFG",
+ "CHROMA_EN_CFG",
+ "CHROMA_SUP_CFG",
+ "MCE_CFG", /* 20 */
+ "SK_ENHAN_CFG",
+ "ASF_CFG",
+ "S2Y_CFG",
+ "S2CbCr_CFG",
+ "CHROMA_SUBS_CFG", /* 25 */
+ "OUT_CLAMP_CFG",
+ "FRAME_SKIP_CFG",
+ "DUMMY_1",
+ "DUMMY_2",
+ "DUMMY_3", /* 30 */
+ "UPDATE",
+ "BL_LVL_UPDATE",
+ "DEMUX_UPDATE",
+ "FOV_UPDATE",
+ "MAIN_SCALER_UPDATE", /* 35 */
+ "WB_UPDATE",
+ "COLOR_COR_UPDATE",
+ "RGB_G_UPDATE",
+ "LA_UPDATE",
+ "CHROMA_EN_UPDATE", /* 40 */
+ "CHROMA_SUP_UPDATE",
+ "MCE_UPDATE",
+ "SK_ENHAN_UPDATE",
+ "S2CbCr_UPDATE",
+ "S2Y_UPDATE", /* 45 */
+ "ASF_UPDATE",
+ "FRAME_SKIP_UPDATE",
+ "CAMIF_FRAME_UPDATE",
+ "STATS_AF_UPDATE",
+ "STATS_AE_UPDATE", /* 50 */
+ "STATS_AWB_UPDATE",
+ "STATS_RS_UPDATE",
+ "STATS_CS_UPDATE",
+ "STATS_SKIN_UPDATE",
+ "STATS_IHIST_UPDATE", /* 55 */
+ "DUMMY_4",
+ "EPOCH1_ACK",
+ "EPOCH2_ACK",
+ "START_RECORDING",
+ "STOP_RECORDING", /* 60 */
+ "DUMMY_5",
+ "DUMMY_6",
+ "CAPTURE",
+ "DUMMY_7",
+ "STOP", /* 65 */
+ "GET_HW_VERSION",
+ "GET_FRAME_SKIP_COUNTS",
+ "OUTPUT1_BUFFER_ENQ",
+ "OUTPUT2_BUFFER_ENQ",
+ "OUTPUT3_BUFFER_ENQ", /* 70 */
+ "JPEG_OUT_BUF_ENQ",
+ "RAW_OUT_BUF_ENQ",
+ "RAW_IN_BUF_ENQ",
+ "STATS_AF_ENQ",
+ "STATS_AE_ENQ", /* 75 */
+ "STATS_AWB_ENQ",
+ "STATS_RS_ENQ",
+ "STATS_CS_ENQ",
+ "STATS_SKIN_ENQ",
+ "STATS_IHIST_ENQ", /* 80 */
+ "DUMMY_8",
+ "JPEG_ENC_CFG",
+ "DUMMY_9",
+ "STATS_AF_START",
+ "STATS_AF_STOP", /* 85 */
+ "STATS_AE_START",
+ "STATS_AE_STOP",
+ "STATS_AWB_START",
+ "STATS_AWB_STOP",
+ "STATS_RS_START", /* 90 */
+ "STATS_RS_STOP",
+ "STATS_CS_START",
+ "STATS_CS_STOP",
+ "STATS_SKIN_START",
+ "STATS_SKIN_STOP", /* 95 */
+ "STATS_IHIST_START",
+ "STATS_IHIST_STOP",
+ "DUMMY_10",
+ "SYNC_TIMER_SETTING",
+ "ASYNC_TIMER_SETTING", /* 100 */
+ "LIVESHOT",
+ "LA_SETUP",
+ "LINEARIZATION",
+ "DEMOSAICV3",
+ "DEMOSAICV3_ABCC_CFG", /* 105 */
+ "DEMOSAICV3_DBCC_CFG",
+ "DEMOSAICV3_DBPC_CFG",
+ "DEMOSAICV3_ABF_CFG", /* 108 */
+ "DEMOSAICV3_ABCC_UPDATE",
+ "DEMOSAICV3_DBCC_UPDATE",
+ "DEMOSAICV3_DBPC_UPDATE",
+ "EZTUNE_CFG",
+};
+
+static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo,
+ enum vfe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+ uint8_t outid;
+ switch (type) {
+ case VFE_MSG_OUTPUT_T:
+ case VFE_MSG_OUTPUT_P:
+ case VFE_MSG_OUTPUT_S:
+ case VFE_MSG_OUTPUT_V:
+ pinfo->output_id =
+ ((struct vfe_message *)data)->_u.msgOut.output_id;
+
+ switch (type) {
+ case VFE_MSG_OUTPUT_P:
+ outid = OUTPUT_TYPE_P;
+ break;
+ case VFE_MSG_OUTPUT_V:
+ outid = OUTPUT_TYPE_V;
+ break;
+ case VFE_MSG_OUTPUT_T:
+ outid = OUTPUT_TYPE_T;
+ break;
+ case VFE_MSG_OUTPUT_S:
+ outid = OUTPUT_TYPE_S;
+ break;
+ default:
+ outid = 0xff;
+ break;
+ }
+ pinfo->output_id = outid;
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOut.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOut.cbcrBuffer;
+
+ pinfo->frame_id =
+ ((struct vfe_message *)data)->_u.msgOut.frameCounter;
+
+ ((struct vfe_msg_output *)(vfe32_ctrl->extdata))->bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOut.bpcInfo;
+ ((struct vfe_msg_output *)(vfe32_ctrl->extdata))->asfInfo =
+ ((struct vfe_message *)data)->_u.msgOut.asfInfo;
+ ((struct vfe_msg_output *)(vfe32_ctrl->extdata))->frameCounter =
+ ((struct vfe_message *)data)->_u.msgOut.frameCounter;
+ *ext = vfe32_ctrl->extdata;
+ *elen = vfe32_ctrl->extlen;
+ break;
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_AEC:
+ case VFE_MSG_STATS_AWB:
+ case VFE_MSG_STATS_IHIST:
+ case VFE_MSG_STATS_RS:
+ case VFE_MSG_STATS_CS:
+ pinfo->sbuf_phy =
+ ((struct vfe_message *)data)->_u.msgStats.buffer;
+
+ pinfo->frame_id =
+ ((struct vfe_message *)data)->_u.msgStats.frameCounter;
+
+ break;
+ default:
+ break;
+ } /* switch */
+}
+
+static void vfe32_proc_ops(enum VFE32_MESSAGE_ID id, void *msg, size_t len)
+{
+ struct msm_vfe_resp *rp;
+
+ rp = msm_isp_sync_alloc(sizeof(struct msm_vfe_resp),
+ vfe32_ctrl->syncdata, GFP_ATOMIC);
+ if (!rp) {
+ CDBG("rp: cannot allocate buffer\n");
+ return;
+ }
+ CDBG("vfe32_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 MSG_ID_SNAPSHOT_DONE:
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_ID_OUTPUT_P:
+ rp->type = VFE_MSG_OUTPUT_P;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_T:
+ rp->type = VFE_MSG_OUTPUT_T;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_S:
+ rp->type = VFE_MSG_OUTPUT_S;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_OUTPUT_V:
+ rp->type = VFE_MSG_OUTPUT_V;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT_V,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_STATS_AF:
+ rp->type = VFE_MSG_STATS_AF;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AF,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_AWB:
+ rp->type = VFE_MSG_STATS_AWB;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AWB,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_AEC:
+ rp->type = VFE_MSG_STATS_AEC;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AEC,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_SKIN:
+ rp->type = VFE_MSG_STATS_SKIN;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_SKIN,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_IHIST:
+ rp->type = VFE_MSG_STATS_IHIST;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_IHIST,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_RS:
+ rp->type = VFE_MSG_STATS_RS;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_RS,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_STATS_CS:
+ rp->type = VFE_MSG_STATS_CS;
+ vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_CS,
+ rp->evt_msg.data, NULL, NULL);
+ break;
+
+ case MSG_ID_SYNC_TIMER0_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER0;
+ break;
+
+ case MSG_ID_SYNC_TIMER1_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER1;
+ break;
+
+ case MSG_ID_SYNC_TIMER2_DONE:
+ rp->type = VFE_MSG_SYNC_TIMER2;
+ break;
+
+ default:
+ rp->type = VFE_MSG_GENERAL;
+ break;
+ }
+
+ /* save the frame id.*/
+ rp->evt_msg.frame_id = rp->phy.frame_id;
+
+ v4l2_subdev_notify(vfe32_ctrl->subdev, NOTIFY_VFE_MSG_EVT, rp);
+}
+
+static void vfe_send_outmsg(uint8_t msgid, uint32_t pyaddr,
+ uint32_t pcbcraddr)
+{
+ struct vfe_message msg;
+ uint8_t outid;
+
+ msg._d = msgid; /* now the output mode is redundnat. */
+
+ switch (msgid) {
+ case MSG_ID_OUTPUT_P:
+ outid = OUTPUT_TYPE_P;
+ break;
+ case MSG_ID_OUTPUT_V:
+ outid = OUTPUT_TYPE_V;
+ break;
+ case MSG_ID_OUTPUT_T:
+ outid = OUTPUT_TYPE_T;
+ break;
+ case MSG_ID_OUTPUT_S:
+ outid = OUTPUT_TYPE_S;
+ break;
+ default:
+ outid = 0xff; /* -1 for error condition.*/
+ break;
+ }
+ msg._u.msgOut.output_id = msgid;
+ msg._u.msgOut.yBuffer = pyaddr;
+ msg._u.msgOut.cbcrBuffer = pcbcraddr;
+ vfe32_proc_ops(msgid, &msg, sizeof(struct vfe_message));
+ return;
+}
+
+static void vfe32_stop(void)
+{
+ uint8_t axiBusyFlag = true;
+ unsigned long flags;
+
+ atomic_set(&vfe32_ctrl->vstate, 0);
+
+ /* for reset hw modules, and send msg when reset_irq comes.*/
+ spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+ vfe32_ctrl->stop_ack_pending = TRUE;
+ spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+ /* disable all interrupts. */
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(VFE_CLEAR_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1,
+ vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+ /* in either continuous or snapshot mode, stop command can be issued
+ * at any time. stop camif immediately. */
+ msm_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY,
+ vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+ wmb();
+ /* axi halt command. */
+ msm_io_w(AXI_HALT,
+ vfe32_ctrl->vfebase + VFE_AXI_CMD);
+ wmb();
+ while (axiBusyFlag) {
+ if (msm_io_r(vfe32_ctrl->vfebase + VFE_AXI_STATUS) & 0x1)
+ axiBusyFlag = false;
+ }
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(AXI_HALT_CLEAR,
+ vfe32_ctrl->vfebase + VFE_AXI_CMD);
+
+ /* after axi halt, then ok to apply global reset. */
+ /* enable reset_ack and async timer interrupt only while
+ stopping the pipeline.*/
+ msm_io_w(0xf0000000,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_IMASK_WHILE_STOPPING_1,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(VFE_RESET_UPON_STOP_CMD,
+ vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe32_enqueue_free_buf(struct vfe32_output_ch *outch,
+ uint32_t paddr, uint32_t y_off, uint32_t cbcr_off)
+{
+ struct vfe32_free_buf *free_buf = NULL;
+ unsigned long flags = 0;
+ free_buf = kmalloc(sizeof(struct vfe32_free_buf), GFP_KERNEL);
+ if (!free_buf)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ free_buf->paddr = paddr;
+ free_buf->y_off = y_off;
+ free_buf->cbcr_off = cbcr_off;
+ list_add_tail(&free_buf->node, &outch->free_buf_queue);
+ CDBG("%s: free_buf paddr = 0x%x, y_off = %d, cbcr_off = %d\n",
+ __func__, free_buf->paddr, free_buf->y_off,
+ free_buf->cbcr_off);
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+ return 0;
+}
+
+static struct vfe32_free_buf *vfe32_dequeue_free_buf(
+ struct vfe32_output_ch *outch)
+{
+ unsigned long flags = 0;
+ struct vfe32_free_buf *free_buf = NULL;
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ if (!list_empty(&outch->free_buf_queue)) {
+ free_buf = list_first_entry(&outch->free_buf_queue,
+ struct vfe32_free_buf, node);
+ if (free_buf)
+ list_del_init(&free_buf->node);
+ }
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+ return free_buf;
+}
+
+static void vfe32_reset_free_buf_queue(
+ struct vfe32_output_ch *outch)
+{
+ unsigned long flags = 0;
+ struct vfe32_free_buf *free_buf = NULL;
+ spin_lock_irqsave(&outch->free_buf_lock, flags);
+ while (!list_empty(&outch->free_buf_queue)) {
+ free_buf = list_first_entry(&outch->free_buf_queue,
+ struct vfe32_free_buf, node);
+ if (free_buf) {
+ list_del_init(&free_buf->node);
+ kfree(free_buf);
+ }
+ }
+ spin_unlock_irqrestore(&outch->free_buf_lock, flags);
+}
+
+static void vfe32_init_free_buf_queues(void)
+{
+ INIT_LIST_HEAD(&vfe32_ctrl->outpath.out0.free_buf_queue);
+ INIT_LIST_HEAD(&vfe32_ctrl->outpath.out1.free_buf_queue);
+ INIT_LIST_HEAD(&vfe32_ctrl->outpath.out2.free_buf_queue);
+ spin_lock_init(&vfe32_ctrl->outpath.out0.free_buf_lock);
+ spin_lock_init(&vfe32_ctrl->outpath.out1.free_buf_lock);
+ spin_lock_init(&vfe32_ctrl->outpath.out2.free_buf_lock);
+}
+
+static void vfe32_reset_free_buf_queues(void)
+{
+ vfe32_reset_free_buf_queue(&vfe32_ctrl->outpath.out0);
+ vfe32_reset_free_buf_queue(&vfe32_ctrl->outpath.out1);
+ vfe32_reset_free_buf_queue(&vfe32_ctrl->outpath.out2);
+}
+
+static int vfe32_config_axi(int mode, struct axidata *ad, uint32_t *ao)
+{
+ int ret;
+ int i;
+ uint32_t *p, *p1, *p2;
+ int32_t *ch_info;
+ struct vfe32_output_ch *outp1, *outp2;
+ struct msm_pmem_region *regp1 = NULL;
+ struct msm_pmem_region *regp2 = NULL;
+
+ outp1 = NULL;
+ outp2 = NULL;
+
+ p = ao + 2;
+
+ /* Update the corresponding write masters for each output*/
+ ch_info = ao + V32_AXI_CFG_LEN;
+ vfe32_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
+ vfe32_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe32_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ vfe32_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
+ vfe32_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe32_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ vfe32_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
+ vfe32_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
+ vfe32_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+
+ CDBG("vfe32_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d\n",
+ mode, ad->bufnum1, ad->bufnum2);
+
+ switch (mode) {
+
+ case OUTPUT_2: {
+ if (ad->bufnum2 < 3)
+ return -EINVAL;
+ regp1 = &(ad->region[ad->bufnum1]);
+ outp1 = &(vfe32_ctrl->outpath.out0);
+ vfe32_ctrl->outpath.output_mode |= VFE32_OUTPUT_MODE_PT;
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 12 + i; /* wm1 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe32_enqueue_free_buf(outp1, regp1->paddr,
+ regp1->info.y_off, regp1->info.cbcr_off);
+ if (ret < 0)
+ return ret;
+ regp1++;
+ }
+ }
+ break;
+
+ case OUTPUT_1_AND_2:
+ /* use wm0& 4 for thumbnail, wm1&5 for main image.*/
+ if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1))
+ return -EINVAL;
+ vfe32_ctrl->outpath.output_mode |=
+ VFE32_OUTPUT_MODE_S; /* main image.*/
+ vfe32_ctrl->outpath.output_mode |=
+ VFE32_OUTPUT_MODE_PT; /* thumbnail. */
+
+ regp1 = &(ad->region[0]); /* this is thumbnail buffer. */
+ /* this is main image buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);
+ outp1 = &(vfe32_ctrl->outpath.out0);
+ outp2 = &(vfe32_ctrl->outpath.out1); /* snapshot */
+
+ /* Parse the buffers!!! */
+ if (ad->bufnum2 == 1) { /* assuming bufnum1 = bufnum2 */
+ p1 = ao + 6; /* wm0 ping */
+ *p1++ = (regp1->paddr + regp1->info.y_off);
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ p1 = ao + 30; /* wm4 ping */
+ *p1++ = (regp1->paddr + regp1->info.cbcr_off);
+ /* this is to duplicate ping address to pong.*/
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ p1 = ao + 12; /* wm1 ping */
+ *p1++ = (regp2->paddr + regp2->info.y_off);
+ /* pong = ping,*/
+ *p1 = (regp2->paddr + regp2->info.y_off);
+ p1 = ao + 36; /* wm5 */
+ *p1++ = (regp2->paddr + regp2->info.cbcr_off);
+ *p1 = (regp2->paddr + regp2->info.cbcr_off);
+
+ } else { /* more than one snapshot */
+ /* first fill ping & pong */
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ p1 = ao + 30 + i; /* wm4 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm1 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+ p2 = ao + 36 + i; /* wm5 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2++;
+ }
+
+ for (i = 2; i < ad->bufnum1; i++) {
+ ret = vfe32_enqueue_free_buf(outp1,
+ regp1->paddr,
+ regp1->info.y_off,
+ regp1->info.cbcr_off);
+ if (ret < 0)
+ return ret;
+ regp1++;
+ }
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe32_enqueue_free_buf(outp2,
+ regp2->paddr,
+ regp2->info.y_off,
+ regp2->info.cbcr_off);
+ if (ret < 0)
+ return ret;
+ regp2++;
+ }
+ }
+ break;
+
+ case OUTPUT_1_AND_3:
+ /* use wm0& 4 for preview, wm1&5 for video.*/
+ if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2))
+ return -EINVAL;
+
+ vfe32_ctrl->outpath.output_mode |=
+ VFE32_OUTPUT_MODE_V; /* video*/
+ vfe32_ctrl->outpath.output_mode |=
+ VFE32_OUTPUT_MODE_PT; /* preview */
+
+ regp1 = &(ad->region[0]); /* this is preview buffer. */
+ regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */
+ outp1 = &(vfe32_ctrl->outpath.out0); /* preview */
+ outp2 = &(vfe32_ctrl->outpath.out2); /* video */
+
+
+ for (i = 0; i < 2; i++) {
+ p1 = ao + 6 + i; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+
+ p1 = ao + 30 + i; /* wm1 for cbcr */
+ *p1 = (regp1->paddr + regp1->info.cbcr_off);
+ regp1++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ p2 = ao + 12 + i; /* wm0 for y */
+ *p2 = (regp2->paddr + regp2->info.y_off);
+
+ p2 = ao + 36 + i; /* wm1 for cbcr */
+ *p2 = (regp2->paddr + regp2->info.cbcr_off);
+ regp2++;
+ }
+ for (i = 2; i < ad->bufnum1; i++) {
+ ret = vfe32_enqueue_free_buf(outp1, regp1->paddr,
+ regp1->info.y_off,
+ regp1->info.cbcr_off);
+ if (ret < 0)
+ return ret;
+ regp1++;
+ }
+ for (i = 2; i < ad->bufnum2; i++) {
+ ret = vfe32_enqueue_free_buf(outp2, regp2->paddr,
+ regp2->info.y_off,
+ regp2->info.cbcr_off);
+ if (ret < 0)
+ return ret;
+ regp2++;
+ }
+ break;
+ case CAMIF_TO_AXI_VIA_OUTPUT_2: { /* use wm0 only */
+ if (ad->bufnum2 < 1)
+ return -EINVAL;
+ CDBG("config axi for raw snapshot.\n");
+ vfe32_ctrl->outpath.out1.ch0 = 0; /* raw */
+ regp1 = &(ad->region[ad->bufnum1]);
+ vfe32_ctrl->outpath.output_mode |= VFE32_OUTPUT_MODE_S;
+ p1 = ao + 6; /* wm0 for y */
+ *p1 = (regp1->paddr + regp1->info.y_off);
+ }
+ break;
+ default:
+ break;
+ }
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[V32_AXI_OUT_CFG].offset,
+ ao, vfe32_cmd[V32_AXI_OUT_CFG].length - V32_AXI_CH_INF_LEN);
+ return 0;
+}
+
+static void vfe32_reset_internal_variables(void)
+{
+ unsigned long flags;
+ vfe32_ctrl->vfeImaskCompositePacked = 0;
+ /* state control variables */
+ vfe32_ctrl->start_ack_pending = FALSE;
+ atomic_set(&irq_cnt, 0);
+
+ spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+ vfe32_ctrl->stop_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+ vfe32_ctrl->reset_ack_pending = FALSE;
+
+ spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+ vfe32_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->update_ack_lock, flags);
+
+ vfe32_ctrl->req_stop_video_rec = FALSE;
+ vfe32_ctrl->req_start_video_rec = FALSE;
+
+ atomic_set(&vfe32_ctrl->vstate, 0);
+
+ /* 0 for continuous mode, 1 for snapshot mode */
+ vfe32_ctrl->operation_mode = 0;
+ vfe32_ctrl->outpath.output_mode = 0;
+ vfe32_ctrl->vfe_capture_count = 0;
+
+ /* this is unsigned 32 bit integer. */
+ vfe32_ctrl->vfeFrameId = 0;
+ /* Stats control variables. */
+ memset(&(vfe32_ctrl->afStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->awbStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->aecStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->ihistStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->rsStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe32_ctrl->csStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+}
+
+static void vfe32_reset(void)
+{
+ uint32_t vfe_version;
+ vfe32_reset_free_buf_queues();
+ vfe32_reset_internal_variables();
+ vfe_version = msm_io_r(vfe32_ctrl->vfebase);
+ CDBG("vfe_version = 0x%x\n", vfe_version);
+ /* disable all interrupts. vfeImaskLocal is also reset to 0
+ * to begin with. */
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+
+ msm_io_w(VFE_DISABLE_ALL_IRQS,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* clear all pending interrupts*/
+ msm_io_w(VFE_CLEAR_ALL_IRQS, vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(VFE_CLEAR_ALL_IRQS, vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+ /* enable reset_ack interrupt. */
+ msm_io_w(VFE_IMASK_WHILE_STOPPING_1,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ /* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset
+ * is done, hardware interrupt will be generated. VFE ist processes
+ * the interrupt to complete the function call. Note that the reset
+ * function is synchronous. */
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(VFE_RESET_UPON_RESET_CMD,
+ vfe32_ctrl->vfebase + VFE_GLOBAL_RESET);
+}
+
+static int vfe32_operation_config(uint32_t *cmd)
+{
+ uint32_t *p = cmd;
+
+ vfe32_ctrl->operation_mode = *p;
+ vfe32_ctrl->stats_comp = *(++p);
+
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_CFG);
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_PIXEL_IF_CFG);
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_REALIGN_BUF);
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_CHROMA_UP);
+ msm_io_w(*(++p), vfe32_ctrl->vfebase + VFE_STATS_CFG);
+ return 0;
+}
+
+static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR);
+ vfe32_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+
+ vfe32_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR);
+
+ vfe32_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR);
+
+ vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_WR_PONG_ADDR);
+
+ vfe32_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in)
+{
+ uint32_t *ptr = in->statsBuf;
+ uint32_t addr;
+
+ addr = ptr[0];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PING_ADDR);
+ addr = ptr[1];
+ msm_io_w(addr, vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_WR_PONG_ADDR);
+
+ vfe32_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2];
+ return 0;
+}
+
+static void vfe32_start_common(void)
+{
+
+ vfe32_ctrl->start_ack_pending = TRUE;
+ CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
+ vfe32_ctrl->operation_mode, vfe32_ctrl->outpath.output_mode);
+ msm_io_w(0x00EFE021, vfe32_ctrl->vfebase + VFE_IRQ_MASK_0);
+ msm_io_w(VFE_IMASK_WHILE_STOPPING_1,
+ vfe32_ctrl->vfebase + VFE_IRQ_MASK_1);
+
+ msm_io_dump(vfe32_ctrl->vfebase, 0x740);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ msm_io_w(1, vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+ wmb();
+
+ atomic_set(&vfe32_ctrl->vstate, 1);
+}
+
+static int vfe32_start_recording(void)
+{
+ vfe32_ctrl->req_start_video_rec = TRUE;
+ /* Mask with 0x7 to extract the pixel pattern*/
+ switch (msm_io_r(vfe32_ctrl->vfebase + VFE_CFG) & 0x7) {
+ case VFE_YUV_YCbYCr:
+ case VFE_YUV_YCrYCb:
+ case VFE_YUV_CbYCrY:
+ case VFE_YUV_CrYCbY:
+ msm_io_w_mb(1,
+ vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int vfe32_stop_recording(void)
+{
+ vfe32_ctrl->req_stop_video_rec = TRUE;
+ /* Mask with 0x7 to extract the pixel pattern*/
+ switch (msm_io_r(vfe32_ctrl->vfebase + VFE_CFG) & 0x7) {
+ case VFE_YUV_YCbYCr:
+ case VFE_YUV_YCrYCb:
+ case VFE_YUV_CbYCrY:
+ case VFE_YUV_CrYCbY:
+ msm_io_w_mb(1,
+ vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void vfe32_liveshot(void){
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+ if (p_sync)
+ p_sync->liveshot_enabled = true;
+}
+
+static int vfe32_capture(uint32_t num_frames_capture)
+{
+ uint32_t irq_comp_mask = 0;
+ struct msm_sync* p_sync = (struct msm_sync *)vfe_syncdata;
+ if (p_sync) {
+ p_sync->snap_count = num_frames_capture;
+ p_sync->thumb_count = num_frames_capture;
+ }
+ /* capture command is valid for both idle and active state. */
+ vfe32_ctrl->outpath.out1.capture_cnt = num_frames_capture;
+ if (vfe32_ctrl->operation_mode == VFE_MODE_OF_OPERATION_SNAPSHOT) {
+ vfe32_ctrl->outpath.out0.capture_cnt =
+ num_frames_capture;
+ }
+
+ vfe32_ctrl->vfe_capture_count = num_frames_capture;
+ irq_comp_mask =
+ msm_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ if (vfe32_ctrl->operation_mode == VFE_MODE_OF_OPERATION_SNAPSHOT) {
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PT) {
+ irq_comp_mask |= (0x1 << vfe32_ctrl->outpath.out0.ch0 |
+ 0x1 << vfe32_ctrl->outpath.out0.ch1);
+ }
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ (0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8) |
+ 0x1 << (vfe32_ctrl->outpath.out1.ch1 + 8));
+ }
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PT) {
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+ }
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_S) {
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch1]);
+ }
+ } else { /* this is raw snapshot mode. */
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_S) {
+ irq_comp_mask |=
+ (0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8));
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
+ msm_io_w(0x1000, vfe32_ctrl->vfebase +
+ VFE_BUS_IO_FORMAT_CFG);
+ }
+ }
+ msm_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+ msm_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+ vfe32_start_common();
+ msm_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+ /* for debug */
+ msm_io_w(1, vfe32_ctrl->vfebase + 0x18C);
+ msm_io_w(1, vfe32_ctrl->vfebase + 0x188);
+ return 0;
+}
+
+static int vfe32_start(void)
+{
+ uint32_t irq_comp_mask = 0;
+ /* start command now is only good for continuous mode. */
+ if ((vfe32_ctrl->operation_mode != VFE_MODE_OF_OPERATION_CONTINUOUS) &&
+ (vfe32_ctrl->operation_mode != VFE_MODE_OF_OPERATION_VIDEO))
+ return 0;
+ irq_comp_mask =
+ msm_io_r(vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PT) {
+ irq_comp_mask |= (0x1 << vfe32_ctrl->outpath.out0.ch0 |
+ 0x1 << vfe32_ctrl->outpath.out0.ch1);
+ }
+
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_V) {
+ irq_comp_mask |= (0x1 << (vfe32_ctrl->outpath.out2.ch0 + 16)|
+ 0x1 << (vfe32_ctrl->outpath.out2.ch1 + 16));
+ }
+
+ msm_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
+
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_PT) {
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch0]);
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out0.ch1]);
+ }
+ vfe32_start_common();
+ return 0;
+}
+
+static void vfe32_update(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+ vfe32_ctrl->update_ack_pending = TRUE;
+ spin_unlock_irqrestore(&vfe32_ctrl->update_ack_lock, flags);
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ return;
+}
+
+static void vfe32_sync_timer_stop(void)
+{
+ uint32_t value = 0;
+ vfe32_ctrl->sync_timer_state = 0;
+ if (vfe32_ctrl->sync_timer_number == 0)
+ value = 0x10000;
+ else if (vfe32_ctrl->sync_timer_number == 1)
+ value = 0x20000;
+ else if (vfe32_ctrl->sync_timer_number == 2)
+ value = 0x40000;
+
+ /* Timer Stop */
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF);
+}
+
+static void vfe32_sync_timer_start(const uint32_t *tbl)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = 1;
+ uint32_t val;
+
+ vfe32_ctrl->sync_timer_state = *tbl++;
+ vfe32_ctrl->sync_timer_repeat_count = *tbl++;
+ vfe32_ctrl->sync_timer_number = *tbl++;
+ CDBG("%s timer_state %d, repeat_cnt %d timer number %d\n",
+ __func__, vfe32_ctrl->sync_timer_state,
+ vfe32_ctrl->sync_timer_repeat_count,
+ vfe32_ctrl->sync_timer_number);
+
+ if (vfe32_ctrl->sync_timer_state) { /* Start Timer */
+ value = value << vfe32_ctrl->sync_timer_number;
+ } else { /* Stop Timer */
+ CDBG("Failed to Start timer\n");
+ return;
+ }
+
+ /* Timer Start */
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF);
+ /* Sync Timer Line Start */
+ value = *tbl++;
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+ 4 + ((vfe32_ctrl->sync_timer_number) * 12));
+ /* Sync Timer Pixel Start */
+ value = *tbl++;
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+ 8 + ((vfe32_ctrl->sync_timer_number) * 12));
+ /* Sync Timer Pixel Duration */
+ value = *tbl++;
+ val = camio_clk.vfe_clk_rate / 10000;
+ val = 10000000 / val;
+ val = value * 10000 / val;
+ CDBG("%s: Pixel Clk Cycles!!! %d\n", __func__, val);
+ msm_io_w(val, vfe32_ctrl->vfebase + V32_SYNC_TIMER_OFF +
+ 12 + ((vfe32_ctrl->sync_timer_number) * 12));
+ /* Timer0 Active High/LOW */
+ value = *tbl++;
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_SYNC_TIMER_POLARITY_OFF);
+ /* Selects sync timer 0 output to drive onto timer1 port */
+ value = 0;
+ msm_io_w(value, vfe32_ctrl->vfebase + V32_TIMER_SELECT_OFF);
+}
+
+static void vfe32_program_dmi_cfg(enum VFE32_DMI_RAM_SEL bankSel)
+{
+ /* set bit 8 for auto increment. */
+ uint32_t value = VFE_DMI_CFG_DEFAULT;
+ value += (uint32_t)bankSel;
+
+ msm_io_w(value, vfe32_ctrl->vfebase + VFE_DMI_CFG);
+ /* by default, always starts with offset 0.*/
+ msm_io_w(0, vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+}
+static void vfe32_write_gamma_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+ const uint32_t *tbl)
+{
+ int i;
+ uint32_t value, value1, value2;
+ vfe32_program_dmi_cfg(channel_sel);
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < (VFE32_GAMMA_NUM_ENTRIES/2) ; i++) {
+ value = *tbl++;
+ value1 = value & 0x0000FFFF;
+ value2 = (value & 0xFFFF0000)>>16;
+ msm_io_w((value1), vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ msm_io_w((value2), vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+static void vfe32_write_la_cfg(enum VFE32_DMI_RAM_SEL channel_sel,
+ const uint32_t *tbl)
+{
+ uint32_t i;
+ uint32_t value, value1, value2;
+
+ vfe32_program_dmi_cfg(channel_sel);
+ /* for loop for extracting init table. */
+ for (i = 0 ; i < (VFE32_LA_TABLE_LENGTH/2) ; i++) {
+ value = *tbl++;
+ value1 = value & 0x0000FFFF;
+ value2 = (value & 0xFFFF0000)>>16;
+ msm_io_w((value1), vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ msm_io_w((value2), vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+}
+
+
+static int vfe32_proc_general(struct msm_vfe32_cmd *cmd)
+{
+ int i , rc = 0;
+ uint32_t old_val = 0 , new_val = 0;
+ uint32_t *cmdp = NULL;
+ uint32_t *cmdp_local = NULL;
+ uint32_t snapshot_cnt = 0;
+
+ CDBG("vfe32_proc_general: cmdID = %s, length = %d\n",
+ vfe32_general_cmd[cmd->id], cmd->length);
+ switch (cmd->id) {
+ case V32_RESET:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ vfe32_reset();
+ break;
+ case V32_START:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ rc = vfe32_start();
+ break;
+ case V32_UPDATE:
+ vfe32_update();
+ break;
+ case V32_CAPTURE:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ if (copy_from_user(&snapshot_cnt, (void __user *)(cmd->value),
+ sizeof(uint32_t))) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe32_capture(snapshot_cnt);
+ break;
+ case V32_START_RECORDING:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ rc = vfe32_start_recording();
+ break;
+ case V32_STOP_RECORDING:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ rc = vfe32_stop_recording();
+ break;
+ case V32_OPERATION_CFG: {
+ if (cmd->length != V32_OPERATION_CFG_LEN) {
+ rc = -EINVAL;
+ goto proc_general_done;
+ }
+ cmdp = kmalloc(V32_OPERATION_CFG_LEN, GFP_ATOMIC);
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ V32_OPERATION_CFG_LEN)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ rc = vfe32_operation_config(cmdp);
+ }
+ break;
+
+ case V32_STATS_AE_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AE_BG_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+ case V32_STATS_AF_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AF_BF_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+ case V32_STATS_AWB_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= AWB_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+ case V32_STATS_IHIST_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= IHIST_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+
+ case V32_STATS_RS_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /*
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= RS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ */
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+ case V32_STATS_CS_START: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ /*
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ */
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+ case V32_MCE_UPDATE:
+ case V32_MCE_CFG:{
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ /* Incrementing with 4 so as to point to the 2nd Register as
+ the 2nd register has the mce_enable bit */
+ old_val = msm_io_r(vfe32_ctrl->vfebase + V32_CHROMA_EN_OFF + 4);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+ old_val &= MCE_EN_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_CHROMA_EN_OFF + 4,
+ &new_val, 4);
+ cmdp_local += 1;
+
+ old_val = msm_io_r(vfe32_ctrl->vfebase + V32_CHROMA_EN_OFF + 8);
+ new_val = *cmdp_local;
+ old_val &= MCE_Q_K_MASK;
+ new_val = new_val | old_val;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_CHROMA_EN_OFF + 8,
+ &new_val, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp_local, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+ case V32_BLACK_LEVEL_CFG:
+ rc = -EFAULT;
+ goto proc_general_done;
+ case V32_ROLL_OFF_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value) , cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp_local, 16);
+ cmdp_local += 4;
+ vfe32_program_dmi_cfg(ROLLOFF_RAM);
+ /* for loop for extrcting init table. */
+ for (i = 0 ; i < (VFE32_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) {
+ msm_io_w(*cmdp_local ,
+ vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ CDBG("done writing init table\n");
+ /* by default, always starts with offset 0. */
+ msm_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET,
+ vfe32_ctrl->vfebase + VFE_DMI_ADDR);
+ /* for loop for extracting delta table. */
+ for (i = 0 ; i < (VFE32_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) {
+ msm_io_w(*cmdp_local,
+ vfe32_ctrl->vfebase + VFE_DMI_DATA_LO);
+ cmdp_local++;
+ }
+ vfe32_program_dmi_cfg(NO_MEM_SELECTED);
+ }
+ break;
+
+ case V32_LA_CFG:
+ case V32_LA_UPDATE: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+
+ old_val = *cmdp;
+ cmdp += 1;
+ if (old_val == 0x0)
+ vfe32_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK0 , cmdp);
+ else
+ vfe32_write_la_cfg(LUMA_ADAPT_LUT_RAM_BANK1 , cmdp);
+ cmdp -= 1;
+ }
+ break;
+
+ case V32_SK_ENHAN_CFG:
+ case V32_SK_ENHAN_UPDATE:{
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_SCE_OFF,
+ cmdp, V32_SCE_LEN);
+ }
+ break;
+
+ case V32_LIVESHOT:
+ vfe32_liveshot();
+ break;
+
+ case V32_LINEARIZATION:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF1,
+ cmdp_local, V32_LINEARIZATION_LEN1);
+ cmdp_local += 4;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_LINEARIZATION_OFF2,
+ cmdp_local,
+ V32_LINEARIZATION_LEN2);
+ break;
+
+ case V32_DEMOSAICV3:
+ if (cmd->length !=
+ V32_DEMOSAICV3_0_LEN+V32_DEMOSAICV3_1_LEN) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+ cmdp_local, V32_DEMOSAICV3_0_LEN);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_1_OFF,
+ cmdp_local, V32_DEMOSAICV3_1_LEN);
+ break;
+
+ case V32_DEMOSAICV3_ABCC_CFG:
+ rc = -EFAULT;
+ break;
+
+ case V32_DEMOSAICV3_DBCC_CFG:
+ case V32_DEMOSAICV3_DBCC_UPDATE:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = msm_io_r(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+ old_val &= DBCC_MASK;
+
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF,
+ cmdp_local, 4);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp_local, (vfe32_cmd[cmd->id].length));
+ break;
+
+ case V32_DEMOSAICV3_DBPC_CFG:
+ case V32_DEMOSAICV3_DBPC_UPDATE:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ cmdp_local = cmdp;
+ new_val = *cmdp_local;
+
+ old_val = msm_io_r(vfe32_ctrl->vfebase + V32_DEMOSAICV3_0_OFF);
+ old_val &= DBPC_MASK;
+
+ new_val = new_val | old_val;
+ *cmdp_local = new_val;
+ msm_io_memcpy(vfe32_ctrl->vfebase +
+ V32_DEMOSAICV3_0_OFF,
+ cmdp_local, V32_DEMOSAICV3_LEN);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase +
+ V32_DEMOSAICV3_DBPC_CFG_OFF,
+ cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase +
+ V32_DEMOSAICV3_DBPC_CFG_OFF0,
+ cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase +
+ V32_DEMOSAICV3_DBPC_CFG_OFF1,
+ cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+ cmdp_local += 1;
+ msm_io_memcpy(vfe32_ctrl->vfebase +
+ V32_DEMOSAICV3_DBPC_CFG_OFF2,
+ cmdp_local, V32_DEMOSAICV3_DBPC_LEN);
+ break;
+
+ case V32_RGB_G_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_RGB_G_OFF,
+ cmdp, 4);
+ cmdp += 1;
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0 , cmdp);
+ cmdp -= 1;
+ }
+ break;
+
+ case V32_RGB_G_UPDATE: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+
+ msm_io_memcpy(vfe32_ctrl->vfebase + V32_RGB_G_OFF, cmdp, 4);
+ old_val = *cmdp;
+ cmdp += 1;
+
+ if (old_val) {
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1 , cmdp);
+ } else {
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0 , cmdp);
+ vfe32_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0 , cmdp);
+ }
+ cmdp -= 1;
+ }
+ break;
+
+ case V32_STATS_AWB_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AWB_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V32_STATS_AE_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AE_BG_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V32_STATS_AF_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~AF_BF_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V32_STATS_IHIST_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~IHIST_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V32_STATS_RS_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~RS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+
+ case V32_STATS_CS_STOP: {
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ break;
+ case V32_STOP:
+ pr_info("vfe32_proc_general: cmdID = %s\n",
+ vfe32_general_cmd[cmd->id]);
+ vfe32_stop();
+ break;
+
+ case V32_SYNC_TIMER_SETTING:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp, (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ vfe32_sync_timer_start(cmdp);
+ break;
+
+ case V32_EZTUNE_CFG: {
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto proc_general_done;
+ }
+ *cmdp &= ~STATS_ENABLE_MASK;
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= STATS_ENABLE_MASK;
+ *cmdp |= old_val;
+
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+ default: {
+ if (cmd->length != vfe32_cmd[cmd->id].length)
+ return -EINVAL;
+
+ cmdp = kmalloc(vfe32_cmd[cmd->id].length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto proc_general_done;
+ }
+
+ CHECKED_COPY_FROM_USER(cmdp);
+ msm_io_memcpy(vfe32_ctrl->vfebase + vfe32_cmd[cmd->id].offset,
+ cmdp, (vfe32_cmd[cmd->id].length));
+ }
+ break;
+
+ }
+
+proc_general_done:
+ kfree(cmdp);
+
+ return rc;
+}
+
+static void vfe32_stats_af_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+ vfe32_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->afStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+}
+
+static void vfe32_stats_awb_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+ vfe32_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->awbStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+}
+
+static void vfe32_stats_aec_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+ vfe32_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->aecStatsControl.ackPending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+}
+
+static void vfe32_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->ihistStatsControl.ackPending = FALSE;
+}
+static void vfe32_stats_rs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe32_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->rsStatsControl.ackPending = FALSE;
+}
+static void vfe32_stats_cs_ack(struct vfe_cmd_stats_ack *pAck)
+{
+ vfe32_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf;
+ vfe32_ctrl->csStatsControl.ackPending = FALSE;
+}
+
+
+static inline void vfe32_read_irq_status(struct vfe32_irq_status *out)
+{
+ uint32_t *temp;
+ memset(out, 0, sizeof(struct vfe32_irq_status));
+ temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_IRQ_STATUS_0);
+ out->vfeIrqStatus0 = msm_io_r(temp);
+
+ temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_IRQ_STATUS_1);
+ out->vfeIrqStatus1 = msm_io_r(temp);
+
+ temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_CAMIF_STATUS);
+ out->camifStatus = msm_io_r(temp);
+ CDBG("camifStatus = 0x%x\n", out->camifStatus);
+
+ /* clear the pending interrupt of the same kind.*/
+ msm_io_w(out->vfeIrqStatus0, vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_0);
+ msm_io_w(out->vfeIrqStatus1, vfe32_ctrl->vfebase + VFE_IRQ_CLEAR_1);
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(1, vfe32_ctrl->vfebase + VFE_IRQ_CMD);
+
+}
+
+static void vfe32_send_msg_no_payload(enum VFE32_MESSAGE_ID id)
+{
+ struct vfe_message msg;
+
+ CDBG("vfe32_send_msg_no_payload\n");
+ msg._d = id;
+ vfe32_proc_ops(id, &msg, 0);
+}
+
+static void vfe32_process_reg_update_irq(void)
+{
+ uint32_t temp, old_val;
+ unsigned long flags;
+ if (vfe32_ctrl->req_start_video_rec) {
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_V) {
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out2.ch0]);
+ msm_io_w(1, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out2.ch1]);
+ /* Mask with 0x7 to extract the pixel pattern*/
+ switch (msm_io_r(vfe32_ctrl->vfebase + VFE_CFG)
+ & 0x7) {
+ case VFE_YUV_YCbYCr:
+ case VFE_YUV_YCrYCb:
+ case VFE_YUV_CbYCrY:
+ case VFE_YUV_CrYCbY:
+ msm_io_w_mb(1,
+ vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ break;
+ default:
+ break;
+ }
+ }
+ vfe32_ctrl->req_start_video_rec = FALSE;
+ if (vpe_ctrl && vpe_ctrl->dis_en) {
+ old_val = msm_io_r(
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val |= RS_CS_ENABLE_MASK;
+ msm_io_w(old_val,
+ vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ }
+ CDBG("start video triggered .\n");
+ } else if (vfe32_ctrl->req_stop_video_rec) {
+ if (vfe32_ctrl->outpath.output_mode & VFE32_OUTPUT_MODE_V) {
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out2.ch0]);
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out2.ch1]);
+ /* Mask with 0x7 to extract the pixel pattern*/
+ switch (msm_io_r(vfe32_ctrl->vfebase + VFE_CFG)
+ & 0x7) {
+ case VFE_YUV_YCbYCr:
+ case VFE_YUV_YCrYCb:
+ case VFE_YUV_CbYCrY:
+ case VFE_YUV_CrYCbY:
+ msm_io_w_mb(1,
+ vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ break;
+ default:
+ break;
+ }
+ }
+ vfe32_ctrl->req_stop_video_rec = FALSE;
+ vfe32_send_msg_no_payload(MSG_ID_STOP_REC_ACK);
+
+ /*disable rs& cs when stop recording. */
+ old_val = msm_io_r(vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= (~RS_CS_ENABLE_MASK);
+ msm_io_w(old_val, vfe32_ctrl->vfebase + VFE_MODULE_CFG);
+
+ CDBG("stop video triggered .\n");
+ }
+ if (vfe32_ctrl->start_ack_pending == TRUE) {
+ vfe32_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe32_ctrl->start_ack_pending = FALSE;
+ } else {
+ spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
+ if (vfe32_ctrl->update_ack_pending == TRUE) {
+ vfe32_ctrl->update_ack_pending = FALSE;
+ spin_unlock_irqrestore(
+ &vfe32_ctrl->update_ack_lock, flags);
+ vfe32_send_msg_no_payload(MSG_ID_UPDATE_ACK);
+ } else {
+ spin_unlock_irqrestore(
+ &vfe32_ctrl->update_ack_lock, flags);
+ }
+ }
+ if (vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT) { /* in snapshot mode */
+ /* later we need to add check for live snapshot mode. */
+ vfe32_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe32_ctrl->vfe_capture_count == 0) {
+ /* stop the bus output: write master enable = 0*/
+ if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_PT) {
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->
+ outpath.out0.ch0]);
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->
+ outpath.out0.ch1]);
+ }
+ if (vfe32_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_S) {
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->
+ outpath.out1.ch0]);
+ msm_io_w(0, vfe32_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[vfe32_ctrl->
+ outpath.out1.ch1]);
+ }
+
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+
+ /* Ensure the read order while reading
+ to the command register using the barrier */
+ temp = msm_io_r_mb(vfe32_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ /* then do reg_update. */
+ msm_io_w(1, vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
+ }
+ } /* if snapshot mode. */
+}
+
+static void vfe32_set_default_reg_values(void)
+{
+ msm_io_w(0x800080, vfe32_ctrl->vfebase + VFE_DEMUX_GAIN_0);
+ msm_io_w(0x800080, vfe32_ctrl->vfebase + VFE_DEMUX_GAIN_1);
+ /* What value should we program CGC_OVERRIDE to? */
+ msm_io_w(0xFFFFF, vfe32_ctrl->vfebase + VFE_CGC_OVERRIDE);
+
+ /* default frame drop period and pattern */
+ msm_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG);
+ msm_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG);
+ msm_io_w(0xFFFFFFFF, vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN);
+ msm_io_w(0xFFFFFFFF,
+ vfe32_ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN);
+ msm_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y);
+ msm_io_w(0x1f, vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR);
+ msm_io_w(0xFFFFFFFF,
+ vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN);
+ msm_io_w(0xFFFFFFFF,
+ vfe32_ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN);
+ msm_io_w(0, vfe32_ctrl->vfebase + VFE_CLAMP_MIN);
+ msm_io_w(0xFFFFFF, vfe32_ctrl->vfebase + VFE_CLAMP_MAX);
+
+ /* stats UB config */
+ msm_io_w(0x3980007, vfe32_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
+ msm_io_w(0x3A00007, vfe32_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
+ msm_io_w(0x3A8000F, vfe32_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
+ msm_io_w(0x3B80007, vfe32_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
+ msm_io_w(0x3C0001F, vfe32_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
+ msm_io_w(0x3E0001F, vfe32_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+}
+
+static void vfe32_process_reset_irq(void)
+{
+ unsigned long flags;
+
+ atomic_set(&vfe32_ctrl->vstate, 0);
+
+ spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+ if (vfe32_ctrl->stop_ack_pending) {
+ vfe32_ctrl->stop_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+ vfe32_send_msg_no_payload(MSG_ID_STOP_ACK);
+ } else {
+ spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+ /* this is from reset command. */
+ vfe32_set_default_reg_values();
+
+ /* reload all write masters. (frame & line)*/
+ msm_io_w(0x7FFF, vfe32_ctrl->vfebase + VFE_BUS_CMD);
+ vfe32_send_msg_no_payload(MSG_ID_RESET_ACK);
+ }
+}
+
+static void vfe32_process_camif_sof_irq(void)
+{
+ uint32_t temp;
+
+ /* in raw snapshot mode */
+ if (vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
+ if (vfe32_ctrl->start_ack_pending) {
+ vfe32_send_msg_no_payload(MSG_ID_START_ACK);
+ vfe32_ctrl->start_ack_pending = FALSE;
+ }
+ vfe32_ctrl->vfe_capture_count--;
+ /* if last frame to be captured: */
+ if (vfe32_ctrl->vfe_capture_count == 0) {
+ /* Ensure the write order while writing
+ to the command register using the barrier */
+ msm_io_w_mb(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY,
+ vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
+ temp = msm_io_r_mb(vfe32_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+ } /* if raw snapshot mode. */
+ vfe32_send_msg_no_payload(MSG_ID_SOF_ACK);
+ vfe32_ctrl->vfeFrameId++;
+ CDBG("camif_sof_irq, frameId = %d\n", vfe32_ctrl->vfeFrameId);
+
+ if (vfe32_ctrl->sync_timer_state) {
+ if (vfe32_ctrl->sync_timer_repeat_count == 0)
+ vfe32_sync_timer_stop();
+ else
+ vfe32_ctrl->sync_timer_repeat_count--;
+ }
+}
+
+static void vfe32_process_error_irq(uint32_t errStatus)
+{
+ uint32_t camifStatus;
+ uint32_t *temp;
+
+ if (errStatus & VFE32_IMASK_CAMIF_ERROR) {
+ pr_err("vfe32_irq: camif errors\n");
+ temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_CAMIF_STATUS);
+ camifStatus = msm_io_r(temp);
+ pr_err("camifStatus = 0x%x\n", camifStatus);
+ vfe32_send_msg_no_payload(MSG_ID_CAMIF_ERROR);
+ }
+
+ if (errStatus & VFE32_IMASK_BHIST_OVWR)
+ pr_err("vfe32_irq: stats bhist overwrite\n");
+
+ if (errStatus & VFE32_IMASK_STATS_CS_OVWR)
+ pr_err("vfe32_irq: stats cs overwrite\n");
+
+ if (errStatus & VFE32_IMASK_STATS_IHIST_OVWR)
+ pr_err("vfe32_irq: stats ihist overwrite\n");
+
+ if (errStatus & VFE32_IMASK_REALIGN_BUF_Y_OVFL)
+ pr_err("vfe32_irq: realign bug Y overflow\n");
+
+ if (errStatus & VFE32_IMASK_REALIGN_BUF_CB_OVFL)
+ pr_err("vfe32_irq: realign bug CB overflow\n");
+
+ if (errStatus & VFE32_IMASK_REALIGN_BUF_CR_OVFL)
+ pr_err("vfe32_irq: realign bug CR overflow\n");
+
+ if (errStatus & VFE32_IMASK_VIOLATION)
+ pr_err("vfe32_irq: violation interrupt\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_0_BUS_OVFL)
+ pr_err("vfe32_irq: image master 0 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_1_BUS_OVFL)
+ pr_err("vfe32_irq: image master 1 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_2_BUS_OVFL)
+ pr_err("vfe32_irq: image master 2 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_3_BUS_OVFL)
+ pr_err("vfe32_irq: image master 3 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_4_BUS_OVFL)
+ pr_err("vfe32_irq: image master 4 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_5_BUS_OVFL)
+ pr_err("vfe32_irq: image master 5 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_IMG_MAST_6_BUS_OVFL)
+ pr_err("vfe32_irq: image master 6 bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_AE_BG_BUS_OVFL)
+ pr_err("vfe32_irq: ae/bg stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_AF_BF_BUS_OVFL)
+ pr_err("vfe32_irq: af/bf stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_AWB_BUS_OVFL)
+ pr_err("vfe32_irq: awb stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_RS_BUS_OVFL)
+ pr_err("vfe32_irq: rs stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_CS_BUS_OVFL)
+ pr_err("vfe32_irq: cs stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_IHIST_BUS_OVFL)
+ pr_err("vfe32_irq: ihist stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_STATS_SKIN_BHIST_BUS_OVFL)
+ pr_err("vfe32_irq: skin/bhist stats bus overflow\n");
+
+ if (errStatus & VFE32_IMASK_AXI_ERROR)
+ pr_err("vfe32_irq: axi error\n");
+}
+
+#define VFE32_AXI_OFFSET 0x0050
+#define vfe32_get_ch_ping_addr(chn) \
+ (msm_io_r(vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe32_get_ch_pong_addr(chn) \
+ (msm_io_r(vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe32_get_ch_addr(ping_pong, chn) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe32_get_ch_pong_addr(chn) : vfe32_get_ch_ping_addr(chn))
+
+#define vfe32_put_ch_ping_addr(chn, addr) \
+ (msm_io_w((addr), vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn)))
+#define vfe32_put_ch_pong_addr(chn, addr) \
+ (msm_io_w((addr), vfe32_ctrl->vfebase + 0x0050 + 0x18 * (chn) + 4))
+#define vfe32_put_ch_addr(ping_pong, chn, addr) \
+ (((ping_pong) & (1 << (chn))) == 0 ? \
+ vfe32_put_ch_pong_addr((chn), (addr)) : \
+ vfe32_put_ch_ping_addr((chn), (addr)))
+
+static void vfe32_process_output_path_irq_0(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ uint8_t out_bool = 0;
+ struct vfe32_free_buf *free_buf = NULL;
+ free_buf = vfe32_dequeue_free_buf(&vfe32_ctrl->outpath.out0);
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT ||
+ vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) &&
+ (vfe32_ctrl->vfe_capture_count <= 1)) ||
+ free_buf;
+ if (out_bool) {
+ ping_pong = msm_io_r(vfe32_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out0.ch1);
+
+ CDBG("output path 0, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out0.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out0.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ }
+ if (vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT) {
+ /* will add message for multi-shot. */
+ vfe32_ctrl->outpath.out0.capture_cnt--;
+ vfe_send_outmsg(MSG_ID_OUTPUT_T, pyaddr,
+ pcbcraddr);
+ } else {
+ /* always send message for continous mode. */
+ /* if continuous mode, for display. (preview) */
+ vfe_send_outmsg(MSG_ID_OUTPUT_P, pyaddr,
+ pcbcraddr);
+ }
+ } else {
+ vfe32_ctrl->outpath.out0.frame_drop_cnt++;
+ pr_warning("path_irq_0 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out0.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out0.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out0.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out0.ch0,
+ pyaddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out0.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out0.ch1,
+ pcbcraddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out0.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+}
+
+static void vfe32_process_output_path_irq_1(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ /* this must be snapshot main image output. */
+ uint8_t out_bool = 0;
+ struct vfe32_free_buf *free_buf = NULL;
+ free_buf = vfe32_dequeue_free_buf(&vfe32_ctrl->outpath.out1);
+
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ -- when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT ||
+ vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) &&
+ (vfe32_ctrl->vfe_capture_count <= 1)) || free_buf;
+ if (out_bool) {
+ ping_pong = msm_io_r(vfe32_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out1.ch1);
+
+ CDBG("snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ if (free_buf) {
+ /* Y channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out1.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out1.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ }
+ if (vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT ||
+ vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
+ vfe32_ctrl->outpath.out1.capture_cnt--;
+ vfe_send_outmsg(MSG_ID_OUTPUT_S, pyaddr,
+ pcbcraddr);
+ }
+ } else {
+ vfe32_ctrl->outpath.out1.frame_drop_cnt++;
+ pr_warning("path_irq_1 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out1.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out1.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out1.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out1.ch0,
+ pyaddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out1.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out1.ch1,
+ pcbcraddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out1.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+}
+
+static void vfe32_process_output_path_irq_2(void)
+{
+ uint32_t ping_pong;
+ uint32_t pyaddr, pcbcraddr;
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ uint32_t pyaddr_ping, pcbcraddr_ping, pyaddr_pong, pcbcraddr_pong;
+#endif
+ uint8_t out_bool = 0;
+ struct vfe32_free_buf *free_buf = NULL;
+ free_buf = vfe32_dequeue_free_buf(&vfe32_ctrl->outpath.out2);
+
+ /* we render frames in the following conditions:
+ 1. Continuous mode and the free buffer is avaialable.
+ 2. In snapshot shot mode, free buffer is not always available.
+ -- when pending snapshot count is <=1, then no need to use
+ free buffer.
+ */
+ out_bool =
+ ((vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT) &&
+ (vfe32_ctrl->vfe_capture_count <= 1)) || free_buf;
+
+ CDBG("%s: op mode = %d, capture_cnt = %d\n", __func__,
+ vfe32_ctrl->operation_mode, vfe32_ctrl->vfe_capture_count);
+
+ if (out_bool) {
+ ping_pong = msm_io_r(vfe32_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS);
+
+ /* Y channel */
+ pyaddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr = vfe32_get_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out2.ch1);
+
+ CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+
+ if (free_buf) {
+ /* Y channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out2.ch0,
+ free_buf->paddr + free_buf->y_off);
+ /* Chroma channel */
+ vfe32_put_ch_addr(ping_pong,
+ vfe32_ctrl->outpath.out2.ch1,
+ free_buf->paddr + free_buf->cbcr_off);
+ }
+ vfe_send_outmsg(MSG_ID_OUTPUT_V, pyaddr, pcbcraddr);
+ } else {
+ vfe32_ctrl->outpath.out2.frame_drop_cnt++;
+ pr_warning("path_irq_2 - no free buffer!\n");
+#ifdef CONFIG_MSM_CAMERA_V4L2
+ pr_info("Swapping ping and pong\n");
+
+ /*get addresses*/
+ /* Y channel */
+ pyaddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr_ping = vfe32_get_ch_ping_addr(
+ vfe32_ctrl->outpath.out2.ch1);
+ /* Y channel */
+ pyaddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out2.ch0);
+ /* Chroma channel */
+ pcbcraddr_pong = vfe32_get_ch_pong_addr(
+ vfe32_ctrl->outpath.out2.ch1);
+
+ CDBG("ping = 0x%p, pong = 0x%p\n", (void *)pyaddr_ping,
+ (void *)pyaddr_pong);
+ CDBG("ping_cbcr = 0x%p, pong_cbcr = 0x%p\n",
+ (void *)pcbcraddr_ping, (void *)pcbcraddr_pong);
+
+ /*put addresses*/
+ /* SWAP y channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out2.ch0,
+ pyaddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out2.ch0,
+ pyaddr_ping);
+ /* SWAP chroma channel*/
+ vfe32_put_ch_ping_addr(vfe32_ctrl->outpath.out2.ch1,
+ pcbcraddr_pong);
+ vfe32_put_ch_pong_addr(vfe32_ctrl->outpath.out2.ch1,
+ pcbcraddr_ping);
+ CDBG("after swap: ping = 0x%p, pong = 0x%p\n",
+ (void *)pyaddr_pong, (void *)pyaddr_ping);
+#endif
+ }
+}
+
+static void vfe32_process_stats_comb_irq(uint32_t *irqstatus)
+{
+ return;
+}
+
+static uint32_t vfe32_process_stats_irq_common(uint32_t statsNum,
+ uint32_t newAddr) {
+
+ uint32_t pingpongStatus;
+ uint32_t returnAddr;
+ uint32_t pingpongAddr;
+
+ /* must be 0=ping, 1=pong */
+ pingpongStatus =
+ ((msm_io_r(vfe32_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS))
+ & ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
+ /* stats bits starts at 7 */
+ CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+ pingpongAddr =
+ ((uint32_t)(vfe32_ctrl->vfebase +
+ VFE_BUS_STATS_PING_PONG_BASE)) +
+ (3*statsNum)*4 + (1-pingpongStatus)*4;
+ returnAddr = msm_io_r((uint32_t *)pingpongAddr);
+ msm_io_w(newAddr, (uint32_t *)pingpongAddr);
+ return returnAddr;
+}
+
+static void
+vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum)
+{
+ unsigned long flags;
+ struct vfe_message msg;
+ /* fill message with right content. */
+ /* @todo This is causing issues, need further investigate */
+ /* spin_lock_irqsave(&ctrl->state_lock, flags); */
+ msg._u.msgStats.frameCounter = vfe32_ctrl->vfeFrameId;
+ msg._u.msgStats.buffer = bufAddress;
+
+ switch (statsNum) {
+ case statsAeNum:{
+ msg._d = MSG_ID_STATS_AEC;
+ spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+ vfe32_ctrl->aecStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+ }
+ break;
+ case statsAfNum:{
+ msg._d = MSG_ID_STATS_AF;
+ spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+ vfe32_ctrl->afStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+ }
+ break;
+ case statsAwbNum: {
+ msg._d = MSG_ID_STATS_AWB;
+ spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+ vfe32_ctrl->awbStatsControl.ackPending = TRUE;
+ spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+ }
+ break;
+
+ case statsIhistNum: {
+ msg._d = MSG_ID_STATS_IHIST;
+ vfe32_ctrl->ihistStatsControl.ackPending = TRUE;
+ }
+ break;
+ case statsRsNum: {
+ msg._d = MSG_ID_STATS_RS;
+ vfe32_ctrl->rsStatsControl.ackPending = TRUE;
+ }
+ break;
+ case statsCsNum: {
+ msg._d = MSG_ID_STATS_CS;
+ vfe32_ctrl->csStatsControl.ackPending = TRUE;
+ }
+ break;
+
+ default:
+ goto stats_done;
+ }
+
+ vfe32_proc_ops(msg._d,
+ &msg, sizeof(struct vfe_message));
+stats_done:
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return;
+}
+
+static void vfe32_process_stats_ae_irq(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->aec_ack_lock, flags);
+ if (!(vfe32_ctrl->aecStatsControl.ackPending)) {
+ spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+ vfe32_ctrl->aecStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsAeNum,
+ vfe32_ctrl->aecStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->aecStatsControl.bufToRender,
+ statsAeNum);
+ } else{
+ spin_unlock_irqrestore(&vfe32_ctrl->aec_ack_lock, flags);
+ vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+static void vfe32_process_stats_awb_irq(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->awb_ack_lock, flags);
+ if (!(vfe32_ctrl->awbStatsControl.ackPending)) {
+ spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+ vfe32_ctrl->awbStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsAwbNum,
+ vfe32_ctrl->awbStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->awbStatsControl.bufToRender,
+ statsAwbNum);
+ } else{
+ spin_unlock_irqrestore(&vfe32_ctrl->awb_ack_lock, flags);
+ vfe32_ctrl->awbStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+static void vfe32_process_stats_af_irq(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->af_ack_lock, flags);
+ if (!(vfe32_ctrl->afStatsControl.ackPending)) {
+ spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+ vfe32_ctrl->afStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsAfNum,
+ vfe32_ctrl->afStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->afStatsControl.bufToRender,
+ statsAfNum);
+ } else{
+ spin_unlock_irqrestore(&vfe32_ctrl->af_ack_lock, flags);
+ vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+ }
+}
+
+static void vfe32_process_stats_ihist_irq(void)
+{
+ if (!(vfe32_ctrl->ihistStatsControl.ackPending)) {
+ vfe32_ctrl->ihistStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsIhistNum,
+ vfe32_ctrl->ihistStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->ihistStatsControl.bufToRender,
+ statsIhistNum);
+ } else
+ vfe32_ctrl->ihistStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe32_process_stats_rs_irq(void)
+{
+ if (!(vfe32_ctrl->rsStatsControl.ackPending)) {
+ vfe32_ctrl->rsStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsRsNum,
+ vfe32_ctrl->rsStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->rsStatsControl.bufToRender,
+ statsRsNum);
+ } else
+ vfe32_ctrl->rsStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe32_process_stats_cs_irq(void)
+{
+ if (!(vfe32_ctrl->csStatsControl.ackPending)) {
+ vfe32_ctrl->csStatsControl.bufToRender =
+ vfe32_process_stats_irq_common(statsCsNum,
+ vfe32_ctrl->csStatsControl.nextFrameAddrBuf);
+
+ vfe_send_stats_msg(vfe32_ctrl->csStatsControl.bufToRender,
+ statsCsNum);
+ } else
+ vfe32_ctrl->csStatsControl.droppedStatsFrameCount++;
+}
+
+static void vfe32_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+
+ struct vfe32_isr_queue_cmd *qcmd = NULL;
+
+ CDBG("=== vfe32_do_tasklet start ===\n");
+
+ while (atomic_read(&irq_cnt)) {
+ spin_lock_irqsave(&vfe32_ctrl->tasklet_lock, flags);
+ qcmd = list_first_entry(&vfe32_ctrl->tasklet_q,
+ struct vfe32_isr_queue_cmd, list);
+ atomic_sub(1, &irq_cnt);
+
+ if (!qcmd) {
+ spin_unlock_irqrestore(&vfe32_ctrl->tasklet_lock,
+ flags);
+ return;
+ }
+
+ list_del(&qcmd->list);
+ spin_unlock_irqrestore(&vfe32_ctrl->tasklet_lock,
+ flags);
+
+ /* interrupt to be processed, *qcmd has the payload. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_REG_UPDATE_MASK) {
+ CDBG("irq regUpdateIrq\n");
+ vfe32_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus1 &
+ VFE_IMASK_WHILE_STOPPING_1) {
+ CDBG("irq resetAckIrq\n");
+ vfe32_process_reset_irq();
+ }
+
+ if (atomic_read(&vfe32_ctrl->vstate)) {
+ if (qcmd->vfeInterruptStatus1 &
+ VFE32_IMASK_ERROR_ONLY_1) {
+ pr_err("irq errorIrq\n");
+ vfe32_process_error_irq(
+ qcmd->vfeInterruptStatus1 &
+ VFE32_IMASK_ERROR_ONLY_1);
+ }
+ /* next, check output path related interrupts. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) {
+ CDBG("Image composite done 0 irq occured.\n");
+ vfe32_process_output_path_irq_0();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) {
+ CDBG("Image composite done 1 irq occured.\n");
+ vfe32_process_output_path_irq_1();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) {
+ CDBG("Image composite done 2 irq occured.\n");
+ vfe32_process_output_path_irq_2();
+ }
+ /* in snapshot mode if done then send
+ snapshot done message */
+ if (vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_SNAPSHOT ||
+ vfe32_ctrl->operation_mode ==
+ VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
+ if ((vfe32_ctrl->outpath.out0.capture_cnt == 0)
+ && (vfe32_ctrl->outpath.out1.
+ capture_cnt == 0)) {
+ vfe32_send_msg_no_payload(
+ MSG_ID_SNAPSHOT_DONE);
+
+ /* Ensure the write order while writing
+ to the cmd register using barrier */
+ msm_io_w_mb(
+ CAMIF_COMMAND_STOP_IMMEDIATELY,
+ vfe32_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
+ }
+ /* then process stats irq. */
+ if (vfe32_ctrl->stats_comp) {
+ /* process stats comb interrupt. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) {
+ CDBG("Stats composite irq occured.\n");
+ vfe32_process_stats_comb_irq(
+ &qcmd->vfeInterruptStatus0);
+ }
+ } else {
+ /* process individual stats interrupt. */
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_AEC) {
+ CDBG("Stats AEC irq occured.\n");
+ vfe32_process_stats_ae_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_AWB) {
+ CDBG("Stats AWB irq occured.\n");
+ vfe32_process_stats_awb_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_AF) {
+ CDBG("Stats AF irq occured.\n");
+ vfe32_process_stats_af_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_IHIST) {
+ CDBG("Stats IHIST irq occured.\n");
+ vfe32_process_stats_ihist_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_RS) {
+ CDBG("Stats RS irq occured.\n");
+ vfe32_process_stats_rs_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_CS) {
+ CDBG("Stats CS irq occured.\n");
+ vfe32_process_stats_cs_irq();
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER0) {
+ CDBG("SYNC_TIMER 0 irq occured.\n");
+ vfe32_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER0_DONE);
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER1) {
+ CDBG("SYNC_TIMER 1 irq occured.\n");
+ vfe32_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER1_DONE);
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_SYNC_TIMER2) {
+ CDBG("SYNC_TIMER 2 irq occured.\n");
+ vfe32_send_msg_no_payload(
+ MSG_ID_SYNC_TIMER2_DONE);
+ }
+ }
+ }
+ if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_CAMIF_SOF_MASK) {
+ CDBG("irq camifSofIrq\n");
+ vfe32_process_camif_sof_irq();
+ }
+ kfree(qcmd);
+ }
+ CDBG("=== vfe32_do_tasklet end ===\n");
+}
+
+DECLARE_TASKLET(vfe32_tasklet, vfe32_do_tasklet, 0);
+
+static irqreturn_t vfe32_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct vfe32_irq_status irq;
+ struct vfe32_isr_queue_cmd *qcmd;
+
+ CDBG("vfe_parse_irq\n");
+
+ vfe32_read_irq_status(&irq);
+
+ if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) {
+ CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n");
+ return IRQ_HANDLED;
+ }
+
+ qcmd = kzalloc(sizeof(struct vfe32_isr_queue_cmd),
+ GFP_ATOMIC);
+ if (!qcmd) {
+ pr_err("vfe_parse_irq: qcmd malloc failed!\n");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&vfe32_ctrl->stop_flag_lock, flags);
+ if (vfe32_ctrl->stop_ack_pending) {
+ irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0;
+ irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1;
+ }
+ spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
+
+ CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n",
+ irq.vfeIrqStatus0, irq.vfeIrqStatus1);
+
+ qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0;
+ qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1;
+
+ spin_lock_irqsave(&vfe32_ctrl->tasklet_lock, flags);
+ list_add_tail(&qcmd->list, &vfe32_ctrl->tasklet_q);
+
+ atomic_add(1, &irq_cnt);
+ spin_unlock_irqrestore(&vfe32_ctrl->tasklet_lock, flags);
+ tasklet_schedule(&vfe32_tasklet);
+ return IRQ_HANDLED;
+}
+
+static int vfe32_resource_init(struct platform_device *pdev, void *sdata)
+{
+ struct resource *vfemem, *vfeirq, *vfeio;
+ int rc;
+ struct msm_camera_sensor_info *s_info;
+ s_info = pdev->dev.platform_data;
+
+ pdev->resource = s_info->resource;
+ pdev->num_resources = s_info->num_resources;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ pr_err("%s: VFE region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ vfe32_ctrl = kzalloc(sizeof(struct vfe32_ctrl_type), GFP_KERNEL);
+ if (!vfe32_ctrl) {
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+
+ vfe32_ctrl->vfeirq = vfeirq->start;
+
+ vfe32_ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!vfe32_ctrl->vfebase) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto cmd_init_failed2;
+ }
+
+ vfe32_ctrl->extdata =
+ kmalloc(sizeof(struct vfe32_frame_extra), GFP_KERNEL);
+ if (!vfe32_ctrl->extdata) {
+ rc = -ENOMEM;
+ goto cmd_init_failed3;
+ }
+
+ vfe32_ctrl->extlen = sizeof(struct vfe32_frame_extra);
+
+ spin_lock_init(&vfe32_ctrl->stop_flag_lock);
+ spin_lock_init(&vfe32_ctrl->state_lock);
+ spin_lock_init(&vfe32_ctrl->io_lock);
+ spin_lock_init(&vfe32_ctrl->update_ack_lock);
+ spin_lock_init(&vfe32_ctrl->tasklet_lock);
+
+ spin_lock_init(&vfe32_ctrl->aec_ack_lock);
+ spin_lock_init(&vfe32_ctrl->awb_ack_lock);
+ spin_lock_init(&vfe32_ctrl->af_ack_lock);
+ INIT_LIST_HEAD(&vfe32_ctrl->tasklet_q);
+ vfe32_init_free_buf_queues();
+
+ vfe32_ctrl->syncdata = sdata;
+ vfe32_ctrl->vfemem = vfemem;
+ vfe32_ctrl->vfeio = vfeio;
+ return 0;
+
+cmd_init_failed3:
+ free_irq(vfe32_ctrl->vfeirq, 0);
+ iounmap(vfe32_ctrl->vfebase);
+cmd_init_failed2:
+ kfree(vfe32_ctrl);
+cmd_init_failed1:
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ return rc;
+}
+
+static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int subdev_cmd, void *arg)
+{
+ struct msm_vfe32_cmd vfecmd;
+ struct msm_camvfe_params *vfe_params =
+ (struct msm_camvfe_params *)arg;
+ struct msm_vfe_cfg_cmd *cmd = vfe_params->vfe_cfg;
+ void *data = vfe_params->data;
+
+ long rc = 0;
+ uint32_t i = 0;
+ struct vfe_cmd_stats_buf *scfg = NULL;
+ struct msm_pmem_region *regptr = NULL;
+ struct vfe_cmd_stats_ack *sack = NULL;
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
+ if (copy_from_user(&vfecmd,
+ (void __user *)(cmd->value),
+ sizeof(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n", __func__,
+ __LINE__);
+ return -EFAULT;
+ }
+ } else {
+ /* here eith stats release or frame release. */
+ if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE) {
+ /* then must be stats release. */
+ if (!data)
+ return -EFAULT;
+ sack = kmalloc(sizeof(struct vfe_cmd_stats_ack),
+ GFP_ATOMIC);
+ if (!sack)
+ return -ENOMEM;
+
+ sack->nextStatsBuf = *(uint32_t *)data;
+ }
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+ if ((cmd->cmd_type == CMD_STATS_AF_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AWB_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_RS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_CS_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_AEC_ENABLE)) {
+ struct axidata *axid;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ goto vfe32_config_done;
+ }
+
+ scfg =
+ kmalloc(sizeof(struct vfe_cmd_stats_buf),
+ GFP_ATOMIC);
+ if (!scfg) {
+ rc = -ENOMEM;
+ goto vfe32_config_done;
+ }
+ regptr = axid->region;
+ if (axid->bufnum1 > 0) {
+ for (i = 0; i < axid->bufnum1; i++) {
+ scfg->statsBuf[i] =
+ (uint32_t)(regptr->paddr);
+ regptr++;
+ }
+ }
+ /* individual */
+ switch (cmd->cmd_type) {
+ case CMD_STATS_AEC_ENABLE:
+ rc = vfe_stats_aec_buf_init(scfg);
+ break;
+ case CMD_STATS_AF_ENABLE:
+ rc = vfe_stats_af_buf_init(scfg);
+ break;
+ case CMD_STATS_AWB_ENABLE:
+ rc = vfe_stats_awb_buf_init(scfg);
+ break;
+ case CMD_STATS_IHIST_ENABLE:
+ rc = vfe_stats_ihist_buf_init(scfg);
+ break;
+ case CMD_STATS_RS_ENABLE:
+ rc = vfe_stats_rs_buf_init(scfg);
+ break;
+ case CMD_STATS_CS_ENABLE:
+ rc = vfe_stats_cs_buf_init(scfg);
+ break;
+ }
+ }
+ switch (cmd->cmd_type) {
+ case CMD_GENERAL:
+ rc = vfe32_proc_general(&vfecmd);
+ break;
+ case CMD_FRAME_BUF_RELEASE: {
+ struct msm_frame *b;
+ unsigned long p;
+ struct vfe32_output_ch *outch = NULL;
+ if (!data) {
+ rc = -EFAULT;
+ break;
+ }
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path);
+
+ if ((b->path & OUTPUT_TYPE_P) || (b->path & OUTPUT_TYPE_T)) {
+ CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n");
+ outch = &vfe32_ctrl->outpath.out0;
+ } else if (b->path & OUTPUT_TYPE_S) {
+ outch = &vfe32_ctrl->outpath.out1;
+ } else if (b->path & OUTPUT_TYPE_V) {
+ outch = &vfe32_ctrl->outpath.out2;
+ } else {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = vfe32_enqueue_free_buf(outch, p, b->y_off, b->cbcr_off);
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+ case CMD_STATS_AEC_BUF_RELEASE:
+ vfe32_stats_aec_ack(sack);
+ break;
+ case CMD_STATS_AF_BUF_RELEASE:
+ vfe32_stats_af_ack(sack);
+ break;
+ case CMD_STATS_AWB_BUF_RELEASE:
+ vfe32_stats_awb_ack(sack);
+ break;
+
+ case CMD_STATS_IHIST_BUF_RELEASE:
+ vfe32_stats_ihist_ack(sack);
+ break;
+ case CMD_STATS_RS_BUF_RELEASE:
+ vfe32_stats_rs_ack(sack);
+ break;
+ case CMD_STATS_CS_BUF_RELEASE:
+ vfe32_stats_cs_ack(sack);
+ break;
+
+ case CMD_AXI_CFG_PREVIEW: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+ axio =
+ kmalloc(vfe32_cmd[V32_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe32_cmd[V32_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe32_config_axi(OUTPUT_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_RAW_PICT_AXI_CFG: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+ axio = kmalloc(vfe32_cmd[V32_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe32_cmd[V32_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe32_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ axio =
+ kmalloc(vfe32_cmd[V32_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe32_cmd[V32_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe32_config_axi(OUTPUT_1_AND_2, axid, axio);
+ kfree(axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO: {
+ struct axidata *axid;
+ uint32_t *axio = NULL;
+ axid = data;
+ if (!axid) {
+ rc = -EFAULT;
+ break;
+ }
+
+ axio = kmalloc(vfe32_cmd[V32_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe32_cmd[V32_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe32_config_axi(OUTPUT_1_AND_3, axid, axio);
+ kfree(axio);
+ }
+ break;
+ default:
+ break;
+ }
+vfe32_config_done:
+ kfree(scfg);
+ kfree(sack);
+ CDBG("%s done: rc = %d\n", __func__, (int) rc);
+ return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_vfe_subdev_core_ops = {
+ .ioctl = msm_vfe_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_vfe_subdev_ops = {
+ .core = &msm_vfe_subdev_core_ops,
+};
+
+int msm_vfe_subdev_init(struct v4l2_subdev *sd, void *data,
+ 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;
+
+ v4l2_subdev_init(sd, &msm_vfe_subdev_ops);
+ v4l2_set_subdev_hostdata(sd, data);
+ snprintf(sd->name, sizeof(sd->name), "vfe3.2");
+
+ vfe_syncdata = data;
+
+ camio_clk = camdev->ioclk;
+
+ rc = vfe32_resource_init(pdev, vfe_syncdata);
+ if (rc < 0)
+ return rc;
+
+ vfe32_ctrl->subdev = sd;
+ /* Bring up all the required GPIOs and Clocks */
+ rc = msm_camio_enable(pdev);
+ msm_camio_set_perf_lvl(S_INIT);
+ msm_camio_set_perf_lvl(S_PREVIEW);
+
+ /* TO DO: Need to release the VFE resources */
+ rc = request_irq(vfe32_ctrl->vfeirq, vfe32_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+
+ return rc;
+}
+
+void msm_vfe_subdev_release(struct platform_device *pdev)
+{
+ struct resource *vfemem, *vfeio;
+
+ vfe32_reset_free_buf_queues();
+ CDBG("%s, free_irq\n", __func__);
+ free_irq(vfe32_ctrl->vfeirq, 0);
+ tasklet_kill(&vfe32_tasklet);
+
+ if (atomic_read(&irq_cnt))
+ pr_warning("%s, Warning IRQ Count not ZERO\n", __func__);
+
+ vfemem = vfe32_ctrl->vfemem;
+ vfeio = vfe32_ctrl->vfeio;
+
+ kfree(vfe32_ctrl->extdata);
+ iounmap(vfe32_ctrl->vfebase);
+ kfree(vfe32_ctrl);
+ vfe32_ctrl = NULL;
+ release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ CDBG("%s, msm_camio_disable\n", __func__);
+ msm_camio_disable(pdev);
+ msm_camio_set_perf_lvl(S_EXIT);
+
+ vfe_syncdata = NULL;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+ fptr->vpe_reg = msm_vpe_reg;
+ fptr->send_frame_to_vpe = msm_send_frame_to_vpe;
+ fptr->vpe_config = msm_vpe_config;
+ fptr->vpe_cfg_update = msm_vpe_cfg_update;
+ fptr->dis = &(vpe_ctrl->dis_en);
+ vpe_ctrl->syncdata = data;
+}
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
new file mode 100644
index 0000000..4d48c6b
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -0,0 +1,1104 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef __MSM_VFE32_H__
+#define __MSM_VFE32_H__
+
+#define TRUE 1
+#define FALSE 0
+
+/* 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-32 = 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-32 = 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
+
+#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK 0x00000001
+#define VFE_IRQ_STATUS0_REG_UPDATE_MASK 0x00000020
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000
+#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000
+#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000
+#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000
+
+#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */
+
+#define VFE_IRQ_STATUS0_SYNC_TIMER0 0x2000000 /* bit 25 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER1 0x4000000 /* bit 26 */
+#define VFE_IRQ_STATUS0_SYNC_TIMER2 0x8000000 /* bit 27 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER0 0x10000000 /* bit 28 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER1 0x20000000 /* bit 29 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER2 0x40000000 /* bit 30 */
+#define VFE_IRQ_STATUS0_ASYNC_TIMER3 0x80000000 /* bit 32 */
+
+/* imask for while waiting for stop ack, driver has already
+ * requested stop, waiting for reset irq, and async timer irq.
+ * For irq_status_0, bit 28-32 are for async timer. For
+ * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack
+ irq */
+#define VFE_IMASK_WHILE_STOPPING_0 0xF0000000
+#define VFE_IMASK_WHILE_STOPPING_1 0x00800000
+
+/* no error irq in mask 0 */
+#define VFE_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE_IMASK_ERROR_ONLY_1 0x003fffff
+
+/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */
+#define BPC_MASK 0xF80C0FFE
+
+/* For BPC bit 1 and 2 are set to zero and other's 1 */
+#define ABF_MASK 0xFFFFFFF9
+
+
+/* For DBPC bit 0 is set to zero and other's 1 */
+#define DBPC_MASK 0xFFFFFFFE
+
+/* For DBPC bit 1 is set to zero and other's 1 */
+#define DBCC_MASK 0xFFFFFFFD
+
+/* For MCE enable bit 28 set to zero and other's 1 */
+#define MCE_EN_MASK 0xEFFFFFFF
+
+/* For MCE Q_K bit 28 to 32 set to zero and other's 1 */
+#define MCE_Q_K_MASK 0x0FFFFFFF
+
+#define AE_BG_ENABLE_MASK 0x00000020 /* bit 5 */
+#define AF_BF_ENABLE_MASK 0x00000040 /* bit 6 */
+#define AWB_ENABLE_MASK 0x00000080 /* bit 7 */
+#define RS_ENABLE_MASK 0x00000100 /* bit 8 */
+#define CS_ENABLE_MASK 0x00000200 /* bit 9 */
+#define RS_CS_ENABLE_MASK 0x00000300 /* bit 8,9 */
+#define CLF_ENABLE_MASK 0x00002000 /* bit 13 */
+#define IHIST_ENABLE_MASK 0x00010000 /* bit 16 */
+#define STATS_ENABLE_MASK 0x000903E0 /* bit 19,16,9,8,7,6,5*/
+
+#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_AE_PINGPONG_STATUS_BIT 0x80
+#define VFE_AF_PINGPONG_STATUS_BIT 0x100
+#define VFE_AWB_PINGPONG_STATUS_BIT 0x200
+
+
+enum VFE32_DMI_RAM_SEL {
+ NO_MEM_SELECTED = 0,
+ BLACK_LUT_RAM_BANK0 = 0x1,
+ BLACK_LUT_RAM_BANK1 = 0x2,
+ ROLLOFF_RAM = 0x3,
+ DEMOSAIC_LUT_RAM_BANK0 = 0x4,
+ DEMOSAIC_LUT_RAM_BANK1 = 0x5,
+ STATS_BHIST_RAM0 = 0x6,
+ STATS_BHIST_RAM1 = 0x7,
+ RGBLUT_RAM_CH0_BANK0 = 0x8,
+ RGBLUT_RAM_CH0_BANK1 = 0x9,
+ RGBLUT_RAM_CH1_BANK0 = 0xa,
+ RGBLUT_RAM_CH1_BANK1 = 0xb,
+ RGBLUT_RAM_CH2_BANK0 = 0xc,
+ RGBLUT_RAM_CH2_BANK1 = 0xd,
+ RGBLUT_CHX_BANK0 = 0xe,
+ RGBLUT_CHX_BANK1 = 0xf,
+ STATS_IHIST_RAM = 0x10,
+ LUMA_ADAPT_LUT_RAM_BANK0 = 0x11,
+ LUMA_ADAPT_LUT_RAM_BANK1 = 0x12
+};
+
+enum VFE_STATE {
+ VFE_STATE_IDLE,
+ VFE_STATE_ACTIVE
+};
+
+#define V32_DUMMY_0 0
+#define V32_SET_CLK 1
+#define V32_RESET 2
+#define V32_START 3
+#define V32_TEST_GEN_START 4
+#define V32_OPERATION_CFG 5
+#define V32_AXI_OUT_CFG 6
+#define V32_CAMIF_CFG 7
+#define V32_AXI_INPUT_CFG 8
+#define V32_BLACK_LEVEL_CFG 9
+#define V32_ROLL_OFF_CFG 10
+#define V32_DEMUX_CFG 11
+#define V32_FOV_CFG 12
+#define V32_MAIN_SCALER_CFG 13
+#define V32_WB_CFG 14
+#define V32_COLOR_COR_CFG 15
+#define V32_RGB_G_CFG 16
+#define V32_LA_CFG 17
+#define V32_CHROMA_EN_CFG 18
+#define V32_CHROMA_SUP_CFG 19
+#define V32_MCE_CFG 20
+#define V32_SK_ENHAN_CFG 21
+#define V32_ASF_CFG 22
+#define V32_S2Y_CFG 23
+#define V32_S2CbCr_CFG 24
+#define V32_CHROMA_SUBS_CFG 25
+#define V32_OUT_CLAMP_CFG 26
+#define V32_FRAME_SKIP_CFG 27
+#define V32_DUMMY_1 28
+#define V32_DUMMY_2 29
+#define V32_DUMMY_3 30
+#define V32_UPDATE 31
+#define V32_BL_LVL_UPDATE 32
+#define V32_DEMUX_UPDATE 33
+#define V32_FOV_UPDATE 34
+#define V32_MAIN_SCALER_UPDATE 35
+#define V32_WB_UPDATE 36
+#define V32_COLOR_COR_UPDATE 37
+#define V32_RGB_G_UPDATE 38
+#define V32_LA_UPDATE 39
+#define V32_CHROMA_EN_UPDATE 40
+#define V32_CHROMA_SUP_UPDATE 41
+#define V32_MCE_UPDATE 42
+#define V32_SK_ENHAN_UPDATE 43
+#define V32_S2CbCr_UPDATE 44
+#define V32_S2Y_UPDATE 45
+#define V32_ASF_UPDATE 46
+#define V32_FRAME_SKIP_UPDATE 47
+#define V32_CAMIF_FRAME_UPDATE 48
+#define V32_STATS_AF_UPDATE 49
+#define V32_STATS_AE_UPDATE 50
+#define V32_STATS_AWB_UPDATE 51
+#define V32_STATS_RS_UPDATE 52
+#define V32_STATS_CS_UPDATE 53
+#define V32_STATS_SKIN_UPDATE 54
+#define V32_STATS_IHIST_UPDATE 55
+#define V32_DUMMY_4 56
+#define V32_EPOCH1_ACK 57
+#define V32_EPOCH2_ACK 58
+#define V32_START_RECORDING 59
+#define V32_STOP_RECORDING 60
+#define V32_DUMMY_5 61
+#define V32_DUMMY_6 62
+#define V32_CAPTURE 63
+#define V32_DUMMY_7 64
+#define V32_STOP 65
+#define V32_GET_HW_VERSION 66
+#define V32_GET_FRAME_SKIP_COUNTS 67
+#define V32_OUTPUT1_BUFFER_ENQ 68
+#define V32_OUTPUT2_BUFFER_ENQ 69
+#define V32_OUTPUT3_BUFFER_ENQ 70
+#define V32_JPEG_OUT_BUF_ENQ 71
+#define V32_RAW_OUT_BUF_ENQ 72
+#define V32_RAW_IN_BUF_ENQ 73
+#define V32_STATS_AF_ENQ 74
+#define V32_STATS_AE_ENQ 75
+#define V32_STATS_AWB_ENQ 76
+#define V32_STATS_RS_ENQ 77
+#define V32_STATS_CS_ENQ 78
+#define V32_STATS_SKIN_ENQ 79
+#define V32_STATS_IHIST_ENQ 80
+#define V32_DUMMY_8 81
+#define V32_JPEG_ENC_CFG 82
+#define V32_DUMMY_9 83
+#define V32_STATS_AF_START 84
+#define V32_STATS_AF_STOP 85
+#define V32_STATS_AE_START 86
+#define V32_STATS_AE_STOP 87
+#define V32_STATS_AWB_START 88
+#define V32_STATS_AWB_STOP 89
+#define V32_STATS_RS_START 90
+#define V32_STATS_RS_STOP 91
+#define V32_STATS_CS_START 92
+#define V32_STATS_CS_STOP 93
+#define V32_STATS_SKIN_START 94
+#define V32_STATS_SKIN_STOP 95
+#define V32_STATS_IHIST_START 96
+#define V32_STATS_IHIST_STOP 97
+#define V32_DUMMY_10 98
+#define V32_SYNC_TIMER_SETTING 99
+#define V32_ASYNC_TIMER_SETTING 100
+#define V32_LIVESHOT 101
+#define V32_LA_SETUP 102
+
+#define V32_LINEARIZATION 103
+#define V32_DEMOSAICV3 104
+#define V32_DEMOSAICV3_ABCC_CFG 105
+#define V32_DEMOSAICV3_DBCC_CFG 106
+#define V32_DEMOSAICV3_DBPC_CFG 107
+#define V32_DEMOSAICV3_ABF_CFG 108
+#define V32_DEMOSAICV3_ABCC_UPDATE 109
+#define V32_DEMOSAICV3_DBCC_UPDATE 110
+#define V32_DEMOSAICV3_DBPC_UPDATE 111
+#define V32_EZTUNE_CFG 112
+
+#define V32_CLF_CFG 118
+#define V32_CLF_UPDATE 119
+#define V32_STATS_IHIST3_2_START 120
+#define V32_STATS_IHIST3_2_UPDATE 121
+#define V32_CAMIF3_2_CONFIG 122
+
+#define V32_CAMIF_OFF 0x000001E4
+#define V32_CAMIF_LEN 32
+
+#define V32_DEMUX_OFF 0x00000284
+#define V32_DEMUX_LEN 20
+
+#define V32_DEMOSAICV3_0_OFF 0x00000298
+#define V32_DEMOSAICV3_0_LEN 4
+#define V32_DEMOSAICV3_1_OFF 0x0000061C
+#define V32_DEMOSAICV3_1_LEN 88
+/* BPC */
+#define V32_DEMOSAIC_2_OFF 0x0000029C
+#define V32_DEMOSAIC_2_LEN 8
+
+#define V32_OUT_CLAMP_OFF 0x00000524
+#define V32_OUT_CLAMP_LEN 8
+
+#define V32_OPERATION_CFG_LEN 32
+
+#define V32_AXI_OUT_OFF 0x00000038
+#define V32_AXI_OUT_LEN 212
+#define V32_AXI_CH_INF_LEN 24
+#define V32_AXI_CFG_LEN 47
+
+#define V32_FRAME_SKIP_OFF 0x00000504
+#define V32_FRAME_SKIP_LEN 32
+
+#define V32_CHROMA_SUBS_OFF 0x000004F8
+#define V32_CHROMA_SUBS_LEN 12
+
+#define V32_FOV_OFF 0x00000360
+#define V32_FOV_LEN 8
+
+#define V32_MAIN_SCALER_OFF 0x00000368
+#define V32_MAIN_SCALER_LEN 28
+
+#define V32_S2Y_OFF 0x000004D0
+#define V32_S2Y_LEN 20
+
+#define V32_S2CbCr_OFF 0x000004E4
+#define V32_S2CbCr_LEN 20
+
+#define V32_CHROMA_EN_OFF 0x000003C4
+#define V32_CHROMA_EN_LEN 36
+
+#define V32_SYNC_TIMER_OFF 0x0000020C
+#define V32_SYNC_TIMER_POLARITY_OFF 0x00000234
+#define V32_TIMER_SELECT_OFF 0x0000025C
+#define V32_SYNC_TIMER_LEN 28
+
+#define V32_ASYNC_TIMER_OFF 0x00000238
+#define V32_ASYNC_TIMER_LEN 28
+
+#define V32_BLACK_LEVEL_OFF 0x00000264
+#define V32_BLACK_LEVEL_LEN 16
+
+#define V32_ROLL_OFF_CFG_OFF 0x00000274
+#define V32_ROLL_OFF_CFG_LEN 16
+
+#define V32_COLOR_COR_OFF 0x00000388
+#define V32_COLOR_COR_LEN 52
+
+#define V32_WB_OFF 0x00000384
+#define V32_WB_LEN 4
+
+#define V32_RGB_G_OFF 0x000003BC
+#define V32_RGB_G_LEN 4
+
+#define V32_LA_OFF 0x000003C0
+#define V32_LA_LEN 4
+
+#define V32_SCE_OFF 0x00000418
+#define V32_SCE_LEN 136
+
+#define V32_CHROMA_SUP_OFF 0x000003E8
+#define V32_CHROMA_SUP_LEN 12
+
+#define V32_MCE_OFF 0x000003E8
+#define V32_MCE_LEN 36
+#define V32_STATS_AF_OFF 0x0000053c
+#define V32_STATS_AF_LEN 16
+
+#define V32_STATS_AE_OFF 0x00000534
+#define V32_STATS_AE_LEN 8
+
+#define V32_STATS_AWB_OFF 0x0000054c
+#define V32_STATS_AWB_LEN 32
+
+#define V32_STATS_IHIST_OFF 0x0000057c
+#define V32_STATS_IHIST_LEN 8
+
+#define V32_STATS_RS_OFF 0x0000056c
+#define V32_STATS_RS_LEN 8
+
+#define V32_STATS_CS_OFF 0x00000574
+#define V32_STATS_CS_LEN 8
+
+
+#define V32_ASF_OFF 0x000004A0
+#define V32_ASF_LEN 48
+#define V32_ASF_UPDATE_LEN 36
+
+#define V32_CAPTURE_LEN 4
+
+#define V32_LINEARIZATION_OFF1 0x00000264
+#define V32_LINEARIZATION_LEN1 16
+
+#define V32_LINEARIZATION_OFF2 0x0000067C
+#define V32_LINEARIZATION_LEN2 52
+
+#define V32_DEMOSAICV3_OFF 0x00000298
+#define V32_DEMOSAICV3_LEN 4
+
+#define V32_DEMOSAICV3_DBPC_CFG_OFF 0x0000029C
+#define V32_DEMOSAICV3_DBPC_LEN 4
+
+#define V32_DEMOSAICV3_DBPC_CFG_OFF0 0x000002a0
+#define V32_DEMOSAICV3_DBPC_CFG_OFF1 0x00000604
+#define V32_DEMOSAICV3_DBPC_CFG_OFF2 0x00000608
+
+#define V32_DEMOSAICV3_DBCC_OFF 0x0000060C
+#define V32_DEMOSAICV3_DBCC_LEN 16
+
+#define V32_DEMOSAICV3_ABF_OFF 0x0000029C
+#define V32_DEMOSAICV3_ABF_LEN
+
+#define V32_EZTUNE_CFG_OFF 0x00000010
+#define V32_EZTUNE_CFG_LEN 4
+
+struct vfe_cmd_hw_version {
+ uint32_t minorVersion;
+ uint32_t majorVersion;
+ uint32_t coreVersion;
+};
+
+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
+};
+
+
+#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_per_write_master {
+ 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_per_write_master firstWM;
+ struct vfe_cmds_per_write_master secondWM;
+};
+
+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_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_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_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
+};
+
+
+/* 13*1 */
+#define VFE32_ROLL_OFF_INIT_TABLE_SIZE 13
+/* 13*16 */
+#define VFE32_ROLL_OFF_DELTA_TABLE_SIZE 208
+
+#define VFE32_GAMMA_NUM_ENTRIES 64
+
+#define VFE32_LA_TABLE_LENGTH 64
+
+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 64
+
+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_bus_pm_start {
+ uint8_t output2YWrPmEnable;
+ uint8_t output2CbcrWrPmEnable;
+ uint8_t output1YWrPmEnable;
+ uint8_t output1CbcrWrPmEnable;
+};
+
+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
+};
+
+enum VFE32_MESSAGE_ID {
+ MSG_ID_RESET_ACK, /* 0 */
+ MSG_ID_START_ACK,
+ MSG_ID_STOP_ACK,
+ MSG_ID_UPDATE_ACK,
+ MSG_ID_OUTPUT_P,
+ MSG_ID_OUTPUT_T,
+ MSG_ID_OUTPUT_S,
+ MSG_ID_OUTPUT_V,
+ MSG_ID_SNAPSHOT_DONE,
+ MSG_ID_STATS_AEC,
+ MSG_ID_STATS_AF, /* 10 */
+ MSG_ID_STATS_AWB,
+ MSG_ID_STATS_RS,
+ MSG_ID_STATS_CS,
+ MSG_ID_STATS_IHIST,
+ MSG_ID_STATS_SKIN,
+ MSG_ID_EPOCH1,
+ MSG_ID_EPOCH2,
+ MSG_ID_SYNC_TIMER0_DONE,
+ MSG_ID_SYNC_TIMER1_DONE,
+ MSG_ID_SYNC_TIMER2_DONE, /* 20 */
+ MSG_ID_ASYNC_TIMER0_DONE,
+ MSG_ID_ASYNC_TIMER1_DONE,
+ MSG_ID_ASYNC_TIMER2_DONE,
+ MSG_ID_ASYNC_TIMER3_DONE,
+ MSG_ID_AE_OVERFLOW,
+ MSG_ID_AF_OVERFLOW,
+ MSG_ID_AWB_OVERFLOW,
+ MSG_ID_RS_OVERFLOW,
+ MSG_ID_CS_OVERFLOW,
+ MSG_ID_IHIST_OVERFLOW, /* 30 */
+ MSG_ID_SKIN_OVERFLOW,
+ MSG_ID_AXI_ERROR,
+ MSG_ID_CAMIF_OVERFLOW,
+ MSG_ID_VIOLATION,
+ MSG_ID_CAMIF_ERROR,
+ MSG_ID_BUS_OVERFLOW,
+ MSG_ID_SOF_ACK,
+ MSG_ID_STOP_REC_ACK,
+};
+
+struct vfe_msg_stats {
+ uint32_t buffer;
+ 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 vfe32_irq_status {
+ uint32_t vfeIrqStatus0;
+ uint32_t vfeIrqStatus1;
+ uint32_t camifStatus;
+ uint32_t demosaicStatus;
+ uint32_t asfMaxEdge;
+};
+
+struct vfe_msg_output {
+ uint8_t output_id;
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ struct vfe_frame_bpc_info bpcInfo;
+ struct vfe_frame_asf_info asfInfo;
+ uint32_t frameCounter;
+};
+
+struct vfe_message {
+ enum VFE32_MESSAGE_ID _d;
+ union {
+ struct vfe_msg_output msgOut;
+ struct vfe_msg_stats msgStats;
+ struct vfe_msg_camif_status msgCamifError;
+ } _u;
+};
+
+/* New one for 7x30 */
+struct msm_vfe32_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+#define V32_PREVIEW_AXI_FLAG 0x00000001
+#define V32_SNAPSHOT_AXI_FLAG (0x00000001<<1)
+
+struct vfe32_cmd_type {
+ uint16_t id;
+ uint32_t length;
+ uint32_t offset;
+ uint32_t flag;
+};
+
+struct vfe32_free_buf {
+ struct list_head node;
+ uint32_t paddr;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+};
+
+struct vfe32_output_ch {
+ struct list_head free_buf_queue;
+ spinlock_t free_buf_lock;
+ uint16_t output_fmt;
+ int8_t ch0;
+ int8_t ch1;
+ int8_t ch2;
+ uint32_t capture_cnt;
+ uint32_t frame_drop_cnt;
+};
+
+/* no error irq in mask 0 */
+#define VFE32_IMASK_ERROR_ONLY_0 0x0
+/* when normal case, don't want to block error status. */
+/* bit 0-21 are error irq bits */
+#define VFE32_IMASK_ERROR_ONLY_1 0x005FFFFF
+#define VFE32_IMASK_CAMIF_ERROR (0x00000001<<0)
+#define VFE32_IMASK_BHIST_OVWR (0x00000001<<1)
+#define VFE32_IMASK_STATS_CS_OVWR (0x00000001<<2)
+#define VFE32_IMASK_STATS_IHIST_OVWR (0x00000001<<3)
+#define VFE32_IMASK_REALIGN_BUF_Y_OVFL (0x00000001<<4)
+#define VFE32_IMASK_REALIGN_BUF_CB_OVFL (0x00000001<<5)
+#define VFE32_IMASK_REALIGN_BUF_CR_OVFL (0x00000001<<6)
+#define VFE32_IMASK_VIOLATION (0x00000001<<7)
+#define VFE32_IMASK_IMG_MAST_0_BUS_OVFL (0x00000001<<8)
+#define VFE32_IMASK_IMG_MAST_1_BUS_OVFL (0x00000001<<9)
+#define VFE32_IMASK_IMG_MAST_2_BUS_OVFL (0x00000001<<10)
+#define VFE32_IMASK_IMG_MAST_3_BUS_OVFL (0x00000001<<11)
+#define VFE32_IMASK_IMG_MAST_4_BUS_OVFL (0x00000001<<12)
+#define VFE32_IMASK_IMG_MAST_5_BUS_OVFL (0x00000001<<13)
+#define VFE32_IMASK_IMG_MAST_6_BUS_OVFL (0x00000001<<14)
+#define VFE32_IMASK_STATS_AE_BG_BUS_OVFL (0x00000001<<15)
+#define VFE32_IMASK_STATS_AF_BF_BUS_OVFL (0x00000001<<16)
+#define VFE32_IMASK_STATS_AWB_BUS_OVFL (0x00000001<<17)
+#define VFE32_IMASK_STATS_RS_BUS_OVFL (0x00000001<<18)
+#define VFE32_IMASK_STATS_CS_BUS_OVFL (0x00000001<<19)
+#define VFE32_IMASK_STATS_IHIST_BUS_OVFL (0x00000001<<20)
+#define VFE32_IMASK_STATS_SKIN_BHIST_BUS_OVFL (0x00000001<<21)
+#define VFE32_IMASK_AXI_ERROR (0x00000001<<22)
+
+struct vfe32_output_path {
+ uint16_t output_mode; /* bitmask */
+
+ struct vfe32_output_ch out0; /* preview and thumbnail */
+ struct vfe32_output_ch out1; /* snapshot */
+ struct vfe32_output_ch out2; /* video */
+};
+
+struct vfe32_frame_extra {
+ uint32_t greenDefectPixelCount;
+ uint32_t redBlueDefectPixelCount;
+
+ uint32_t asfMaxEdge;
+ uint32_t asfHbiCount;
+
+ uint32_t yWrPmStats0;
+ uint32_t yWrPmStats1;
+ uint32_t cbcrWrPmStats0;
+ uint32_t cbcrWrPmStats1;
+
+ uint32_t frameCounter;
+};
+
+#define VFE_DISABLE_ALL_IRQS 0
+#define VFE_CLEAR_ALL_IRQS 0xffffffff
+
+#define VFE_HW_VERSION 0x00000000
+#define VFE_GLOBAL_RESET 0x00000004
+#define VFE_MODULE_RESET 0x00000008
+#define VFE_CGC_OVERRIDE 0x0000000C
+#define VFE_MODULE_CFG 0x00000010
+#define VFE_CFG 0x00000014
+#define VFE_IRQ_CMD 0x00000018
+#define VFE_IRQ_MASK_0 0x0000001C
+#define VFE_IRQ_MASK_1 0x00000020
+#define VFE_IRQ_CLEAR_0 0x00000024
+#define VFE_IRQ_CLEAR_1 0x00000028
+#define VFE_IRQ_STATUS_0 0x0000002C
+#define VFE_IRQ_STATUS_1 0x00000030
+#define VFE_IRQ_COMP_MASK 0x00000034
+#define VFE_BUS_CMD 0x00000038
+#define VFE_BUS_PING_PONG_STATUS 0x00000180
+#define VFE_AXI_CMD 0x000001D8
+#define VFE_AXI_STATUS 0x000001DC
+#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4
+
+#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4
+#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8
+#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC
+#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100
+#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104
+#define VFE_BUS_STATS_AF_UB_CFG 0x00000108
+#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C
+#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110
+#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114
+#define VFE_BUS_STATS_RS_WR_PING_ADDR 0x00000118
+#define VFE_BUS_STATS_RS_WR_PONG_ADDR 0x0000011C
+#define VFE_BUS_STATS_RS_UB_CFG 0x00000120
+
+#define VFE_BUS_STATS_CS_WR_PING_ADDR 0x00000124
+#define VFE_BUS_STATS_CS_WR_PONG_ADDR 0x00000128
+#define VFE_BUS_STATS_CS_UB_CFG 0x0000012C
+#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130
+#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134
+#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138
+#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C
+#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140
+#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144
+#define VFE_CAMIF_COMMAND 0x000001E0
+#define VFE_CAMIF_STATUS 0x00000204
+#define VFE_REG_UPDATE_CMD 0x00000260
+#define VFE_DEMUX_GAIN_0 0x00000288
+#define VFE_DEMUX_GAIN_1 0x0000028C
+#define VFE_CHROMA_UP 0x0000035C
+#define VFE_FRAMEDROP_ENC_Y_CFG 0x00000504
+#define VFE_FRAMEDROP_ENC_CBCR_CFG 0x00000508
+#define VFE_FRAMEDROP_ENC_Y_PATTERN 0x0000050C
+#define VFE_FRAMEDROP_ENC_CBCR_PATTERN 0x00000510
+#define VFE_FRAMEDROP_VIEW_Y 0x00000514
+#define VFE_FRAMEDROP_VIEW_CBCR 0x00000518
+#define VFE_FRAMEDROP_VIEW_Y_PATTERN 0x0000051C
+#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520
+#define VFE_CLAMP_MAX 0x00000524
+#define VFE_CLAMP_MIN 0x00000528
+#define VFE_REALIGN_BUF 0x0000052C
+#define VFE_STATS_CFG 0x00000530
+#define VFE_DMI_CFG 0x00000598
+#define VFE_DMI_ADDR 0x0000059C
+#define VFE_DMI_DATA_LO 0x000005A4
+#define VFE_BUS_IO_FORMAT_CFG 0x000006F8
+#define VFE_PIXEL_IF_CFG 0x000006FC
+
+struct vfe_stats_control {
+ uint8_t ackPending;
+ uint32_t nextFrameAddrBuf;
+ uint32_t droppedStatsFrameCount;
+ uint32_t bufToRender;
+};
+
+struct vfe32_ctrl_type {
+ uint16_t operation_mode; /* streaming or snapshot */
+ struct vfe32_output_path outpath;
+
+ uint32_t vfeImaskCompositePacked;
+
+ spinlock_t stop_flag_lock;
+ spinlock_t update_ack_lock;
+ spinlock_t state_lock;
+ spinlock_t io_lock;
+
+ spinlock_t aec_ack_lock;
+ spinlock_t awb_ack_lock;
+ spinlock_t af_ack_lock;
+
+ uint32_t extlen;
+ void *extdata;
+
+ int8_t start_ack_pending;
+ int8_t stop_ack_pending;
+ int8_t reset_ack_pending;
+ int8_t update_ack_pending;
+ int8_t req_start_video_rec;
+ int8_t req_stop_video_rec;
+
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ int vfeirq;
+ void __iomem *vfebase;
+ void *syncdata;
+
+ struct resource *vfemem;
+ struct resource *vfeio;
+
+ uint32_t stats_comp;
+ atomic_t vstate;
+ uint32_t vfe_capture_count;
+ uint32_t sync_timer_repeat_count;
+ uint32_t sync_timer_state;
+ uint32_t sync_timer_number;
+
+ uint32_t vfeFrameId;
+ uint32_t output1Pattern;
+ uint32_t output1Period;
+ uint32_t output2Pattern;
+ uint32_t output2Period;
+ uint32_t vfeFrameSkipCount;
+ uint32_t vfeFrameSkipPeriod;
+ struct vfe_stats_control afStatsControl;
+ struct vfe_stats_control awbStatsControl;
+ struct vfe_stats_control aecStatsControl;
+ struct vfe_stats_control ihistStatsControl;
+ struct vfe_stats_control rsStatsControl;
+ struct vfe_stats_control csStatsControl;
+
+ /* v4l2 subdev */
+ struct v4l2_subdev *subdev;
+};
+
+#define statsAeNum 0
+#define statsAfNum 1
+#define statsAwbNum 2
+#define statsRsNum 3
+#define statsCsNum 4
+#define statsIhistNum 5
+#define statsSkinNum 6
+
+struct vfe_cmd_stats_ack {
+ uint32_t nextStatsBuf;
+};
+
+#define VFE_STATS_BUFFER_COUNT 3
+
+struct vfe_cmd_stats_buf {
+ uint32_t statsBuf[VFE_STATS_BUFFER_COUNT];
+};
+#endif /* __MSM_VFE32_H__ */
diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c
new file mode 100644
index 0000000..316aacf
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.c
@@ -0,0 +1,783 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <mach/msm_adsp.h>
+#include <mach/clk.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include "msm_vfe7x.h"
+#include <linux/pm_qos_params.h>
+
+#define QDSP_CMDQUEUE 25
+
+#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
+#define MSG_OUTPUT_S 10
+#define MSG_OUTPUT_T 11
+
+#define VFE_ADSP_EVENT 0xFFFF
+#define SNAPSHOT_MASK_MODE 0x00000002
+#define MSM_AXI_QOS_PREVIEW 192000
+#define MSM_AXI_QOS_SNAPSHOT 192000
+
+
+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 uint32_t vfetask_state;
+static int cnt;
+
+static struct stop_event stopevent;
+
+unsigned long paddr_s_y;
+unsigned long paddr_s_cbcr;
+unsigned long paddr_t_y;
+unsigned long paddr_t_cbcr;
+
+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_OUTPUT_P: {
+ pinfo->y_phy = ((struct vfe_endframe *)data)->y_address;
+ pinfo->cbcr_phy =
+ ((struct vfe_endframe *)data)->cbcr_address;
+
+ pinfo->output_id = OUTPUT_TYPE_P;
+
+ 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_OUTPUT_S: {
+ pinfo->y_phy = paddr_s_y;
+ pinfo->cbcr_phy = paddr_s_cbcr;
+ pinfo->output_id = OUTPUT_TYPE_S;
+ CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+ }
+ break;
+
+ case VFE_MSG_OUTPUT_T: {
+ pinfo->y_phy = paddr_t_y;
+ pinfo->cbcr_phy = paddr_t_cbcr;
+ pinfo->output_id = OUTPUT_TYPE_T;
+ CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+ }
+ 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;
+ CDBG("%s:id=%d\n", __func__, id);
+
+ len = (id == VFE_ADSP_EVENT) ? 0 : len;
+ data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len,
+ vfe_syncdata, GFP_ATOMIC);
+
+ if (!data) {
+ pr_err("%s: rp: cannot allocate buffer\n", __func__);
+ return;
+ }
+ rp = (struct msm_vfe_resp *)data;
+ rp->evt_msg.len = len;
+
+ if (id == VFE_ADSP_EVENT) {
+ /* 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];
+ CDBG("%s:event:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata,
+ GFP_ATOMIC);
+ } 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);
+ CDBG("%s:messages:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+
+ switch (rp->evt_msg.msg_id) {
+ case MSG_SNAPSHOT:
+ update_axi_qos(MSM_AXI_QOS_PREVIEW);
+ vfe_7x_ops(driver_data, MSG_OUTPUT_S, len, getevent);
+ vfe_7x_ops(driver_data, MSG_OUTPUT_T, len, getevent);
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+
+ case MSG_OUTPUT_S:
+ rp->type = VFE_MSG_OUTPUT_S;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_OUTPUT_T:
+ rp->type = VFE_MSG_OUTPUT_T;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_OUTPUT1:
+ case MSG_OUTPUT2:
+ rp->type = VFE_MSG_OUTPUT_P;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+ 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, GFP_ATOMIC);
+ }
+}
+
+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);
+ vfetask_state = 1;
+ }
+
+ if (!cnt) {
+ add_axi_qos();
+ cnt++;
+ }
+ 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);
+ vfetask_state = 0;
+ }
+
+ 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);
+ vfetask_state = 0;
+
+ msm_adsp_put(qcam_mod);
+ msm_adsp_put(vfe_mod);
+
+ msm_camio_disable(pdev);
+
+ kfree(extdata);
+ extlen = 0;
+
+ /* Release AXI */
+ release_axi_qos();
+ cnt = 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(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);
+ if (mode == OUTPUT_1_AND_2) {
+ paddr_t_y = regptr->paddr + regptr->info.y_off;
+ paddr_t_cbcr = regptr->paddr + regptr->info.cbcr_off;
+ }
+
+ CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off,
+ regptr->info.cbcr_off);
+
+ bptr = &ao->output1buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.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);
+ paddr_s_y = regptr->paddr + regptr->info.y_off;
+ paddr_s_cbcr = regptr->paddr + regptr->info.cbcr_off;
+ CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off);
+
+ bptr = &ao->output2buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.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, op_mode;
+ uint32_t *_mode;
+
+ 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_AEC_AWB_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[0];
+
+ 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();
+ vfestopped = 0;
+ break;
+
+ case VFE_START_CMD:
+ _mode = (uint32_t *)cmd_data;
+ op_mode = *(++_mode);
+ if (op_mode & SNAPSHOT_MASK_MODE) {
+ /* request AXI bus for snapshot */
+ if (update_axi_qos(MSM_AXI_QOS_SNAPSHOT)
+ < 0) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+ } else {
+ /* request AXI bus for snapshot */
+ if (update_axi_qos(MSM_AXI_QOS_PREVIEW)
+ < 0) {
+ rc = -EFAULT;
+ goto config_failure;
+ }
+ }
+ 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_PREVIEW:
+ 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: {
+ 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);
+ if (vfetask_state)
+ 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;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+ fptr->vpe_reg = NULL;
+ fptr->send_frame_to_vpe = NULL;
+ fptr->vpe_config = NULL;
+ fptr->vpe_cfg_update = NULL;
+ fptr->dis = NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe7x.h b/drivers/media/video/msm/msm_vfe7x.h
new file mode 100644
index 0000000..dd3571f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x.h
@@ -0,0 +1,265 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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/media/video/msm/msm_vfe7x27a.c b/drivers/media/video/msm/msm_vfe7x27a.c
new file mode 100644
index 0000000..c8bfacc
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a.c
@@ -0,0 +1,742 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/msm_adsp.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <linux/pm_qos_params.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <mach/msm_adsp.h>
+#include <mach/clk.h>
+#include <mach/camera.h>
+#include "msm_vfe7x27a.h"
+
+#define QDSP_CMDQUEUE 25
+
+#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
+#define MSG_OUTPUT_S 23
+#define MSG_OUTPUT_T 22
+#define MSG_SOF 15
+
+#define VFE_ADSP_EVENT 0xFFFF
+#define SNAPSHOT_MASK_MODE 0x00000002
+#define MSM_AXI_QOS_PREVIEW 122000
+#define MSM_AXI_QOS_SNAPSHOT 192000
+
+
+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;
+
+unsigned long paddr_s_y;
+unsigned long paddr_s_cbcr;
+unsigned long paddr_t_y;
+unsigned long paddr_t_cbcr;
+static uint32_t op_mode;
+
+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_OUTPUT_P: {
+ pinfo->y_phy = ((struct vfe_endframe *)data)->y_address;
+ pinfo->cbcr_phy =
+ ((struct vfe_endframe *)data)->cbcr_address;
+
+ pinfo->output_id = OUTPUT_TYPE_P;
+
+ CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+
+ memcpy(((struct vfe_frame_extra *)extdata),
+ &((struct vfe_endframe *)data)->extra,
+ sizeof(struct vfe_frame_extra));
+
+ *ext = extdata;
+ *elen = extlen;
+ pinfo->frame_id =
+ ((struct vfe_frame_extra *)extdata)->frame_id;
+ }
+ break;
+ case VFE_MSG_OUTPUT_S: {
+ pinfo->y_phy = paddr_s_y;
+ pinfo->cbcr_phy = paddr_s_cbcr;
+ pinfo->output_id = OUTPUT_TYPE_S;
+ CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+ }
+ break;
+ case VFE_MSG_OUTPUT_T: {
+ pinfo->y_phy = paddr_t_y;
+ pinfo->cbcr_phy = paddr_t_cbcr;
+ pinfo->output_id = OUTPUT_TYPE_T;
+ CDBG("vfe_7x_convert: y_phy = 0x%x cbcr_phy = 0x%x\n",
+ pinfo->y_phy, pinfo->cbcr_phy);
+ }
+ break;
+ case VFE_MSG_STATS_AF:
+ case VFE_MSG_STATS_WE:
+ pinfo->sbuf_phy = *(uint32_t *)data;
+ pinfo->frame_id = *(((uint32_t *)data) + 1);
+ CDBG("frame id = %d\n", pinfo->frame_id);
+ break;
+ default:
+ break;
+ }
+}
+
+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;
+ CDBG("%s:id=%d\n", __func__, id);
+
+ len = (id == VFE_ADSP_EVENT) ? 0 : len;
+ data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len,
+ vfe_syncdata, GFP_ATOMIC);
+
+ if (!data) {
+ pr_err("%s: rp: cannot allocate buffer\n", __func__);
+ return;
+ }
+ rp = data;
+ rp->evt_msg.len = len;
+
+ if (id == VFE_ADSP_EVENT) {
+ /* 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];
+ CDBG("%s:event:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata,
+ GFP_ATOMIC);
+ } 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);
+ CDBG("%s:messages:msg_id=%d\n", __func__, rp->evt_msg.msg_id);
+
+ switch (rp->evt_msg.msg_id) {
+ case MSG_SNAPSHOT:
+ msm_camio_set_perf_lvl(S_PREVIEW);
+ vfe_7x_ops(driver_data, MSG_OUTPUT_S, len, getevent);
+ vfe_7x_ops(driver_data, MSG_OUTPUT_T, len, getevent);
+ rp->type = VFE_MSG_SNAPSHOT;
+ break;
+ case MSG_OUTPUT_S:
+ rp->type = VFE_MSG_OUTPUT_S;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_S,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+ case MSG_OUTPUT_T:
+ rp->type = VFE_MSG_OUTPUT_T;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_T,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+ case MSG_OUTPUT1:
+ case MSG_OUTPUT2:
+ if (op_mode & SNAPSHOT_MASK_MODE) {
+ resp->vfe_free(data);
+ return;
+ }
+ rp->type = VFE_MSG_OUTPUT_P;
+ vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT_P,
+ 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;
+ }
+ if (id != MSG_SOF)
+ resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG,
+ vfe_syncdata, GFP_ATOMIC);
+ }
+}
+
+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;
+ static int cnt;
+
+ if (!strcmp(enable->name, "QCAMTASK"))
+ rc = msm_adsp_enable(qcam_mod);
+ else if (!strcmp(enable->name, "VFETASK"))
+ rc = msm_adsp_enable(vfe_mod);
+
+ if (!cnt) {
+ msm_camio_set_perf_lvl(S_INIT);
+ cnt++;
+ }
+ 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;
+
+ /* set back the AXI frequency to default */
+ /* TODO msm_camio_set_perf_lvl(S_DEFAULT); */
+}
+
+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;
+
+ extlen = sizeof(struct vfe_frame_extra);
+
+ extdata = kmalloc(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);
+ if (mode == OUTPUT_1_AND_2) {
+ paddr_t_y = regptr->paddr + regptr->info.y_off;
+ paddr_t_cbcr = regptr->paddr + regptr->info.cbcr_off;
+ }
+
+ CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off,
+ regptr->info.cbcr_off);
+
+ bptr = &ao->output1buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum1; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+ bptr++;
+ }
+ }
+
+ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) {
+ regptr = &(ad->region[ad->bufnum1]);
+
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+ paddr_s_y = regptr->paddr + regptr->info.y_off;
+ paddr_s_cbcr = regptr->paddr + regptr->info.cbcr_off;
+
+ CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n",
+ regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off);
+
+ bptr = &ao->output2buffer1_y_phy;
+ for (cnt = 0; cnt < ad->bufnum2; cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.cbcr_off;
+
+ bptr++;
+ regptr++;
+ }
+
+ regptr--;
+ for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) {
+ *bptr = regptr->paddr + regptr->info.y_off;
+ bptr++;
+ *bptr = regptr->paddr + regptr->info.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;
+ uint32_t *_mode;
+
+ 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_AEC_AWB_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[0];
+
+ 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();
+ vfestopped = 0;
+ break;
+ case VFE_START_CMD:
+ _mode = (uint32_t *)cmd_data;
+ op_mode = *(++_mode);
+ if (op_mode & SNAPSHOT_MASK_MODE)
+ msm_camio_set_perf_lvl(S_CAPTURE);
+ else
+ msm_camio_set_perf_lvl(S_PREVIEW);
+ vfestopped = 0;
+ break;
+ case VFE_STOP_CMD:
+ vfestopped = 1;
+ goto config_send;
+
+ default:
+ break;
+ }
+ } /* QDSP_CMDQUEUE */
+ }
+ break;
+ case CMD_AXI_CFG_PREVIEW:
+ 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: {
+ 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;
+ }
+
+ 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:
+ 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;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+ fptr->vpe_reg = NULL;
+ fptr->send_frame_to_vpe = NULL;
+ fptr->vpe_config = NULL;
+ fptr->vpe_cfg_update = NULL;
+ fptr->dis = NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe7x27a.h b/drivers/media/video/msm/msm_vfe7x27a.h
new file mode 100644
index 0000000..a488206
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe7x27a.h
@@ -0,0 +1,300 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#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:23;
+ uint32_t rvd1:9;
+ uint32_t bl_oddcol:23;
+ uint32_t rvd2:9;
+
+ uint32_t d_dbpc_stats_hot:16;
+ uint32_t d_dbpc_stats_cold:16;
+
+ uint32_t d_dbpc_stats_0_hot:10;
+ uint32_t rvd3:6;
+ uint32_t d_dbpc_stats_0_cold:10;
+ uint32_t rvd4:6;
+ uint32_t d_dbpc_stats_1_hot:10;
+ uint32_t rvd5:6;
+ uint32_t d_dbpc_stats_1_cold:10;
+ uint32_t rvd6:6;
+
+ uint32_t asf_max_edge;
+
+ uint32_t e_y_wm_pm_stats_0:21;
+ uint32_t rvd7:11;
+ uint32_t e_y_wm_pm_stats_1_bl:8;
+ uint32_t rvd8:8;
+ uint32_t e_y_wm_pm_stats_1_nl:12;
+ uint32_t rvd9:4;
+
+ uint32_t e_cbcr_wm_pm_stats_0:21;
+ uint32_t rvd10:11;
+ uint32_t e_cbcr_wm_pm_stats_1_bl:8;
+ uint32_t rvd11:8;
+ uint32_t e_cbcr_wm_pm_stats_1_nl:12;
+ uint32_t rvd12:4;
+
+ uint32_t v_y_wm_pm_stats_0:21;
+ uint32_t rvd13:11;
+ uint32_t v_y_wm_pm_stats_1_bl:8;
+ uint32_t rvd14:8;
+ uint32_t v_y_wm_pm_stats_1_nl:12;
+ uint32_t rvd15:4;
+
+ uint32_t v_cbcr_wm_pm_stats_0:21;
+ uint32_t rvd16:11;
+ uint32_t v_cbcr_wm_pm_stats_1_bl:8;
+ uint32_t rvd17:8;
+ uint32_t v_cbcr_wm_pm_stats_1_nl:12;
+ uint32_t rvd18:4;
+
+ uint32_t frame_id;
+};
+
+struct vfe_endframe {
+ uint32_t y_address;
+ uint32_t cbcr_address;
+
+ struct vfe_frame_extra extra;
+} __packed;
+
+struct vfe_outputack {
+ uint32_t header;
+ void *output2newybufferaddress;
+ void *output2newcbcrbufferaddress;
+} __packed;
+
+struct vfe_stats_ack {
+ uint32_t header;
+ /* MUST BE 64 bit ALIGNED */
+ void *bufaddr;
+} __packed;
+
+/* 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;
+} __packed;
+
+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];
+} __packed;
+
+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];
+} __packed; /* 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;
+} __packed;
+
+/* 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/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c
new file mode 100644
index 0000000..0bf1785
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.c
@@ -0,0 +1,842 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include "msm_vfe8x_proc.h"
+#include <linux/pm_qos_params.h>
+
+#define ON 1
+#define OFF 0
+
+static const char *vfe_general_cmd[] = {
+ "START", /* 0 */
+ "RESET",
+ "AXI_INPUT_CONFIG",
+ "CAMIF_CONFIG",
+ "AXI_OUTPUT_CONFIG",
+ "BLACK_LEVEL_CONFIG", /* 5 */
+ "ROLL_OFF_CONFIG",
+ "DEMUX_CHANNEL_GAIN_CONFIG",
+ "DEMOSAIC_CONFIG",
+ "FOV_CROP_CONFIG",
+ "MAIN_SCALER_CONFIG", /* 10 */
+ "WHITE_BALANCE_CONFIG",
+ "COLOR_CORRECTION_CONFIG",
+ "LA_CONFIG",
+ "RGB_GAMMA_CONFIG",
+ "CHROMA_ENHAN_CONFIG", /* 15 */
+ "CHROMA_SUPPRESSION_CONFIG",
+ "ASF_CONFIG",
+ "SCALER2Y_CONFIG",
+ "SCALER2CbCr_CONFIG",
+ "CHROMA_SUBSAMPLE_CONFIG", /* 20 */
+ "FRAME_SKIP_CONFIG",
+ "OUTPUT_CLAMP_CONFIG",
+ "TEST_GEN_START",
+ "UPDATE",
+ "OUTPUT1_ACK", /* 25 */
+ "OUTPUT2_ACK",
+ "EPOCH1_ACK",
+ "EPOCH2_ACK",
+ "STATS_AUTOFOCUS_ACK",
+ "STATS_WB_EXP_ACK", /* 30 */
+ "BLACK_LEVEL_UPDATE",
+ "DEMUX_CHANNEL_GAIN_UPDATE",
+ "DEMOSAIC_BPC_UPDATE",
+ "DEMOSAIC_ABF_UPDATE",
+ "FOV_CROP_UPDATE", /* 35 */
+ "WHITE_BALANCE_UPDATE",
+ "COLOR_CORRECTION_UPDATE",
+ "LA_UPDATE",
+ "RGB_GAMMA_UPDATE",
+ "CHROMA_ENHAN_UPDATE", /* 40 */
+ "CHROMA_SUPPRESSION_UPDATE",
+ "MAIN_SCALER_UPDATE",
+ "SCALER2CbCr_UPDATE",
+ "SCALER2Y_UPDATE",
+ "ASF_UPDATE", /* 45 */
+ "FRAME_SKIP_UPDATE",
+ "CAMIF_FRAME_UPDATE",
+ "STATS_AUTOFOCUS_UPDATE",
+ "STATS_WB_EXP_UPDATE",
+ "STOP", /* 50 */
+ "GET_HW_VERSION",
+ "STATS_SETTING",
+ "STATS_AUTOFOCUS_START",
+ "STATS_AUTOFOCUS_STOP",
+ "STATS_WB_EXP_START", /* 55 */
+ "STATS_WB_EXP_STOP",
+ "ASYNC_TIMER_SETTING",
+};
+
+static void *vfe_syncdata;
+
+static int vfe_enable(struct camera_enable_cmd *enable)
+{
+ return 0;
+}
+
+static int vfe_disable(struct camera_enable_cmd *enable,
+ struct platform_device *dev)
+{
+ vfe_stop();
+ msm_camio_disable(dev);
+ return 0;
+}
+
+static void vfe_release(struct platform_device *dev)
+{
+ msm_camio_disable(dev);
+ vfe_cmd_release(dev);
+ update_axi_qos(PM_QOS_DEFAULT_VALUE);
+ vfe_syncdata = NULL;
+}
+
+static void vfe_config_axi(int mode,
+ struct axidata *ad,
+ struct vfe_cmd_axi_output_config *ao)
+{
+ struct msm_pmem_region *regptr, *regptr1;
+ 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->info.y_off;
+ p1++;
+
+ *p2 = regptr->paddr + regptr->info.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->info.y_off, regptr->info.cbcr_off);
+
+ for (j = 0; j < ao->output2.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->info.y_off;
+ CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+ p1++;
+
+ *p2 = regptr->paddr + regptr->info.cbcr_off;
+ CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+ p2++;
+ }
+ regptr++;
+ }
+ }
+ /* For video configuration */
+ if (mode == OUTPUT_1_AND_3) {
+ /* this is preview buffer. */
+ regptr = &(ad->region[0]);
+ /* this is video buffer. */
+ regptr1 = &(ad->region[ad->bufnum1]);
+ CDBG("bufnum1 = %d\n", ad->bufnum1);
+ CDBG("bufnum2 = %d\n", ad->bufnum2);
+
+ for (i = 0; i < ad->bufnum1; i++) {
+ p1 = &(ao->output1.outputY.outFragments[i][0]);
+ p2 = &(ao->output1.outputCbcr.outFragments[i][0]);
+
+ CDBG("config_axi: O1, phy = 0x%lx, y_off = %d, "\
+ "cbcr_off = %d\n", regptr->paddr,
+ regptr->info.y_off, regptr->info.cbcr_off);
+
+ for (j = 0; j < ao->output1.fragmentCount; j++) {
+
+ *p1 = regptr->paddr + regptr->info.y_off;
+ CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+ p1++;
+
+ *p2 = regptr->paddr + regptr->info.cbcr_off;
+ CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+ p2++;
+ }
+ regptr++;
+ }
+ 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", regptr1->paddr,
+ regptr1->info.y_off, regptr1->info.cbcr_off);
+
+ for (j = 0; j < ao->output2.fragmentCount; j++) {
+
+ *p1 = regptr1->paddr + regptr1->info.y_off;
+ CDBG("vfe_config_axi: p1 = 0x%x\n", *p1);
+ p1++;
+
+ *p2 = regptr1->paddr + regptr1->info.cbcr_off;
+ CDBG("vfe_config_axi: p2 = 0x%x\n", *p2);
+ p2++;
+ }
+ regptr1++;
+ }
+ }
+
+}
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (cmd->length != sizeof(*(in))) { \
+ pr_err("msm_camera: %s:%d cmd %d: user data size %d " \
+ "!= kernel data size %d\n", \
+ __func__, __LINE__, \
+ cmd->id, cmd->length, sizeof(*(in))); \
+ rc = -EIO; \
+ break; \
+ } \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ sizeof(*(in)))) { \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+static int vfe_proc_general(struct msm_vfe_command_8k *cmd)
+{
+ int rc = 0;
+
+ CDBG("%s: cmdID = %s\n", __func__, vfe_general_cmd[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;
+ CHECKED_COPY_FROM_USER(&start);
+
+ /* 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;
+ CHECKED_COPY_FROM_USER(&camif);
+
+ vfe_camif_config(&camif);
+ }
+ break;
+
+ case VFE_CMD_ID_BLACK_LEVEL_CONFIG: {
+ struct vfe_cmd_black_level_config bl;
+ CHECKED_COPY_FROM_USER(&bl);
+
+ vfe_black_level_config(&bl);
+ }
+ break;
+
+ case VFE_CMD_ID_ROLL_OFF_CONFIG:{
+ /* rolloff is too big to be on the stack */
+ struct vfe_cmd_roll_off_config *rolloff =
+ kmalloc(sizeof(struct vfe_cmd_roll_off_config),
+ GFP_KERNEL);
+ if (!rolloff) {
+ pr_err("%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ break;
+ }
+ /* Wrap CHECKED_COPY_FROM_USER() in a do-while(0) loop
+ * to make sure we free rolloff when copy_from_user()
+ * fails.
+ */
+ do {
+ CHECKED_COPY_FROM_USER(rolloff);
+ vfe_roll_off_config(rolloff);
+ } while (0);
+ kfree(rolloff);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: {
+ struct vfe_cmd_demux_channel_gain_config demuxc;
+ CHECKED_COPY_FROM_USER(&demuxc);
+
+ /* demux is always enabled. */
+ vfe_demux_channel_gain_config(&demuxc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_CONFIG: {
+ struct vfe_cmd_demosaic_config demosaic;
+ CHECKED_COPY_FROM_USER(&demosaic);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&fov);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&mainds);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&wb);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&cc);
+
+ vfe_color_correction_config(&cc);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_CONFIG: {
+ struct vfe_cmd_la_config la;
+ CHECKED_COPY_FROM_USER(&la);
+
+ vfe_la_config(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_CONFIG: {
+ struct vfe_cmd_rgb_gamma_config rgb;
+ CHECKED_COPY_FROM_USER(&rgb);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&chrom);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&chromsup);
+
+ vfe_chroma_sup_config(&chromsup);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_CONFIG: {
+ struct vfe_cmd_asf_config asf;
+ CHECKED_COPY_FROM_USER(&asf);
+
+ vfe_asf_config(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2Y_CONFIG:
+ case VFE_CMD_ID_SCALER2Y_UPDATE: {
+ struct vfe_cmd_scaler2_config ds2y;
+ CHECKED_COPY_FROM_USER(&ds2y);
+
+ vfe_scaler2y_config(&ds2y);
+ }
+ break;
+
+ case VFE_CMD_ID_SCALER2CbCr_CONFIG:
+ case VFE_CMD_ID_SCALER2CbCr_UPDATE: {
+ struct vfe_cmd_scaler2_config ds2cbcr;
+ CHECKED_COPY_FROM_USER(&ds2cbcr);
+
+ vfe_scaler2cbcr_config(&ds2cbcr);
+ }
+ break;
+
+ case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: {
+ struct vfe_cmd_chroma_subsample_config sub;
+ CHECKED_COPY_FROM_USER(&sub);
+
+ vfe_chroma_subsample_config(&sub);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_CONFIG: {
+ struct vfe_cmd_frame_skip_config fskip;
+ CHECKED_COPY_FROM_USER(&fskip);
+
+ vfe_frame_skip_config(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: {
+ struct vfe_cmd_output_clamp_config clamp;
+ CHECKED_COPY_FROM_USER(&clamp);
+
+ vfe_output_clamp_config(&clamp);
+ }
+ break;
+
+ /* module update commands */
+ case VFE_CMD_ID_BLACK_LEVEL_UPDATE: {
+ struct vfe_cmd_black_level_config blk;
+ CHECKED_COPY_FROM_USER(&blk);
+
+ vfe_black_level_update(&blk);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: {
+ struct vfe_cmd_demux_channel_gain_config dmu;
+ CHECKED_COPY_FROM_USER(&dmu);
+
+ vfe_demux_channel_gain_update(&dmu);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: {
+ struct vfe_cmd_demosaic_bpc_update demo_bpc;
+ CHECKED_COPY_FROM_USER(&demo_bpc);
+
+ vfe_demosaic_bpc_update(&demo_bpc);
+ }
+ break;
+
+ case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: {
+ struct vfe_cmd_demosaic_abf_update demo_abf;
+ CHECKED_COPY_FROM_USER(&demo_abf);
+
+ vfe_demosaic_abf_update(&demo_abf);
+ }
+ break;
+
+ case VFE_CMD_ID_LA_UPDATE: {
+ struct vfe_cmd_la_config la;
+ CHECKED_COPY_FROM_USER(&la);
+
+ vfe_la_update(&la);
+ }
+ break;
+
+ case VFE_CMD_ID_RGB_GAMMA_UPDATE: {
+ struct vfe_cmd_rgb_gamma_config rgb;
+ CHECKED_COPY_FROM_USER(&rgb);
+
+ rc = vfe_rgb_gamma_update(&rgb);
+ }
+ break;
+
+ case VFE_CMD_ID_ASF_UPDATE: {
+ struct vfe_cmd_asf_update asf;
+ CHECKED_COPY_FROM_USER(&asf);
+
+ vfe_asf_update(&asf);
+ }
+ break;
+
+ case VFE_CMD_ID_FRAME_SKIP_UPDATE: {
+ struct vfe_cmd_frame_skip_update fskip;
+ CHECKED_COPY_FROM_USER(&fskip);
+ /* Start recording */
+ if (fskip.output2Pattern == 0xffffffff)
+ update_axi_qos(MSM_AXI_QOS_RECORDING);
+ else if (fskip.output2Pattern == 0)
+ update_axi_qos(MSM_AXI_QOS_PREVIEW);
+
+ vfe_frame_skip_update(&fskip);
+ }
+ break;
+
+ case VFE_CMD_ID_CAMIF_FRAME_UPDATE: {
+ struct vfe_cmds_camif_frame fup;
+ CHECKED_COPY_FROM_USER(&fup);
+
+ vfe_camif_frame_update(&fup);
+ }
+ break;
+
+ /* stats update commands */
+ case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: {
+ struct vfe_cmd_stats_af_update afup;
+ CHECKED_COPY_FROM_USER(&afup);
+
+ vfe_stats_update_af(&afup);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_WB_EXP_UPDATE: {
+ struct vfe_cmd_stats_wb_exp_update wbexp;
+ CHECKED_COPY_FROM_USER(&wbexp);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&stats);
+
+ vfe_stats_setting(&stats);
+ }
+ break;
+
+ case VFE_CMD_ID_STATS_AUTOFOCUS_START: {
+ struct vfe_cmd_stats_af_start af;
+ CHECKED_COPY_FROM_USER(&af);
+
+ 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;
+ CHECKED_COPY_FROM_USER(&awexp);
+
+ 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:
+ pr_err("%s: invalid cmd id %d\n", __func__, cmd->id);
+ rc = -EINVAL;
+ 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;
+ struct vfe_cmd_axi_output_config axio;
+ struct axidata *axid = data;
+
+ int rc = 0;
+
+
+ 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(vfecmd))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ }
+
+ CDBG("%s: cmdType = %d\n", __func__, 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: {
+ int i;
+ struct vfe_cmd_stats_setting scfg;
+
+ BUG_ON(!axid);
+
+ if (vfecmd.length != sizeof(scfg)) {
+ pr_err
+ ("msm_camera: %s: cmd %d: user-space "\
+ "data size %d != kernel data size %d\n",
+ __func__,
+ cmd->cmd_type, vfecmd.length,
+ sizeof(scfg));
+ return -EIO;
+ }
+
+ if (copy_from_user(&scfg,
+ (void __user *)(vfecmd.value),
+ sizeof(scfg))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ 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_setting(&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;
+
+ BUG_ON(!data);
+
+ b = (struct msm_frame *)(cmd->value);
+ p = *(unsigned long *)data;
+
+ fack.ybufaddr[0] = (uint32_t) (p + b->y_off);
+
+ fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off);
+
+ if (b->path == OUTPUT_TYPE_P)
+ vfe_output_p_ack(&fack);
+
+ if ((b->path == OUTPUT_TYPE_V)
+ || (b->path == OUTPUT_TYPE_S))
+ vfe_output_v_ack(&fack);
+ }
+ break;
+
+ case CMD_SNAP_BUF_RELEASE:
+ break;
+
+ case CMD_STATS_BUF_RELEASE: {
+ struct vfe_cmd_stats_wb_exp_ack sack;
+
+ BUG_ON(!data);
+
+ sack.nextWbExpOutputBufferAddr = *(uint32_t *)data;
+ vfe_stats_wb_exp_ack(&sack);
+ }
+ break;
+
+ case CMD_STATS_AF_BUF_RELEASE: {
+ struct vfe_cmd_stats_af_ack ack;
+
+ BUG_ON(!data);
+
+ ack.nextAFOutputBufferAddr = *(uint32_t *)data;
+ vfe_stats_af_ack(&ack);
+ }
+ break;
+
+ case CMD_AXI_CFG_PREVIEW:
+ case CMD_RAW_PICT_AXI_CFG: {
+
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* Validate the data from user space */
+ if (axio.output2.fragmentCount <
+ VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output2.fragmentCount >
+ VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+ return -EINVAL;
+
+ vfe_config_axi(OUTPUT_2, axid, &axio);
+ axio.outputDataSize = 0;
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_SNAP: {
+
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* Validate the data from user space */
+ if (axio.output1.fragmentCount <
+ VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output1.fragmentCount >
+ VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output2.fragmentCount <
+ VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output2.fragmentCount >
+ VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+ return -EINVAL;
+
+ vfe_config_axi(OUTPUT_1_AND_2, axid, &axio);
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ case CMD_AXI_CFG_VIDEO: {
+ BUG_ON(!axid);
+
+ if (copy_from_user(&axio, (void __user *)(vfecmd.value),
+ sizeof(axio))) {
+ pr_err("%s %d: copy_from_user failed\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* Validate the data from user space */
+ if (axio.output1.fragmentCount <
+ VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output1.fragmentCount >
+ VFE_MAX_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output2.fragmentCount <
+ VFE_MIN_NUM_FRAGMENTS_PER_FRAME ||
+ axio.output2.fragmentCount >
+ VFE_MAX_NUM_FRAGMENTS_PER_FRAME)
+ return -EINVAL;
+
+ vfe_config_axi(OUTPUT_1_AND_3, axid, &axio);
+ axio.outputDataSize = 0;
+ vfe_axi_output_config(&axio);
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ 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 */
+ rc = msm_camio_enable(dev);
+
+ return rc;
+}
+
+void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data)
+{
+ 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;
+}
+
+void msm_camvpe_fn_init(struct msm_camvpe_fn *fptr, void *data)
+{
+ fptr->vpe_reg = NULL;
+ fptr->send_frame_to_vpe = NULL;
+ fptr->vpe_config = NULL;
+ fptr->vpe_cfg_update = NULL;
+ fptr->dis = NULL;
+}
diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h
new file mode 100644
index 0000000..1b3148f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x.h
@@ -0,0 +1,909 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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_MIN_NUM_FRAGMENTS_PER_FRAME 1
+#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_OUTPUT_P,
+ VFE_MSG_ID_OUTPUT_V,
+ VFE_MSG_ID_OUTPUT_S,
+ VFE_MSG_ID_OUTPUT_T,
+ 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,
+ VFE_MSG_ID_SOF_ACK,
+};
+
+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 {
+ int 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/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c
new file mode 100644
index 0000000..9764557
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.c
@@ -0,0 +1,3888 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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>
+#include <mach/board.h>
+
+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;
+};
+
+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;
+
+ atomic_t vfe_serv_interrupt;
+
+ 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;
+
+ struct msm_vfe_callback *resp;
+ struct vfe_frame_extra extdata;
+
+ struct isr_queue_cmd irqs[10];
+ spinlock_t irqs_lock;
+ int irq_get;
+ int irq_put;
+
+ int vfeirq;
+ void __iomem *vfebase;
+
+ void *syncdata;
+};
+
+static struct msm_vfe8x_ctrl *ctrl;
+static spinlock_t msm_vfe_ctrl_lock;
+
+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_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;
+
+ memset(&cmd, 0, sizeof(cmd));
+ /* 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;
+
+ memset(&cmd, 0, sizeof(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)(*initGb))<<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 = (((int)(*pDeltaR)) & 0x0000FFFF) |
+ (((int)(*pDeltaGr))<<16);
+ pDeltaR++;
+ pDeltaGr++;
+
+ writel(data, ctrl->vfebase + VFE_DMI_DATA_LO);
+
+ data = (((int)(*pDeltaB)) & 0x0000FFFF) |
+ (((int)(*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_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_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 uint32_t vfe_read_axi_status(void)
+{
+ return readl(ctrl->vfebase + VFE_AXI_STATUS);
+}
+
+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 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,
+ int *elen)
+{
+ switch (type) {
+ case VFE_MSG_OUTPUT_P:
+ case VFE_MSG_OUTPUT_V:{
+ pinfo->y_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vfe_message *)data)->_u.msgOutput2.
+ cbcrBuffer;
+ ctrl->extdata.bpcInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo;
+ ctrl->extdata.asfInfo =
+ ((struct vfe_message *)data)->_u.msgOutput2.asfInfo;
+ ctrl->extdata.frameCounter =
+ ((struct vfe_message *)data)->_u.msgOutput2.
+ frameCounter;
+ ctrl->extdata.pmData =
+ ((struct vfe_message *)data)->_u.msgOutput2.pmData;
+ *ext = &ctrl->extdata;
+ *elen = sizeof(ctrl->extdata);
+ }
+ 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 boolean vfe_send_preview_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_video_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+static boolean vfe_send_sof_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data);
+
+static boolean invalid(struct msm_vfe_resp *rp,
+ struct vfe_message *_m, void *_d)
+{
+ BUG_ON(1); /* this function should not be called. */
+ return FALSE;
+}
+
+static struct {
+ boolean (*fn)(struct msm_vfe_resp *rp, struct vfe_message *msg,
+ void *data);
+ enum vfe_resp_msg rt; /* reponse type */
+} vfe_funcs[] = {
+ [VFE_MSG_ID_RESET_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_START_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_STOP_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_UPDATE_ACK] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_OUTPUT_P] = { vfe_send_preview_msg, VFE_MSG_OUTPUT_P },
+ [VFE_MSG_ID_OUTPUT_V] = { vfe_send_video_msg, VFE_MSG_OUTPUT_V },
+ [VFE_MSG_ID_OUTPUT_S] = { vfe_send_mainimage_msg, VFE_MSG_OUTPUT_S },
+ [VFE_MSG_ID_OUTPUT_T] = { vfe_send_thumbnail_msg, VFE_MSG_OUTPUT_T },
+ [VFE_MSG_ID_SNAPSHOT_DONE] = { NULL, VFE_MSG_SNAPSHOT },
+ [VFE_MSG_ID_STATS_AUTOFOCUS] = { vfe_send_af_stats_msg,
+ VFE_MSG_STATS_AF },
+ [VFE_MSG_ID_STATS_WB_EXP] = { vfe_send_awb_stats_msg,
+ VFE_MSG_STATS_WE },
+ [VFE_MSG_ID_EPOCH1] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_EPOCH2] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_SYNC_TIMER0_DONE] = { invalid },
+ [VFE_MSG_ID_SYNC_TIMER1_DONE] = { invalid },
+ [VFE_MSG_ID_SYNC_TIMER2_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER0_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER1_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER2_DONE] = { invalid },
+ [VFE_MSG_ID_ASYNC_TIMER3_DONE] = { invalid },
+ [VFE_MSG_ID_AF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_AWB_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_AXI_ERROR] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_CAMIF_OVERFLOW] = { NULL, VFE_MSG_GENERAL },
+ [VFE_MSG_ID_VIOLATION] = { invalid },
+ [VFE_MSG_ID_CAMIF_ERROR] = { vfe_send_camif_error_msg,
+ VFE_MSG_GENERAL },
+ [VFE_MSG_ID_BUS_OVERFLOW] = { vfe_send_bus_overflow_msg,
+ VFE_MSG_GENERAL },
+ [VFE_MSG_ID_SOF_ACK] = { vfe_send_sof_msg,
+ VFE_MSG_GENERAL },
+};
+
+static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data)
+{
+ struct msm_vfe_resp *rp;
+ struct vfe_message *msg;
+
+ if (id >= ARRAY_SIZE(vfe_funcs) || vfe_funcs[id].fn == invalid) {
+ pr_err("%s: invalid VFE message id %d\n", __func__, id);
+ return;
+ }
+
+ /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before SNAPSHOT_DONE.
+ * We don't send such messages to the user. Note that we can do
+ * this in the vfe_func[] callback, but that would cause us to
+ * allocate and then immediately free the msm_vfe_resp structure,
+ * which is wasteful.
+ */
+ if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) &&
+ (id == VFE_MSG_ID_OUTPUT_T ||
+ id == VFE_MSG_ID_OUTPUT_S))
+ return;
+
+ rp = ctrl->resp->vfe_alloc(sizeof(*rp) +
+ (vfe_funcs[id].fn ? sizeof(*msg) : 0),
+ ctrl->syncdata,
+ GFP_ATOMIC);
+ if (!rp) {
+ pr_err("%s: out of memory\n", __func__);
+ return;
+ }
+
+ rp->type = vfe_funcs[id].rt;
+ rp->evt_msg.type = MSM_CAMERA_MSG;
+ rp->evt_msg.msg_id = id;
+
+ if (!vfe_funcs[id].fn) {
+ rp->evt_msg.len = 0;
+ rp->evt_msg.data = 0;
+ } else {
+ /* populate the message accordingly */
+ if (vfe_funcs[id].fn)
+ rp->evt_msg.data = msg =
+ (struct vfe_message *)(rp + 1);
+ else
+ rp->evt_msg.data = msg = 0;
+ rp->evt_msg.len = sizeof(*msg);
+ msg->_d = id;
+ if (vfe_funcs[id].fn(rp, msg, data) == FALSE) {
+ pr_warning("%s: freeing memory: handler for %d "
+ "returned false\n", __func__, id);
+ ctrl->resp->vfe_free(rp);
+ return;
+ }
+}
+
+ ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata, GFP_KERNEL);
+}
+
+static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg,
+ void *data)
+{
+#if 0
+ memcpy(&(msg->_u.msgBusOverflow),
+ &ctrl->vfePmData, sizeof(ctrl->vfePmData));
+#endif
+ return TRUE;
+}
+
+static boolean vfe_send_sof_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg,
+ void *data)
+{
+ return TRUE;
+}
+static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg,
+ void *data)
+{
+#if 0
+ memcpy(&(msg->_u.msgCamifError),
+ &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal));
+#endif
+ return TRUE;
+}
+
+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_proc_ops(VFE_MSG_ID_AF_OVERFLOW, NULL);
+
+ if (irqstatus->awbOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_AWB_OVERFLOW, NULL);
+
+ if (irqstatus->axiErrorIrq)
+ vfe_proc_ops(VFE_MSG_ID_AXI_ERROR, NULL);
+
+ if (irqstatus->busOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, NULL);
+
+ if (irqstatus->camifErrorIrq) {
+ CDBG("vfe_irq: camif errors\n");
+ vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, NULL);
+ }
+
+ if (irqstatus->camifOverflowIrq)
+ vfe_proc_ops(VFE_MSG_ID_CAMIF_OVERFLOW, NULL);
+
+ if (irqstatus->violationIrq)
+ pr_err("%s: violation irq\n", __func__);
+}
+
+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;
+ }
+ vfe_proc_ops(VFE_MSG_ID_SOF_ACK, NULL);
+}
+
+static boolean vfe_get_af_pingpong_status(void)
+{
+ uint32_t busPingPongStatus =
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+ return !!(busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT);
+}
+
+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 boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ uint32_t afBufAddress = (uint32_t)data;
+
+ /* 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)
+ return FALSE;
+
+ msg->_u.msgStatsAf.afBuffer = afBufAddress;
+ msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId;
+
+ ctrl->afStatsControl.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy), rp->type, msg, NULL, NULL);
+ /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */
+ return TRUE;
+}
+
+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_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS,
+ (void *)ctrl->afStatsControl.bufToRender);
+ } else
+ ctrl->afStatsControl.droppedStatsFrameCount++;
+}
+
+static boolean vfe_get_awb_pingpong_status(void)
+{
+ uint32_t busPingPongStatus =
+
+ readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS);
+
+ return !!(busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT);
+
+}
+
+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 boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ uint32_t awbBufAddress = (uint32_t)data;
+
+ /* 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)
+ return FALSE;
+
+ msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress;
+ msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId;
+
+
+ ctrl->awbStatsControl.ackPending = TRUE;
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ NULL, NULL);
+
+ return TRUE;
+}
+
+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_proc_ops(VFE_MSG_ID_STATS_WB_EXP,
+ (void *)ctrl->awbStatsControl.bufToRender);
+
+ } else
+ ctrl->awbStatsControl.droppedStatsFrameCount++;
+}
+
+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);
+
+/* this for YUV performance tuning
+ 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 void
+vfe_parse_interrupt_status(struct vfe_interrupt_status *ret,
+uint32_t irqStatusIn)
+{
+ struct vfe_irqenable hwstat;
+ 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->awbOverflowIrq ||
+ ret->awbPingpongIrq ||
+ ret->afPingpongIrq ||
+ 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;
+}
+
+static void
+vfe_get_asf_frame_info(struct vfe_frame_asf_info *rc,
+struct vfe_irq_thread_msg *in)
+{
+ struct vfe_asf_info asfInfoTemp;
+
+ 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;
+}
+
+static void
+vfe_get_demosaic_frame_info(struct vfe_frame_bpc_info *rc,
+struct vfe_irq_thread_msg *in)
+{
+ struct vfe_bps_info bpcInfoTemp;
+
+ 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;
+}
+
+static void
+vfe_get_camif_status(struct vfe_msg_camif_status *rc,
+struct vfe_irq_thread_msg *in)
+{
+ struct vfe_camif_stats camifStatusTemp;
+
+ 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;
+}
+
+static void
+vfe_get_performance_monitor_data(struct vfe_bus_performance_monitor *rc,
+ struct vfe_irq_thread_msg *in)
+{
+ 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;
+}
+
+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_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ } else
+ vfe_proc_ops(VFE_MSG_ID_UPDATE_ACK, NULL);
+}
+
+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_proc_ops(VFE_MSG_ID_STOP_ACK, NULL);
+ } else {
+ vfe_set_default_reg_values();
+ vfe_proc_ops(VFE_MSG_ID_RESET_ACK, NULL);
+ }
+}
+
+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 boolean vfe_send_video_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+ memcpy(&(msg->_u),
+ (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ rp->phy.output_id = OUTPUT_TYPE_V;
+ CDBG("vfe_send_video_msg rp->type= %d\n", rp->type);
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+ return TRUE;
+}
+
+static boolean vfe_send_preview_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ rp->phy.output_id = OUTPUT_TYPE_P;
+ CDBG("vfe_send_preview_msg rp->type= %d\n", rp->type);
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+
+ return TRUE;
+}
+
+
+static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ rp->phy.output_id = OUTPUT_TYPE_T;
+ CDBG("vfe_send_thumbnail_msg rp->type= %d\n", rp->type);
+
+ if (ctrl->viewPath.snapshotPendingCount <= 1)
+ ctrl->viewPath.ackPending = FALSE;
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+ return TRUE;
+}
+
+static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp,
+ struct vfe_message *msg, void *data)
+{
+ struct vfe_msg_output *pPayload = data;
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return FALSE;
+
+ memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output));
+
+ rp->phy.output_id = OUTPUT_TYPE_S;
+ CDBG("vfe_send_mainimage_msg rp->type= %d\n", rp->type);
+
+ if (ctrl->encPath.snapshotPendingCount <= 1) {
+ ctrl->encPath.ackPending = FALSE;
+ }
+
+ vfe_addr_convert(&(rp->phy),
+ rp->type, msg,
+ &(rp->extdata), &(rp->extlen));
+
+ return TRUE;
+}
+
+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; */
+ ctrl->encPath.ackPending = TRUE;
+
+ if (ctrl->vfeOperationMode == 0) {
+ if (ctrl->axiOutputMode ==
+ VFE_AXI_OUTPUT_MODE_Output1AndOutput2) {
+ /* video mode */
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT_V, &msgPayload);
+ } else{
+ /* preview mode */
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload);
+ }
+ } else {
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT_S, &msgPayload);
+ }
+
+ } else {
+ /* physical output1 path from vfe */
+ ctrl->viewPath.ackPending = TRUE;
+
+ if (ctrl->vfeOperationMode == 0) {
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload);
+ CDBG(" video mode display output.\n");
+
+ } else{
+ vfe_proc_ops(VFE_MSG_ID_OUTPUT_T, &msgPayload);
+ CDBG(" snapshot mode thumbnail output.\n");
+ }
+ }
+}
+
+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];
+
+ 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_proc_ops(VFE_MSG_ID_SNAPSHOT_DONE, NULL);
+ vfe_camif_stop_immediately();
+ vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP);
+ vfe_pm_stop();
+ }
+ }
+}
+
+static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd)
+{
+ if (qcmd->vfeInterruptStatus.regUpdateIrq) {
+ CDBG("irq regUpdateIrq\n");
+ vfe_process_reg_update_irq();
+ }
+
+ if (qcmd->vfeInterruptStatus.resetAckIrq) {
+ CDBG("%s: process resetAckIrq\n", __func__);
+ vfe_process_reset_irq();
+ }
+
+ if (ctrl->vstate != VFE_STATE_ACTIVE)
+ return;
+
+#if 0
+ if (qcmd->vfeInterruptStatus.camifEpoch1Irq)
+ vfe_proc_ops(VFE_MSG_ID_EPOCH1);
+
+ if (qcmd->vfeInterruptStatus.camifEpoch2Irq)
+ vfe_proc_ops(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();
+ }
+}
+
+static struct isr_queue_cmd *get_irq_cmd_nosync(void)
+{
+ int old_get = ctrl->irq_get++;
+ ctrl->irq_get = ctrl->irq_get % ARRAY_SIZE(ctrl->irqs);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ pr_err("%s: out of irq command packets\n", __func__);
+ ctrl->irq_get = old_get;
+ return NULL;
+ }
+
+ return ctrl->irqs + old_get;
+}
+
+static struct isr_queue_cmd *next_irq_cmd(void)
+{
+ unsigned long flags;
+ struct isr_queue_cmd *cmd;
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return NULL; /* already empty */
+ }
+ cmd = ctrl->irqs + ctrl->irq_put;
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return cmd;
+}
+
+static void put_irq_cmd(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ if (ctrl->irq_get == ctrl->irq_put) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ return; /* already empty */
+ }
+ ctrl->irq_put++;
+ ctrl->irq_put %= ARRAY_SIZE(ctrl->irqs);
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+}
+
+static void vfe_do_tasklet(unsigned long data)
+{
+ int cnt = 0;
+ unsigned long flags;
+ struct isr_queue_cmd *qcmd = NULL;
+
+ spin_lock_irqsave(&msm_vfe_ctrl_lock, flags);
+ if (!ctrl) {
+ spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+ return;
+ }
+
+ CDBG("%s\n", __func__);
+
+ while ((qcmd = next_irq_cmd())) {
+ __vfe_do_tasklet(qcmd);
+ put_irq_cmd();
+ cnt++;
+ }
+
+ if (cnt > ARRAY_SIZE(ctrl->irqs)/2)
+ CDBG("%s: serviced %d vfe interrupts\n", __func__, cnt);
+
+ spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+}
+
+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");
+
+ if (!atomic_read(&ctrl->vfe_serv_interrupt))
+ return IRQ_HANDLED;
+
+ vfe_read_irq_status(&irq);
+
+ if (irq.vfeIrqStatus == 0) {
+ CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n");
+ return IRQ_HANDLED;
+ }
+
+ if (ctrl->vfeStopAckPending)
+ irqStatusLocal = (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus);
+ else
+ irqStatusLocal =
+ ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) &
+ irq.vfeIrqStatus);
+
+ spin_lock_irqsave(&ctrl->irqs_lock, flags);
+ qcmd = get_irq_cmd_nosync();
+ if (!qcmd) {
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ goto done;
+ }
+ /* first parse the interrupt status to local data structures. */
+ vfe_parse_interrupt_status(&qcmd->vfeInterruptStatus, irqStatusLocal);
+ vfe_get_asf_frame_info(&qcmd->vfeAsfFrameInfo, &irq);
+ vfe_get_demosaic_frame_info(&qcmd->vfeBpcFrameInfo, &irq);
+ vfe_get_camif_status(&qcmd->vfeCamifStatusLocal, &irq);
+ vfe_get_performance_monitor_data(&qcmd->vfePmData, &irq);
+ spin_unlock_irqrestore(&ctrl->irqs_lock, flags);
+ tasklet_schedule(&vfe_tasklet);
+
+done:
+ /* 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;
+ struct msm_camera_sensor_info *s_info;
+ s_info = pdev->dev.platform_data;
+
+ pdev->resource = s_info->resource;
+ pdev->num_resources = s_info->num_resources;
+
+ vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vfemem) {
+ pr_err("%s: no mem resource\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vfeirq) {
+ pr_err("%s: no irq resource\n", __func__);
+ return -ENODEV;
+ }
+
+ vfeio = request_mem_region(vfemem->start,
+ resource_size(vfemem), pdev->name);
+ if (!vfeio) {
+ pr_err("%s: VFE region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ ctrl = kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ pr_err("%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ goto cmd_init_failed1;
+ }
+ atomic_set(&ctrl->vfe_serv_interrupt, 0);
+ ctrl->vfeirq = vfeirq->start;
+
+ ctrl->vfebase =
+ ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1);
+ if (!ctrl->vfebase) {
+ pr_err("%s: ioremap failed\n", __func__);
+ rc = -ENOMEM;
+ goto cmd_init_failed2;
+ }
+
+ rc = request_irq(ctrl->vfeirq, vfe_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", 0);
+ if (rc < 0) {
+ pr_err("%s: request_irq(%d) failed\n", __func__, ctrl->vfeirq);
+ goto cmd_init_failed2;
+ }
+
+ if (presp && presp->vfe_resp)
+ ctrl->resp = presp;
+ else {
+ pr_err("%s: no vfe_resp function\n", __func__);
+
+ rc = -EIO;
+ goto cmd_init_failed3;
+ }
+
+ 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;
+ unsigned long flags;
+ atomic_set(&ctrl->vfe_serv_interrupt, 0);
+ 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);
+
+ spin_lock_irqsave(&msm_vfe_ctrl_lock, flags);
+ kfree(ctrl);
+ ctrl = 0;
+ spin_unlock_irqrestore(&msm_vfe_ctrl_lock, flags);
+}
+
+void vfe_stats_af_stop(void)
+{
+ ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE;
+ ctrl->vfeImaskLocal.afPingpongIrq = FALSE;
+}
+
+void vfe_stop(void)
+{
+ int spin_cnt = 0;
+ 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);
+
+ do {
+ vfeAxiStauts = vfe_read_axi_status();
+ spin_cnt++;
+ } while (!(vfeAxiStauts & AXI_STATUS_BUSY_MASK));
+ if (spin_cnt > 1)
+ pr_warning("%s: spin_cnt %d\n", __func__, spin_cnt);
+
+ 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:
+ pr_err("%s: invalid gamma channel %d\n", __func__,
+ in->channelSelect);
+ 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:
+ pr_err("%s: invalid gamma channel %d\n", __func__,
+ in->channelSelect);
+ 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_output_v_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ CDBG("video_frame_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_output_p_ack(struct vfe_cmd_output_ack *in)
+{
+ const uint32_t *psrc;
+ uint32_t *pdest;
+ uint8_t i;
+
+ if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_Output1AndOutput2) {
+ /* video mode, preview comes from output1 path */
+
+ 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;
+
+ } else { /* preview mode, preview comes from output2 path. */
+ pdest = ctrl->encPath.nextFrameAddrBuf;
+
+ 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_start(struct vfe_cmd_start *in)
+{
+ 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) {
+
+ update_axi_qos(MSM_AXI_QOS_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;
+ }
+ } else
+ update_axi_qos(MSM_AXI_QOS_PREVIEW);
+
+ /* 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_proc_ops(VFE_MSG_ID_START_ACK, NULL);
+ ctrl->vfeStartAckPendingFlag = FALSE;
+ }
+
+ ctrl->vstate = VFE_STATE_ACTIVE;
+}
+
+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.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));
+}
+
+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_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)
+{
+ /* 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;
+
+ ctrl->vstate = VFE_STATE_IDLE;
+
+ 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)
+{
+ spin_lock_init(&msm_vfe_ctrl_lock);
+ vfe_reset_internal_variables();
+
+ atomic_set(&ctrl->vfe_serv_interrupt, 1);
+ 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/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h
new file mode 100644
index 0000000..da00e8f
--- /dev/null
+++ b/drivers/media/video/msm/msm_vfe8x_proc.h
@@ -0,0 +1,1563 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef __MSM_VFE8X_REG_H__
+#define __MSM_VFE8X_REG_H__
+
+#include <mach/msm_iomap.h>
+#include <mach/camera.h>
+#include "msm_vfe8x.h"
+
+
+#define MSM_AXI_QOS_PREVIEW 128000
+#define MSM_AXI_QOS_SNAPSHOT 128000
+#define MSM_AXI_QOS_RECORDING 128000
+
+
+/* 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_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_output_p_ack(struct vfe_cmd_output_ack *);
+void vfe_output_v_ack(struct vfe_cmd_output_ack *);
+#endif /* __MSM_VFE8X_REG_H__ */
diff --git a/drivers/media/video/msm/msm_vpe1.c b/drivers/media/video/msm/msm_vpe1.c
new file mode 100644
index 0000000..70b9448
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe1.c
@@ -0,0 +1,1446 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "msm_vpe1.h"
+#include <mach/msm_reqs.h>
+#include <linux/pm_qos_params.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <asm/div64.h>
+
+static int vpe_enable(uint32_t);
+static int vpe_disable(void);
+static int vpe_update_scaler(struct video_crop_t *pcrop);
+static struct vpe_device_type vpe_device_data;
+static struct vpe_device_type *vpe_device;
+struct vpe_ctrl_type *vpe_ctrl;
+char *vpe_general_cmd[] = {
+ "VPE_DUMMY_0", /* 0 */
+ "VPE_SET_CLK",
+ "VPE_RESET",
+ "VPE_START",
+ "VPE_ABORT",
+ "VPE_OPERATION_MODE_CFG", /* 5 */
+ "VPE_INPUT_PLANE_CFG",
+ "VPE_OUTPUT_PLANE_CFG",
+ "VPE_INPUT_PLANE_UPDATE",
+ "VPE_SCALE_CFG_TYPE",
+ "VPE_ROTATION_CFG_TYPE", /* 10 */
+ "VPE_AXI_OUT_CFG",
+ "VPE_CMD_DIS_OFFSET_CFG",
+ "VPE_ENABLE",
+ "VPE_DISABLE",
+};
+static uint32_t orig_src_y, orig_src_cbcr;
+
+#define CHECKED_COPY_FROM_USER(in) { \
+ if (copy_from_user((in), (void __user *)cmd->value, \
+ cmd->length)) { \
+ rc = -EFAULT; \
+ break; \
+ } \
+}
+
+#define msm_dequeue_vpe(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+})
+
+/*
+static struct vpe_cmd_type vpe_cmd[] = {
+ {VPE_DUMMY_0, 0},
+ {VPE_SET_CLK, 0},
+ {VPE_RESET, 0},
+ {VPE_START, 0},
+ {VPE_ABORT, 0},
+ {VPE_OPERATION_MODE_CFG, VPE_OPERATION_MODE_CFG_LEN},
+ {VPE_INPUT_PLANE_CFG, VPE_INPUT_PLANE_CFG_LEN},
+ {VPE_OUTPUT_PLANE_CFG, VPE_OUTPUT_PLANE_CFG_LEN},
+ {VPE_INPUT_PLANE_UPDATE, VPE_INPUT_PLANE_UPDATE_LEN},
+ {VPE_SCALE_CFG_TYPE, VPE_SCALER_CONFIG_LEN},
+ {VPE_ROTATION_CFG_TYPE, 0},
+ {VPE_AXI_OUT_CFG, 0},
+ {VPE_CMD_DIS_OFFSET_CFG, VPE_DIS_OFFSET_CFG_LEN},
+};
+*/
+
+static long long vpe_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+static int vpe_start(void)
+{
+ /* enable the frame irq, bit 0 = Display list 0 ROI done */
+ msm_io_w(1, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+ msm_io_dump(vpe_device->vpebase + 0x10000, 0x250);
+ /* this triggers the operation. */
+ msm_io_w(1, vpe_device->vpebase + VPE_DL0_START_OFFSET);
+
+ return 0;
+}
+
+void vpe_reset_state_variables(void)
+{
+ /* initialize local variables for state control, etc.*/
+ vpe_ctrl->op_mode = 0;
+ vpe_ctrl->state = VPE_STATE_INIT;
+ spin_lock_init(&vpe_ctrl->tasklet_lock);
+ spin_lock_init(&vpe_ctrl->state_lock);
+ INIT_LIST_HEAD(&vpe_ctrl->tasklet_q);
+}
+
+static void vpe_config_axi_default(void)
+{
+ msm_io_w(0x25, vpe_device->vpebase + VPE_AXI_ARB_2_OFFSET);
+
+ CDBG("%s: yaddr %ld cbcraddr %ld", __func__,
+ vpe_ctrl->out_y_addr, vpe_ctrl->out_cbcr_addr);
+
+ if (!vpe_ctrl->out_y_addr || !vpe_ctrl->out_cbcr_addr)
+ return;
+
+ msm_io_w(vpe_ctrl->out_y_addr,
+ vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+ /* for video CbCr address */
+ msm_io_w(vpe_ctrl->out_cbcr_addr,
+ vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+}
+
+static int vpe_reset(void)
+{
+ uint32_t vpe_version;
+ uint32_t rc;
+
+ vpe_reset_state_variables();
+ vpe_version = msm_io_r(vpe_device->vpebase + VPE_HW_VERSION_OFFSET);
+ CDBG("vpe_version = 0x%x\n", vpe_version);
+
+ /* disable all interrupts.*/
+ msm_io_w(0, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+ /* clear all pending interrupts*/
+ msm_io_w(0x1fffff, vpe_device->vpebase + VPE_INTR_CLEAR_OFFSET);
+
+ /* write sw_reset to reset the core. */
+ msm_io_w(0x10, vpe_device->vpebase + VPE_SW_RESET_OFFSET);
+
+ /* then poll the reset bit, it should be self-cleared. */
+ while (1) {
+ rc =
+ msm_io_r(vpe_device->vpebase + VPE_SW_RESET_OFFSET) & 0x10;
+ if (rc == 0)
+ break;
+ }
+
+ /* at this point, hardware is reset. Then pogram to default
+ values. */
+ msm_io_w(VPE_AXI_RD_ARB_CONFIG_VALUE,
+ vpe_device->vpebase + VPE_AXI_RD_ARB_CONFIG_OFFSET);
+
+ msm_io_w(VPE_CGC_ENABLE_VALUE,
+ vpe_device->vpebase + VPE_CGC_EN_OFFSET);
+
+ msm_io_w(1, vpe_device->vpebase + VPE_CMD_MODE_OFFSET);
+
+ msm_io_w(VPE_DEFAULT_OP_MODE_VALUE,
+ vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+ msm_io_w(VPE_DEFAULT_SCALE_CONFIG,
+ vpe_device->vpebase + VPE_SCALE_CONFIG_OFFSET);
+
+ vpe_config_axi_default();
+ return 0;
+}
+
+int msm_vpe_cfg_update(void *pinfo)
+{
+ uint32_t rot_flag, rc = 0;
+ struct video_crop_t *pcrop = (struct video_crop_t *)pinfo;
+
+ rot_flag = msm_io_r(vpe_device->vpebase +
+ VPE_OP_MODE_OFFSET) & 0xE00;
+ if (pinfo != NULL) {
+ CDBG("Crop info in2_w = %d, in2_h = %d "
+ "out2_h = %d out2_w = %d \n", pcrop->in2_w,
+ pcrop->in2_h,
+ pcrop->out2_h, pcrop->out2_w);
+ rc = vpe_update_scaler(pcrop);
+ }
+ CDBG("return rc = %d rot_flag = %d\n", rc, rot_flag);
+ rc |= rot_flag;
+
+ return rc;
+}
+
+void vpe_update_scale_coef(uint32_t *p)
+{
+ uint32_t i, offset;
+ offset = *p;
+ for (i = offset; i < (VPE_SCALE_COEFF_NUM + offset); i++) {
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SCALE_COEFF_LSBn(i));
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SCALE_COEFF_MSBn(i));
+ }
+}
+
+void vpe_input_plane_config(uint32_t *p)
+{
+ msm_io_w(*p, vpe_device->vpebase + VPE_SRC_FORMAT_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SRC_UNPACK_PATTERN1_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SRC_IMAGE_SIZE_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SRC_YSTRIDE1_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+ vpe_ctrl->in_h_w = *p;
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+}
+
+void vpe_output_plane_config(uint32_t *p)
+{
+ msm_io_w(*p, vpe_device->vpebase + VPE_OUT_FORMAT_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_OUT_PACK_PATTERN1_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_OUT_YSTRIDE1_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_OUT_SIZE_OFFSET);
+ msm_io_w(*(++p), vpe_device->vpebase + VPE_OUT_XY_OFFSET);
+ vpe_ctrl->pcbcr_dis_offset = *(++p);
+}
+
+static int vpe_operation_config(uint32_t *p)
+{
+ uint32_t outw, outh, temp;
+ msm_io_w(*p, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+ temp = msm_io_r(vpe_device->vpebase + VPE_OUT_SIZE_OFFSET);
+ outw = temp & 0xFFF;
+ outh = (temp & 0xFFF0000) >> 16;
+
+ if (*p++ & 0xE00) {
+ /* rotation enabled. */
+ vpe_ctrl->out_w = outh;
+ vpe_ctrl->out_h = outw;
+ } else {
+ vpe_ctrl->out_w = outw;
+ vpe_ctrl->out_h = outh;
+ }
+ vpe_ctrl->dis_en = *p;
+ return 0;
+}
+
+/* Later we can separate the rotation and scaler calc. If
+* rotation is enabled, simply swap the destination dimension.
+* And then pass the already swapped output size to this
+* function. */
+static int vpe_update_scaler(struct video_crop_t *pcrop)
+{
+ uint32_t out_ROI_width, out_ROI_height;
+ uint32_t src_ROI_width, src_ROI_height;
+
+ uint32_t rc = 0; /* default to no zoom. */
+ /*
+ * phase_step_x, phase_step_y, phase_init_x and phase_init_y
+ * are represented in fixed-point, unsigned 3.29 format
+ */
+ uint32_t phase_step_x = 0;
+ uint32_t phase_step_y = 0;
+ uint32_t phase_init_x = 0;
+ uint32_t phase_init_y = 0;
+
+ uint32_t src_roi, src_x, src_y, src_xy, temp;
+ uint32_t yscale_filter_sel, xscale_filter_sel;
+ uint32_t scale_unit_sel_x, scale_unit_sel_y;
+ uint64_t numerator, denominator;
+
+ if ((pcrop->in2_w >= pcrop->out2_w) &&
+ (pcrop->in2_h >= pcrop->out2_h)) {
+ CDBG(" =======VPE no zoom needed.\n");
+
+ temp = msm_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET)
+ & 0xfffffffc;
+ msm_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+
+ msm_io_w(0, vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+
+ CDBG("vpe_ctrl->in_h_w = %d \n", vpe_ctrl->in_h_w);
+ msm_io_w(vpe_ctrl->in_h_w , vpe_device->vpebase +
+ VPE_SRC_SIZE_OFFSET);
+
+ return rc;
+ }
+ /* If fall through then scaler is needed.*/
+
+ CDBG("========VPE zoom needed.\n");
+ /* assumption is both direction need zoom. this can be
+ improved. */
+ temp =
+ msm_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET) | 0x3;
+ msm_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+ src_ROI_width = pcrop->in2_w;
+ src_ROI_height = pcrop->in2_h;
+ out_ROI_width = pcrop->out2_w;
+ out_ROI_height = pcrop->out2_h;
+
+ CDBG("src w = 0x%x, h=0x%x, dst w = 0x%x, h =0x%x.\n",
+ src_ROI_width, src_ROI_height, out_ROI_width,
+ out_ROI_height);
+ src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+ msm_io_w(src_roi, vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+
+ src_x = (out_ROI_width - src_ROI_width)/2;
+ src_y = (out_ROI_height - src_ROI_height)/2;
+
+ CDBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+ src_xy = src_y*(1<<16) + src_x;
+ msm_io_w(src_xy, vpe_device->vpebase +
+ VPE_SRC_XY_OFFSET);
+ CDBG("src_xy = %d, src_roi=%d.\n", src_xy, src_roi);
+
+ /* decide whether to use FIR or M/N for scaling */
+ if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+ (src_ROI_width < 4 * out_ROI_width - 3))
+ scale_unit_sel_x = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_x = 1;/* use M/N scalar */
+
+ if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+ (src_ROI_height < 4 * out_ROI_height - 3))
+ scale_unit_sel_y = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_y = 1;/* use M/N scalar */
+
+ /* calculate phase step for the x direction */
+
+ /* if destination is only 1 pixel wide,
+ the value of phase_step_x
+ is unimportant. Assigning phase_step_x to
+ src ROI width as an arbitrary value. */
+ if (out_ROI_width == 1)
+ phase_step_x = (uint32_t) ((src_ROI_width) <<
+ SCALER_PHASE_BITS);
+
+ /* if using FIR scalar */
+ else if (scale_unit_sel_x == 0) {
+
+ /* Calculate the quotient ( src_ROI_width - 1 )
+ / ( out_ROI_width - 1)
+ with u3.29 precision. Quotient is rounded up to
+ the larger 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_width - 1) <<
+ SCALER_PHASE_BITS;
+ /* never equals to 0 because of the
+ "(out_ROI_width == 1 )"*/
+ denominator = (uint64_t)(out_ROI_width - 1);
+ /* divide and round up to the larger 29th
+ decimal point. */
+ phase_step_x = (uint32_t) vpe_do_div((numerator +
+ denominator - 1), denominator);
+ } else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+ /* Calculate the quotient ( src_ROI_width ) /
+ ( out_ROI_width)
+ with u3.29 precision. Quotient is rounded down to the
+ smaller 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_width) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_width);
+ phase_step_x =
+ (uint32_t) vpe_do_div(numerator, denominator);
+ }
+ /* calculate phase step for the y direction */
+
+ /* if destination is only 1 pixel wide, the value of
+ phase_step_x is unimportant. Assigning phase_step_x
+ to src ROI width as an arbitrary value. */
+ if (out_ROI_height == 1)
+ phase_step_y =
+ (uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+ /* if FIR scalar */
+ else if (scale_unit_sel_y == 0) {
+ /* Calculate the quotient ( src_ROI_height - 1 ) /
+ ( out_ROI_height - 1)
+ with u3.29 precision. Quotient is rounded up to the
+ larger 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_height - 1) <<
+ SCALER_PHASE_BITS;
+ /* never equals to 0 because of the "
+ ( out_ROI_height == 1 )" case */
+ denominator = (uint64_t)(out_ROI_height - 1);
+ /* Quotient is rounded up to the larger
+ 29th decimal point. */
+ phase_step_y =
+ (uint32_t) vpe_do_div(
+ (numerator + denominator - 1), denominator);
+ } else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+ /* Calculate the quotient ( src_ROI_height )
+ / ( out_ROI_height)
+ with u3.29 precision. Quotient is rounded down
+ to the smaller 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_height) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_height);
+ phase_step_y = (uint32_t) vpe_do_div(
+ numerator, denominator);
+ }
+
+ /* decide which set of FIR coefficients to use */
+ if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+ xscale_filter_sel = 0;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+ xscale_filter_sel = 1;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+ xscale_filter_sel = 2;
+ else
+ xscale_filter_sel = 3;
+
+ if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+ yscale_filter_sel = 0;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+ yscale_filter_sel = 1;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+ yscale_filter_sel = 2;
+ else
+ yscale_filter_sel = 3;
+
+ /* calculate phase init for the x direction */
+
+ /* if using FIR scalar */
+ if (scale_unit_sel_x == 0) {
+ if (out_ROI_width == 1)
+ phase_init_x =
+ (uint32_t) ((src_ROI_width - 1) <<
+ SCALER_PHASE_BITS);
+ else
+ phase_init_x = 0;
+ } else if (scale_unit_sel_x == 1) /* M over N scalar */
+ phase_init_x = 0;
+
+ /* calculate phase init for the y direction
+ if using FIR scalar */
+ if (scale_unit_sel_y == 0) {
+ if (out_ROI_height == 1)
+ phase_init_y =
+ (uint32_t) ((src_ROI_height -
+ 1) << SCALER_PHASE_BITS);
+ else
+ phase_init_y = 0;
+ } else if (scale_unit_sel_y == 1) /* M over N scalar */
+ phase_init_y = 0;
+
+ CDBG("phase step x = %d, step y = %d.\n",
+ phase_step_x, phase_step_y);
+ CDBG("phase init x = %d, init y = %d.\n",
+ phase_init_x, phase_init_y);
+
+ msm_io_w(phase_step_x, vpe_device->vpebase +
+ VPE_SCALE_PHASEX_STEP_OFFSET);
+ msm_io_w(phase_step_y, vpe_device->vpebase +
+ VPE_SCALE_PHASEY_STEP_OFFSET);
+
+ msm_io_w(phase_init_x, vpe_device->vpebase +
+ VPE_SCALE_PHASEX_INIT_OFFSET);
+
+ msm_io_w(phase_init_y, vpe_device->vpebase +
+ VPE_SCALE_PHASEY_INIT_OFFSET);
+
+ return 1;
+}
+
+static int vpe_update_scaler_with_dis(struct video_crop_t *pcrop,
+ struct dis_offset_type *dis_offset)
+{
+ uint32_t out_ROI_width, out_ROI_height;
+ uint32_t src_ROI_width, src_ROI_height;
+
+ uint32_t rc = 0; /* default to no zoom. */
+ /*
+ * phase_step_x, phase_step_y, phase_init_x and phase_init_y
+ * are represented in fixed-point, unsigned 3.29 format
+ */
+ uint32_t phase_step_x = 0;
+ uint32_t phase_step_y = 0;
+ uint32_t phase_init_x = 0;
+ uint32_t phase_init_y = 0;
+
+ uint32_t src_roi, temp;
+ int32_t src_x, src_y, src_xy;
+ uint32_t yscale_filter_sel, xscale_filter_sel;
+ uint32_t scale_unit_sel_x, scale_unit_sel_y;
+ uint64_t numerator, denominator;
+ int32_t zoom_dis_x, zoom_dis_y;
+
+ CDBG("%s: pcrop->in2_w = %d, pcrop->in2_h = %d\n", __func__,
+ pcrop->in2_w, pcrop->in2_h);
+ CDBG("%s: pcrop->out2_w = %d, pcrop->out2_h = %d\n", __func__,
+ pcrop->out2_w, pcrop->out2_h);
+
+ if ((pcrop->in2_w >= pcrop->out2_w) &&
+ (pcrop->in2_h >= pcrop->out2_h)) {
+ CDBG(" =======VPE no zoom needed, DIS is still enabled. \n");
+
+ temp = msm_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET)
+ & 0xfffffffc;
+ msm_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+ /* no zoom, use dis offset directly. */
+ src_xy = dis_offset->dis_offset_y * (1<<16) +
+ dis_offset->dis_offset_x;
+
+ msm_io_w(src_xy, vpe_device->vpebase + VPE_SRC_XY_OFFSET);
+
+ CDBG("vpe_ctrl->in_h_w = 0x%x \n", vpe_ctrl->in_h_w);
+ msm_io_w(vpe_ctrl->in_h_w, vpe_device->vpebase +
+ VPE_SRC_SIZE_OFFSET);
+ return rc;
+ }
+ /* If fall through then scaler is needed.*/
+
+ CDBG("========VPE zoom needed + DIS enabled.\n");
+ /* assumption is both direction need zoom. this can be
+ improved. */
+ temp = msm_io_r(vpe_device->vpebase +
+ VPE_OP_MODE_OFFSET) | 0x3;
+ msm_io_w(temp, vpe_device->vpebase +
+ VPE_OP_MODE_OFFSET);
+ zoom_dis_x = dis_offset->dis_offset_x *
+ pcrop->in2_w / pcrop->out2_w;
+ zoom_dis_y = dis_offset->dis_offset_y *
+ pcrop->in2_h / pcrop->out2_h;
+
+ src_x = zoom_dis_x + (pcrop->out2_w-pcrop->in2_w)/2;
+ src_y = zoom_dis_y + (pcrop->out2_h-pcrop->in2_h)/2;
+
+ out_ROI_width = vpe_ctrl->out_w;
+ out_ROI_height = vpe_ctrl->out_h;
+
+ src_ROI_width = out_ROI_width * pcrop->in2_w / pcrop->out2_w;
+ src_ROI_height = out_ROI_height * pcrop->in2_h / pcrop->out2_h;
+
+ /* clamp to output size. This is because along
+ processing, we mostly do truncation, therefore
+ dis_offset tends to be
+ smaller values. The intention was to make sure that the
+ offset does not exceed margin. But in the case it could
+ result src_roi bigger, due to subtract a smaller value. */
+ CDBG("src w = 0x%x, h=0x%x, dst w = 0x%x, h =0x%x.\n",
+ src_ROI_width, src_ROI_height, out_ROI_width,
+ out_ROI_height);
+
+ src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+ msm_io_w(src_roi, vpe_device->vpebase + VPE_SRC_SIZE_OFFSET);
+
+ CDBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+ src_xy = src_y*(1<<16) + src_x;
+ msm_io_w(src_xy, vpe_device->vpebase +
+ VPE_SRC_XY_OFFSET);
+ CDBG("src_xy = 0x%x, src_roi=0x%x.\n", src_xy, src_roi);
+
+ /* decide whether to use FIR or M/N for scaling */
+ if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+ (src_ROI_width < 4 * out_ROI_width - 3))
+ scale_unit_sel_x = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_x = 1;/* use M/N scalar */
+
+ if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+ (src_ROI_height < 4 * out_ROI_height - 3))
+ scale_unit_sel_y = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_y = 1;/* use M/N scalar */
+ /* calculate phase step for the x direction */
+
+ /* if destination is only 1 pixel wide, the value of
+ phase_step_x is unimportant. Assigning phase_step_x
+ to src ROI width as an arbitrary value. */
+ if (out_ROI_width == 1)
+ phase_step_x = (uint32_t) ((src_ROI_width) <<
+ SCALER_PHASE_BITS);
+ else if (scale_unit_sel_x == 0) { /* if using FIR scalar */
+ /* Calculate the quotient ( src_ROI_width - 1 )
+ / ( out_ROI_width - 1)with u3.29 precision.
+ Quotient is rounded up to the larger
+ 29th decimal point. */
+ numerator =
+ (uint64_t)(src_ROI_width - 1) <<
+ SCALER_PHASE_BITS;
+ /* never equals to 0 because of the "
+ (out_ROI_width == 1 )"*/
+ denominator = (uint64_t)(out_ROI_width - 1);
+ /* divide and round up to the larger 29th
+ decimal point. */
+ phase_step_x = (uint32_t) vpe_do_div(
+ (numerator + denominator - 1), denominator);
+ } else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+ /* Calculate the quotient
+ ( src_ROI_width ) / ( out_ROI_width)
+ with u3.29 precision. Quotient is rounded
+ down to the smaller 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_width) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_width);
+ phase_step_x =
+ (uint32_t) vpe_do_div(numerator, denominator);
+ }
+ /* calculate phase step for the y direction */
+
+ /* if destination is only 1 pixel wide, the value of
+ phase_step_x is unimportant. Assigning phase_step_x
+ to src ROI width as an arbitrary value. */
+ if (out_ROI_height == 1)
+ phase_step_y =
+ (uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+ else if (scale_unit_sel_y == 0) { /* if FIR scalar */
+ /* Calculate the quotient
+ ( src_ROI_height - 1 ) / ( out_ROI_height - 1)
+ with u3.29 precision. Quotient is rounded up to the
+ larger 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_height - 1) <<
+ SCALER_PHASE_BITS;
+ /* never equals to 0 because of the
+ "( out_ROI_height == 1 )" case */
+ denominator = (uint64_t)(out_ROI_height - 1);
+ /* Quotient is rounded up to the larger 29th
+ decimal point. */
+ phase_step_y =
+ (uint32_t) vpe_do_div(
+ (numerator + denominator - 1), denominator);
+ } else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+ /* Calculate the quotient ( src_ROI_height ) / ( out_ROI_height)
+ with u3.29 precision. Quotient is rounded down to the smaller
+ 29th decimal point. */
+ numerator = (uint64_t)(src_ROI_height) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_height);
+ phase_step_y = (uint32_t) vpe_do_div(
+ numerator, denominator);
+ }
+
+ /* decide which set of FIR coefficients to use */
+ if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+ xscale_filter_sel = 0;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+ xscale_filter_sel = 1;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+ xscale_filter_sel = 2;
+ else
+ xscale_filter_sel = 3;
+
+ if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+ yscale_filter_sel = 0;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+ yscale_filter_sel = 1;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+ yscale_filter_sel = 2;
+ else
+ yscale_filter_sel = 3;
+
+ /* calculate phase init for the x direction */
+
+ /* if using FIR scalar */
+ if (scale_unit_sel_x == 0) {
+ if (out_ROI_width == 1)
+ phase_init_x =
+ (uint32_t) ((src_ROI_width - 1) <<
+ SCALER_PHASE_BITS);
+ else
+ phase_init_x = 0;
+
+ } else if (scale_unit_sel_x == 1) /* M over N scalar */
+ phase_init_x = 0;
+
+ /* calculate phase init for the y direction
+ if using FIR scalar */
+ if (scale_unit_sel_y == 0) {
+ if (out_ROI_height == 1)
+ phase_init_y =
+ (uint32_t) ((src_ROI_height -
+ 1) << SCALER_PHASE_BITS);
+ else
+ phase_init_y = 0;
+
+ } else if (scale_unit_sel_y == 1) /* M over N scalar */
+ phase_init_y = 0;
+
+ CDBG("phase step x = %d, step y = %d.\n",
+ phase_step_x, phase_step_y);
+ CDBG("phase init x = %d, init y = %d.\n",
+ phase_init_x, phase_init_y);
+
+ msm_io_w(phase_step_x, vpe_device->vpebase +
+ VPE_SCALE_PHASEX_STEP_OFFSET);
+
+ msm_io_w(phase_step_y, vpe_device->vpebase +
+ VPE_SCALE_PHASEY_STEP_OFFSET);
+
+ msm_io_w(phase_init_x, vpe_device->vpebase +
+ VPE_SCALE_PHASEX_INIT_OFFSET);
+
+ msm_io_w(phase_init_y, vpe_device->vpebase +
+ VPE_SCALE_PHASEY_INIT_OFFSET);
+
+ return 1;
+}
+
+void msm_send_frame_to_vpe(uint32_t pyaddr, uint32_t pcbcraddr,
+ struct timespec *ts, int output_type)
+{
+ uint32_t temp_pyaddr = 0, temp_pcbcraddr = 0;
+
+ CDBG("vpe input, pyaddr = 0x%x, pcbcraddr = 0x%x\n",
+ pyaddr, pcbcraddr);
+ msm_io_w(pyaddr, vpe_device->vpebase + VPE_SRCP0_ADDR_OFFSET);
+ msm_io_w(pcbcraddr, vpe_device->vpebase + VPE_SRCP1_ADDR_OFFSET);
+
+ if (vpe_ctrl->state == VPE_STATE_ACTIVE)
+ CDBG(" =====VPE is busy!!! Wrong!========\n");
+
+ if (output_type != OUTPUT_TYPE_ST_R)
+ vpe_ctrl->ts = *ts;
+
+ if (output_type == OUTPUT_TYPE_ST_L) {
+ vpe_ctrl->pcbcr_before_dis = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ temp_pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ temp_pcbcraddr = temp_pyaddr + PAD_TO_2K(vpe_ctrl->out_w *
+ vpe_ctrl->out_h * 2, vpe_ctrl->pad_2k_bool);
+ msm_io_w(temp_pcbcraddr, vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ }
+
+ if (vpe_ctrl->dis_en) {
+ /* Changing the VPE output CBCR address,
+ to make Y/CBCR continuous */
+ vpe_ctrl->pcbcr_before_dis = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ temp_pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ temp_pcbcraddr = temp_pyaddr + vpe_ctrl->pcbcr_dis_offset;
+ msm_io_w(temp_pcbcraddr, vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ }
+
+ vpe_ctrl->output_type = output_type;
+ vpe_ctrl->state = VPE_STATE_ACTIVE;
+ vpe_start();
+}
+
+static int vpe_proc_general(struct msm_vpe_cmd *cmd)
+{
+ int rc = 0;
+ uint32_t *cmdp = NULL;
+ struct msm_queue_cmd *qcmd = NULL;
+ struct msm_vpe_buf_info *vpe_buf;
+ int turbo_mode = 0;
+ struct msm_sync *sync = (struct msm_sync *)vpe_ctrl->syncdata;
+ CDBG("vpe_proc_general: cmdID = %s, length = %d\n",
+ vpe_general_cmd[cmd->id], cmd->length);
+ switch (cmd->id) {
+ case VPE_ENABLE:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto vpe_proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ turbo_mode = *((int *)(cmd->value));
+ rc = turbo_mode ? vpe_enable(VPE_TURBO_MODE_CLOCK_RATE)
+ : vpe_enable(VPE_NORMAL_MODE_CLOCK_RATE);
+ break;
+ case VPE_DISABLE:
+ rc = vpe_disable();
+ break;
+ case VPE_RESET:
+ case VPE_ABORT:
+ rc = vpe_reset();
+ break;
+ case VPE_START:
+ rc = vpe_start();
+ break;
+
+ case VPE_INPUT_PLANE_CFG:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto vpe_proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ vpe_input_plane_config(cmdp);
+ break;
+
+ case VPE_OPERATION_MODE_CFG:
+ CDBG("cmd->length = %d \n", cmd->length);
+ if (cmd->length != VPE_OPERATION_MODE_CFG_LEN) {
+ rc = -EINVAL;
+ goto vpe_proc_general_done;
+ }
+ cmdp = kmalloc(VPE_OPERATION_MODE_CFG_LEN,
+ GFP_ATOMIC);
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ VPE_OPERATION_MODE_CFG_LEN)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ rc = vpe_operation_config(cmdp);
+ CDBG("rc = %d \n", rc);
+ break;
+
+ case VPE_OUTPUT_PLANE_CFG:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto vpe_proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ vpe_output_plane_config(cmdp);
+ break;
+
+ case VPE_SCALE_CFG_TYPE:
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto vpe_proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ vpe_update_scale_coef(cmdp);
+ break;
+
+ case VPE_CMD_DIS_OFFSET_CFG: {
+ struct msm_vfe_resp *vdata;
+ /* first get the dis offset and frame id. */
+ cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+ if (!cmdp) {
+ rc = -ENOMEM;
+ goto vpe_proc_general_done;
+ }
+ if (copy_from_user(cmdp,
+ (void __user *)(cmd->value),
+ cmd->length)) {
+ rc = -EFAULT;
+ goto vpe_proc_general_done;
+ }
+ /* get the offset. */
+ vpe_ctrl->dis_offset = *(struct dis_offset_type *)cmdp;
+ qcmd = msm_dequeue_vpe(&sync->vpe_q, list_vpe_frame);
+ if (!qcmd) {
+ pr_err("%s: no video frame.\n", __func__);
+ kfree(cmdp);
+ return -EAGAIN;
+ }
+ vdata = (struct msm_vfe_resp *)(qcmd->command);
+ vpe_buf = &vdata->vpe_bf;
+ vpe_update_scaler_with_dis(&(vpe_buf->vpe_crop),
+ &(vpe_ctrl->dis_offset));
+
+ msm_send_frame_to_vpe(vpe_buf->y_phy, vpe_buf->cbcr_phy,
+ &(vpe_buf->ts), OUTPUT_TYPE_V);
+
+ if (!qcmd || !atomic_read(&qcmd->on_heap)) {
+ kfree(cmdp);
+ return -EAGAIN;
+ }
+ if (!atomic_sub_return(1, &qcmd->on_heap))
+ kfree(qcmd);
+ break;
+ }
+
+ default:
+ break;
+ }
+vpe_proc_general_done:
+ kfree(cmdp);
+ return rc;
+}
+
+static void vpe_addr_convert(struct msm_vpe_phy_info *pinfo,
+ enum vpe_resp_msg type, void *data, void **ext, int32_t *elen)
+{
+ CDBG("In vpe_addr_convert type = %d\n", type);
+ switch (type) {
+ case VPE_MSG_OUTPUT_V:
+ pinfo->output_id = OUTPUT_TYPE_V;
+ break;
+ case VPE_MSG_OUTPUT_ST_R:
+ /* output_id will be used by user space only. */
+ pinfo->output_id = OUTPUT_TYPE_V;
+ break;
+ default:
+ break;
+ } /* switch */
+
+ CDBG("In vpe_addr_convert output_id = %d\n", pinfo->output_id);
+
+ pinfo->y_phy =
+ ((struct vpe_message *)data)->_u.msgOut.yBuffer;
+ pinfo->cbcr_phy =
+ ((struct vpe_message *)data)->_u.msgOut.cbcrBuffer;
+ *ext = vpe_ctrl->extdata;
+ *elen = vpe_ctrl->extlen;
+}
+
+void vpe_proc_ops(uint8_t id, void *msg, size_t len)
+{
+ struct msm_vpe_resp *rp;
+
+ rp = vpe_ctrl->resp->vpe_alloc(sizeof(struct msm_vpe_resp),
+ vpe_ctrl->syncdata, GFP_ATOMIC);
+ if (!rp) {
+ CDBG("rp: cannot allocate buffer\n");
+ return;
+ }
+
+ CDBG("vpe_proc_ops, msgId = %d rp->evt_msg.msg_id = %d\n",
+ id, rp->evt_msg.msg_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 MSG_ID_VPE_OUTPUT_V:
+ rp->type = VPE_MSG_OUTPUT_V;
+ vpe_addr_convert(&(rp->phy), VPE_MSG_OUTPUT_V,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_VPE_OUTPUT_ST_R:
+ rp->type = VPE_MSG_OUTPUT_ST_R;
+ vpe_addr_convert(&(rp->phy), VPE_MSG_OUTPUT_ST_R,
+ rp->evt_msg.data, &(rp->extdata),
+ &(rp->extlen));
+ break;
+
+ case MSG_ID_VPE_OUTPUT_ST_L:
+ rp->type = VPE_MSG_OUTPUT_ST_L;
+ break;
+
+ default:
+ rp->type = VPE_MSG_GENERAL;
+ break;
+ }
+ CDBG("%s: time = %ld\n",
+ __func__, vpe_ctrl->ts.tv_nsec);
+
+ vpe_ctrl->resp->vpe_resp(rp, MSM_CAM_Q_VPE_MSG,
+ vpe_ctrl->syncdata,
+ &(vpe_ctrl->ts), GFP_ATOMIC);
+}
+
+int vpe_config_axi(struct axidata *ad)
+{
+ uint32_t p1;
+ struct msm_pmem_region *regp1 = NULL;
+ CDBG("vpe_config_axi:bufnum1 = %d.\n", ad->bufnum1);
+
+ if (ad->bufnum1 != 1)
+ return -EINVAL;
+
+ regp1 = &(ad->region[0]);
+ /* for video Y address */
+ p1 = (regp1->paddr + regp1->info.y_off);
+ msm_io_w(p1, vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+ /* for video CbCr address */
+ p1 = (regp1->paddr + regp1->info.cbcr_off);
+ msm_io_w(p1, vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+ return 0;
+}
+
+int msm_vpe_config(struct msm_vpe_cfg_cmd *cmd, void *data)
+{
+ struct msm_vpe_cmd vpecmd;
+ int rc = 0;
+ if (copy_from_user(&vpecmd,
+ (void __user *)(cmd->value),
+ sizeof(vpecmd))) {
+ pr_err("%s %d: copy_from_user failed\n", __func__,
+ __LINE__);
+ return -EFAULT;
+ }
+ CDBG("%s: cmd_type %d\n", __func__, cmd->cmd_type);
+ switch (cmd->cmd_type) {
+ case CMD_VPE:
+ rc = vpe_proc_general(&vpecmd);
+ CDBG(" rc = %d\n", rc);
+ break;
+
+ case CMD_AXI_CFG_VPE:
+ case CMD_AXI_CFG_SNAP_VPE:
+ case CMD_AXI_CFG_SNAP_THUMB_VPE: {
+ struct axidata *axid;
+ axid = data;
+ if (!axid)
+ return -EFAULT;
+ vpe_config_axi(axid);
+ break;
+ }
+ default:
+ break;
+ }
+ CDBG("%s: rc = %d\n", __func__, rc);
+ return rc;
+}
+
+void msm_vpe_offset_update(int frame_pack, uint32_t pyaddr, uint32_t pcbcraddr,
+ struct timespec *ts, int output_id, struct msm_st_half st_half,
+ int frameid)
+{
+ struct msm_vpe_buf_info vpe_buf;
+ uint32_t input_stride;
+
+ vpe_buf.vpe_crop.in2_w = st_half.stCropInfo.in_w;
+ vpe_buf.vpe_crop.in2_h = st_half.stCropInfo.in_h;
+ vpe_buf.vpe_crop.out2_w = st_half.stCropInfo.out_w;
+ vpe_buf.vpe_crop.out2_h = st_half.stCropInfo.out_h;
+ vpe_ctrl->dis_offset.dis_offset_x = st_half.pix_x_off;
+ vpe_ctrl->dis_offset.dis_offset_y = st_half.pix_y_off;
+ vpe_ctrl->dis_offset.frame_id = frameid;
+ vpe_ctrl->frame_pack = frame_pack;
+ vpe_ctrl->output_type = output_id;
+
+ input_stride = (st_half.buf_cbcr_stride * (1<<16)) +
+ st_half.buf_y_stride;
+
+ msm_io_w(input_stride, vpe_device->vpebase + VPE_SRC_YSTRIDE1_OFFSET);
+
+ vpe_update_scaler_with_dis(&(vpe_buf.vpe_crop),
+ &(vpe_ctrl->dis_offset));
+
+ msm_send_frame_to_vpe(pyaddr, pcbcraddr, ts, output_id);
+}
+
+static void vpe_send_outmsg(uint8_t msgid, uint32_t pyaddr,
+ uint32_t pcbcraddr)
+{
+ struct vpe_message msg;
+ uint8_t outid;
+ msg._d = outid = msgid;
+ msg._u.msgOut.output_id = msgid;
+ msg._u.msgOut.yBuffer = pyaddr;
+ msg._u.msgOut.cbcrBuffer = pcbcraddr;
+ vpe_proc_ops(outid, &msg, sizeof(struct vpe_message));
+ return;
+}
+
+int msm_vpe_reg(struct msm_vpe_callback *presp)
+{
+ if (presp && presp->vpe_resp)
+ vpe_ctrl->resp = presp;
+
+ return 0;
+}
+
+static void vpe_send_msg_no_payload(enum VPE_MESSAGE_ID id)
+{
+ struct vpe_message msg;
+
+ CDBG("vfe31_send_msg_no_payload\n");
+ msg._d = id;
+ vpe_proc_ops(id, &msg, 0);
+}
+
+static void vpe_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ uint32_t pyaddr = 0, pcbcraddr = 0;
+ uint32_t src_y, src_cbcr, temp;
+
+ struct vpe_isr_queue_cmd_type *qcmd = NULL;
+
+ CDBG("=== vpe_do_tasklet start === \n");
+
+ spin_lock_irqsave(&vpe_ctrl->tasklet_lock, flags);
+ qcmd = list_first_entry(&vpe_ctrl->tasklet_q,
+ struct vpe_isr_queue_cmd_type, list);
+
+ if (!qcmd) {
+ spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+ return;
+ }
+
+ list_del(&qcmd->list);
+ spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+
+ /* interrupt to be processed, *qcmd has the payload. */
+ if (qcmd->irq_status & 0x1) {
+ if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_L) {
+ CDBG("vpe left frame done.\n");
+ vpe_ctrl->output_type = 0;
+ CDBG("vpe send out msg.\n");
+ orig_src_y = msm_io_r(vpe_device->vpebase +
+ VPE_SRCP0_ADDR_OFFSET);
+ orig_src_cbcr = msm_io_r(vpe_device->vpebase +
+ VPE_SRCP1_ADDR_OFFSET);
+
+ pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ pcbcraddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ CDBG("%s: out_w = %d, out_h = %d\n", __func__,
+ vpe_ctrl->out_w, vpe_ctrl->out_h);
+
+ if ((vpe_ctrl->frame_pack == TOP_DOWN_FULL) ||
+ (vpe_ctrl->frame_pack == TOP_DOWN_HALF)) {
+ msm_io_w(pyaddr + (vpe_ctrl->out_w *
+ vpe_ctrl->out_h), vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ msm_io_w(pcbcraddr + (vpe_ctrl->out_w *
+ vpe_ctrl->out_h/2),
+ vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ } else if ((vpe_ctrl->frame_pack ==
+ SIDE_BY_SIDE_HALF) || (vpe_ctrl->frame_pack ==
+ SIDE_BY_SIDE_FULL)) {
+ msm_io_w(pyaddr + vpe_ctrl->out_w,
+ vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ msm_io_w(pcbcraddr + vpe_ctrl->out_w,
+ vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ } else
+ CDBG("%s: Invalid packing = %d\n", __func__,
+ vpe_ctrl->frame_pack);
+
+ vpe_send_msg_no_payload(MSG_ID_VPE_OUTPUT_ST_L);
+ vpe_ctrl->state = VPE_STATE_INIT;
+ kfree(qcmd);
+ return;
+ } else if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_R) {
+ src_y = orig_src_y;
+ src_cbcr = orig_src_cbcr;
+ CDBG("%s: out_w = %d, out_h = %d\n", __func__,
+ vpe_ctrl->out_w, vpe_ctrl->out_h);
+
+ if ((vpe_ctrl->frame_pack == TOP_DOWN_FULL) ||
+ (vpe_ctrl->frame_pack == TOP_DOWN_HALF)) {
+ pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET) -
+ (vpe_ctrl->out_w * vpe_ctrl->out_h);
+ } else if ((vpe_ctrl->frame_pack ==
+ SIDE_BY_SIDE_HALF) || (vpe_ctrl->frame_pack ==
+ SIDE_BY_SIDE_FULL)) {
+ pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET) - vpe_ctrl->out_w;
+ } else
+ CDBG("%s: Invalid packing = %d\n", __func__,
+ vpe_ctrl->frame_pack);
+
+ pcbcraddr = vpe_ctrl->pcbcr_before_dis;
+ } else {
+ src_y = msm_io_r(vpe_device->vpebase +
+ VPE_SRCP0_ADDR_OFFSET);
+ src_cbcr = msm_io_r(vpe_device->vpebase +
+ VPE_SRCP1_ADDR_OFFSET);
+ pyaddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ pcbcraddr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ }
+
+ if (vpe_ctrl->dis_en)
+ pcbcraddr = vpe_ctrl->pcbcr_before_dis;
+
+ msm_io_w(src_y,
+ vpe_device->vpebase + VPE_OUTP0_ADDR_OFFSET);
+ msm_io_w(src_cbcr,
+ vpe_device->vpebase + VPE_OUTP1_ADDR_OFFSET);
+
+ temp = msm_io_r(vpe_device->vpebase + VPE_OP_MODE_OFFSET) &
+ 0xFFFFFFFC;
+ msm_io_w(temp, vpe_device->vpebase + VPE_OP_MODE_OFFSET);
+
+ /* now pass this frame to msm_camera.c. */
+ if (vpe_ctrl->output_type == OUTPUT_TYPE_ST_R) {
+ CDBG("vpe send out R msg.\n");
+ vpe_send_outmsg(MSG_ID_VPE_OUTPUT_ST_R, pyaddr,
+ pcbcraddr);
+ } else if (vpe_ctrl->output_type == OUTPUT_TYPE_V) {
+ CDBG("vpe send out V msg.\n");
+ vpe_send_outmsg(MSG_ID_VPE_OUTPUT_V, pyaddr, pcbcraddr);
+ }
+
+ vpe_ctrl->output_type = 0;
+ vpe_ctrl->state = VPE_STATE_INIT; /* put it back to idle. */
+
+ }
+ kfree(qcmd);
+}
+DECLARE_TASKLET(vpe_tasklet, vpe_do_tasklet, 0);
+
+static irqreturn_t vpe_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t irq_status = 0;
+ struct vpe_isr_queue_cmd_type *qcmd;
+
+ CDBG("vpe_parse_irq.\n");
+ /* read and clear back-to-back. */
+ irq_status = msm_io_r_mb(vpe_device->vpebase +
+ VPE_INTR_STATUS_OFFSET);
+ msm_io_w_mb(irq_status, vpe_device->vpebase +
+ VPE_INTR_CLEAR_OFFSET);
+
+ msm_io_w(0, vpe_device->vpebase + VPE_INTR_ENABLE_OFFSET);
+
+ if (irq_status == 0) {
+ pr_err("%s: irq_status = 0,Something is wrong!\n", __func__);
+ return IRQ_HANDLED;
+ }
+ irq_status &= 0x1;
+ /* apply mask. only interested in bit 0. */
+ if (irq_status) {
+ qcmd = kzalloc(sizeof(struct vpe_isr_queue_cmd_type),
+ GFP_ATOMIC);
+ if (!qcmd) {
+ pr_err("%s: qcmd malloc failed!\n", __func__);
+ return IRQ_HANDLED;
+ }
+ /* must be 0x1 now. so in bottom half we don't really
+ need to check. */
+ qcmd->irq_status = irq_status & 0x1;
+ spin_lock_irqsave(&vpe_ctrl->tasklet_lock, flags);
+ list_add_tail(&qcmd->list, &vpe_ctrl->tasklet_q);
+ spin_unlock_irqrestore(&vpe_ctrl->tasklet_lock, flags);
+ tasklet_schedule(&vpe_tasklet);
+ }
+ return IRQ_HANDLED;
+}
+
+static int vpe_enable_irq(void)
+{
+ uint32_t rc = 0;
+ rc = request_irq(vpe_device->vpeirq,
+ vpe_parse_irq,
+ IRQF_TRIGGER_HIGH, "vpe", 0);
+ return rc;
+}
+
+int msm_vpe_open(void)
+{
+ int rc = 0;
+
+ CDBG("%s: In \n", __func__);
+
+ vpe_ctrl = kzalloc(sizeof(struct vpe_ctrl_type), GFP_KERNEL);
+ if (!vpe_ctrl) {
+ pr_err("%s: no memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&vpe_ctrl->ops_lock);
+ CDBG("%s: Out\n", __func__);
+
+ return rc;
+}
+
+int msm_vpe_release(void)
+{
+ /* clean up....*/
+ int rc = 0;
+ CDBG("%s: state %d\n", __func__, vpe_ctrl->state);
+ if (vpe_ctrl->state != VPE_STATE_IDLE)
+ rc = vpe_disable();
+
+ kfree(vpe_ctrl);
+ return rc;
+}
+
+
+int vpe_enable(uint32_t clk_rate)
+{
+ int rc = 0;
+ unsigned long flags = 0;
+ /* don't change the order of clock and irq.*/
+ CDBG("%s: enable_clock rate %u\n", __func__, clk_rate);
+ spin_lock_irqsave(&vpe_ctrl->ops_lock, flags);
+ if (vpe_ctrl->state != VPE_STATE_IDLE) {
+ CDBG("%s: VPE already enabled", __func__);
+ spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+ return 0;
+ }
+ vpe_ctrl->state = VPE_STATE_INIT;
+ spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+
+ rc = msm_camio_vpe_clk_enable(clk_rate);
+ if (rc < 0) {
+ pr_err("%s: msm_camio_vpe_clk_enable failed", __func__);
+ vpe_ctrl->state = VPE_STATE_IDLE;
+ return rc;
+ }
+
+ CDBG("%s: enable_irq\n", __func__);
+ vpe_enable_irq();
+
+ /* initialize the data structure - lock, queue etc. */
+ spin_lock_init(&vpe_ctrl->tasklet_lock);
+ INIT_LIST_HEAD(&vpe_ctrl->tasklet_q);
+
+ return rc;
+}
+
+int vpe_disable(void)
+{
+ int rc = 0;
+ unsigned long flags = 0;
+ CDBG("%s: called", __func__);
+ spin_lock_irqsave(&vpe_ctrl->ops_lock, flags);
+ if (vpe_ctrl->state == VPE_STATE_IDLE) {
+ CDBG("%s: VPE already disabled", __func__);
+ spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+ return 0;
+ }
+ vpe_ctrl->state = VPE_STATE_IDLE;
+ spin_unlock_irqrestore(&vpe_ctrl->ops_lock, flags);
+ vpe_ctrl->out_y_addr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP0_ADDR_OFFSET);
+ vpe_ctrl->out_cbcr_addr = msm_io_r(vpe_device->vpebase +
+ VPE_OUTP1_ADDR_OFFSET);
+ free_irq(vpe_device->vpeirq, 0);
+ tasklet_kill(&vpe_tasklet);
+ rc = msm_camio_vpe_clk_disable();
+ return rc;
+}
+
+static int __msm_vpe_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *vpemem, *vpeirq, *vpeio;
+ void __iomem *vpebase;
+
+ /* first allocate */
+
+ vpe_device = &vpe_device_data;
+ memset(vpe_device, 0, sizeof(struct vpe_device_type));
+
+ /* does the device exist? */
+ vpeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!vpeirq) {
+ pr_err("%s: no vpe irq resource.\n", __func__);
+ rc = -ENODEV;
+ goto vpe_free_device;
+ }
+ vpemem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vpemem) {
+ pr_err("%s: no vpe mem resource!\n", __func__);
+ rc = -ENODEV;
+ goto vpe_free_device;
+ }
+ vpeio = request_mem_region(vpemem->start,
+ resource_size(vpemem), pdev->name);
+ if (!vpeio) {
+ pr_err("%s: VPE region already claimed.\n", __func__);
+ rc = -EBUSY;
+ goto vpe_free_device;
+ }
+
+ vpebase =
+ ioremap(vpemem->start,
+ (vpemem->end - vpemem->start) + 1);
+ if (!vpebase) {
+ pr_err("%s: vpe ioremap failed.\n", __func__);
+ rc = -ENOMEM;
+ goto vpe_release_mem_region;
+ }
+
+ /* Fall through, _probe is successful. */
+ vpe_device->vpeirq = vpeirq->start;
+ vpe_device->vpemem = vpemem;
+ vpe_device->vpeio = vpeio;
+ vpe_device->vpebase = vpebase;
+ return rc; /* this rc should be zero.*/
+
+ iounmap(vpe_device->vpebase); /* this path should never occur */
+
+/* from this part it is error handling. */
+vpe_release_mem_region:
+ release_mem_region(vpemem->start, (vpemem->end - vpemem->start) + 1);
+vpe_free_device:
+ return rc; /* this rc should have error code. */
+}
+
+static int __msm_vpe_remove(struct platform_device *pdev)
+{
+ struct resource *vpemem;
+ vpemem = vpe_device->vpemem;
+
+ iounmap(vpe_device->vpebase);
+ release_mem_region(vpemem->start,
+ (vpemem->end - vpemem->start) + 1);
+ return 0;
+}
+
+static struct platform_driver msm_vpe_driver = {
+ .probe = __msm_vpe_probe,
+ .remove = __msm_vpe_remove,
+ .driver = {
+ .name = "msm_vpe",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_vpe_init(void)
+{
+ return platform_driver_register(&msm_vpe_driver);
+}
+module_init(msm_vpe_init);
+
+static void __exit msm_vpe_exit(void)
+{
+ platform_driver_unregister(&msm_vpe_driver);
+}
+module_exit(msm_vpe_exit);
+
+MODULE_DESCRIPTION("msm vpe 1.0 driver");
+MODULE_VERSION("msm vpe driver 1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_vpe1.h b/drivers/media/video/msm/msm_vpe1.h
new file mode 100644
index 0000000..ed7112e
--- /dev/null
+++ b/drivers/media/video/msm/msm_vpe1.h
@@ -0,0 +1,253 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef _msm_vpe1_h_
+#define _msm_vpe1_h_
+
+#include <mach/camera.h>
+
+/*********** start of register offset *********************/
+#define VPE_INTR_ENABLE_OFFSET 0x0020
+#define VPE_INTR_STATUS_OFFSET 0x0024
+#define VPE_INTR_CLEAR_OFFSET 0x0028
+#define VPE_DL0_START_OFFSET 0x0030
+#define VPE_HW_VERSION_OFFSET 0x0070
+#define VPE_SW_RESET_OFFSET 0x0074
+#define VPE_AXI_RD_ARB_CONFIG_OFFSET 0x0078
+#define VPE_SEL_CLK_OR_HCLK_TEST_BUS_OFFSET 0x007C
+#define VPE_CGC_EN_OFFSET 0x0100
+#define VPE_CMD_STATUS_OFFSET 0x10008
+#define VPE_PROFILE_EN_OFFSET 0x10010
+#define VPE_PROFILE_COUNT_OFFSET 0x10014
+#define VPE_CMD_MODE_OFFSET 0x10060
+#define VPE_SRC_SIZE_OFFSET 0x10108
+#define VPE_SRCP0_ADDR_OFFSET 0x1010C
+#define VPE_SRCP1_ADDR_OFFSET 0x10110
+#define VPE_SRC_YSTRIDE1_OFFSET 0x1011C
+#define VPE_SRC_FORMAT_OFFSET 0x10124
+#define VPE_SRC_UNPACK_PATTERN1_OFFSET 0x10128
+#define VPE_OP_MODE_OFFSET 0x10138
+#define VPE_SCALE_PHASEX_INIT_OFFSET 0x1013C
+#define VPE_SCALE_PHASEY_INIT_OFFSET 0x10140
+#define VPE_SCALE_PHASEX_STEP_OFFSET 0x10144
+#define VPE_SCALE_PHASEY_STEP_OFFSET 0x10148
+#define VPE_OUT_FORMAT_OFFSET 0x10150
+#define VPE_OUT_PACK_PATTERN1_OFFSET 0x10154
+#define VPE_OUT_SIZE_OFFSET 0x10164
+#define VPE_OUTP0_ADDR_OFFSET 0x10168
+#define VPE_OUTP1_ADDR_OFFSET 0x1016C
+#define VPE_OUT_YSTRIDE1_OFFSET 0x10178
+#define VPE_OUT_XY_OFFSET 0x1019C
+#define VPE_SRC_XY_OFFSET 0x10200
+#define VPE_SRC_IMAGE_SIZE_OFFSET 0x10208
+#define VPE_SCALE_CONFIG_OFFSET 0x10230
+#define VPE_DEINT_STATUS_OFFSET 0x30000
+#define VPE_DEINT_DECISION_OFFSET 0x30004
+#define VPE_DEINT_COEFF0_OFFSET 0x30010
+#define VPE_SCALE_STATUS_OFFSET 0x50000
+#define VPE_SCALE_SVI_PARAM_OFFSET 0x50010
+#define VPE_SCALE_SHARPEN_CFG_OFFSET 0x50020
+#define VPE_SCALE_COEFF_LSP_0_OFFSET 0x50400
+#define VPE_SCALE_COEFF_MSP_0_OFFSET 0x50404
+
+#define VPE_AXI_ARB_2_OFFSET 0x004C
+
+#define VPE_SCALE_COEFF_LSBn(n) (0x50400 + 8 * (n))
+#define VPE_SCALE_COEFF_MSBn(n) (0x50404 + 8 * (n))
+#define VPE_SCALE_COEFF_NUM 32
+
+/*********** end of register offset ********************/
+
+
+#define VPE_HARDWARE_VERSION 0x00080308
+#define VPE_SW_RESET_VALUE 0x00000010 /* bit 4 for PPP*/
+#define VPE_AXI_RD_ARB_CONFIG_VALUE 0x124924
+#define VPE_CMD_MODE_VALUE 0x1
+#define VPE_DEFAULT_OP_MODE_VALUE 0x40FC0004
+#define VPE_CGC_ENABLE_VALUE 0xffff
+#define VPE_DEFAULT_SCALE_CONFIG 0x3c
+
+#define VPE_NORMAL_MODE_CLOCK_RATE 150000000
+#define VPE_TURBO_MODE_CLOCK_RATE 200000000
+/**************************************************/
+/*********** Start of command id ******************/
+/**************************************************/
+enum VPE_CMD_ID_ENUM {
+ VPE_DUMMY_0 = 0,
+ VPE_SET_CLK,
+ VPE_RESET,
+ VPE_START,
+ VPE_ABORT,
+ VPE_OPERATION_MODE_CFG, /* 5 */
+ VPE_INPUT_PLANE_CFG,
+ VPE_OUTPUT_PLANE_CFG,
+ VPE_INPUT_PLANE_UPDATE,
+ VPE_SCALE_CFG_TYPE,
+ VPE_ROTATION_CFG_TYPE, /* 10 */
+ VPE_AXI_OUT_CFG,
+ VPE_CMD_DIS_OFFSET_CFG,
+ VPE_ENABLE,
+ VPE_DISABLE,
+};
+
+/* Length of each command. In bytes. (payload only) */
+#define VPE_OPERATION_MODE_CFG_LEN 8
+#define VPE_INPUT_PLANE_CFG_LEN 24
+#define VPE_OUTPUT_PLANE_CFG_LEN 20
+#define VPE_INPUT_PLANE_UPDATE_LEN 12
+#define VPE_SCALER_CONFIG_LEN 260
+#define VPE_DIS_OFFSET_CFG_LEN 12
+/**************************************************/
+/*********** End of command id ********************/
+/**************************************************/
+
+struct msm_vpe_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+struct vpe_cmd_type {
+ uint16_t id;
+ uint32_t length;
+};
+
+struct vpe_isr_queue_cmd_type {
+ struct list_head list;
+ uint32_t irq_status;
+};
+
+enum VPE_MESSAGE_ID {
+ MSG_ID_VPE_OUTPUT_V = 7, /* To match with that of VFE */
+ MSG_ID_VPE_OUTPUT_ST_L,
+ MSG_ID_VPE_OUTPUT_ST_R,
+};
+
+enum vpe_state {
+ VPE_STATE_IDLE,
+ VPE_STATE_INIT,
+ VPE_STATE_ACTIVE,
+};
+
+struct vpe_device_type {
+ /* device related. */
+ int vpeirq;
+ void __iomem *vpebase;
+ struct resource *vpemem;
+ struct resource *vpeio;
+ void *device_extdata;
+};
+
+struct dis_offset_type {
+ int32_t dis_offset_x;
+ int32_t dis_offset_y;
+ uint32_t frame_id;
+};
+
+struct vpe_ctrl_type {
+ spinlock_t tasklet_lock;
+ spinlock_t state_lock;
+ spinlock_t ops_lock;
+
+ struct list_head tasklet_q;
+ void *syncdata;
+ uint16_t op_mode;
+ void *extdata;
+ uint32_t extlen;
+ struct msm_vpe_callback *resp;
+ uint32_t in_h_w;
+ uint32_t out_h; /* this is BEFORE rotation. */
+ uint32_t out_w; /* this is BEFORE rotation. */
+ uint32_t dis_en;
+ struct timespec ts;
+ struct dis_offset_type dis_offset;
+ uint32_t pcbcr_before_dis;
+ uint32_t pcbcr_dis_offset;
+ int output_type;
+ int frame_pack;
+ uint8_t pad_2k_bool;
+ enum vpe_state state;
+ unsigned long out_y_addr;
+ unsigned long out_cbcr_addr;
+};
+
+/*
+* vpe_input_update
+*
+* Define the parameters for output plane
+*/
+/* this is the dimension of ROI. width / height. */
+struct vpe_src_size_packed {
+ uint32_t src_w;
+ uint32_t src_h;
+};
+
+struct vpe_src_xy_packed {
+ uint32_t src_x;
+ uint32_t src_y;
+};
+
+struct vpe_input_plane_update_type {
+ struct vpe_src_size_packed src_roi_size;
+ /* DIS updates this set. */
+ struct vpe_src_xy_packed src_roi_offset;
+ /* input address*/
+ uint8_t *src_p0_addr;
+ uint8_t *src_p1_addr;
+};
+
+struct vpe_msg_stats{
+ uint32_t buffer;
+ uint32_t frameCounter;
+};
+
+struct vpe_msg_output {
+ uint8_t output_id;
+ uint32_t yBuffer;
+ uint32_t cbcrBuffer;
+ uint32_t frameCounter;
+};
+
+struct vpe_message {
+ uint8_t _d;
+ union {
+ struct vpe_msg_output msgOut;
+ struct vpe_msg_stats msgStats;
+ } _u;
+};
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50 0x50000000
+#define HAL_MDP_PHASE_STEP_1P66 0x35555555
+#define HAL_MDP_PHASE_STEP_1P25 0x28000000
+
+struct phase_val_t {
+ int32_t phase_init_x;
+ int32_t phase_init_y;
+ int32_t phase_step_x;
+ int32_t phase_step_y;
+};
+
+extern struct vpe_ctrl_type *vpe_ctrl;
+
+int msm_vpe_open(void);
+int msm_vpe_release(void);
+int msm_vpe_reg(struct msm_vpe_callback *presp);
+void msm_send_frame_to_vpe(uint32_t pyaddr, uint32_t pcbcraddr,
+ struct timespec *ts, int output_id);
+int msm_vpe_config(struct msm_vpe_cfg_cmd *cmd, void *data);
+int msm_vpe_cfg_update(void *pinfo);
+void msm_vpe_offset_update(int frame_pack, uint32_t pyaddr, uint32_t pcbcraddr,
+ struct timespec *ts, int output_id, struct msm_st_half st_half,
+ int frameid);
+#endif /*_msm_vpe1_h_*/
+
diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c
new file mode 100644
index 0000000..a7b5156
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.c
@@ -0,0 +1,845 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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
+
+#define SENSOR_DEBUG 0
+
+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);
+DEFINE_SEMAPHORE(mt9d112_sem);
+static int16_t mt9d112_effect = CAMERA_EFFECT_OFF;
+
+/*=============================================================
+ 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);
+ msleep(20);
+ gpio_set_value_cansleep(dev->sensor_reset, 1);
+ msleep(20);
+ }
+
+ 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 SENSOR_DEBUG
+ if (length == 2)
+ CDBG("msm_io_i2c_w: 0x%04x 0x%04x\n",
+ *(u16 *) txdata, *(u16 *) (txdata + 2));
+ else if (length == 4)
+ CDBG("msm_io_i2c_w: 0x%04x\n", *(u16 *) txdata);
+ else
+ CDBG("msm_io_i2c_w: length = %d\n", length);
+#endif
+ 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 SENSOR_DEBUG
+ if (length == 2)
+ CDBG("msm_io_i2c_r: 0x%04x 0x%04x\n",
+ *(u16 *) rxdata, *(u16 *) (rxdata + 2));
+ else if (length == 4)
+ CDBG("msm_io_i2c_r: 0x%04x\n", *(u16 *) rxdata);
+ else
+ CDBG("msm_io_i2c_r: length = %d\n", length);
+#endif
+
+ 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_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_RAW_SNAPSHOT_MODE:
+ 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;
+
+ 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;
+ }
+ }
+ mt9d112_effect = effect;
+ /* 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 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 = 0x23C;
+
+ 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;
+
+ msleep(40);/*waiting for the delay of one frame*/
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ msleep(80);/*waiting for the delay of two frames*/
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ msleep(40);/*waiting for the delay of one frame*/
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ /* Setting the effect to CAMERA_EFFECT_OFF */
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0x279B, WORD_LEN);
+ if (rc < 0)
+ return rc;
+
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x6440, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ msleep(40);/*waiting for the delay of one frame*/
+ /* 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;
+ msleep(80);/*waiting for the delay of two frames frame*/
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x338C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ msleep(40);/*waiting for the delay of one frame*/
+ rc =
+ mt9d112_i2c_write(mt9d112_client->addr,
+ 0x3390, 0x0002, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
+ }
+
+ msm_camio_clk_rate_set(24000000);
+ msleep(20);
+
+ /* 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); */
+ gpio_set_value_cansleep(mt9d112_ctrl->sensordata->sensor_reset, 0);
+ msleep(20);
+ gpio_free(mt9d112_ctrl->sensordata->sensor_reset);
+ 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) {
+ gpio_free(info->sensor_reset);
+ goto probe_done;
+ }
+ s->s_init = mt9d112_sensor_init;
+ s->s_release = mt9d112_sensor_release;
+ s->s_config = mt9d112_sensor_config;
+ s->s_camera_type = FRONT_CAMERA_2D;
+ s->s_mount_angle = 0;
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ msleep(20);
+ gpio_free(info->sensor_reset);
+
+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/media/video/msm/mt9d112.h b/drivers/media/video/msm/mt9d112.h
new file mode 100644
index 0000000..309fcec
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9D112_H
+#define MT9D112_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+extern struct mt9d112_reg mt9d112_regs;
+
+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/media/video/msm/mt9d112_reg.c b/drivers/media/video/msm/mt9d112_reg.c
new file mode 100644
index 0000000..24edaf2
--- /dev/null
+++ b/drivers/media/video/msm/mt9d112_reg.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "mt9d112.h"
+
+
+struct register_address_value_pair const
+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, 0x061B}, /* Line Length (P) = 1563 */
+ {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 const
+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/media/video/msm/mt9d113.c b/drivers/media/video/msm/mt9d113.c
new file mode 100644
index 0000000..a6b6a28
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113.c
@@ -0,0 +1,664 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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 "mt9d113.h"
+
+/* Micron MT9D113 Registers and their values */
+#define REG_MT9D113_MODEL_ID 0x0000
+#define MT9D113_MODEL_ID 0x2580
+#define Q8 0x00000100
+
+struct mt9d113_work {
+ struct work_struct work;
+};
+
+static struct mt9d113_work *mt9d113_sensorw;
+static struct i2c_client *mt9d113_client;
+
+struct mt9d113_ctrl {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+ uint16_t config_csi;
+ enum mt9d113_resolution_t prev_res;
+ enum mt9d113_resolution_t pict_res;
+ enum mt9d113_resolution_t curr_res;
+ enum mt9d113_test_mode_t set_test;
+};
+
+static struct mt9d113_ctrl *mt9d113_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(mt9d113_wait_queue);
+DEFINE_MUTEX(mt9d113_mut);
+
+static int mt9d113_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(mt9d113_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9d113_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t mt9d113_i2c_read(unsigned short saddr,
+ unsigned short raddr,
+ unsigned short *rdata,
+ enum mt9d113_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 = mt9d113_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+ *rdata = buf[0] << 8 | buf[1];
+ }
+ break;
+ default:
+ break;
+ }
+ if (rc < 0)
+ CDBG("mt9d113_i2c_read failed !\n");
+ return rc;
+}
+
+static int32_t mt9d113_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(mt9d113_client->adapter, msg, 1) < 0) {
+ CDBG("mt9d113_i2c_txdata failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t mt9d113_i2c_write(unsigned short saddr,
+ unsigned short waddr,
+ unsigned short wdata,
+ enum mt9d113_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 = mt9d113_i2c_txdata(saddr, buf, 4);
+ }
+ break;
+ case BYTE_LEN: {
+ buf[0] = waddr;
+ buf[1] = wdata;
+ rc = mt9d113_i2c_txdata(saddr, buf, 2);
+ }
+ break;
+ default:
+ break;
+ }
+ if (rc < 0)
+ printk(KERN_ERR
+ "i2c_write failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ return rc;
+}
+
+static int32_t mt9d113_i2c_write_table(
+ struct mt9d113_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 = mt9d113_i2c_write(mt9d113_client->addr,
+ reg_conf_tbl->waddr, reg_conf_tbl->wdata,
+ WORD_LEN);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static long mt9d113_reg_init(void)
+{
+ uint16_t data = 0;
+ int32_t rc = 0;
+ int count = 0;
+ struct msm_camera_csi_params mt9d113_csi_params;
+ if (!mt9d113_ctrl->config_csi) {
+ mt9d113_csi_params.lane_cnt = 1;
+ mt9d113_csi_params.data_format = CSI_8BIT;
+ mt9d113_csi_params.lane_assign = 0xe4;
+ mt9d113_csi_params.dpcm_scheme = 0;
+ mt9d113_csi_params.settle_cnt = 0x14;
+ rc = msm_camio_csi_config(&mt9d113_csi_params);
+ mt9d113_ctrl->config_csi = 1;
+ msleep(50);
+ }
+ /* Disable parallel and enable mipi*/
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x001A,
+ 0x0051, WORD_LEN);
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x001A,
+ 0x0050,
+ WORD_LEN);
+ msleep(20);
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x001A,
+ 0x0058,
+ WORD_LEN);
+
+ /* Preset pll settings begin*/
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.pll_tbl[0],
+ mt9d113_regs.pll_tbl_size);
+ if (rc < 0)
+ return rc;
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0014, &data, WORD_LEN);
+ data = data&0x8000;
+ /* Poll*/
+ while (data == 0x0000) {
+ data = 0;
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0014, &data, WORD_LEN);
+ data = data & 0x8000;
+ usleep_range(11000, 12000);
+ count++;
+ if (count == 100) {
+ CDBG(" Timeout:1\n");
+ break;
+ }
+ }
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0014,
+ 0x20FA,
+ WORD_LEN);
+
+ /*Preset pll Ends*/
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0018,
+ 0x402D,
+ WORD_LEN);
+
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0018,
+ 0x402C,
+ WORD_LEN);
+ /*POLL_REG=0x0018,0x4000,!=0x0000,DELAY=10,TIMEOUT=100*/
+ data = 0;
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0018, &data, WORD_LEN);
+ data = data & 0x4000;
+ count = 0;
+ while (data != 0x0000) {
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0018, &data, WORD_LEN);
+ data = data & 0x4000;
+ CDBG(" data is %d\n" , data);
+ usleep_range(11000, 12000);
+ count++;
+ if (count == 100) {
+ CDBG(" Loop2 timeout: MT9D113\n");
+ break;
+ }
+ CDBG(" Not streaming\n");
+ }
+ CDBG("MT9D113: Start stream\n");
+ /*Preset Register Wizard Conf*/
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.register_tbl[0],
+ mt9d113_regs.register_tbl_size);
+ if (rc < 0)
+ return rc;
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.err_tbl[0],
+ mt9d113_regs.err_tbl_size);
+ if (rc < 0)
+ return rc;
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.eeprom_tbl[0],
+ mt9d113_regs.eeprom_tbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.low_light_tbl[0],
+ mt9d113_regs.low_light_tbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.awb_tbl[0],
+ mt9d113_regs.awb_tbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9d113_i2c_write_table(&mt9d113_regs.patch_tbl[0],
+ mt9d113_regs.patch_tbl_size);
+ if (rc < 0)
+ return rc;
+
+ /*check patch load*/
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C,
+ 0xA024,
+ WORD_LEN);
+ count = 0;
+ /*To check if patch is loaded properly
+ poll the register 0x990 till the condition is
+ met or till the timeout*/
+ data = 0;
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0990, &data, WORD_LEN);
+ while (data == 0) {
+ data = 0;
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ 0x0990, &data, WORD_LEN);
+ usleep_range(11000, 12000);
+ count++;
+ if (count == 100) {
+ CDBG("Timeout in patch loading\n");
+ break;
+ }
+ }
+ /*BITFIELD=0x0018, 0x0004, 0*/
+ /*Preset continue begin */
+ rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028,
+ WORD_LEN);
+ CDBG(" mt9d113 wait for seq done\n");
+ /* syncronize the FW with the sensor
+ MCU_ADDRESS [SEQ_CMD]*/
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA103, WORD_LEN);
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0990, 0x0006, WORD_LEN);
+ /*mt9d113 wait for seq done
+ syncronize the FW with the sensor */
+ msleep(20);
+ /*Preset continue end */
+ CDBG(" MT9D113: Preset continue end\n");
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0012,
+ 0x00F5,
+ WORD_LEN);
+ /*continue begin */
+ CDBG(" MT9D113: Preset continue begin\n");
+ rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0018, 0x0028 ,
+ WORD_LEN);
+ /*mt9d113 wait for seq done
+ syncronize the FW with the sensor
+ MCU_ADDRESS [SEQ_CMD]*/
+ msleep(20);
+ rc = mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA103, WORD_LEN);
+ /* MCU DATA */
+ rc = mt9d113_i2c_write(mt9d113_client->addr, 0x0990,
+ 0x0006, WORD_LEN);
+ /*mt9d113 wait for seq done
+ syncronize the FW with the sensor */
+ /* MCU_ADDRESS [SEQ_CMD]*/
+ msleep(20);
+ /*Preset continue end*/
+ return rc;
+
+}
+
+static long mt9d113_set_sensor_mode(int mode)
+{
+ long rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9d113_reg_init();
+ CDBG("MT9D113: configure to preview begin\n");
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA115, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x0990, 0x0000, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA103, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0x0001, WORD_LEN);
+ if (rc < 0)
+ return rc;
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA115, WORD_LEN);
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0x0002, WORD_LEN);
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0xA103, WORD_LEN);
+ rc =
+ mt9d113_i2c_write(mt9d113_client->addr,
+ 0x098C, 0x0002, WORD_LEN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mt9d113_sensor_init_probe(const struct
+ msm_camera_sensor_info * data)
+{
+ uint16_t model_id = 0;
+ int rc = 0;
+ /* Read the Model ID of the sensor */
+ rc = mt9d113_i2c_read(mt9d113_client->addr,
+ REG_MT9D113_MODEL_ID,
+ &model_id, WORD_LEN);
+ if (rc < 0)
+ goto init_probe_fail;
+ /* Check if it matches it with the value in Datasheet */
+ if (model_id != MT9D113_MODEL_ID)
+ printk(KERN_INFO "mt9d113 model_id = 0x%x\n", model_id);
+ if (rc < 0)
+ goto init_probe_fail;
+ return rc;
+init_probe_fail:
+ printk(KERN_INFO "probe fail\n");
+ return rc;
+}
+
+static int mt9d113_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9d113_wait_queue);
+ return 0;
+}
+
+int mt9d113_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;
+ mutex_lock(&mt9d113_mut);
+ CDBG("mt9d113_ioctl, cfgtype = %d, mode = %d\n",
+ cfg_data.cfgtype, cfg_data.mode);
+ switch (cfg_data.cfgtype) {
+ case CFG_SET_MODE:
+ rc = mt9d113_set_sensor_mode(
+ cfg_data.mode);
+ break;
+ case CFG_SET_EFFECT:
+ return rc;
+ case CFG_GET_AF_MAX_STEPS:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&mt9d113_mut);
+ return rc;
+}
+
+int mt9d113_sensor_release(void)
+{
+ int rc = 0;
+
+ mutex_lock(&mt9d113_mut);
+ gpio_set_value_cansleep(mt9d113_ctrl->sensordata->sensor_reset, 0);
+ msleep(20);
+ gpio_free(mt9d113_ctrl->sensordata->sensor_reset);
+ kfree(mt9d113_ctrl);
+ mutex_unlock(&mt9d113_mut);
+
+ return rc;
+}
+
+static int mt9d113_probe_init_done(const struct msm_camera_sensor_info
+ *data)
+{
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9d113_probe_init_sensor(const struct msm_camera_sensor_info
+ *data)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = gpio_request(data->sensor_pwd, "mt9d113");
+ if (!rc) {
+ printk(KERN_INFO "sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_pwd, 0);
+ usleep_range(11000, 12000);
+ } else {
+ goto init_probe_done;
+ }
+ msleep(20);
+ rc = gpio_request(data->sensor_reset, "mt9d113");
+ printk(KERN_INFO " mt9d113_probe_init_sensor\n");
+ if (!rc) {
+ printk(KERN_INFO "sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ usleep_range(11000, 12000);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ usleep_range(11000, 12000);
+ } else
+ goto init_probe_done;
+ printk(KERN_INFO " mt9d113_probe_init_sensor called\n");
+ rc = mt9d113_i2c_read(mt9d113_client->addr, REG_MT9D113_MODEL_ID,
+ &chipid, 2);
+ if (rc < 0)
+ goto init_probe_fail;
+ /*Compare sensor ID to MT9D113 ID: */
+ if (chipid != MT9D113_MODEL_ID) {
+ printk(KERN_INFO "mt9d113_probe_init_sensor chip idis%d\n",
+ chipid);
+ }
+ CDBG("mt9d113_probe_init_sensor Success\n");
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" ov2720_probe_init_sensor fails\n");
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ mt9d113_probe_init_done(data);
+init_probe_done:
+ printk(KERN_INFO " mt9d113_probe_init_sensor finishes\n");
+ return rc;
+}
+
+static int mt9d113_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;
+ }
+ mt9d113_sensorw =
+ kzalloc(sizeof(struct mt9d113_work), GFP_KERNEL);
+ if (!mt9d113_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+ i2c_set_clientdata(client, mt9d113_sensorw);
+ mt9d113_init_client(client);
+ mt9d113_client = client;
+ CDBG("mt9d113_probe succeeded!\n");
+ return 0;
+probe_failure:
+ kfree(mt9d113_sensorw);
+ mt9d113_sensorw = NULL;
+ CDBG("mt9d113_probe failed!\n");
+ return rc;
+}
+
+static const struct i2c_device_id mt9d113_i2c_id[] = {
+ { "mt9d113", 0},
+ {},
+};
+
+static struct i2c_driver mt9d113_i2c_driver = {
+ .id_table = mt9d113_i2c_id,
+ .probe = mt9d113_i2c_probe,
+ .remove = __exit_p(mt9d113_i2c_remove),
+ .driver = {
+ .name = "mt9d113",
+ },
+};
+
+int mt9d113_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ mt9d113_ctrl = kzalloc(sizeof(struct mt9d113_ctrl), GFP_KERNEL);
+ if (!mt9d113_ctrl) {
+ printk(KERN_INFO "mt9d113_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ mt9d113_ctrl->fps_divider = 1 * 0x00000400;
+ mt9d113_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9d113_ctrl->set_test = TEST_OFF;
+ mt9d113_ctrl->config_csi = 0;
+ mt9d113_ctrl->prev_res = QTR_SIZE;
+ mt9d113_ctrl->pict_res = FULL_SIZE;
+ mt9d113_ctrl->curr_res = INVALID_SIZE;
+ if (data)
+ mt9d113_ctrl->sensordata = data;
+ if (rc < 0) {
+ printk(KERN_INFO "mt9d113_sensor_open_init fail\n");
+ return rc;
+ }
+ /* enable mclk first */
+ msm_camio_clk_rate_set(24000000);
+ msleep(20);
+ rc = mt9d113_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+ mt9d113_ctrl->fps = 30*Q8;
+ rc = mt9d113_sensor_init_probe(data);
+ if (rc < 0) {
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ goto init_fail;
+ } else
+ printk(KERN_ERR "%s: %d\n", __func__, __LINE__);
+ goto init_done;
+init_fail:
+ printk(KERN_INFO "init_fail\n");
+ mt9d113_probe_init_done(data);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+}
+
+static int mt9d113_sensor_probe(const struct msm_camera_sensor_info
+ *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&mt9d113_i2c_driver);
+ if (rc < 0 || mt9d113_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(24000000);
+ usleep_range(5000, 6000);
+ rc = mt9d113_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = mt9d113_sensor_open_init;
+ s->s_release = mt9d113_sensor_release;
+ s->s_config = mt9d113_sensor_config;
+ s->s_camera_type = FRONT_CAMERA_2D;
+ s->s_mount_angle = 0;
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ mt9d113_probe_init_done(info);
+ return rc;
+probe_fail:
+ printk(KERN_INFO "mt9d113_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __mt9d113_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9d113_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9d113_probe,
+ .driver = {
+ .name = "msm_camera_mt9d113",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9d113_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9d113_init);
+
+MODULE_DESCRIPTION("Micron 2MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/mt9d113.h b/drivers/media/video/msm/mt9d113.h
new file mode 100644
index 0000000..f22f16c
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9D113_H
+#define MT9D113_H
+
+#include <linux/types.h>
+#include <mach/camera.h>
+
+extern struct mt9d113_reg mt9d113_regs;
+
+enum mt9d113_width {
+ WORD_LEN,
+ BYTE_LEN
+};
+
+struct mt9d113_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct mt9d113_reg {
+ const struct mt9d113_i2c_reg_conf *pll_tbl;
+ uint16_t pll_tbl_size;
+ const struct mt9d113_i2c_reg_conf *register_tbl;
+ uint16_t register_tbl_size;
+ const struct mt9d113_i2c_reg_conf *err_tbl;
+ uint16_t err_tbl_size;
+ const struct mt9d113_i2c_reg_conf *low_light_tbl;
+ uint16_t low_light_tbl_size;
+ const struct mt9d113_i2c_reg_conf *awb_tbl;
+ uint16_t awb_tbl_size;
+ const struct mt9d113_i2c_reg_conf *patch_tbl;
+ uint16_t patch_tbl_size;
+ const struct mt9d113_i2c_reg_conf *eeprom_tbl ;
+ uint16_t eeprom_tbl_size ;
+};
+
+enum mt9d113_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9d113_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9d113_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+#endif /* MT9D113_H */
diff --git a/drivers/media/video/msm/mt9d113_reg.c b/drivers/media/video/msm/mt9d113_reg.c
new file mode 100644
index 0000000..cd5be0f
--- /dev/null
+++ b/drivers/media/video/msm/mt9d113_reg.c
@@ -0,0 +1,455 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "mt9d113.h"
+
+struct mt9d113_i2c_reg_conf const
+ pll_tbl_settings[] = {
+ {0x0014, 0x21F9 }, /*PLL control: BYPASS PLL = 8697*/
+ {0x0010, 0x0115 }, /*PLL Dividers = 277*/
+ {0x0012, 0x0F5 }, /*PLL P Dividers = 245*/
+ {0x0014, 0x21FB }, /*PLL control: PLL_ENABLE on = 8699*/
+ {0x0014, 0x20FB }, /*PLL control: SEL_LOCK_DET on = 8443*/
+};
+
+struct mt9d113_i2c_reg_conf const
+ register_wizard_settings[] = {
+ {0x098C, 0x2719},
+ {0x0990, 0x005A},
+ {0x098C, 0x271B},
+ {0x0990, 0x01BE},
+ {0x098C, 0x271D},
+ {0x0990, 0x0131},
+ {0x098C, 0x271F},
+ {0x0990, 0x02BB},
+ {0x098C, 0x2721},
+ {0x0990, 0x0888},
+ {0x098C, 0x272F},
+ {0x0990, 0x003A},
+ {0x098C, 0x2731},
+ {0x0990, 0x00F6},
+ {0x098C, 0x2733},
+ {0x0990, 0x008B},
+ {0x098C, 0x2735},
+ {0x0990, 0x0521},
+ {0x098C, 0x2737},
+ {0x0990, 0x0888},
+ {0x098C, 0x275F},
+ {0x0990, 0x0194},
+ {0x098C, 0x2761},
+ {0x0990, 0x0014},
+ {0x098C, 0xA765},
+ {0x0990, 0x0044},
+ {0x098C, 0xA24F},
+ {0x0990, 0x0028},
+ {0x098C, 0xA20E},
+ {0x0990, 0x00A0},
+ {0x098C, 0xA20C},
+ {0x0990, 0x000E},
+ {0x098C, 0x2222},
+ {0x0990, 0x00A0},
+ {0x098C, 0x2212},
+ {0x0990, 0x01EE},
+ {0x098C, 0xA408},
+ {0x0990, 0x0026},
+ {0x098C, 0xA409},
+ {0x0990, 0x0029},
+ {0x098C, 0xA40A},
+ {0x0990, 0x002E},
+ {0x098C, 0xA40B},
+ {0x0990, 0x0031},
+ {0x098C, 0x2411},
+ {0x0990, 0x00A0},
+ {0x098C, 0x2413},
+ {0x0990, 0x00C0},
+ {0x098C, 0x2415},
+ {0x0990, 0x00A0},
+ {0x098C, 0x2417},
+ {0x0990, 0x00C0},
+};
+
+struct mt9d113_i2c_reg_conf const
+ err_settings[] = {
+ {0x3084, 0x240C},
+ {0x3092, 0x0A4C},
+ {0x3094, 0x4C4C},
+ {0x3096, 0x4C54},
+};
+
+struct mt9d113_i2c_reg_conf const
+ patch_settings[] = {
+ {0x098C, 0x0415}, /* MCU_ADDRESS*/
+ {0x0990, 0xF601},
+ {0x0992, 0x42C1},
+ {0x0994, 0x0326},
+ {0x0996, 0x11F6},
+ {0x0998, 0x0143},
+ {0x099A, 0xC104},
+ {0x099C, 0x260A},
+ {0x099E, 0xCC04},
+ {0x098C, 0x0425},
+ {0x0990, 0x33BD},
+ {0x0992, 0xA362},
+ {0x0994, 0xBD04},
+ {0x0996, 0x3339},
+ {0x0998, 0xC6FF},
+ {0x099A, 0xF701},
+ {0x099C, 0x6439},
+ {0x099E, 0xFE01},
+ {0x098C, 0x0435},
+ {0x0990, 0x6918},
+ {0x0992, 0xCE03},
+ {0x0994, 0x25CC},
+ {0x0996, 0x0013},
+ {0x0998, 0xBDC2},
+ {0x099A, 0xB8CC},
+ {0x099C, 0x0489},
+ {0x099E, 0xFD03},
+ {0x098C, 0x0445},
+ {0x0990, 0x27CC},
+ {0x0992, 0x0325},
+ {0x0994, 0xFD01},
+ {0x0996, 0x69FE},
+ {0x0998, 0x02BD},
+ {0x099A, 0x18CE},
+ {0x099C, 0x0339},
+ {0x099E, 0xCC00},
+ {0x098C, 0x0455},
+ {0x0990, 0x11BD},
+ {0x0992, 0xC2B8},
+ {0x0994, 0xCC04},
+ {0x0996, 0xC8FD},
+ {0x0998, 0x0347},
+ {0x099A, 0xCC03},
+ {0x099C, 0x39FD},
+ {0x099E, 0x02BD},
+ {0x098C, 0x0465},
+ {0x0990, 0xDE00},
+ {0x0992, 0x18CE},
+ {0x0994, 0x00C2},
+ {0x0996, 0xCC00},
+ {0x0998, 0x37BD},
+ {0x099A, 0xC2B8},
+ {0x099C, 0xCC04},
+ {0x099E, 0xEFDD},
+ {0x098C, 0x0475},
+ {0x0990, 0xE6CC},
+ {0x0992, 0x00C2},
+ {0x0994, 0xDD00},
+ {0x0996, 0xC601},
+ {0x0998, 0xF701},
+ {0x099A, 0x64C6},
+ {0x099C, 0x03F7},
+ {0x099E, 0x0165},
+ {0x098C, 0x0485},
+ {0x0990, 0x7F01},
+ {0x0992, 0x6639},
+ {0x0994, 0x3C3C},
+ {0x0996, 0x3C34},
+ {0x0998, 0xCC32},
+ {0x099A, 0x3EBD},
+ {0x099C, 0xA558},
+ {0x099E, 0x30ED},
+ {0x098C, 0x0495},
+ {0x0990, 0x04BD},
+ {0x0992, 0xB2D7},
+ {0x0994, 0x30E7},
+ {0x0996, 0x06CC},
+ {0x0998, 0x323E},
+ {0x099A, 0xED00},
+ {0x099C, 0xEC04},
+ {0x099E, 0xBDA5},
+ {0x098C, 0x04A5},
+ {0x0990, 0x44CC},
+ {0x0992, 0x3244},
+ {0x0994, 0xBDA5},
+ {0x0996, 0x585F},
+ {0x0998, 0x30ED},
+ {0x099A, 0x02CC},
+ {0x099C, 0x3244},
+ {0x099E, 0xED00},
+ {0x098C, 0x04B5},
+ {0x0990, 0xF601},
+ {0x0992, 0xD54F},
+ {0x0994, 0xEA03},
+ {0x0996, 0xAA02},
+ {0x0998, 0xBDA5},
+ {0x099A, 0x4430},
+ {0x099C, 0xE606},
+ {0x099E, 0x3838},
+ {0x098C, 0x04C5},
+ {0x0990, 0x3831},
+ {0x0992, 0x39BD},
+ {0x0994, 0xD661},
+ {0x0996, 0xF602},
+ {0x0998, 0xF4C1},
+ {0x099A, 0x0126},
+ {0x099C, 0x0BFE},
+ {0x099E, 0x02BD},
+ {0x098C, 0x04D5},
+ {0x0990, 0xEE10},
+ {0x0992, 0xFC02},
+ {0x0994, 0xF5AD},
+ {0x0996, 0x0039},
+ {0x0998, 0xF602},
+ {0x099A, 0xF4C1},
+ {0x099C, 0x0226},
+ {0x099E, 0x0AFE},
+ {0x098C, 0x04E5},
+ {0x0990, 0x02BD},
+ {0x0992, 0xEE10},
+ {0x0994, 0xFC02},
+ {0x0996, 0xF7AD},
+ {0x0998, 0x0039},
+ {0x099A, 0x3CBD},
+ {0x099C, 0xB059},
+ {0x099E, 0xCC00},
+ {0x098C, 0x04F5},
+ {0x0990, 0x28BD},
+ {0x0992, 0xA558},
+ {0x0994, 0x8300},
+ {0x0996, 0x0027},
+ {0x0998, 0x0BCC},
+ {0x099A, 0x0026},
+ {0x099C, 0x30ED},
+ {0x099E, 0x00C6},
+ {0x098C, 0x0505},
+ {0x0990, 0x03BD},
+ {0x0992, 0xA544},
+ {0x0994, 0x3839},
+ {0x098C, 0x2006},
+ {0x0990, 0x0415},
+ {0x098C, 0xA005},
+ {0x0990, 0x0001},
+};
+
+struct mt9d113_i2c_reg_conf const
+ eeprom_settings[] = {
+ {0x3658, 0x0110},
+ {0x365A, 0x1B6D},
+ {0x365C, 0x01F2},
+ {0x365E, 0xFBCD},
+ {0x3660, 0x8C91},
+ {0x3680, 0xB9ED},
+ {0x3682, 0x0EE},
+ {0x3684, 0x256F},
+ {0x3686, 0x824F},
+ {0x3688, 0xD293},
+ {0x36A8, 0x5BF2},
+ {0x36AA, 0x1711},
+ {0x36AC, 0xA095},
+ {0x36AE, 0x642C},
+ {0x36B0, 0x0E38},
+ {0x36D0, 0x88B0},
+ {0x36D2, 0x2EB2},
+ {0x36D4, 0x4C74},
+ {0x36D6, 0x9F96},
+ {0x36D8, 0x9557},
+ {0x36F8, 0xCE51},
+ {0x36FA, 0xB354},
+ {0x36FC, 0x2817},
+ {0x36FE, 0x14B8},
+ {0x3700, 0xB019},
+ {0x364E, 0x0710},
+ {0x3650, 0x30ED},
+ {0x3652, 0x03F2},
+ {0x3654, 0xF12E},
+ {0x3656, 0x8492},
+ {0x3676, 0xD9AD},
+ {0x3678, 0x88D0},
+ {0x367A, 0x7DED},
+ {0x367C, 0x3E31},
+ {0x367E, 0x91B3},
+ {0x369E, 0x7032},
+ {0x36A0, 0x2791},
+ {0x36A2, 0xBB55},
+ {0x36A4, 0xAB32},
+ {0x36A6, 0x1A58},
+ {0x36C6, 0xB50F},
+ {0x36C8, 0x0011},
+ {0x36CA, 0x6DB4},
+ {0x36CC, 0x96F5},
+ {0x36CE, 0x9BB7},
+ {0x36EE, 0x9353},
+ {0x36F0, 0xDF74},
+ {0x36F2, 0x04F8},
+ {0x36F4, 0x0FD8},
+ {0x36F6, 0xA87A},
+ {0x3662, 0x0170},
+ {0x3664, 0x6F0C},
+ {0x3666, 0x0112},
+ {0x3668, 0xCBAB},
+ {0x366A, 0x9111},
+ {0x368A, 0xB38D},
+ {0x368C, 0xE96F},
+ {0x368E, 0xCC0F},
+ {0x3690, 0x5851},
+ {0x3692, 0xFDD2},
+ {0x36B2, 0x5F92},
+ {0x36B4, 0x33B2},
+ {0x36B6, 0x9815},
+ {0x36B8, 0x86F5},
+ {0x36BA, 0x0578},
+ {0x36DA, 0xCD90},
+ {0x36DC, 0x1131},
+ {0x36DE, 0x5275},
+ {0x36E0, 0xE855},
+ {0x36E2, 0xD037},
+ {0x3702, 0xAAD1},
+ {0x3704, 0xEB75},
+ {0x3706, 0x0CD7},
+ {0x3708, 0x2C79},
+ {0x370A, 0xE0B9},
+ {0x366C, 0x0190},
+ {0x366E, 0x1C8D},
+ {0x3670, 0x0052},
+ {0x3672, 0xD66E},
+ {0x3674, 0xF511},
+ {0x3694, 0xB54D},
+ {0x3696, 0x6E4E},
+ {0x3698, 0x142E},
+ {0x369A, 0xC190},
+ {0x369C, 0xA753},
+ {0x36BC, 0x70F2},
+ {0x36BE, 0x04F1},
+ {0x36C0, 0xBD95},
+ {0x36C2, 0x0CEE},
+ {0x36C4, 0x1BF8},
+ {0x36E4, 0x806F},
+ {0x36E6, 0x1672},
+ {0x36E8, 0x2DF4},
+ {0x36EA, 0x8F16},
+ {0x36EC, 0xF776},
+ {0x370C, 0xAD73},
+ {0x370E, 0xB534},
+ {0x3710, 0x0D18},
+ {0x3712, 0x6057},
+ {0x3714, 0xBD1A},
+ {0x3644, 0x0354},
+ {0x3642, 0x0234},
+ {0x3210, 0x01B8},
+};
+
+struct mt9d113_i2c_reg_conf const
+ awb_settings[] = {
+ {0x098C, 0x2306},
+ {0x0990, 0x0180},
+ {0x098C, 0x2308},
+ {0x0990, 0xFF00},
+ {0x098C, 0x230A},
+ {0x0990, 0x0080},
+ {0x098C, 0x230C},
+ {0x0990, 0xFF66},
+ {0x098C, 0x230E},
+ {0x0990, 0x0180},
+ {0x098C, 0x2310},
+ {0x0990, 0xFFEE},
+ {0x098C, 0x2312},
+ {0x0990, 0xFFCD},
+ {0x098C, 0x2314},
+ {0x0990, 0xFECD},
+ {0x098C, 0x2316},
+ {0x0990, 0x019A},
+ {0x098C, 0x2318},
+ {0x0990, 0x0020},
+ {0x098C, 0x231A},
+ {0x0990, 0x0033},
+ {0x098C, 0x231C},
+ {0x0990, 0x0100},
+ {0x098C, 0x231E},
+ {0x0990, 0xFF9A},
+ {0x098C, 0x2320},
+ {0x0990, 0x0000},
+ {0x098C, 0x2322},
+ {0x0990, 0x004D},
+ {0x098C, 0x2324},
+ {0x0990, 0xFFCD},
+ {0x098C, 0x2326},
+ {0x0990, 0xFFB8},
+ {0x098C, 0x2328},
+ {0x0990, 0x004D},
+ {0x098C, 0x232A},
+ {0x0990, 0x0080},
+ {0x098C, 0x232C},
+ {0x0990, 0xFF66},
+ {0x098C, 0x232E},
+ {0x0990, 0x0008},
+ {0x098C, 0x2330},
+ {0x0990, 0xFFF7},
+ {0x098C, 0xA363},
+ {0x0990, 0x00D2},
+ {0x098C, 0xA364},
+ {0x0990, 0x00EE},
+ {0x3244, 0x0328},
+ {0x323E, 0xC22C},
+};
+
+struct mt9d113_i2c_reg_conf const
+ low_light_setting[] = {
+ {0x098C, 0x2B28},
+ {0x0990, 0x35E8},
+ {0x098C, 0x2B2A},
+ {0x0990, 0xB3B0},
+ {0x098C, 0xAB20},
+ {0x0990, 0x004B},
+ {0x098C, 0xAB24},
+ {0x0990, 0x0000},
+ {0x098C, 0xAB25},
+ {0x0990, 0x00FF},
+ {0x098C, 0xAB30},
+ {0x0990, 0x00FF},
+ {0x098C, 0xAB31},
+ {0x0990, 0x00FF},
+ {0x098C, 0xAB32},
+ {0x0990, 0x00FF},
+ {0x098C, 0xAB33},
+ {0x0990, 0x0057},
+ {0x098C, 0xAB34},
+ {0x0990, 0x0080},
+ {0x098C, 0xAB35},
+ {0x0990, 0x00FF},
+ {0x098C, 0xAB36},
+ {0x0990, 0x0014},
+ {0x098C, 0xAB37},
+ {0x0990, 0x0003},
+ {0x098C, 0x2B38},
+ {0x0990, 0x32C8},
+ {0x098C, 0x2B3A},
+ {0x0990, 0x7918},
+ {0x098C, 0x2B62},
+ {0x0990, 0xFFFE},
+ {0x098C, 0x2B64},
+ {0x0990, 0xFFFF},
+};
+
+struct mt9d113_reg mt9d113_regs = {
+ .pll_tbl = pll_tbl_settings,
+ .pll_tbl_size = ARRAY_SIZE(
+ pll_tbl_settings),
+ .register_tbl = register_wizard_settings,
+ .register_tbl_size = ARRAY_SIZE(
+ register_wizard_settings),
+ .err_tbl = err_settings,
+ .err_tbl_size = ARRAY_SIZE(err_settings),
+ .low_light_tbl = low_light_setting,
+ .low_light_tbl_size = ARRAY_SIZE(low_light_setting),
+ .awb_tbl = awb_settings,
+ .awb_tbl_size = ARRAY_SIZE(awb_settings),
+ .patch_tbl = patch_settings,
+ .patch_tbl_size = ARRAY_SIZE(patch_settings),
+ .eeprom_tbl = eeprom_settings,
+ .eeprom_tbl_size = ARRAY_SIZE(eeprom_settings),
+};
+
+
+
diff --git a/drivers/media/video/msm/mt9e013.c b/drivers/media/video/msm/mt9e013.c
new file mode 100644
index 0000000..94546f4
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013.c
@@ -0,0 +1,1140 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "mt9e013.h"
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME 0x3012
+/* Gain */
+#define REG_GLOBAL_GAIN 0x305E
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES 0x0340
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+#define REG_VCM_NEW_CODE 0x30F2
+
+/*============================================================================
+ TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define Q8 0x00000100
+#define Q10 0x00000400
+#define MT9E013_MASTER_CLK_RATE 24000000
+
+/* AF Total steps parameters */
+#define MT9E013_TOTAL_STEPS_NEAR_TO_FAR 32
+
+uint16_t mt9e013_step_position_table[MT9E013_TOTAL_STEPS_NEAR_TO_FAR+1];
+uint16_t mt9e013_nl_region_boundary1;
+uint16_t mt9e013_nl_region_code_per_step1;
+uint16_t mt9e013_l_region_code_per_step = 4;
+uint16_t mt9e013_damping_threshold = 10;
+uint16_t mt9e013_sw_damping_time_wait = 1;
+
+struct mt9e013_work_t {
+ struct work_struct work;
+};
+
+static struct mt9e013_work_t *mt9e013_sensorw;
+static struct i2c_client *mt9e013_client;
+
+struct mt9e013_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ uint16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum mt9e013_resolution_t prev_res;
+ enum mt9e013_resolution_t pict_res;
+ enum mt9e013_resolution_t curr_res;
+ enum mt9e013_test_mode_t set_test;
+};
+
+
+static bool CSI_CONFIG;
+static struct mt9e013_ctrl_t *mt9e013_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9e013_wait_queue);
+DEFINE_MUTEX(mt9e013_mut);
+
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+/*=============================================================*/
+
+static int mt9e013_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 = 2,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(mt9e013_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9e013_i2c_rxdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t mt9e013_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(mt9e013_client->adapter, msg, 1) < 0) {
+ CDBG("mt9e013_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9e013_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = mt9e013_i2c_rxdata(mt9e013_client->addr<<1, buf, rlen);
+ if (rc < 0) {
+ CDBG("mt9e013_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("mt9e013_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+ return rc;
+}
+
+static int32_t mt9e013_i2c_write_w_sensor(unsigned short waddr, uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+ rc = mt9e013_i2c_txdata(mt9e013_client->addr<<1, buf, 4);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+ return rc;
+}
+
+static int32_t mt9e013_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = mt9e013_i2c_txdata(mt9e013_client->addr<<1, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+
+static int32_t mt9e013_i2c_write_w_table(struct mt9e013_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = mt9e013_i2c_write_w_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static void mt9e013_group_hold_on(void)
+{
+ mt9e013_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+}
+
+static void mt9e013_group_hold_off(void)
+{
+ mt9e013_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void mt9e013_start_stream(void)
+{
+ mt9e013_i2c_write_w_sensor(0x301A, 0x8250);
+ mt9e013_i2c_write_w_sensor(0x301A, 0x8650);
+ mt9e013_i2c_write_w_sensor(0x301A, 0x8658);
+ mt9e013_i2c_write_b_sensor(0x0104, 0x00);
+ mt9e013_i2c_write_w_sensor(0x301A, 0x065C);
+}
+
+static void mt9e013_stop_stream(void)
+{
+ mt9e013_i2c_write_w_sensor(0x301A, 0x0058);
+ mt9e013_i2c_write_w_sensor(0x301A, 0x0050);
+ mt9e013_i2c_write_b_sensor(0x0104, 0x01);
+}
+
+static void mt9e013_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider, d1, d2;
+
+ d1 = mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata
+ * 0x00000400/
+ mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+ d2 = mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata
+ * 0x00000400/
+ mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+ divider = d1 * d2 / 0x400;
+
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+ /* 2 is the ratio of no.of snapshot channels
+ to number of preview channels */
+}
+
+static uint16_t mt9e013_get_prev_lines_pf(void)
+{
+ if (mt9e013_ctrl->prev_res == QTR_SIZE)
+ return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->prev_res == FULL_SIZE)
+ return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->prev_res == HFR_60FPS)
+ return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->prev_res == HFR_90FPS)
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+ else
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+}
+
+static uint16_t mt9e013_get_prev_pixels_pl(void)
+{
+ if (mt9e013_ctrl->prev_res == QTR_SIZE)
+ return mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->prev_res == FULL_SIZE)
+ return mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->prev_res == HFR_60FPS)
+ return mt9e013_regs.reg_60fps[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->prev_res == HFR_90FPS)
+ return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+ else
+ return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+}
+
+static uint16_t mt9e013_get_pict_lines_pf(void)
+{
+ if (mt9e013_ctrl->pict_res == QTR_SIZE)
+ return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+ return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+ return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+ else
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+}
+
+static uint16_t mt9e013_get_pict_pixels_pl(void)
+{
+ if (mt9e013_ctrl->pict_res == QTR_SIZE)
+ return mt9e013_regs.reg_prev[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+ return mt9e013_regs.reg_snap[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+ return mt9e013_regs.reg_60fps[E013_LINE_LENGTH_PCK].wdata;
+ else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+ return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+ else
+ return mt9e013_regs.reg_120fps[E013_LINE_LENGTH_PCK].wdata;
+}
+
+static uint32_t mt9e013_get_pict_max_exp_lc(void)
+{
+ if (mt9e013_ctrl->pict_res == QTR_SIZE)
+ return mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata
+ * 24;
+ else if (mt9e013_ctrl->pict_res == FULL_SIZE)
+ return mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata
+ * 24;
+ else if (mt9e013_ctrl->pict_res == HFR_60FPS)
+ return mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata
+ * 24;
+ else if (mt9e013_ctrl->pict_res == HFR_90FPS)
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata
+ * 24;
+ else
+ return mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata
+ * 24;
+}
+
+static int32_t mt9e013_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ if (mt9e013_ctrl->curr_res == QTR_SIZE)
+ total_lines_per_frame =
+ mt9e013_regs.reg_prev[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->curr_res == FULL_SIZE)
+ total_lines_per_frame =
+ mt9e013_regs.reg_snap[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->curr_res == HFR_60FPS)
+ total_lines_per_frame =
+ mt9e013_regs.reg_60fps[E013_FRAME_LENGTH_LINES].wdata;
+ else if (mt9e013_ctrl->curr_res == HFR_90FPS)
+ total_lines_per_frame =
+ mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+ else
+ total_lines_per_frame =
+ mt9e013_regs.reg_120fps[E013_FRAME_LENGTH_LINES].wdata;
+
+ mt9e013_ctrl->fps_divider = fps->fps_div;
+ mt9e013_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ if (mt9e013_ctrl->curr_res == FULL_SIZE) {
+ total_lines_per_frame = (uint16_t)
+ (total_lines_per_frame * mt9e013_ctrl->pict_fps_divider/0x400);
+ } else {
+ total_lines_per_frame = (uint16_t)
+ (total_lines_per_frame * mt9e013_ctrl->fps_divider/0x400);
+ }
+
+ mt9e013_group_hold_on();
+ rc = mt9e013_i2c_write_w_sensor(REG_FRAME_LENGTH_LINES,
+ total_lines_per_frame);
+ mt9e013_group_hold_off();
+ return rc;
+}
+
+static int32_t mt9e013_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0xE7F;
+ int32_t rc = 0;
+ if (gain > max_legal_gain) {
+ CDBG("Max legal gain Line:%d\n", __LINE__);
+ gain = max_legal_gain;
+ }
+
+ if (mt9e013_ctrl->curr_res != FULL_SIZE) {
+ mt9e013_ctrl->my_reg_gain = gain;
+ mt9e013_ctrl->my_reg_line_count = (uint16_t) line;
+ line = (uint32_t) (line * mt9e013_ctrl->fps_divider /
+ 0x00000400);
+ } else {
+ line = (uint32_t) (line * mt9e013_ctrl->pict_fps_divider /
+ 0x00000400);
+ }
+
+ gain |= 0x1000;
+
+ mt9e013_group_hold_on();
+ rc = mt9e013_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+ rc = mt9e013_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+ mt9e013_group_hold_off();
+ return rc;
+}
+
+static int32_t mt9e013_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = mt9e013_write_exp_gain(gain, line);
+ mt9e013_i2c_write_w_sensor(0x301A, 0x065C|0x2);
+ return rc;
+}
+
+#define DIV_CEIL(x, y) (x/y + (x%y) ? 1 : 0)
+
+static int32_t mt9e013_move_focus(int direction,
+ int32_t num_steps)
+{
+ int16_t step_direction, dest_lens_position, dest_step_position;
+ int16_t target_dist, small_step, next_lens_position;
+ if (direction == MOVE_NEAR)
+ step_direction = 1;
+ else
+ step_direction = -1;
+
+ dest_step_position = mt9e013_ctrl->curr_step_pos
+ + (step_direction * num_steps);
+
+ if (dest_step_position < 0)
+ dest_step_position = 0;
+ else if (dest_step_position > MT9E013_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = MT9E013_TOTAL_STEPS_NEAR_TO_FAR;
+
+ if (dest_step_position == mt9e013_ctrl->curr_step_pos)
+ return 0;
+
+ dest_lens_position = mt9e013_step_position_table[dest_step_position];
+ target_dist = step_direction *
+ (dest_lens_position - mt9e013_ctrl->curr_lens_pos);
+
+ if (step_direction < 0 && (target_dist >=
+ mt9e013_step_position_table[mt9e013_damping_threshold])) {
+ small_step = DIV_CEIL(target_dist, 10);
+ mt9e013_sw_damping_time_wait = 10;
+ } else {
+ small_step = DIV_CEIL(target_dist, 4);
+ mt9e013_sw_damping_time_wait = 4;
+ }
+
+ for (next_lens_position = mt9e013_ctrl->curr_lens_pos
+ + (step_direction * small_step);
+ (step_direction * next_lens_position) <=
+ (step_direction * dest_lens_position);
+ next_lens_position += (step_direction * small_step)) {
+ mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE,
+ next_lens_position);
+ mt9e013_ctrl->curr_lens_pos = next_lens_position;
+ usleep(mt9e013_sw_damping_time_wait*50);
+ }
+
+ if (mt9e013_ctrl->curr_lens_pos != dest_lens_position) {
+ mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE,
+ dest_lens_position);
+ usleep(mt9e013_sw_damping_time_wait*50);
+ }
+ mt9e013_ctrl->curr_lens_pos = dest_lens_position;
+ mt9e013_ctrl->curr_step_pos = dest_step_position;
+ return 0;
+}
+
+static int32_t mt9e013_set_default_focus(uint8_t af_step)
+{
+ int32_t rc = 0;
+ if (mt9e013_ctrl->curr_step_pos != 0) {
+ rc = mt9e013_move_focus(MOVE_FAR,
+ mt9e013_ctrl->curr_step_pos);
+ } else {
+ mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, 0x00);
+ }
+
+ mt9e013_ctrl->curr_lens_pos = 0;
+ mt9e013_ctrl->curr_step_pos = 0;
+
+ return rc;
+}
+
+static void mt9e013_init_focus(void)
+{
+ uint8_t i;
+ mt9e013_step_position_table[0] = 0;
+ for (i = 1; i <= MT9E013_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ if (i <= mt9e013_nl_region_boundary1) {
+ mt9e013_step_position_table[i] =
+ mt9e013_step_position_table[i-1]
+ + mt9e013_nl_region_code_per_step1;
+ } else {
+ mt9e013_step_position_table[i] =
+ mt9e013_step_position_table[i-1]
+ + mt9e013_l_region_code_per_step;
+ }
+
+ if (mt9e013_step_position_table[i] > 255)
+ mt9e013_step_position_table[i] = 255;
+ }
+}
+
+static int32_t mt9e013_test(enum mt9e013_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+ 1: Bypass Signal Processing
+ REG_0x30D8[5] is EBDMASK: 0:
+ Output Embedded data, 1: No output embedded data */
+ if (mt9e013_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t mt9e013_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ struct msm_camera_csi_params mt9e013_csi_params;
+ uint8_t stored_af_step = 0;
+ CDBG("sensor_settings\n");
+ stored_af_step = mt9e013_ctrl->curr_step_pos;
+ mt9e013_set_default_focus(0);
+ mt9e013_stop_stream();
+ msleep(15);
+ if (update_type == REG_INIT) {
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_mipi,
+ mt9e013_regs.reg_mipi_size);
+ mt9e013_i2c_write_w_table(mt9e013_regs.rec_settings,
+ mt9e013_regs.rec_size);
+ cam_debug_init();
+ CSI_CONFIG = 0;
+ } else if (update_type == UPDATE_PERIODIC) {
+ if (rt == QTR_SIZE) {
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll,
+ mt9e013_regs.reg_pll_size);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_prev,
+ mt9e013_regs.reg_prev_size);
+ } else if (rt == FULL_SIZE) {
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll,
+ mt9e013_regs.reg_pll_size);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_snap,
+ mt9e013_regs.reg_snap_size);
+ } else if (rt == HFR_60FPS) {
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+ mt9e013_regs.reg_pll_120fps_size);
+ mt9e013_i2c_write_w_sensor(0x0306, 0x0029);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+ mt9e013_regs.reg_120fps_size);
+ } else if (rt == HFR_90FPS) {
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+ mt9e013_regs.reg_pll_120fps_size);
+ mt9e013_i2c_write_w_sensor(0x0306, 0x003D);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+ mt9e013_regs.reg_120fps_size);
+ } else if (rt == HFR_120FPS) {
+ msm_camio_vfe_clk_rate_set(266667000);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_pll_120fps,
+ mt9e013_regs.reg_pll_120fps_size);
+ mt9e013_i2c_write_w_table(mt9e013_regs.reg_120fps,
+ mt9e013_regs.reg_120fps_size);
+ }
+ if (!CSI_CONFIG) {
+ msm_camio_vfe_clk_rate_set(192000000);
+ mt9e013_csi_params.data_format = CSI_10BIT;
+ mt9e013_csi_params.lane_cnt = 2;
+ mt9e013_csi_params.lane_assign = 0xe4;
+ mt9e013_csi_params.dpcm_scheme = 0;
+ mt9e013_csi_params.settle_cnt = 0x18;
+ rc = msm_camio_csi_config(&mt9e013_csi_params);
+ msleep(10);
+ CSI_CONFIG = 1;
+ }
+ mt9e013_move_focus(MOVE_NEAR, stored_af_step);
+ mt9e013_start_stream();
+ }
+ return rc;
+}
+
+static int32_t mt9e013_video_config(int mode)
+{
+
+ int32_t rc = 0;
+
+ CDBG("video config\n");
+ /* change sensor resolution if needed */
+ if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+ mt9e013_ctrl->prev_res) < 0)
+ return rc;
+ if (mt9e013_ctrl->set_test) {
+ if (mt9e013_test(mt9e013_ctrl->set_test) < 0)
+ return rc;
+ }
+
+ mt9e013_ctrl->curr_res = mt9e013_ctrl->prev_res;
+ mt9e013_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t mt9e013_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /*change sensor resolution if needed */
+ if (mt9e013_ctrl->curr_res != mt9e013_ctrl->pict_res) {
+ if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+ mt9e013_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ mt9e013_ctrl->curr_res = mt9e013_ctrl->pict_res;
+ mt9e013_ctrl->sensormode = mode;
+ return rc;
+} /*end of mt9e013_snapshot_config*/
+
+static int32_t mt9e013_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /* change sensor resolution if needed */
+ if (mt9e013_ctrl->curr_res != mt9e013_ctrl->pict_res) {
+ if (mt9e013_sensor_setting(UPDATE_PERIODIC,
+ mt9e013_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ mt9e013_ctrl->curr_res = mt9e013_ctrl->pict_res;
+ mt9e013_ctrl->sensormode = mode;
+ return rc;
+} /*end of mt9e013_raw_snapshot_config*/
+
+static int32_t mt9e013_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ case SENSOR_HFR_60FPS_MODE:
+ case SENSOR_HFR_90FPS_MODE:
+ case SENSOR_HFR_120FPS_MODE:
+ mt9e013_ctrl->prev_res = res;
+ rc = mt9e013_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ mt9e013_ctrl->pict_res = res;
+ rc = mt9e013_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ mt9e013_ctrl->pict_res = res;
+ rc = mt9e013_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t mt9e013_power_down(void)
+{
+ return 0;
+}
+
+static int mt9e013_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ CDBG("probe done\n");
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int mt9e013_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "mt9e013");
+ CDBG(" mt9e013_probe_init_sensor\n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(10);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(10);
+ } else {
+ goto init_probe_done;
+ }
+
+ CDBG(" mt9e013_probe_init_sensor is called\n");
+ rc = mt9e013_i2c_read(0x0000, &chipid, 2);
+ CDBG("ID: %d\n", chipid);
+ /* 4. Compare sensor ID to MT9E013 ID: */
+ if (chipid != 0x4B00) {
+ rc = -ENODEV;
+ CDBG("mt9e013_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+
+ mt9e013_ctrl = kzalloc(sizeof(struct mt9e013_ctrl_t), GFP_KERNEL);
+ if (!mt9e013_ctrl) {
+ CDBG("mt9e013_init failed!\n");
+ rc = -ENOMEM;
+ }
+ mt9e013_ctrl->fps_divider = 1 * 0x00000400;
+ mt9e013_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9e013_ctrl->set_test = TEST_OFF;
+ mt9e013_ctrl->prev_res = QTR_SIZE;
+ mt9e013_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9e013_ctrl->sensordata = data;
+
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" mt9e013_probe_init_sensor fails\n");
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ mt9e013_probe_init_done(data);
+init_probe_done:
+ CDBG(" mt9e013_probe_init_sensor finishes\n");
+ return rc;
+}
+/* camsensor_mt9e013_reset */
+
+int mt9e013_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling mt9e013_sensor_open_init\n");
+
+ mt9e013_ctrl = kzalloc(sizeof(struct mt9e013_ctrl_t), GFP_KERNEL);
+ if (!mt9e013_ctrl) {
+ CDBG("mt9e013_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ mt9e013_ctrl->fps_divider = 1 * 0x00000400;
+ mt9e013_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9e013_ctrl->set_test = TEST_OFF;
+ mt9e013_ctrl->prev_res = QTR_SIZE;
+ mt9e013_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9e013_ctrl->sensordata = data;
+ if (rc < 0) {
+ CDBG("Calling mt9e013_sensor_open_init fail1\n");
+ return rc;
+ }
+ CDBG("%s: %d\n", __func__, __LINE__);
+ /* enable mclk first */
+ msm_camio_clk_rate_set(MT9E013_MASTER_CLK_RATE);
+ rc = mt9e013_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ CDBG("init settings\n");
+ rc = mt9e013_sensor_setting(REG_INIT, mt9e013_ctrl->prev_res);
+ mt9e013_ctrl->fps = 30*Q8;
+ mt9e013_init_focus();
+ if (rc < 0) {
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ goto init_fail;
+ } else
+ goto init_done;
+init_fail:
+ CDBG("init_fail\n");
+ mt9e013_probe_init_done(data);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+} /*endof mt9e013_sensor_open_init*/
+
+static int mt9e013_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9e013_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id mt9e013_i2c_id[] = {
+ {"mt9e013", 0},
+ { }
+};
+
+static int mt9e013_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("mt9e013_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ mt9e013_sensorw = kzalloc(sizeof(struct mt9e013_work_t), GFP_KERNEL);
+ if (!mt9e013_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9e013_sensorw);
+ mt9e013_init_client(client);
+ mt9e013_client = client;
+
+
+ CDBG("mt9e013_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("mt9e013_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int mt9e013_send_wb_info(struct wb_info_cfg *wb)
+{
+ return 0;
+
+} /*end of mt9e013_snapshot_config*/
+
+static int __exit mt9e013_remove(struct i2c_client *client)
+{
+ struct mt9e013_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ mt9e013_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver mt9e013_i2c_driver = {
+ .id_table = mt9e013_i2c_id,
+ .probe = mt9e013_i2c_probe,
+ .remove = __exit_p(mt9e013_i2c_remove),
+ .driver = {
+ .name = "mt9e013",
+ },
+};
+
+int mt9e013_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;
+ mutex_lock(&mt9e013_mut);
+ CDBG("mt9e013_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9e013_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 =
+ mt9e013_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 =
+ mt9e013_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 =
+ mt9e013_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 =
+ mt9e013_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 =
+ mt9e013_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 = mt9e013_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ mt9e013_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ mt9e013_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9e013_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9e013_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc =
+ mt9e013_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ mt9e013_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9E013_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = mt9e013_set_default_focus(
+ cdata.cfg.effect);
+ break;
+
+
+ case CFG_SEND_WB_INFO:
+ rc = mt9e013_send_wb_info(
+ &(cdata.cfg.wb_info));
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&mt9e013_mut);
+
+ return rc;
+}
+
+static int mt9e013_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&mt9e013_mut);
+ mt9e013_power_down();
+ gpio_set_value_cansleep(mt9e013_ctrl->sensordata->sensor_reset, 0);
+ msleep(5);
+ gpio_free(mt9e013_ctrl->sensordata->sensor_reset);
+ kfree(mt9e013_ctrl);
+ mt9e013_ctrl = NULL;
+ CDBG("mt9e013_release completed\n");
+ mutex_unlock(&mt9e013_mut);
+
+ return rc;
+}
+
+static int mt9e013_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&mt9e013_i2c_driver);
+ if (rc < 0 || mt9e013_client == NULL) {
+ rc = -ENOTSUPP;
+ CDBG("I2C add driver failed");
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(MT9E013_MASTER_CLK_RATE);
+ rc = mt9e013_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = mt9e013_sensor_open_init;
+ s->s_release = mt9e013_sensor_release;
+ s->s_config = mt9e013_sensor_config;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ mt9e013_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("mt9e013_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __mt9e013_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9e013_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9e013_probe,
+ .driver = {
+ .name = "msm_camera_mt9e013",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9e013_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9e013_init);
+void mt9e013_exit(void)
+{
+ i2c_del_driver(&mt9e013_i2c_driver);
+}
+MODULE_DESCRIPTION("Aptina 8 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+static bool streaming = 1;
+
+static int mt9e013_focus_test(void *data, u64 *val)
+{
+ int i = 0;
+ mt9e013_set_default_focus(0);
+
+ for (i = 90; i < 256; i++) {
+ mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, i);
+ msleep(5000);
+ }
+ msleep(5000);
+ for (i = 255; i > 90; i--) {
+ mt9e013_i2c_write_w_sensor(REG_VCM_NEW_CODE, i);
+ msleep(5000);
+ }
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_focus, mt9e013_focus_test,
+ NULL, "%lld\n");
+
+static int mt9e013_step_test(void *data, u64 *val)
+{
+ int i = 0;
+ mt9e013_set_default_focus(0);
+
+ for (i = 0; i < MT9E013_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ mt9e013_move_focus(MOVE_NEAR, 1);
+ msleep(5000);
+ }
+
+ mt9e013_move_focus(MOVE_FAR, MT9E013_TOTAL_STEPS_NEAR_TO_FAR);
+ msleep(5000);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_step, mt9e013_step_test,
+ NULL, "%lld\n");
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+ int rc = 0;
+
+ if (val) {
+ mt9e013_start_stream();
+ streaming = 1;
+ } else {
+ mt9e013_stop_stream();
+ streaming = 0;
+ }
+
+ return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+ *val = streaming;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+ cam_debug_stream_set, "%llu\n");
+
+
+static int cam_debug_init(void)
+{
+ struct dentry *cam_dir;
+ debugfs_base = debugfs_create_dir("sensor", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ cam_dir = debugfs_create_dir("mt9e013", debugfs_base);
+ if (!cam_dir)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("focus", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_focus))
+ return -ENOMEM;
+ if (!debugfs_create_file("step", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_step))
+ return -ENOMEM;
+ if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_stream))
+ return -ENOMEM;
+
+ return 0;
+}
+
+
+
diff --git a/drivers/media/video/msm/mt9e013.h b/drivers/media/video/msm/mt9e013.h
new file mode 100644
index 0000000..9052a35
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013.h
@@ -0,0 +1,174 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9E013_H
+#define MT9E013_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct mt9e013_reg mt9e013_regs;
+struct reg_struct_init {
+ uint8_t reg_0x0112; /* 0x0112*/
+ uint8_t reg_0x0113; /* 0x0113*/
+ uint8_t vt_pix_clk_div; /* 0x0301*/
+ uint8_t pre_pll_clk_div; /* 0x0305*/
+ uint8_t pll_multiplier; /* 0x0307*/
+ uint8_t op_pix_clk_div; /* 0x0309*/
+ uint8_t reg_0x3030; /*0x3030*/
+ uint8_t reg_0x0111; /*0x0111*/
+ uint8_t reg_0x0b00; /*0x0b00*/
+ uint8_t reg_0x3001; /*0x3001*/
+ uint8_t reg_0x3004; /*0x3004*/
+ uint8_t reg_0x3007; /*0x3007*/
+ uint8_t reg_0x3016; /*0x3016*/
+ uint8_t reg_0x301d; /*0x301d*/
+ uint8_t reg_0x317e; /*0x317E*/
+ uint8_t reg_0x317f; /*0x317F*/
+ uint8_t reg_0x3400; /*0x3400*/
+ uint8_t reg_0x0b06; /*0x0b06*/
+ uint8_t reg_0x0b07; /*0x0b07*/
+ uint8_t reg_0x0b08; /*0x0b08*/
+ uint8_t reg_0x0b09; /*0x0b09*/
+ uint8_t reg_0x0136;
+ uint8_t reg_0x0137;
+ /* Edof */
+ uint8_t reg_0x0b83; /*0x0b83*/
+ uint8_t reg_0x0b84; /*0x0b84*/
+ uint8_t reg_0x0b85; /*0x0b85*/
+ uint8_t reg_0x0b88; /*0x0b88*/
+ uint8_t reg_0x0b89; /*0x0b89*/
+ uint8_t reg_0x0b8a; /*0x0b8a*/
+ };
+struct reg_struct {
+ uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+ uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+ uint8_t analogue_gain_code_global;
+ uint8_t frame_length_lines_hi; /* 0x0340*/
+ uint8_t frame_length_lines_lo; /* 0x0341*/
+ uint8_t line_length_pck_hi; /* 0x0342*/
+ uint8_t line_length_pck_lo; /* 0x0343*/
+ uint8_t reg_0x3005; /* 0x3005*/
+ uint8_t reg_0x3010; /* 0x3010*/
+ uint8_t reg_0x3011; /* 0x3011*/
+ uint8_t reg_0x301a; /* 0x301a*/
+ uint8_t reg_0x3035; /* 0x3035*/
+ uint8_t reg_0x3036; /* 0x3036*/
+ uint8_t reg_0x3041; /*0x3041*/
+ uint8_t reg_0x3042; /*0x3042*/
+ uint8_t reg_0x3045; /*0x3045*/
+ uint8_t reg_0x0b80; /* 0x0b80*/
+ uint8_t reg_0x0900; /*0x0900*/
+ uint8_t reg_0x0901; /* 0x0901*/
+ uint8_t reg_0x0902; /*0x0902*/
+ uint8_t reg_0x0383; /*0x0383*/
+ uint8_t reg_0x0387; /* 0x0387*/
+ uint8_t reg_0x034c; /* 0x034c*/
+ uint8_t reg_0x034d; /*0x034d*/
+ uint8_t reg_0x034e; /* 0x034e*/
+ uint8_t reg_0x034f; /* 0x034f*/
+ uint8_t reg_0x1716; /*0x1716*/
+ uint8_t reg_0x1717; /*0x1717*/
+ uint8_t reg_0x1718; /*0x1718*/
+ uint8_t reg_0x1719; /*0x1719*/
+ uint8_t reg_0x3210;/*0x3210*/
+ uint8_t reg_0x111; /*0x111*/
+ uint8_t reg_0x3410; /*0x3410*/
+ uint8_t reg_0x3098;
+ uint8_t reg_0x309D;
+ uint8_t reg_0x0200;
+ uint8_t reg_0x0201;
+ };
+struct mt9e013_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+enum mt9e013_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9e013_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ HFR_60FPS,
+ HFR_90FPS,
+ HFR_120FPS,
+ INVALID_SIZE
+};
+enum mt9e013_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+enum mt9e013_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 mt9e013_reg_pll {
+ E013_VT_PIX_CLK_DIV,
+ E013_VT_SYS_CLK_DIV,
+ E013_PRE_PLL_CLK_DIV,
+ E013_PLL_MULTIPLIER,
+ E013_OP_PIX_CLK_DIV,
+ E013_OP_SYS_CLK_DIV
+};
+
+enum mt9e013_reg_mode {
+ E013_X_ADDR_START,
+ E013_X_ADDR_END,
+ E013_Y_ADDR_START,
+ E013_Y_ADDR_END,
+ E013_X_OUTPUT_SIZE,
+ E013_Y_OUTPUT_SIZE,
+ E013_DATAPATH_SELECT,
+ E013_READ_MODE,
+ E013_ANALOG_CONTROL5,
+ E013_DAC_LD_4_5,
+ E013_SCALING_MODE,
+ E013_SCALE_M,
+ E013_LINE_LENGTH_PCK,
+ E013_FRAME_LENGTH_LINES,
+ E013_COARSE_INTEGRATION_TIME,
+ E013_FINE_INTEGRATION_TIME,
+ E013_FINE_CORRECTION
+};
+
+struct mt9e013_reg {
+ const struct mt9e013_i2c_reg_conf *reg_mipi;
+ const unsigned short reg_mipi_size;
+ const struct mt9e013_i2c_reg_conf *rec_settings;
+ const unsigned short rec_size;
+ const struct mt9e013_i2c_reg_conf *reg_pll;
+ const unsigned short reg_pll_size;
+ const struct mt9e013_i2c_reg_conf *reg_pll_60fps;
+ const unsigned short reg_pll_60fps_size;
+ const struct mt9e013_i2c_reg_conf *reg_pll_120fps;
+ const unsigned short reg_pll_120fps_size;
+ const struct mt9e013_i2c_reg_conf *reg_prev;
+ const unsigned short reg_prev_size;
+ const struct mt9e013_i2c_reg_conf *reg_snap;
+ const unsigned short reg_snap_size;
+ const struct mt9e013_i2c_reg_conf *reg_60fps;
+ const unsigned short reg_60fps_size;
+ const struct mt9e013_i2c_reg_conf *reg_120fps;
+ const unsigned short reg_120fps_size;
+};
+#endif /* MT9E013_H */
diff --git a/drivers/media/video/msm/mt9e013_reg.c b/drivers/media/video/msm/mt9e013_reg.c
new file mode 100644
index 0000000..9a4bd7e
--- /dev/null
+++ b/drivers/media/video/msm/mt9e013_reg.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+
+#include "mt9e013.h"
+
+static struct mt9e013_i2c_reg_conf mipi_settings[] = {
+ /*Disable embedded data*/
+ {0x3064, 0x7800},/*SMIA_TEST*/
+ /*configure 2-lane MIPI*/
+ {0x31AE, 0x0202},/*SERIAL_FORMAT*/
+ {0x31B8, 0x0E3F},/*MIPI_TIMING_2*/
+ /*set data to RAW10 format*/
+ {0x0112, 0x0A0A},/*CCP_DATA_FORMAT*/
+ {0x30F0, 0x8000},/*VCM CONTROL*/
+};
+
+/*PLL Configuration
+(Ext=24MHz, vt_pix_clk=174MHz, op_pix_clk=69.6MHz)*/
+static struct mt9e013_i2c_reg_conf pll_settings[] = {
+ {0x0300, 0x0004},/*VT_PIX_CLK_DIV*/
+ {0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+ {0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+ {0x0306, 0x003A},/*PLL_MULTIPLIER*/
+ {0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+ {0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings[] = {
+ /*Output Size (1632x1224)*/
+ {0x0344, 0x0008},/*X_ADDR_START*/
+ {0x0348, 0x0CC9},/*X_ADDR_END*/
+ {0x0346, 0x0008},/*Y_ADDR_START*/
+ {0x034A, 0x0999},/*Y_ADDR_END*/
+ {0x034C, 0x0660},/*X_OUTPUT_SIZE*/
+ {0x034E, 0x04C8},/*Y_OUTPUT_SIZE*/
+ {0x306E, 0xFCB0},/*DATAPATH_SELECT*/
+ {0x3040, 0x04C3},/*READ_MODE*/
+ {0x3178, 0x0000},/*ANALOG_CONTROL5*/
+ {0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+ {0x0400, 0x0002},/*SCALING_MODE*/
+ {0x0404, 0x0010},/*SCALE_M*/
+ /*Timing configuration*/
+ {0x0342, 0x1018},/*LINE_LENGTH_PCK*/
+ {0x0340, 0x055B},/*FRAME_LENGTH_LINES*/
+ {0x0202, 0x0557},/*COARSE_INTEGRATION_TIME*/
+ {0x3014, 0x0846},/*FINE_INTEGRATION_TIME_*/
+ {0x3010, 0x0130},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf snap_settings[] = {
+ /*Output Size (3264x2448)*/
+ {0x0344, 0x0008},/*X_ADDR_START */
+ {0x0348, 0x0CD7},/*X_ADDR_END*/
+ {0x0346, 0x0008},/*Y_ADDR_START */
+ {0x034A, 0x09A7},/*Y_ADDR_END*/
+ {0x034C, 0x0CD0},/*X_OUTPUT_SIZE*/
+ {0x034E, 0x09A0},/*Y_OUTPUT_SIZE*/
+ {0x306E, 0xFC80},/*DATAPATH_SELECT*/
+ {0x3040, 0x0041},/*READ_MODE*/
+ {0x3178, 0x0000},/*ANALOG_CONTROL5*/
+ {0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+ {0x0400, 0x0000},/*SCALING_MODE*/
+ {0x0404, 0x0010},/*SCALE_M*/
+ /*Timing configuration*/
+ {0x0342, 0x13F8},/*LINE_LENGTH_PCK*/
+ {0x0340, 0x0A2F},/*FRAME_LENGTH_LINES*/
+ {0x0202, 0x0A1F},/*COARSE_INTEGRATION_TIME*/
+ {0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_ */
+ {0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf pll_settings_60fps[] = {
+ {0x0300, 0x0004},/*VT_PIX_CLK_DIV*/
+ {0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+ {0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+ {0x0306, 0x0042},/*PLL_MULTIPLIER*/
+ {0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+ {0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings_60fps[] = {
+ /*Output Size (1632x1224)*/
+ {0x0344, 0x0008},/*X_ADDR_START*/
+ {0x0348, 0x0CC5},/*X_ADDR_END*/
+ {0x0346, 0x013a},/*Y_ADDR_START*/
+ {0x034A, 0x0863},/*Y_ADDR_END*/
+ {0x034C, 0x0660},/*X_OUTPUT_SIZE*/
+ {0x034E, 0x0396},/*Y_OUTPUT_SIZE*/
+ {0x306E, 0xFC80},/*DATAPATH_SELECT*/
+ {0x3040, 0x00C3},/*READ_MODE*/
+ {0x3178, 0x0000},/*ANALOG_CONTROL5*/
+ {0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+ {0x0400, 0x0000},/*SCALING_MODE*/
+ {0x0404, 0x0010},/*SCALE_M*/
+ /*Timing configuration*/
+ {0x0342, 0x0BE8},/*LINE_LENGTH_PCK*/
+ {0x0340, 0x0425},/*FRAME_LENGTH_LINES*/
+ {0x0202, 0x0425},/*COARSE_INTEGRATION_TIME*/
+ {0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+ {0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf pll_settings_120fps[] = {
+ {0x0300, 0x0005},/*VT_PIX_CLK_DIV*/
+ {0x0302, 0x0001},/*VT_SYS_CLK_DIV*/
+ {0x0304, 0x0002},/*PRE_PLL_CLK_DIV*/
+ {0x0306, 0x0052},/*PLL_MULTIPLIER*/
+ {0x0308, 0x000A},/*OP_PIX_CLK_DIV*/
+ {0x030A, 0x0001},/*OP_SYS_CLK_DIV*/
+};
+
+static struct mt9e013_i2c_reg_conf prev_settings_120fps[] = {
+ {0x0344, 0x0008},/*X_ADDR_START*/
+ {0x0348, 0x0685},/*X_ADDR_END*/
+ {0x0346, 0x013a},/*Y_ADDR_START*/
+ {0x034A, 0x055B},/*Y_ADDR_END*/
+ {0x034C, 0x0340},/*X_OUTPUT_SIZE*/
+ {0x034E, 0x0212},/*Y_OUTPUT_SIZE*/
+ {0x306E, 0xFC80},/*DATAPATH_SELECT*/
+ {0x3040, 0x00C3},/*READ_MODE*/
+ {0x3178, 0x0000},/*ANALOG_CONTROL5*/
+ {0x3ED0, 0x1E24},/*DAC_LD_4_5*/
+ {0x0400, 0x0000},/*SCALING_MODE*/
+ {0x0404, 0x0010},/*SCALE_M*/
+ /*Timing configuration*/
+ {0x0342, 0x0970},/*LINE_LENGTH_PCK*/
+ {0x0340, 0x02A1},/*FRAME_LENGTH_LINES*/
+ {0x0202, 0x02A1},/*COARSE_INTEGRATION_TIME*/
+ {0x3014, 0x03F6},/*FINE_INTEGRATION_TIME_*/
+ {0x3010, 0x0078},/*FINE_CORRECTION*/
+};
+
+static struct mt9e013_i2c_reg_conf recommend_settings[] = {
+ {0x3044, 0x0590},
+ {0x306E, 0xFC80},
+ {0x30B2, 0xC000},
+ {0x30D6, 0x0800},
+ {0x316C, 0xB42F},
+ {0x316E, 0x869C},
+ {0x3170, 0x210E},
+ {0x317A, 0x010E},
+ {0x31E0, 0x1FB9},
+ {0x31E6, 0x07FC},
+ {0x37C0, 0x0000},
+ {0x37C2, 0x0000},
+ {0x37C4, 0x0000},
+ {0x37C6, 0x0000},
+ {0x3E02, 0x8801},
+ {0x3E04, 0x2301},
+ {0x3E06, 0x8449},
+ {0x3E08, 0x6841},
+ {0x3E0A, 0x400C},
+ {0x3E0C, 0x1001},
+ {0x3E0E, 0x2103},
+ {0x3E10, 0x4B41},
+ {0x3E12, 0x4B26},
+ {0x3E16, 0x8802},
+ {0x3E18, 0x84FF},
+ {0x3E1A, 0x8601},
+ {0x3E1C, 0x8401},
+ {0x3E1E, 0x840A},
+ {0x3E20, 0xFF00},
+ {0x3E22, 0x8401},
+ {0x3E24, 0x00FF},
+ {0x3E26, 0x0088},
+ {0x3E28, 0x2E8A},
+ {0x3E32, 0x8801},
+ {0x3E34, 0x4024},
+ {0x3E38, 0x8469},
+ {0x3E3C, 0x2301},
+ {0x3E3E, 0x3E25},
+ {0x3E40, 0x1C01},
+ {0x3E42, 0x8486},
+ {0x3E44, 0x8401},
+ {0x3E46, 0x00FF},
+ {0x3E48, 0x8401},
+ {0x3E4A, 0x8601},
+ {0x3E4C, 0x8402},
+ {0x3E4E, 0x00FF},
+ {0x3E50, 0x6623},
+ {0x3E52, 0x8340},
+ {0x3E54, 0x00FF},
+ {0x3E56, 0x4A42},
+ {0x3E58, 0x2203},
+ {0x3E5A, 0x674D},
+ {0x3E5C, 0x3F25},
+ {0x3E5E, 0x846A},
+ {0x3E60, 0x4C01},
+ {0x3E62, 0x8401},
+ {0x3E66, 0x3901},
+ {0x3ECC, 0x00EB},
+ {0x3ED0, 0x1E24},
+ {0x3ED4, 0xAFC4},
+ {0x3ED6, 0x909B},
+ {0x3ED8, 0x0006},
+ {0x3EDA, 0xCFC6},
+ {0x3EDC, 0x4FE4},
+ {0x3EE0, 0x2424},
+ {0x3EE2, 0x9797},
+ {0x3EE4, 0xC100},
+ {0x3EE6, 0x0540}
+};
+
+struct mt9e013_reg mt9e013_regs = {
+ .reg_mipi = &mipi_settings[0],
+ .reg_mipi_size = ARRAY_SIZE(mipi_settings),
+ .rec_settings = &recommend_settings[0],
+ .rec_size = ARRAY_SIZE(recommend_settings),
+ .reg_pll = &pll_settings[0],
+ .reg_pll_size = ARRAY_SIZE(pll_settings),
+ .reg_prev = &prev_settings[0],
+ .reg_pll_60fps = &pll_settings_60fps[0],
+ .reg_pll_60fps_size = ARRAY_SIZE(pll_settings_60fps),
+ .reg_pll_120fps = &pll_settings_120fps[0],
+ .reg_pll_120fps_size = ARRAY_SIZE(pll_settings_120fps),
+ .reg_prev_size = ARRAY_SIZE(prev_settings),
+ .reg_snap = &snap_settings[0],
+ .reg_snap_size = ARRAY_SIZE(snap_settings),
+ .reg_60fps = &prev_settings_60fps[0],
+ .reg_60fps_size = ARRAY_SIZE(prev_settings_60fps),
+ .reg_120fps = &prev_settings_120fps[0],
+ .reg_120fps_size = ARRAY_SIZE(prev_settings_120fps),
+};
diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h
new file mode 100644
index 0000000..0579813
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9T012_H
+#define MT9T012_H
+
+#include <linux/types.h>
+
+extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */
+
+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 const *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9p012_i2c_reg_conf const *ttbl;
+ uint16_t ttbl_size;
+ struct mt9p012_i2c_reg_conf const *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* MT9T012_H */
diff --git a/drivers/media/video/msm/mt9p012_bam.c b/drivers/media/video/msm/mt9p012_bam.c
new file mode 100644
index 0000000..9197380
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_bam.c
@@ -0,0 +1,1426 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 0x0A
+
+/* AF Total steps parameters */
+#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF 20
+#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR 20
+
+#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 uint16_t bam_macro, bam_infinite;
+static uint16_t bam_step_lookup_table[MT9P012_TOTAL_STEPS_NEAR_TO_FAR + 1];
+static uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+DEFINE_MUTEX(mt9p012_mut);
+
+/*=============================================================*/
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, int slength,
+ unsigned char *rxdata, int rxlength)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = slength,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = rxlength,
+ .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_b(unsigned short saddr, unsigned char raddr,
+ unsigned short *rdata)
+{
+ int32_t rc = 0;
+ if (!rdata)
+ return -EIO;
+ rc = mt9p012_i2c_rxdata(saddr, 1, &raddr, 1);
+ if (rc < 0)
+ return rc;
+ *rdata = raddr;
+ if (rc < 0)
+ CDBG("mt9p012_i2c_read_b failed!\n");
+ return rc;
+}
+
+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, 2, 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 const
+ *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.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;
+ enum mt9p012_setting setting;
+
+ 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;
+
+ if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+ setting = RES_PREVIEW;
+ else
+ setting = RES_CAPTURE;
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ (mt9p012_regs.reg_pat[setting].frame_length_lines *
+ fps->fps_div / 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_PREVIEW_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_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);
+
+ 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},
+ };
+ if (update_type == REG_INIT) {
+ update_type = rupdate;
+ return rc;
+ }
+ 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;
+ }
+ update_type = rupdate;
+ 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)
+{
+ int32_t rc;
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ uint8_t code_val;
+ uint8_t time_out;
+ uint8_t temp_pos;
+
+ uint16_t actual_position_target;
+ 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 = -1;
+ else if (direction == MOVE_FAR)
+ step_direction = 1;
+ 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 > MT9P012_TOTAL_STEPS_NEAR_TO_FAR)
+ next_position = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (next_position < 0)
+ next_position = 0;
+
+ if (num_steps >= 10)
+ time_out = 100;
+ else
+ time_out = 30;
+ code_val = next_position;
+ actual_position_target = bam_step_lookup_table[code_val];
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+ if (rc < 0)
+ return rc;
+ temp_pos = (uint8_t) (actual_position_target >> 8);
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, temp_pos);
+ if (rc < 0)
+ return rc;
+ temp_pos = (uint8_t) (actual_position_target & 0x00FF);
+ /* code_val_lsb |= mode_mask; */
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, temp_pos);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, time_out);
+ if (rc < 0)
+ return rc;
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+ if (rc < 0)
+ return rc;
+
+ mdelay(time_out);
+
+ /* Storing the current lens Position */
+ mt9p012_ctrl->curr_lens_pos = next_position;
+
+ return rc;
+}
+
+static int32_t mt9p012_set_default_focus(void)
+{
+ int32_t rc = 0;
+
+ uint8_t temp_pos;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+ if (rc < 0)
+ return rc;
+ temp_pos = (uint8_t) (bam_infinite >> 8);
+
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, temp_pos);
+ if (rc < 0)
+ return rc;
+ temp_pos = (uint8_t) (bam_infinite & 0x00FF);
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, temp_pos);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+ if (rc < 0)
+ return rc;
+
+ mdelay(140);
+
+ mt9p012_ctrl->curr_lens_pos = MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+
+ 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;
+
+ msleep(20);
+
+ /* RESET the sensor image part via I2C command */
+ 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;
+ }
+
+ msleep(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) {
+ 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 */
+ 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;
+ }
+
+ 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;
+ unsigned short temp_pos;
+ uint8_t i;
+ uint16_t temp;
+
+ 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;
+
+ 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 */
+ 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;
+ }
+
+ /* enable AF actuator */
+ 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);
+
+ bam_infinite = 0;
+ bam_macro = 0;
+ /*initialize AF actuator */
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x09);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x2E);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0A, 0x01);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x17, 0x06);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x16, 0x0A);
+
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, 0x00);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, 0x00);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+ mdelay(140);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x01, 0x29);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x05, 0x03);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x06, 0xFF);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x0B, 0x64);
+ mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, 0x07, 0x27);
+ mdelay(140);
+
+ if (mt9p012_i2c_read_b(MT9P012_AF_I2C_ADDR >> 1, 0x12, &temp_pos)
+ >= 0) {
+ bam_infinite = (uint16_t) temp_pos;
+ if (mt9p012_i2c_read_b
+ (MT9P012_AF_I2C_ADDR >> 1, 0x13, &temp_pos) >= 0)
+ bam_infinite =
+ (bam_infinite << 8) | ((uint16_t) temp_pos);
+ } else {
+ bam_infinite = 100;
+ }
+
+ if (mt9p012_i2c_read_b(MT9P012_AF_I2C_ADDR >> 1, 0x14, &temp_pos)
+ >= 0) {
+ bam_macro = (uint16_t) temp_pos;
+ if (mt9p012_i2c_read_b
+ (MT9P012_AF_I2C_ADDR >> 1, 0x15, &temp_pos) >= 0)
+ bam_macro = (bam_macro << 8) | ((uint16_t) temp_pos);
+ }
+ temp = (bam_infinite - bam_macro) / MT9P012_TOTAL_STEPS_NEAR_TO_FAR;
+ for (i = 0; i < MT9P012_TOTAL_STEPS_NEAR_TO_FAR; i++)
+ bam_step_lookup_table[i] = bam_macro + temp * i;
+
+ bam_step_lookup_table[MT9P012_TOTAL_STEPS_NEAR_TO_FAR] = bam_infinite;
+
+ rc = mt9p012_set_default_focus();
+ if (rc >= 0)
+ goto init_done;
+
+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;
+
+ mutex_lock(&mt9p012_mut);
+
+ 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: dir=%d 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_EFFECT:
+ 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;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&mt9p012_mut);
+ return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&mt9p012_mut);
+
+ 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");
+
+ mutex_unlock(&mt9p012_mut);
+ 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 int __exit mt9p012_remove(struct i2c_client *client)
+{
+ struct mt9p012_work_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ mt9p012_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+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;
+ s->s_mount_angle = 0;
+ 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);
+void mt9p012_exit(void)
+{
+ i2c_del_driver(&mt9p012_i2c_driver);
+}
diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c
new file mode 100644
index 0000000..4a732f3
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_fox.c
@@ -0,0 +1,1345 @@
+/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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 uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_ctrl *mt9p012_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue);
+DEFINE_MUTEX(mt9p012_mut);
+
+
+/*=============================================================*/
+
+static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ int retry_cnt = 0;
+ int rc;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ do {
+ rc = i2c_transfer(mt9p012_client->adapter, msgs, 2);
+ if (rc > 0)
+ break;
+ retry_cnt++;
+ } while (retry_cnt < 3);
+
+ if (rc < 0) {
+ pr_err("mt9p012_i2c_rxdata failed!:%d %d\n", rc, retry_cnt);
+ 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)
+{
+ int retry_cnt = 0;
+ int rc;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ do {
+ rc = i2c_transfer(mt9p012_client->adapter, msg, 1);
+ if (rc > 0)
+ break;
+ retry_cnt++;
+ } while (retry_cnt < 3);
+
+ if (rc < 0) {
+ pr_err("mt9p012_i2c_txdata failed: %d %d\n", rc, retry_cnt);
+ 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 const
+ *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.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 */
+ uint32_t d1;
+ uint32_t d2;
+
+ d1 =
+ (uint32_t)(
+ (mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ 0x00000400) /
+ mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+ d2 =
+ (uint32_t)(
+ (mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck *
+ 0x00000400) /
+ mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+ divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+ pclk_mult =
+ (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00000400) /
+ (mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+ /* 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;
+ enum mt9p012_setting setting;
+
+ 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;
+
+ if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+ setting = RES_PREVIEW;
+ else
+ setting = RES_CAPTURE;
+
+ rc = mt9p012_i2c_write_w(mt9p012_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ (mt9p012_regs.reg_pat[setting].frame_length_lines *
+ fps->fps_div / 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_PREVIEW_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_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);
+
+ 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},
+ };
+ if (update_type == REG_INIT) {
+ update_type = rupdate;
+ return rc;
+ }
+ 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;
+ }
+ update_type = rupdate;
+ 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;
+
+ msleep(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;
+ }
+
+ msleep(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;
+ }
+ 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;
+
+ 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;
+ }
+
+ /* enable AF actuator */
+ if (mt9p012_ctrl->sensordata->vcm_enable) {
+ 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;
+ }
+ msleep(20);
+ rc = mt9p012_set_default_focus();
+ if (rc < 0) {
+ gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd,
+ 0);
+ gpio_free(mt9p012_ctrl->sensordata->vcm_pwd);
+ }
+ }
+ if (rc >= 0)
+ goto init_done;
+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;
+
+ mutex_lock(&mt9p012_mut);
+
+ 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;
+ }
+
+ mutex_unlock(&mt9p012_mut);
+ return rc;
+}
+
+int mt9p012_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&mt9p012_mut);
+
+ mt9p012_power_down();
+
+ gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, 0);
+ gpio_free(mt9p012_ctrl->sensordata->sensor_reset);
+
+ if (mt9p012_ctrl->sensordata->vcm_enable) {
+ 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");
+
+ mutex_unlock(&mt9p012_mut);
+ 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;
+ s->s_mount_angle = 0;
+ 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/media/video/msm/mt9p012_km.c b/drivers/media/video/msm/mt9p012_km.c
new file mode 100644
index 0000000..c20064c
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km.c
@@ -0,0 +1,1295 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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_km.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define MT9P012_KM_REG_MODEL_ID 0x0000
+#define MT9P012_KM_MODEL_ID 0x2800
+#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_KM_REG_RESET_REGISTER 0x301A
+#define MT9P012_KM_RESET_REGISTER_PWON 0x10CC
+#define MT9P012_KM_RESET_REGISTER_PWOFF 0x10C8
+#define REG_READ_MODE 0x3040
+#define REG_GLOBAL_GAIN 0x305E
+#define REG_TEST_PATTERN_MODE 0x3070
+
+enum mt9p012_km_test_mode {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum mt9p012_km_resolution {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum mt9p012_km_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_km_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+uint8_t mode_mask = 0x04;
+
+/* actuator's Slave Address */
+#define MT9P012_KM_AF_I2C_ADDR (0x18 >> 1)
+
+/* AF Total steps parameters */
+#define MT9P012_KM_STEPS_NEAR_TO_CLOSEST_INF 30
+#define MT9P012_KM_TOTAL_STEPS_NEAR_TO_FAR 30
+
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define MT9P012_KM_RESET_DELAY_MSECS 66
+
+/* for 20 fps preview */
+#define MT9P012_KM_DEFAULT_CLOCK_RATE 24000000
+
+struct mt9p012_km_work {
+ struct work_struct work;
+};
+static struct mt9p012_km_work *mt9p012_km_sensorw;
+static struct i2c_client *mt9p012_km_client;
+
+struct mt9p012_km_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_km_resolution prev_res;
+ enum mt9p012_km_resolution pict_res;
+ enum mt9p012_km_resolution curr_res;
+ enum mt9p012_km_test_mode set_test;
+};
+static uint16_t update_type = UPDATE_PERIODIC;
+static struct mt9p012_km_ctrl *mt9p012_km_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(mt9p012_km_wait_queue);
+DEFINE_MUTEX(mt9p012_km_mut);
+
+/*=============================================================*/
+
+static int mt9p012_km_i2c_rxdata(unsigned short saddr, unsigned char *rxdata,
+ int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr << 1,
+ .flags = 0,
+ .len = 2,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr << 1,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_km_client->adapter, msgs, 2) < 0) {
+ CDBG("mt9p012_km_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9p012_km_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_km_i2c_rxdata(saddr, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ *rdata = buf[0] << 8 | buf[1];
+
+ if (rc < 0)
+ CDBG("mt9p012_km_i2c_read failed!\n");
+
+ return rc;
+}
+
+static int32_t mt9p012_km_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata,
+ int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr << 1,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(mt9p012_km_client->adapter, msg, 1) < 0) {
+ CDBG("mt9p012_km_i2c_txdata failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t mt9p012_km_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_km_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_km_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_km_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_km_i2c_write_w_table(struct mt9p012_km_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+
+ for (i = 0; i < num; i++) {
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+
+ return rc;
+}
+
+static int32_t mt9p012_km_test(enum mt9p012_km_test_mode mo)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ if (mo == TEST_OFF)
+ return 0;
+ else {
+ rc = mt9p012_km_i2c_write_w_table(mt9p012_km_regs.ttbl,
+ mt9p012_km_regs.ttbl_size);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_TEST_PATTERN_MODE, (uint16_t) mo);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t mt9p012_km_lens_shading_enable(uint8_t is_enable)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: entered. enable = %d\n", __func__, is_enable);
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x3780,
+ ((uint16_t) is_enable) << 15);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ CDBG("%s: exiting. rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int32_t mt9p012_km_set_lc(void)
+{
+ int32_t rc;
+
+ rc = mt9p012_km_i2c_write_w_table(mt9p012_km_regs.lctbl,
+ mt9p012_km_regs.lctbl_size);
+
+ return rc;
+}
+
+static void mt9p012_km_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 */
+ uint32_t d1;
+ uint32_t d2;
+
+ d1 =
+ (uint32_t)(
+ (mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ 0x00000400) /
+ mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+ d2 =
+ (uint32_t)(
+ (mt9p012_km_regs.reg_pat[RES_PREVIEW].line_length_pck *
+ 0x00000400) /
+ mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+ divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+ pclk_mult =
+ (uint32_t) ((mt9p012_km_regs.reg_pat[RES_CAPTURE].
+ pll_multiplier * 0x00000400) /
+ (mt9p012_km_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t)((((fps * pclk_mult) / 0x00000400) * divider)/
+ 0x00000400);
+}
+
+static uint16_t mt9p012_km_get_prev_lines_pf(void)
+{
+ if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines;
+ else
+ return mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_km_get_prev_pixels_pl(void)
+{
+ if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+ return mt9p012_km_regs.reg_pat[RES_PREVIEW].line_length_pck;
+ else
+ return mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint16_t mt9p012_km_get_pict_lines_pf(void)
+{
+ return mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines;
+}
+
+static uint16_t mt9p012_km_get_pict_pixels_pl(void)
+{
+ return mt9p012_km_regs.reg_pat[RES_CAPTURE].line_length_pck;
+}
+
+static uint32_t mt9p012_km_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (mt9p012_km_ctrl->pict_res == QTR_SIZE)
+ snapshot_lines_per_frame =
+ mt9p012_km_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1;
+ else
+ snapshot_lines_per_frame =
+ mt9p012_km_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1;
+
+ return snapshot_lines_per_frame * 24;
+}
+
+static int32_t mt9p012_km_set_fps(struct fps_cfg *fps)
+{
+ int32_t rc = 0;
+
+ mt9p012_km_ctrl->fps_divider = fps->fps_div;
+ mt9p012_km_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return -EBUSY;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ mt9p012_km_regs.reg_pat[mt9p012_km_ctrl->sensormode].
+ frame_length_lines *
+ mt9p012_km_ctrl->fps_divider / 0x00000400);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ return rc;
+}
+
+
+static int32_t mt9p012_km_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0x01FF;
+ uint32_t line_length_ratio = 0x00000400;
+ enum mt9p012_km_setting setting;
+ int32_t rc = 0;
+
+ CDBG("Line:%d mt9p012_km_write_exp_gain \n", __LINE__);
+
+ if (mt9p012_km_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ mt9p012_km_ctrl->my_reg_gain = gain;
+ mt9p012_km_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_km_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ line = (uint32_t) (line * mt9p012_km_ctrl->fps_divider /
+ 0x00000400);
+ setting = RES_PREVIEW;
+ } else {
+ line = (uint32_t) (line * mt9p012_km_ctrl->pict_fps_divider /
+ 0x00000400);
+ setting = RES_CAPTURE;
+ }
+
+ gain |= 0x0200;
+
+ if ((mt9p012_km_regs.reg_pat[setting].frame_length_lines - 1) < line) {
+ line_length_ratio = (uint32_t) (line * 0x00000400) /
+ (mt9p012_km_regs.reg_pat[setting].frame_length_lines - 1);
+ } else
+ line_length_ratio = 0x00000400;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0) {
+ CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GLOBAL_GAIN, gain);
+ if (rc < 0) {
+ CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_LINE_LENGTH_PCK,
+ (uint16_t) (mt9p012_km_regs.reg_pat[setting].
+ line_length_pck * line_length_ratio / 0x00000400));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_COARSE_INT_TIME,
+ (uint16_t) ((line * 0x00000400)/
+ line_length_ratio));
+ if (rc < 0) {
+ CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+ if (rc < 0) {
+ CDBG("mt9p012_km_i2c_write_w failed... Line:%d \n", __LINE__);
+ return rc;
+ }
+
+ CDBG("mt9p012_km_write_exp_gain: gain = %d, line = %d\n", gain, line);
+
+ return rc;
+}
+
+static int32_t mt9p012_km_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+
+ CDBG("Line:%d mt9p012_km_set_pict_exp_gain \n", __LINE__);
+
+ rc = mt9p012_km_write_exp_gain(gain, line);
+ if (rc < 0) {
+ CDBG("Line:%d mt9p012_km_set_pict_exp_gain failed... \n",
+ __LINE__);
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ 0x10CC | 0x0002);
+ if (rc < 0) {
+ CDBG("mt9p012_km_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_km_setting(enum mt9p012_km_reg_update rupdate,
+ enum mt9p012_km_setting rt)
+{
+ int32_t rc = 0;
+
+ switch (rupdate) {
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+
+ struct mt9p012_km_i2c_reg_conf ppc_tbl[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ {REG_ROW_SPEED,
+ mt9p012_km_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START,
+ mt9p012_km_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END,
+ mt9p012_km_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START,
+ mt9p012_km_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END,
+ mt9p012_km_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE,
+ mt9p012_km_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M,
+ mt9p012_km_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE,
+ mt9p012_km_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE,
+ mt9p012_km_regs.reg_pat[rt].y_output_size},
+ {REG_LINE_LENGTH_PCK,
+ mt9p012_km_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ (mt9p012_km_regs.reg_pat[rt].frame_length_lines *
+ mt9p012_km_ctrl->fps_divider / 0x00000400)},
+ {REG_COARSE_INT_TIME,
+ mt9p012_km_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME,
+ mt9p012_km_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE},
+ };
+
+ if (update_type == REG_INIT) {
+ update_type = rupdate;
+ return rc;
+ }
+
+ rc = mt9p012_km_i2c_write_w_table(&ppc_tbl[0],
+ ARRAY_SIZE(ppc_tbl));
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_test(mt9p012_km_ctrl->set_test);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ 0x10cc |
+ 0x0002);
+ if (rc < 0)
+ return rc;
+
+ mdelay(15); /* 15? wait for sensor to transition */
+
+ return rc;
+ }
+ break; /* UPDATE_PERIODIC */
+
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct mt9p012_km_i2c_reg_conf ipc_tbl1[] = {
+ {MT9P012_KM_REG_RESET_REGISTER,
+ MT9P012_KM_RESET_REGISTER_PWOFF},
+ {REG_VT_PIX_CLK_DIV,
+ mt9p012_km_regs.reg_pat[rt].vt_pix_clk_div},
+ {REG_VT_SYS_CLK_DIV,
+ mt9p012_km_regs.reg_pat[rt].vt_sys_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ mt9p012_km_regs.reg_pat[rt].pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ mt9p012_km_regs.reg_pat[rt].pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ mt9p012_km_regs.reg_pat[rt].op_pix_clk_div},
+ {REG_OP_SYS_CLK_DIV,
+ mt9p012_km_regs.reg_pat[rt].op_sys_clk_div},
+ {MT9P012_KM_REG_RESET_REGISTER,
+ MT9P012_KM_RESET_REGISTER_PWON},
+ };
+
+ struct mt9p012_km_i2c_reg_conf ipc_tbl2[] = {
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD},
+ /* Optimized register settings for
+ Rev3 Silicon */
+ {0x308A, 0x6424},
+ {0x3092, 0x0A52},
+ {0x3094, 0x4656},
+ {0x3096, 0x5652},
+ {0x0204, 0x0010},
+ {0x0206, 0x0010},
+ {0x0208, 0x0010},
+ {0x020A, 0x0010},
+ {0x020C, 0x0010},
+ {0x3088, 0x6FF6},
+ {0x3154, 0x0282},
+ {0x3156, 0x0381},
+ {0x3162, 0x04CE},
+ };
+
+ struct mt9p012_km_i2c_reg_conf ipc_tbl3[] = {
+ /* Set preview or snapshot mode */
+ {REG_ROW_SPEED,
+ mt9p012_km_regs.reg_pat[rt].row_speed},
+ {REG_X_ADDR_START,
+ mt9p012_km_regs.reg_pat[rt].x_addr_start},
+ {REG_X_ADDR_END,
+ mt9p012_km_regs.reg_pat[rt].x_addr_end},
+ {REG_Y_ADDR_START,
+ mt9p012_km_regs.reg_pat[rt].y_addr_start},
+ {REG_Y_ADDR_END,
+ mt9p012_km_regs.reg_pat[rt].y_addr_end},
+ {REG_READ_MODE,
+ mt9p012_km_regs.reg_pat[rt].read_mode},
+ {REG_SCALE_M,
+ mt9p012_km_regs.reg_pat[rt].scale_m},
+ {REG_X_OUTPUT_SIZE,
+ mt9p012_km_regs.reg_pat[rt].x_output_size},
+ {REG_Y_OUTPUT_SIZE,
+ mt9p012_km_regs.reg_pat[rt].y_output_size},
+ {REG_LINE_LENGTH_PCK,
+ mt9p012_km_regs.reg_pat[rt].line_length_pck},
+ {REG_FRAME_LENGTH_LINES,
+ mt9p012_km_regs.reg_pat[rt].
+ frame_length_lines},
+ {REG_COARSE_INT_TIME,
+ mt9p012_km_regs.reg_pat[rt].coarse_int_time},
+ {REG_FINE_INTEGRATION_TIME,
+ mt9p012_km_regs.reg_pat[rt].fine_int_time},
+ {REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE},
+ };
+
+ /* reset fps_divider */
+ mt9p012_km_ctrl->fps_divider = 1 * 0x0400;
+
+ rc = mt9p012_km_i2c_write_w_table(&ipc_tbl1[0],
+ ARRAY_SIZE(ipc_tbl1));
+ if (rc < 0)
+ return rc;
+
+ mdelay(15);
+
+ rc = mt9p012_km_i2c_write_w_table(&ipc_tbl2[0],
+ ARRAY_SIZE(ipc_tbl2));
+ if (rc < 0)
+ return rc;
+
+ mdelay(5);
+
+ rc = mt9p012_km_i2c_write_w_table(&ipc_tbl3[0],
+ ARRAY_SIZE(ipc_tbl3));
+ if (rc < 0)
+ return rc;
+
+ /* load lens shading */
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_set_lc();
+ if (rc < 0)
+ return rc;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_UPDATE);
+
+ if (rc < 0)
+ return rc;
+ }
+ update_type = rupdate;
+ break; /* case REG_INIT: */
+
+ default:
+ rc = -EINVAL;
+ break;
+ } /* switch (rupdate) */
+
+ return rc;
+}
+
+static int32_t mt9p012_km_video_config(int mode, int res)
+{
+ int32_t rc;
+
+ switch (res) {
+ case QTR_SIZE:
+ rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+
+ CDBG("mt9p012_km sensor configuration done!\n");
+ break;
+
+ case FULL_SIZE:
+ rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ break;
+
+ default:
+ return 0;
+ } /* switch */
+
+ mt9p012_km_ctrl->prev_res = res;
+ mt9p012_km_ctrl->curr_res = res;
+ mt9p012_km_ctrl->sensormode = mode;
+
+ rc = mt9p012_km_write_exp_gain(mt9p012_km_ctrl->my_reg_gain,
+ mt9p012_km_ctrl->my_reg_line_count);
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ 0x10cc | 0x0002);
+
+ mdelay(15);
+ return rc;
+}
+
+static int32_t mt9p012_km_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_km_ctrl->curr_res = mt9p012_km_ctrl->pict_res;
+
+ mt9p012_km_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t mt9p012_km_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_km_setting(UPDATE_PERIODIC, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ mt9p012_km_ctrl->curr_res = mt9p012_km_ctrl->pict_res;
+
+ mt9p012_km_ctrl->sensormode = mode;
+
+ return rc;
+}
+
+static int32_t mt9p012_km_power_down(void)
+{
+ int32_t rc = 0;
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ MT9P012_KM_RESET_REGISTER_PWOFF);
+
+ mdelay(5);
+ return rc;
+}
+
+static int32_t mt9p012_km_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_KM_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = MT9P012_KM_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (num_steps == 0) {
+ CDBG("mt9p012_km_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_km_move_focus failed at line %d ...\n", __LINE__);
+ return -EINVAL;
+ }
+
+ if (mt9p012_km_ctrl->curr_lens_pos <
+ mt9p012_km_ctrl->init_curr_lens_pos)
+ mt9p012_km_ctrl->curr_lens_pos =
+ mt9p012_km_ctrl->init_curr_lens_pos;
+
+ actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+ next_position = (int16_t) (mt9p012_km_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_km_i2c_write_b(MT9P012_KM_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb) < 0) {
+ CDBG("mt9p012_km_move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+
+ /* Storing the current lens Position */
+ mt9p012_km_ctrl->curr_lens_pos = next_position;
+
+ return 0;
+}
+
+static int32_t mt9p012_km_set_default_focus(void)
+{
+ int32_t rc = 0;
+ uint8_t code_val_msb, code_val_lsb;
+
+ code_val_msb = 0x00;
+ code_val_lsb = 0x04;
+
+ /* Write the digital code for current to the actuator */
+ rc = mt9p012_km_i2c_write_b(MT9P012_KM_AF_I2C_ADDR >> 1,
+ code_val_msb, code_val_lsb);
+
+ mt9p012_km_ctrl->curr_lens_pos = 0;
+ mt9p012_km_ctrl->init_curr_lens_pos = 0;
+
+ return rc;
+}
+
+static int mt9p012_km_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_km_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+ uint16_t chipid;
+
+ rc = gpio_request(data->sensor_reset, "mt9p012_km");
+ if (!rc)
+ gpio_direction_output(data->sensor_reset, 1);
+ else
+ goto init_probe_done;
+
+ msleep(20);
+
+ /* RESET the sensor image part via I2C command */
+ CDBG("mt9p012_km_sensor_init(): reseting sensor.\n");
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ 0x10CC | 0x0001);
+ if (rc < 0) {
+ CDBG("sensor reset failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ msleep(MT9P012_KM_RESET_DELAY_MSECS);
+
+ /* 3. Read sensor Model ID: */
+ rc = mt9p012_km_i2c_read_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_MODEL_ID, &chipid);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ /* 4. Compare sensor ID to MT9T012VC ID: */
+ if (chipid != MT9P012_KM_MODEL_ID) {
+ CDBG("mt9p012_km wrong model_id = 0x%x\n", chipid);
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr, 0x306E, 0x9080);
+ 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_km_sensor_init(): enabling parallel interface.\n");
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_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_km_i2c_write_w(mt9p012_km_client->addr, 0x3064, 0x0805);
+
+ if (rc < 0) {
+ CDBG("disable the 2 extra lines failed. rc = %d\n", rc);
+ goto init_probe_fail;
+ }
+
+ goto init_probe_done;
+
+init_probe_fail:
+ mt9p012_km_probe_init_done(data);
+init_probe_done:
+ return rc;
+}
+
+static int
+ mt9p012_km_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+
+ mt9p012_km_ctrl = kzalloc(sizeof(struct mt9p012_km_ctrl), GFP_KERNEL);
+ if (!mt9p012_km_ctrl) {
+ CDBG("mt9p012_km_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ mt9p012_km_ctrl->fps_divider = 1 * 0x00000400;
+ mt9p012_km_ctrl->pict_fps_divider = 1 * 0x00000400;
+ mt9p012_km_ctrl->set_test = TEST_OFF;
+ mt9p012_km_ctrl->prev_res = QTR_SIZE;
+ mt9p012_km_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ mt9p012_km_ctrl->sensordata = data;
+
+ msm_camio_camif_pad_reg_reset();
+ mdelay(20);
+
+ rc = mt9p012_km_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail1;
+
+ if (mt9p012_km_ctrl->prev_res == QTR_SIZE)
+ rc = mt9p012_km_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = mt9p012_km_setting(REG_INIT, RES_CAPTURE);
+
+ if (rc < 0) {
+ CDBG("mt9p012_km_setting failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ /* sensor : output enable */
+ CDBG("mt9p012_km_sensor_open_init(): enabling output.\n");
+ rc = mt9p012_km_i2c_write_w(mt9p012_km_client->addr,
+ MT9P012_KM_REG_RESET_REGISTER,
+ MT9P012_KM_RESET_REGISTER_PWON);
+ if (rc < 0) {
+ CDBG("sensor output enable failed. rc = %d\n", rc);
+ goto init_fail1;
+ }
+
+ if (rc >= 0)
+ goto init_done;
+
+init_fail1:
+ mt9p012_km_probe_init_done(data);
+ kfree(mt9p012_km_ctrl);
+init_done:
+ return rc;
+}
+
+static int mt9p012_km_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&mt9p012_km_wait_queue);
+ return 0;
+}
+
+static int32_t mt9p012_km_set_sensor_mode(int mode, int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = mt9p012_km_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = mt9p012_km_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = mt9p012_km_raw_snapshot_config(mode);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int mt9p012_km_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;
+
+ mutex_lock(&mt9p012_km_mut);
+
+ CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ mt9p012_km_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_km_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_km_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_km_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_km_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_km_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_km_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = mt9p012_km_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_km_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = mt9p012_km_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = mt9p012_km_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ CDBG("mt9p012_km_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_km_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = mt9p012_km_set_default_focus();
+ break;
+
+ case CFG_SET_LENS_SHADING:
+ CDBG("%s: CFG_SET_LENS_SHADING\n", __func__);
+ rc = mt9p012_km_lens_shading_enable(cdata.cfg.lens_shading);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = MT9P012_KM_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;
+ }
+
+ mutex_unlock(&mt9p012_km_mut);
+ return rc;
+}
+
+int mt9p012_km_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&mt9p012_km_mut);
+
+ mt9p012_km_power_down();
+
+ gpio_direction_output(mt9p012_km_ctrl->sensordata->sensor_reset, 0);
+ gpio_free(mt9p012_km_ctrl->sensordata->sensor_reset);
+
+ gpio_direction_output(mt9p012_km_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(mt9p012_km_ctrl->sensordata->vcm_pwd);
+
+ kfree(mt9p012_km_ctrl);
+ mt9p012_km_ctrl = NULL;
+
+ CDBG("mt9p012_km_release completed\n");
+
+ mutex_unlock(&mt9p012_km_mut);
+ return rc;
+}
+
+static int mt9p012_km_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("mt9p012_km_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ mt9p012_km_sensorw = kzalloc(sizeof(struct mt9p012_km_work),
+ GFP_KERNEL);
+ if (!mt9p012_km_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, mt9p012_km_sensorw);
+ mt9p012_km_init_client(client);
+ mt9p012_km_client = client;
+
+ mdelay(50);
+
+ CDBG("mt9p012_km_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("mt9p012_km_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id mt9p012_km_i2c_id[] = {
+ {"mt9p012_km", 0},
+ {}
+};
+
+static struct i2c_driver mt9p012_km_i2c_driver = {
+ .id_table = mt9p012_km_i2c_id,
+ .probe = mt9p012_km_i2c_probe,
+ .remove = __exit_p(mt9p012_km_i2c_remove),
+ .driver = {
+ .name = "mt9p012_km",
+ },
+};
+
+static int mt9p012_km_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&mt9p012_km_i2c_driver);
+ if (rc < 0 || mt9p012_km_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ msm_camio_clk_rate_set(MT9P012_KM_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = mt9p012_km_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = mt9p012_km_sensor_open_init;
+ s->s_release = mt9p012_km_sensor_release;
+ s->s_config = mt9p012_km_sensor_config;
+ s->s_mount_angle = 0;
+ mt9p012_km_probe_init_done(info);
+
+probe_done:
+ CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
+ return rc;
+}
+
+static int __mt9p012_km_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, mt9p012_km_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __mt9p012_km_probe,
+ .driver = {
+ .name = "msm_camera_mt9p012_km",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mt9p012_km_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(mt9p012_km_init);
diff --git a/drivers/media/video/msm/mt9p012_km.h b/drivers/media/video/msm/mt9p012_km.h
new file mode 100644
index 0000000..aefabd4
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9P012_KM_H
+#define MT9P012_KM_H
+
+#include <linux/types.h>
+
+extern struct mt9p012_km_reg mt9p012_km_regs; /* from mt9p012_km_reg.c */
+
+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_km_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+
+struct mt9p012_km_reg {
+ struct reg_struct const *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9p012_km_i2c_reg_conf const *ttbl;
+ uint16_t ttbl_size;
+ struct mt9p012_km_i2c_reg_conf const *lctbl;
+ uint16_t lctbl_size;
+ struct mt9p012_km_i2c_reg_conf const *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* MT9P012_KM_H */
diff --git a/drivers/media/video/msm/mt9p012_km_reg.c b/drivers/media/video/msm/mt9p012_km_reg.c
new file mode 100644
index 0000000..109930b
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_km_reg.c
@@ -0,0 +1,375 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "mt9p012_km.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct const mt9p012_km_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 */
+ 0x006C,
+
+ /* x_output_size REG=0x034C */
+ 1296,
+
+ /* y_output_size REG=0x034E */
+ 972,
+
+ /* line_length_pck REG=0x300C */
+ 3783,
+
+ /* frame_length_lines REG=0x300A */
+ 1074,
+
+ /* 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
+ * 39 for 10fps snapshot */
+ 39,
+
+ /* 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 */
+ 0x0024,
+
+ /* x_output_size REG=0x034C */
+ 2608,
+
+ /* y_output_size REG=0x034E */
+ 1960,
+
+ /* line_length_pck REG=0x300C */
+ 3788,
+
+ /* frame_length_lines REG=0x300A 10 fps snapshot */
+ 2045,
+
+ /* coarse_integration_time REG=0x3012 */
+ 16,
+
+ /* fine_integration_time REG=0x3014 */
+ 882
+ }
+};
+
+struct mt9p012_km_i2c_reg_conf const mt9p012_km_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_km_i2c_reg_conf const mt9p012_km_lc_tbl[] = {
+ {0x360A, 0x00F0},
+ {0x360C, 0x0B29},
+ {0x360E, 0x5ED1},
+ {0x3610, 0x890D},
+ {0x3612, 0x9871},
+ {0x364A, 0xAD2C},
+ {0x364C, 0x0A8C},
+ {0x364E, 0x91EC},
+ {0x3650, 0x94EC},
+ {0x3652, 0xC76B},
+ {0x368A, 0x5931},
+ {0x368C, 0x4FED},
+ {0x368E, 0x8A50},
+ {0x3690, 0x5C0F},
+ {0x3692, 0x8393},
+ {0x36CA, 0xDB8E},
+ {0x36CC, 0xCA4D},
+ {0x36CE, 0x146F},
+ {0x36D0, 0x618F},
+ {0x36D2, 0x014F},
+ {0x370A, 0x1FEE},
+ {0x370C, 0xDD50},
+ {0x370E, 0xDB54},
+ {0x3710, 0xCA92},
+ {0x3712, 0x1896},
+ {0x3600, 0x00F0},
+ {0x3602, 0xA04C},
+ {0x3604, 0x5711},
+ {0x3606, 0x5E6D},
+ {0x3608, 0xA971},
+ {0x3640, 0xDCCC},
+ {0x3642, 0x0529},
+ {0x3644, 0x96ED},
+ {0x3646, 0xF447},
+ {0x3648, 0x4AEE},
+ {0x3680, 0x2171},
+ {0x3682, 0x634F},
+ {0x3684, 0xCC91},
+ {0x3686, 0xA9CE},
+ {0x3688, 0x8751},
+ {0x36C0, 0x8B6D},
+ {0x36C2, 0xE20E},
+ {0x36C4, 0x750F},
+ {0x36C6, 0x0090},
+ {0x36C8, 0x9E91},
+ {0x3700, 0xEAAF},
+ {0x3702, 0xB8AF},
+ {0x3704, 0xE293},
+ {0x3706, 0xAB33},
+ {0x3708, 0x4595},
+ {0x3614, 0x00D0},
+ {0x3616, 0x8AAB},
+ {0x3618, 0x18B1},
+ {0x361A, 0x54AD},
+ {0x361C, 0x9DB0},
+ {0x3654, 0x11EB},
+ {0x3656, 0x332C},
+ {0x3658, 0x316D},
+ {0x365A, 0xF0EB},
+ {0x365C, 0xB4ED},
+ {0x3694, 0x0F31},
+ {0x3696, 0x08D0},
+ {0x3698, 0xA52F},
+ {0x369A, 0xE64F},
+ {0x369C, 0xC9D2},
+ {0x36D4, 0x8C2D},
+ {0x36D6, 0xAD6E},
+ {0x36D8, 0xE1CE},
+ {0x36DA, 0x1750},
+ {0x36DC, 0x8CAD},
+ {0x3714, 0x8CAF},
+ {0x3716, 0x8C11},
+ {0x3718, 0xE453},
+ {0x371A, 0x9693},
+ {0x371C, 0x38B5},
+ {0x361E, 0x00D0},
+ {0x3620, 0xB6CB},
+ {0x3622, 0x4811},
+ {0x3624, 0xB70C},
+ {0x3626, 0xA771},
+ {0x365E, 0xB5A9},
+ {0x3660, 0x05AA},
+ {0x3662, 0x00CF},
+ {0x3664, 0xB86B},
+ {0x3666, 0xA4AF},
+ {0x369E, 0x3E31},
+ {0x36A0, 0x902B},
+ {0x36A2, 0xD251},
+ {0x36A4, 0x5C2F},
+ {0x36A6, 0x8471},
+ {0x36DE, 0x2C6D},
+ {0x36E0, 0xECEE},
+ {0x36E2, 0xB650},
+ {0x36E4, 0x0210},
+ {0x36E6, 0xACAE},
+ {0x371E, 0xAC30},
+ {0x3720, 0x394E},
+ {0x3722, 0xFDD3},
+ {0x3724, 0xBCB2},
+ {0x3726, 0x5AD5},
+ {0x3782, 0x0508},
+ {0x3784, 0x03B4},
+ {0x3780, 0x8000},
+};
+
+struct mt9p012_km_i2c_reg_conf const mt9p012_km_rolloff_tbl[] = {
+ {0x360A, 0x00F0},
+ {0x360C, 0x0B29},
+ {0x360E, 0x5ED1},
+ {0x3610, 0x890D},
+ {0x3612, 0x9871},
+ {0x364A, 0xAD2C},
+ {0x364C, 0x0A8C},
+ {0x364E, 0x91EC},
+ {0x3650, 0x94EC},
+ {0x3652, 0xC76B},
+ {0x368A, 0x5931},
+ {0x368C, 0x4FED},
+ {0x368E, 0x8A50},
+ {0x3690, 0x5C0F},
+ {0x3692, 0x8393},
+ {0x36CA, 0xDB8E},
+ {0x36CC, 0xCA4D},
+ {0x36CE, 0x146F},
+ {0x36D0, 0x618F},
+ {0x36D2, 0x014F},
+ {0x370A, 0x1FEE},
+ {0x370C, 0xDD50},
+ {0x370E, 0xDB54},
+ {0x3710, 0xCA92},
+ {0x3712, 0x1896},
+ {0x3600, 0x00F0},
+ {0x3602, 0xA04C},
+ {0x3604, 0x5711},
+ {0x3606, 0x5E6D},
+ {0x3608, 0xA971},
+ {0x3640, 0xDCCC},
+ {0x3642, 0x0529},
+ {0x3644, 0x96ED},
+ {0x3646, 0xF447},
+ {0x3648, 0x4AEE},
+ {0x3680, 0x2171},
+ {0x3682, 0x634F},
+ {0x3684, 0xCC91},
+ {0x3686, 0xA9CE},
+ {0x3688, 0x8751},
+ {0x36C0, 0x8B6D},
+ {0x36C2, 0xE20E},
+ {0x36C4, 0x750F},
+ {0x36C6, 0x0090},
+ {0x36C8, 0x9E91},
+ {0x3700, 0xEAAF},
+ {0x3702, 0xB8AF},
+ {0x3704, 0xE293},
+ {0x3706, 0xAB33},
+ {0x3708, 0x4595},
+ {0x3614, 0x00D0},
+ {0x3616, 0x8AAB},
+ {0x3618, 0x18B1},
+ {0x361A, 0x54AD},
+ {0x361C, 0x9DB0},
+ {0x3654, 0x11EB},
+ {0x3656, 0x332C},
+ {0x3658, 0x316D},
+ {0x365A, 0xF0EB},
+ {0x365C, 0xB4ED},
+ {0x3694, 0x0F31},
+ {0x3696, 0x08D0},
+ {0x3698, 0xA52F},
+ {0x369A, 0xE64F},
+ {0x369C, 0xC9D2},
+ {0x36D4, 0x8C2D},
+ {0x36D6, 0xAD6E},
+ {0x36D8, 0xE1CE},
+ {0x36DA, 0x1750},
+ {0x36DC, 0x8CAD},
+ {0x3714, 0x8CAF},
+ {0x3716, 0x8C11},
+ {0x3718, 0xE453},
+ {0x371A, 0x9693},
+ {0x371C, 0x38B5},
+ {0x361E, 0x00D0},
+ {0x3620, 0xB6CB},
+ {0x3622, 0x4811},
+ {0x3624, 0xB70C},
+ {0x3626, 0xA771},
+ {0x365E, 0xB5A9},
+ {0x3660, 0x05AA},
+ {0x3662, 0x00CF},
+ {0x3664, 0xB86B},
+ {0x3666, 0xA4AF},
+ {0x369E, 0x3E31},
+ {0x36A0, 0x902B},
+ {0x36A2, 0xD251},
+ {0x36A4, 0x5C2F},
+ {0x36A6, 0x8471},
+ {0x36DE, 0x2C6D},
+ {0x36E0, 0xECEE},
+ {0x36E2, 0xB650},
+ {0x36E4, 0x0210},
+ {0x36E6, 0xACAE},
+ {0x371E, 0xAC30},
+ {0x3720, 0x394E},
+ {0x3722, 0xFDD3},
+ {0x3724, 0xBCB2},
+ {0x3726, 0x5AD5},
+ {0x3782, 0x0508},
+ {0x3784, 0x03B4},
+ {0x3780, 0x8000},
+};
+
+
+struct mt9p012_km_reg mt9p012_km_regs = {
+ .reg_pat = &mt9p012_km_reg_pat[0],
+ .reg_pat_size = ARRAY_SIZE(mt9p012_km_reg_pat),
+ .ttbl = &mt9p012_km_test_tbl[0],
+ .ttbl_size = ARRAY_SIZE(mt9p012_km_test_tbl),
+ .lctbl = &mt9p012_km_lc_tbl[0],
+ .lctbl_size = ARRAY_SIZE(mt9p012_km_lc_tbl),
+ .rftbl = &mt9p012_km_rolloff_tbl[0],
+ .rftbl_size = ARRAY_SIZE(mt9p012_km_rolloff_tbl)
+};
+
+
diff --git a/drivers/media/video/msm/mt9p012_reg.c b/drivers/media/video/msm/mt9p012_reg.c
new file mode 100644
index 0000000..06fe419
--- /dev/null
+++ b/drivers/media/video/msm/mt9p012_reg.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "mt9p012.h"
+#include <linux/kernel.h>
+
+/*Micron settings from Applications for lower power consumption.*/
+struct reg_struct const 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 */
+ 3659,
+
+ /* frame_length_lines REG=0x300A */
+ 1074,
+
+ /* 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 const 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 const mt9p012_rolloff_tbl[] = {
+ {0x360A, 0x0110},
+ {0x360C, 0x270D},
+ {0x360E, 0x0071},
+ {0x3610, 0xA38D},
+ {0x3612, 0xA610},
+ {0x364A, 0x8F49},
+ {0x364C, 0x696A},
+ {0x364E, 0x0FCD},
+ {0x3650, 0x20ED},
+ {0x3652, 0x81ED},
+ {0x368A, 0x1031},
+ {0x368C, 0xBCAD},
+ {0x368E, 0x77AA},
+ {0x3690, 0xD10E},
+ {0x3692, 0xC133},
+ {0x36CA, 0x4F8D},
+ {0x36CC, 0xAC4D},
+ {0x36CE, 0xC8CE},
+ {0x36D0, 0x73AD},
+ {0x36D2, 0xC150},
+ {0x370A, 0xB590},
+ {0x370C, 0x9010},
+ {0x370E, 0xAC52},
+ {0x3710, 0x4D51},
+ {0x3712, 0x5670},
+ {0x3600, 0x00F0},
+ {0x3602, 0xCE4B},
+ {0x3604, 0x4270},
+ {0x3606, 0x8BC9},
+ {0x3608, 0xFA2F},
+ {0x3640, 0x9A09},
+ {0x3642, 0xB40C},
+ {0x3644, 0x4ECD},
+ {0x3646, 0x1BCC},
+ {0x3648, 0xD68E},
+ {0x3680, 0x1BF0},
+ {0x3682, 0xC94D},
+ {0x3684, 0x714F},
+ {0x3686, 0x1491},
+ {0x3688, 0xB8D3},
+ {0x36C0, 0x3E49},
+ {0x36C2, 0x7A6C},
+ {0x36C4, 0xEF2E},
+ {0x36C6, 0xE0EE},
+ {0x36C8, 0x570F},
+ {0x3700, 0xD6AF},
+ {0x3702, 0x2251},
+ {0x3704, 0x8A33},
+ {0x3706, 0xEFB3},
+ {0x3708, 0x1174},
+ {0x3614, 0x0150},
+ {0x3616, 0xA9AB},
+ {0x3618, 0x1770},
+ {0x361A, 0x8809},
+ {0x361C, 0xE3AE},
+ {0x3654, 0x5ACC},
+ {0x3656, 0x35EA},
+ {0x3658, 0x2DEC},
+ {0x365A, 0xB90B},
+ {0x365C, 0x250C},
+ {0x3694, 0x1630},
+ {0x3696, 0xD88C},
+ {0x3698, 0xBD0E},
+ {0x369A, 0x16D1},
+ {0x369C, 0xE492},
+ {0x36D4, 0x5D6D},
+ {0x36D6, 0x906E},
+ {0x36D8, 0x10AE},
+ {0x36DA, 0x7A8E},
+ {0x36DC, 0x9672},
+ {0x3714, 0x8D90},
+ {0x3716, 0x04F1},
+ {0x3718, 0x23F1},
+ {0x371A, 0xF313},
+ {0x371C, 0xE833},
+ {0x361E, 0x0490},
+ {0x3620, 0x14CD},
+ {0x3622, 0x38F0},
+ {0x3624, 0xBAED},
+ {0x3626, 0xFF6F},
+ {0x365E, 0x358C},
+ {0x3660, 0xA9E9},
+ {0x3662, 0x4A4E},
+ {0x3664, 0x398D},
+ {0x3666, 0x890F},
+ {0x369E, 0x2DF0},
+ {0x36A0, 0xF7CE},
+ {0x36A2, 0xB3CC},
+ {0x36A4, 0x118D},
+ {0x36A6, 0x9CB3},
+ {0x36DE, 0x462D},
+ {0x36E0, 0x74AA},
+ {0x36E2, 0xC8CF},
+ {0x36E4, 0x8DEF},
+ {0x36E6, 0xF130},
+ {0x371E, 0x9250},
+ {0x3720, 0x19CC},
+ {0x3722, 0xDFD1},
+ {0x3724, 0x5B70},
+ {0x3726, 0x34D2},
+ {0x3782, 0x0530},
+ {0x3784, 0x03C8},
+ {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),
+ .rftbl = &mt9p012_rolloff_tbl[0],
+ .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl)
+};
+
+
diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c
new file mode 100644
index 0000000..e1f6167
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.c
@@ -0,0 +1,1503 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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 MT9T013_RESET_FAST_TRANSITION 0x0002
+#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);
+DEFINE_SEMAPHORE(mt9t013_sem);
+
+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 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 = 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 */
+ uint32_t d1;
+ uint32_t d2;
+
+ d1 =
+ (uint32_t)(
+ (mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines *
+ 0x00000400) /
+ mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines);
+
+ d2 =
+ (uint32_t)(
+ (mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck *
+ 0x00000400) /
+ mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck);
+
+ divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+ pclk_mult =
+ (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier *
+ 0x00000400) /
+ (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier));
+
+
+ /* 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;
+ enum mt9t013_setting setting;
+
+ 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, f_mult is %d\n",
+ fps->fps_div, fps->f_mult);
+
+ if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+ setting = RES_PREVIEW;
+ else
+ setting = RES_CAPTURE;
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ REG_FRAME_LENGTH_LINES,
+ (uint16_t) (
+ mt9t013_regs.reg_pat[setting].frame_length_lines *
+ fps->fps_div / 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)
+{
+ uint16_t max_legal_gain = 0x01FF;
+ 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;
+
+ if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE)
+ line = (uint32_t) (line * mt9t013_ctrl->fps_divider /
+ 0x00000400);
+ else
+ line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider /
+ 0x00000400);
+
+ /*Set digital gain to 1 */
+ gain |= 0x0200;
+
+ /* 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, line);
+ 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);
+
+ 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|
+ MT9T013_RESET_FAST_TRANSITION);
+ 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;
+
+ rc = mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain,
+ mt9t013_ctrl->my_reg_line_count);
+ if (rc < 0)
+ return rc;
+
+ rc = mt9t013_i2c_write_w(mt9t013_client->addr,
+ MT9T013_REG_RESET_REGISTER,
+ MT9T013_RESET_REGISTER_PWON|MT9T013_RESET_FAST_TRANSITION);
+ if (rc < 0)
+ return rc;
+
+ msleep(5);
+ return rc;
+}
+
+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;
+
+ msleep(10);
+
+ /* 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)
+ if (machine_is_sapphire())
+ 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);
+
+ if (machine_is_sapphire())
+ 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;
+ s->s_mount_angle = 0;
+ 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/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h
new file mode 100644
index 0000000..f6b7c28
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef MT9T013_H
+#define MT9T013_H
+
+#include <linux/types.h>
+
+extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */
+
+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 const *reg_pat;
+ uint16_t reg_pat_size;
+ struct mt9t013_i2c_reg_conf const *ttbl;
+ uint16_t ttbl_size;
+ struct mt9t013_i2c_reg_conf const *lctbl;
+ uint16_t lctbl_size;
+ struct mt9t013_i2c_reg_conf const *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* #define MT9T013_H */
diff --git a/drivers/media/video/msm/mt9t013_reg.c b/drivers/media/video/msm/mt9t013_reg.c
new file mode 100644
index 0000000..9a9867e
--- /dev/null
+++ b/drivers/media/video/msm/mt9t013_reg.c
@@ -0,0 +1,275 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 const 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 const 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/media/video/msm/ov2720.c b/drivers/media/video/msm/ov2720.c
new file mode 100644
index 0000000..cfd6efa
--- /dev/null
+++ b/drivers/media/video/msm/ov2720.c
@@ -0,0 +1,598 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+#include <media/msm_camera.h>
+#include "msm_sensor.h"
+#include "ov2720.h"
+#include "msm.h"
+#define SENSOR_NAME "ov2720"
+#define PLATFORM_DRIVER_NAME "msm_camera_ov2720"
+#define ov2720_obj ov2720_##obj
+
+DEFINE_MUTEX(ov2720_mut);
+static struct msm_sensor_ctrl_t ov2720_s_ctrl;
+
+struct msm_sensor_i2c_reg_conf ov2720_start_settings[] = {
+ {0x0100, 0x01},
+};
+
+struct msm_sensor_i2c_reg_conf ov2720_stop_settings[] = {
+ {0x0100, 0x00},
+};
+
+struct msm_sensor_i2c_reg_conf ov2720_groupon_settings[] = {
+ {0x3208, 0x00},
+};
+
+struct msm_sensor_i2c_reg_conf ov2720_groupoff_settings[] = {
+ {0x3208, 0x10},
+ {0x3208, 0xA0},
+};
+
+static struct msm_sensor_i2c_reg_conf ov2720_prev_settings[] = {
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x02},
+ {0x3804, 0x07},
+ {0x3805, 0x97},
+ {0x3806, 0x04},
+ {0x3807, 0x45},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
+ {0x380c, 0x08},/*Line Length Pclk Hi*/
+ {0x380d, 0x5c},/*Line Length Pclk Lo*/
+ {0x380e, 0x04},/*Frame Length Line Hi*/
+ {0x380f, 0x60},/*Frame Length Line Lo*/
+ {0x3810, 0x00},
+ {0x3811, 0x05},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3820, 0x80},
+ {0x3821, 0x06},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3612, 0x0b},
+ {0x3618, 0x04},
+ {0x3a08, 0x01},
+ {0x3a09, 0x50},
+ {0x3a0a, 0x01},
+ {0x3a0b, 0x18},
+ {0x3a0d, 0x03},
+ {0x3a0e, 0x03},
+ {0x4520, 0x00},
+ {0x4837, 0x1b},
+ {0x3000, 0xff},
+ {0x3001, 0xff},
+ {0x3002, 0xf0},
+ {0x3600, 0x08},
+ {0x3621, 0xc0},
+ {0x3632, 0xd2},
+ {0x3633, 0x23},
+ {0x3634, 0x54},
+ {0x3f01, 0x0c},
+ {0x5001, 0xc1},
+ {0x3614, 0xf0},
+ {0x3630, 0x2d},
+ {0x370b, 0x62},
+ {0x3706, 0x61},
+ {0x4000, 0x02},
+ {0x4002, 0xc5},
+ {0x4005, 0x08},
+ {0x404f, 0x84},
+ {0x4051, 0x00},
+ {0x5000, 0xff},
+ {0x3a18, 0x00},
+ {0x3a19, 0x80},
+ {0x3503, 0x13},
+ {0x4521, 0x00},
+ {0x5183, 0xb0},
+ {0x5184, 0xb0},
+ {0x5185, 0xb0},
+ {0x370c, 0x0c},
+ {0x3035, 0x10},
+ {0x3036, 0x1e},
+ {0x3037, 0x21},
+ {0x303e, 0x19},
+ {0x3038, 0x06},
+ {0x3018, 0x04},
+ {0x3000, 0x00},
+ {0x3001, 0x00},
+ {0x3002, 0x00},
+ {0x3a0f, 0x40},
+ {0x3a10, 0x38},
+ {0x3a1b, 0x48},
+ {0x3a1e, 0x30},
+ {0x3a11, 0x90},
+ {0x3a1f, 0x10},
+ {0x4800, 0x24},
+};
+
+static struct msm_sensor_i2c_reg_conf ov2720_720_settings[] = {
+ {0x3800, 0x01},
+ {0x3801, 0x4a},
+ {0x3802, 0x00},
+ {0x3803, 0xba},
+ {0x3804, 0x06},
+ {0x3805, 0x51+32},
+ {0x3806, 0x03},
+ {0x3807, 0x8d+24},
+ {0x3808, 0x05},
+ {0x3809, 0x00+16},
+ {0x380a, 0x02},
+ {0x380b, 0x78},
+ {0x380c, 0x08},/*Line Length Pclk Hi*/
+ {0x380d, 0x5e},/*Line Length Pclk Lo*/
+ {0x380e, 0x04},/*Frame Length Line Hi*/
+ {0x380f, 0x60},/*Frame Length Line Lo*/
+ {0x3810, 0x00},
+ {0x3811, 0x05},
+ {0x3812, 0x00},
+ {0x3813, 0x02},
+ {0x3820, 0x80},
+ {0x3821, 0x06},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3612, 0x0b},
+ {0x3618, 0x04},
+ {0x3a08, 0x01},
+ {0x3a09, 0x50},
+ {0x3a0a, 0x01},
+ {0x3a0b, 0x18},
+ {0x3a0d, 0x03},
+ {0x3a0e, 0x03},
+ {0x4520, 0x00},
+ {0x4837, 0x1b},
+ {0x3000, 0xff},
+ {0x3001, 0xff},
+ {0x3002, 0xf0},
+ {0x3600, 0x08},
+ {0x3621, 0xc0},
+ {0x3632, 0xd2},
+ {0x3633, 0x23},
+ {0x3634, 0x54},
+ {0x3f01, 0x0c},
+ {0x5001, 0xc1},
+ {0x3614, 0xf0},
+ {0x3630, 0x2d},
+ {0x370b, 0x62},
+ {0x3706, 0x61},
+ {0x4000, 0x02},
+ {0x4002, 0xc5},
+ {0x4005, 0x08},
+ {0x404f, 0x84},
+ {0x4051, 0x00},
+ {0x5000, 0xff},
+ {0x3a18, 0x00},
+ {0x3a19, 0x80},
+ {0x3503, 0x13},
+ {0x4521, 0x00},
+ {0x5183, 0xb0},
+ {0x5184, 0xb0},
+ {0x5185, 0xb0},
+ {0x370c, 0x0c},
+ {0x3035, 0x10},
+ {0x3036, 0x04},
+ {0x3037, 0x61},
+ {0x303e, 0x19},
+ {0x3038, 0x06},
+ {0x3018, 0x04},
+ {0x3000, 0x00},
+ {0x3001, 0x00},
+ {0x3002, 0x00},
+ {0x3a0f, 0x40},
+ {0x3a10, 0x38},
+ {0x3a1b, 0x48},
+ {0x3a1e, 0x30},
+ {0x3a11, 0x90},
+ {0x3a1f, 0x10},
+ {0x4800, 0x24},
+};
+
+static struct msm_sensor_i2c_reg_conf ov2720_vga_settings[] = {
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x02},
+ {0x3804, 0x07},
+ {0x3805, 0x97+32},
+ {0x3806, 0x04},
+ {0x3807, 0x45+24},
+ {0x3808, 0x02},
+ {0x3809, 0x88+16},
+ {0x380a, 0x01},
+ {0x380b, 0xe6+12},
+ {0x380c, 0x08},/*Line Length Pclk Hi*/
+ {0x380d, 0x5e},/*Line Length Pclk Lo*/
+ {0x380e, 0x04},/*Frame Length Line Hi*/
+ {0x380f, 0x68},/*Frame Length Line Lo*/
+ {0x3810, 0x00},
+ {0x3811, 0x03},
+ {0x3812, 0x00},
+ {0x3813, 0x03},
+ {0x3820, 0x80},
+ {0x3821, 0x06},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3612, 0x0b},
+ {0x3618, 0x04},
+ {0x3a08, 0x01},
+ {0x3a09, 0x50},
+ {0x3a0a, 0x01},
+ {0x3a0b, 0x18},
+ {0x3a0d, 0x03},
+ {0x3a0e, 0x03},
+ {0x4520, 0x00},
+ {0x4837, 0x1b},
+ {0x3000, 0xff},
+ {0x3001, 0xff},
+ {0x3002, 0xf0},
+ {0x3600, 0x08},
+ {0x3621, 0xc0},
+ {0x3632, 0xd2},
+ {0x3633, 0x23},
+ {0x3634, 0x54},
+ {0x3f01, 0x0c},
+ {0x5001, 0xc1},
+ {0x3614, 0xf0},
+ {0x3630, 0x2d},
+ {0x370b, 0x62},
+ {0x3706, 0x61},
+ {0x4000, 0x02},
+ {0x4002, 0xc5},
+ {0x4005, 0x08},
+ {0x404f, 0x84},
+ {0x4051, 0x00},
+ {0x5000, 0xff},
+ {0x3a18, 0x00},
+ {0x3a19, 0x80},
+ {0x3503, 0x13},
+ {0x4521, 0x00},
+ {0x5183, 0xb0},
+ {0x5184, 0xb0},
+ {0x5185, 0xb0},
+ {0x370c, 0x0c},
+ {0x3035, 0x10},
+ {0x3036, 0x04},
+ {0x3037, 0x61},
+ {0x303e, 0x19},
+ {0x3038, 0x06},
+ {0x3018, 0x04},
+ {0x3000, 0x00},
+ {0x3001, 0x00},
+ {0x3002, 0x00},
+ {0x3a0f, 0x40},
+ {0x3a10, 0x38},
+ {0x3a1b, 0x48},
+ {0x3a1e, 0x30},
+ {0x3a11, 0x90},
+ {0x3a1f, 0x10},
+ {0x4800, 0x24},
+ {0x3500, 0x00},
+ {0x3501, 0x17},
+ {0x3502, 0xf0},
+ {0x3508, 0x00},
+ {0x3509, 0x20},
+};
+
+static struct msm_sensor_i2c_reg_conf ov2720_recommend_settings[] = {
+ {0x0103, 0x01},
+ {0x3718, 0x10},
+ {0x3702, 0x24},
+ {0x373a, 0x60},
+ {0x3715, 0x01},
+ {0x3703, 0x2e},
+ {0x3705, 0x10},
+ {0x3730, 0x30},
+ {0x3704, 0x62},
+ {0x3f06, 0x3a},
+ {0x371c, 0x00},
+ {0x371d, 0xc4},
+ {0x371e, 0x01},
+ {0x371f, 0x0d},
+ {0x3708, 0x61},
+ {0x3709, 0x12},
+};
+
+static struct msm_camera_csi_params ov2720_csi_params = {
+ .lane_cnt = 2,
+ .data_format = CSI_10BIT,
+ .lane_assign = 0xe4,
+ .dpcm_scheme = 0,
+ .settle_cnt = 0x18,
+};
+
+static struct v4l2_subdev_info ov2720_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+ /* more can be supported, to be added later */
+};
+
+static struct msm_sensor_i2c_conf_array ov2720_init_conf[] = {
+ {&ov2720_recommend_settings[0],
+ ARRAY_SIZE(ov2720_recommend_settings), 0}
+};
+
+static struct msm_sensor_i2c_conf_array ov2720_confs[] = {
+ {&ov2720_prev_settings[0], ARRAY_SIZE(ov2720_prev_settings), 0},
+ {&ov2720_vga_settings[0], ARRAY_SIZE(ov2720_vga_settings), 0},
+ {&ov2720_720_settings[0], ARRAY_SIZE(ov2720_720_settings), 0},
+};
+
+static int32_t ov2720_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t gain, uint32_t line)
+{
+ uint32_t fl_lines, offset;
+ fl_lines =
+ (s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider) / Q10;
+ offset = s_ctrl->vert_offset;
+ if (line > (fl_lines - offset))
+ fl_lines = line + offset;
+
+ pr_err("LINE: 0x%x\n", line);
+ s_ctrl->func_tbl.sensor_group_hold_on(s_ctrl);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->frame_length_lines_addr, fl_lines);
+ msm_sensor_i2c_waddr_write_b(s_ctrl,
+ s_ctrl->coarse_int_time_addr-1, line >> 12);
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->coarse_int_time_addr, ((line << 4) & 0xFFFF));
+ msm_sensor_i2c_waddr_write_w(s_ctrl,
+ s_ctrl->global_gain_addr, gain);
+ s_ctrl->func_tbl.sensor_group_hold_off(s_ctrl);
+ return 0;
+}
+
+static int32_t ov2720_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
+ int update_type, int rt)
+{
+ struct msm_camera_csid_params ov2720_csid_params;
+ struct msm_camera_csiphy_params ov2720_csiphy_params;
+ int32_t rc = 0;
+ s_ctrl->func_tbl.sensor_stop_stream(s_ctrl);
+ msleep(30);
+ if (update_type == MSM_SENSOR_REG_INIT) {
+ s_ctrl->config_csi_flag = 1;
+ msm_sensor_enable_debugfs(s_ctrl);
+ msm_sensor_write_b_init_settings(s_ctrl);
+ } else if (update_type == MSM_SENSOR_UPDATE_PERIODIC) {
+ msm_sensor_write_b_res_settings(s_ctrl, rt);
+ if (s_ctrl->config_csi_flag) {
+ struct msm_camera_csid_vc_cfg ov2720_vccfg[] = {
+ {0, CSI_RAW10, CSI_DECODE_10BIT},
+ {1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+ };
+ ov2720_csid_params.lane_cnt = 2;
+ ov2720_csid_params.lane_assign = 0xe4;
+ ov2720_csid_params.lut_params.num_cid =
+ ARRAY_SIZE(ov2720_vccfg);
+ ov2720_csid_params.lut_params.vc_cfg =
+ &ov2720_vccfg[0];
+ ov2720_csiphy_params.lane_cnt = 2;
+ ov2720_csiphy_params.settle_cnt = 0x1B;
+ rc = msm_camio_csid_config(&ov2720_csid_params);
+ v4l2_subdev_notify(s_ctrl->sensor_v4l2_subdev,
+ NOTIFY_CID_CHANGE, NULL);
+ mb();
+ rc = msm_camio_csiphy_config(&ov2720_csiphy_params);
+ mb();
+ msleep(20);
+ s_ctrl->config_csi_flag = 0;
+ }
+ s_ctrl->func_tbl.sensor_start_stream(s_ctrl);
+ msleep(30);
+ }
+ return rc;
+}
+
+static int ov2720_sensor_config(void __user *argp)
+{
+ return (int) msm_sensor_config(&ov2720_s_ctrl, argp);
+}
+
+static int ov2720_power_down(const struct msm_camera_sensor_info *data)
+{
+ pr_err("%s\n", __func__);
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int ov2720_power_up(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ pr_err("%s: %d\n", __func__, __LINE__);
+ msm_camio_clk_rate_set(MSM_SENSOR_MCLK_24HZ);
+ rc = gpio_request(data->sensor_reset, "SENSOR_NAME");
+ if (rc < 0)
+ goto gpio_request_fail;
+
+ pr_err("%s: reset sensor\n", __func__);
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(50);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(50);
+
+ rc = msm_sensor_match_id(&ov2720_s_ctrl);
+ if (rc < 0)
+ goto init_probe_fail;
+
+ goto init_probe_done;
+gpio_request_fail:
+ pr_err("%s: gpio request fail\n", __func__);
+ return rc;
+init_probe_fail:
+ pr_err(" %s fails\n", __func__);
+ ov2720_power_down(data);
+ return rc;
+init_probe_done:
+ pr_err("%s finishes\n", __func__);
+ return rc;
+}
+
+static int ov2720_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ pr_err("%s: %d\n", __func__, __LINE__);
+ ov2720_s_ctrl.fps = 30*Q8;
+ ov2720_s_ctrl.fps_divider = 1 * 0x00000400;
+ ov2720_s_ctrl.cam_mode = MSM_SENSOR_MODE_INVALID;
+
+ if (data)
+ ov2720_s_ctrl.sensordata = data;
+
+ rc = ov2720_power_up(data);
+ if (rc < 0)
+ goto init_done;
+
+ goto init_done;
+init_done:
+ pr_err("%s finishes\n", __func__);
+ return rc;
+}
+
+static int ov2720_sensor_release(void)
+{
+ mutex_lock(ov2720_s_ctrl.msm_sensor_mutex);
+ gpio_set_value_cansleep(ov2720_s_ctrl.sensordata->sensor_reset, 0);
+ msleep(20);
+ gpio_free(ov2720_s_ctrl.sensordata->sensor_reset);
+ mutex_unlock(ov2720_s_ctrl.msm_sensor_mutex);
+ pr_err("%s completed\n", __func__);
+ return 0;
+}
+
+static const struct i2c_device_id ov2720_i2c_id[] = {
+ {SENSOR_NAME, (kernel_ulong_t)&ov2720_s_ctrl},
+ { }
+};
+
+static struct i2c_driver ov2720_i2c_driver = {
+ .id_table = ov2720_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = SENSOR_NAME,
+ },
+};
+
+static int ov2720_sensor_v4l2_probe(const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+ return msm_sensor_v4l2_probe(&ov2720_s_ctrl, info, sdev, s);
+}
+
+static int ov2720_probe(struct platform_device *pdev)
+{
+ return msm_sensor_register(pdev, ov2720_sensor_v4l2_probe);
+}
+
+struct platform_driver ov2720_driver = {
+ .probe = ov2720_probe,
+ .driver = {
+ .name = PLATFORM_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_sensor_init_module(void)
+{
+ return platform_driver_register(&ov2720_driver);
+}
+
+static struct v4l2_subdev_core_ops ov2720_subdev_core_ops;
+static struct v4l2_subdev_video_ops ov2720_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops ov2720_subdev_ops = {
+ .core = &ov2720_subdev_core_ops,
+ .video = &ov2720_subdev_video_ops,
+};
+
+static struct msm_sensor_ctrl_t ov2720_s_ctrl = {
+ .msm_sensor_reg = {
+ .start_stream_conf = ov2720_start_settings,
+ .start_stream_conf_size = ARRAY_SIZE(ov2720_start_settings),
+ .stop_stream_conf = ov2720_stop_settings,
+ .stop_stream_conf_size = ARRAY_SIZE(ov2720_stop_settings),
+ .group_hold_on_conf = ov2720_groupon_settings,
+ .group_hold_on_conf_size = ARRAY_SIZE(ov2720_groupon_settings),
+ .group_hold_off_conf = ov2720_groupoff_settings,
+ .group_hold_off_conf_size =
+ ARRAY_SIZE(ov2720_groupoff_settings),
+ .init_settings = &ov2720_init_conf[0],
+ .init_size = ARRAY_SIZE(ov2720_init_conf),
+ .res_settings = &ov2720_confs[0],
+ .num_conf = ARRAY_SIZE(ov2720_confs),
+ },
+ .sensor_id_addr = 0x300A,
+ .sensor_id = 0x2720,
+ .frame_length_lines_addr = 0x380e,
+ .coarse_int_time_addr = 0x3501,
+ .global_gain_addr = 0x3508,
+ .line_length_pck_addr = 0x380c,
+ .frame_length_lines_array_addr = 14,
+ .line_length_pck_array_addr = 12,
+ .vert_offset = 6,
+ .cam_mode = MSM_SENSOR_MODE_INVALID,
+ .camera_type = FRONT_CAMERA_2D,
+ .config_csi_flag = 1,
+ .csi_params = &ov2720_csi_params,
+ .msm_sensor_mutex = &ov2720_mut,
+ .msm_sensor_i2c_driver = &ov2720_i2c_driver,
+ .sensor_v4l2_subdev_info = ov2720_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov2720_subdev_info),
+ .sensor_v4l2_subdev_ops = &ov2720_subdev_ops,
+
+ .func_tbl = {
+ .sensor_start_stream = msm_sensor_start_stream,
+ .sensor_stop_stream = msm_sensor_stop_stream,
+ .sensor_group_hold_on = msm_sensor_group_hold_on,
+ .sensor_group_hold_off = msm_sensor_group_hold_off,
+ .sensor_get_prev_lines_pf = msm_sensor_get_prev_lines_pf,
+ .sensor_get_prev_pixels_pl = msm_sensor_get_prev_pixels_pl,
+ .sensor_get_pict_lines_pf = msm_sensor_get_pict_lines_pf,
+ .sensor_get_pict_pixels_pl = msm_sensor_get_pict_pixels_pl,
+ .sensor_get_pict_max_exp_lc = msm_sensor_get_pict_max_exp_lc,
+ .sensor_get_pict_fps = msm_sensor_get_pict_fps,
+ .sensor_set_fps = msm_sensor_set_fps,
+ .sensor_write_exp_gain = ov2720_write_exp_gain,
+ .sensor_setting = ov2720_sensor_setting,
+ .sensor_set_sensor_mode = msm_sensor_set_sensor_mode_b,
+ .sensor_mode_init = msm_sensor_mode_init_bdata,
+ .sensor_config = ov2720_sensor_config,
+ .sensor_open_init = ov2720_sensor_open_init,
+ .sensor_release = ov2720_sensor_release,
+ .sensor_power_up = ov2720_power_up,
+ .sensor_power_down = ov2720_power_down,
+ .sensor_probe = msm_sensor_probe,
+ },
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Omnivision 2MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/video/msm/ov2720.h b/drivers/media/video/msm/ov2720.h
new file mode 100644
index 0000000..7077a7d
--- /dev/null
+++ b/drivers/media/video/msm/ov2720.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <mach/board.h>
+extern struct platform_driver ov2720_driver;
+
diff --git a/drivers/media/video/msm/ov2720_reg.c b/drivers/media/video/msm/ov2720_reg.c
new file mode 100644
index 0000000..bf094a5
--- /dev/null
+++ b/drivers/media/video/msm/ov2720_reg.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "ov2720.h"
+
+struct ov2720_i2c_reg_conf ov2720_prev_settings[] = {
+ {0x3800, 0x00},
+ {0x3801, 0x0c},
+ {0x3802, 0x00},
+ {0x3803, 0x02},
+ {0x3804, 0x07},
+ {0x3805, 0x97},
+ {0x3806, 0x04},
+ {0x3807, 0x45},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
+ {0x380c, 0x08},/*Line Length Pclk Hi*/
+ {0x380d, 0x5c},/*Line Length Pclk Lo*/
+ {0x380e, 0x04},/*Frame Length Line Hi*/
+ {0x380f, 0x60},/*Frame Length Line Lo*/
+ {0x3810, 0x00},
+ {0x3811, 0x05},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3820, 0x80},
+ {0x3821, 0x06},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3612, 0x0b},
+ {0x3618, 0x04},
+ {0x3a08, 0x01},
+ {0x3a09, 0x50},
+ {0x3a0a, 0x01},
+ {0x3a0b, 0x18},
+ {0x3a0d, 0x03},
+ {0x3a0e, 0x03},
+ {0x4520, 0x00},
+ {0x4837, 0x1b},
+ {0x3000, 0xff},
+ {0x3001, 0xff},
+ {0x3002, 0xf0},
+ {0x3600, 0x08},
+ {0x3621, 0xc0},
+ {0x3632, 0xd2},
+ {0x3633, 0x23},
+ {0x3634, 0x54},
+ {0x3f01, 0x0c},
+ {0x5001, 0xc1},
+ {0x3614, 0xf0},
+ {0x3630, 0x2d},
+ {0x370b, 0x62},
+ {0x3706, 0x61},
+ {0x4000, 0x02},
+ {0x4002, 0xc5},
+ {0x4005, 0x08},
+ {0x404f, 0x84},
+ {0x4051, 0x00},
+ {0x5000, 0xff},
+ {0x3a18, 0x00},
+ {0x3a19, 0x80},
+ {0x3503, 0x00},
+ {0x4521, 0x00},
+ {0x5183, 0xb0},
+ {0x5184, 0xb0},
+ {0x5185, 0xb0},
+ {0x370c, 0x0c},
+};
+
+struct ov2720_i2c_reg_conf ov2720_recommend_settings[] = {
+ {0x0103, 0x01},
+ {0x3718, 0x10},
+ {0x3702, 0x24},
+ {0x373a, 0x60},
+ {0x3715, 0x01},
+ {0x3703, 0x2e},
+ {0x3705, 0x10},
+ {0x3730, 0x30},
+ {0x3704, 0x62},
+ {0x3f06, 0x3a},
+ {0x371c, 0x00},
+ {0x371d, 0xc4},
+ {0x371e, 0x01},
+ {0x371f, 0x0d},
+ {0x3708, 0x61},
+ {0x3709, 0x12},
+ {0x3035, 0x10},
+ {0x3036, 0x1e},
+ {0x3037, 0x21},
+ {0x303e, 0x19},
+ {0x3038, 0x06},
+ {0x3018, 0x04},
+ {0x3000, 0x00},
+ {0x3001, 0x00},
+ {0x3002, 0x00},
+ {0x3a0f, 0x40},
+ {0x3a10, 0x38},
+ {0x3a1b, 0x48},
+ {0x3a1e, 0x30},
+ {0x3a11, 0x90},
+ {0x3a1f, 0x10},
+};
+
+struct ov2720_i2c_conf_array ov2720_confs[] = {
+ {&ov2720_prev_settings[0], ARRAY_SIZE(ov2720_prev_settings)},
+};
+
+struct ov2720_reg ov2720_regs = {
+ .rec_settings = &ov2720_recommend_settings[0],
+ .rec_size = ARRAY_SIZE(ov2720_recommend_settings),
+ .conf_array = &ov2720_confs[0],
+};
diff --git a/drivers/media/video/msm/ov7692.c b/drivers/media/video/msm/ov7692.c
new file mode 100644
index 0000000..7372156
--- /dev/null
+++ b/drivers/media/video/msm/ov7692.c
@@ -0,0 +1,595 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/camera.h>
+#include "ov7692.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define Q8 0x00000100
+
+/* Omnivision8810 product ID register address */
+#define REG_OV7692_MODEL_ID_MSB 0x0A
+#define REG_OV7692_MODEL_ID_LSB 0x0B
+
+#define OV7692_MODEL_ID 0x7692
+/* Omnivision8810 product ID */
+
+/* Time in milisecs for waiting for the sensor to reset */
+#define OV7692_RESET_DELAY_MSECS 66
+#define OV7692_DEFAULT_CLOCK_RATE 24000000
+/* Registers*/
+
+/* Color bar pattern selection */
+#define OV7692_COLOR_BAR_PATTERN_SEL_REG 0x82
+/* Color bar enabling control */
+#define OV7692_COLOR_BAR_ENABLE_REG 0x601
+/* Time in milisecs for waiting for the sensor to reset*/
+#define OV7692_RESET_DELAY_MSECS 66
+
+/*============================================================================
+ DATA DECLARATIONS
+============================================================================*/
+/* 96MHz PCLK @ 24MHz MCLK */
+struct reg_addr_val_pair_struct ov7692_init_settings_array[] = {
+ {0x12, 0x80},
+ {0x0e, 0x08},
+ {0x69, 0x52},
+ {0x1e, 0xb3},
+ {0x48, 0x42},
+ {0xff, 0x01},
+ {0xae, 0xa0},
+ {0xa8, 0x26},
+ {0xb4, 0xc0},
+ {0xb5, 0x40},
+ {0xff, 0x00},
+ {0x0c, 0x00},
+ {0x62, 0x10},
+ {0x12, 0x00},
+ {0x17, 0x65},
+ {0x18, 0xa4},
+ {0x19, 0x0a},
+ {0x1a, 0xf6},
+ {0x3e, 0x30},
+ {0x64, 0x0a},
+ {0xff, 0x01},
+ {0xb4, 0xc0},
+ {0xff, 0x00},
+ {0x67, 0x20},
+ {0x81, 0x3f},
+ {0xcc, 0x02},
+ {0xcd, 0x80},
+ {0xce, 0x01},
+ {0xcf, 0xe0},
+ {0xc8, 0x02},
+ {0xc9, 0x80},
+ {0xca, 0x01},
+ {0xcb, 0xe0},
+ {0xd0, 0x48},
+ {0x82, 0x03},
+ {0x0e, 0x00},
+ {0x70, 0x00},
+ {0x71, 0x34},
+ {0x74, 0x28},
+ {0x75, 0x98},
+ {0x76, 0x00},
+ {0x77, 0x64},
+ {0x78, 0x01},
+ {0x79, 0xc2},
+ {0x7a, 0x4e},
+ {0x7b, 0x1f},
+ {0x7c, 0x00},
+ {0x11, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x23},
+ {0x50, 0x9a},
+ {0x51, 0x80},
+ {0x4c, 0x7d},
+ {0x0e, 0x00},
+ {0x80, 0x7f},
+ {0x85, 0x10},
+ {0x86, 0x00},
+ {0x87, 0x00},
+ {0x88, 0x00},
+ {0x89, 0x2a},
+ {0x8a, 0x26},
+ {0x8b, 0x22},
+ {0xbb, 0x7a},
+ {0xbc, 0x69},
+ {0xbd, 0x11},
+ {0xbe, 0x13},
+ {0xbf, 0x81},
+ {0xc0, 0x96},
+ {0xc1, 0x1e},
+ {0xb7, 0x05},
+ {0xb8, 0x09},
+ {0xb9, 0x00},
+ {0xba, 0x18},
+ {0x5a, 0x1f},
+ {0x5b, 0x9f},
+ {0x5c, 0x6a},
+ {0x5d, 0x42},
+ {0x24, 0x78},
+ {0x25, 0x68},
+ {0x26, 0xb3},
+ {0xa3, 0x0b},
+ {0xa4, 0x15},
+ {0xa5, 0x2a},
+ {0xa6, 0x51},
+ {0xa7, 0x63},
+ {0xa8, 0x74},
+ {0xa9, 0x83},
+ {0xaa, 0x91},
+ {0xab, 0x9e},
+ {0xac, 0xaa},
+ {0xad, 0xbe},
+ {0xae, 0xce},
+ {0xaf, 0xe5},
+ {0xb0, 0xf3},
+ {0xb1, 0xfb},
+ {0xb2, 0x06},
+ {0x8c, 0x5c},
+ {0x8d, 0x11},
+ {0x8e, 0x12},
+ {0x8f, 0x19},
+ {0x90, 0x50},
+ {0x91, 0x20},
+ {0x92, 0x96},
+ {0x93, 0x80},
+ {0x94, 0x13},
+ {0x95, 0x1b},
+ {0x96, 0xff},
+ {0x97, 0x00},
+ {0x98, 0x3d},
+ {0x99, 0x36},
+ {0x9a, 0x51},
+ {0x9b, 0x43},
+ {0x9c, 0xf0},
+ {0x9d, 0xf0},
+ {0x9e, 0xf0},
+ {0x9f, 0xff},
+ {0xa0, 0x68},
+ {0xa1, 0x62},
+ {0xa2, 0x0e},
+};
+
+static bool OV7692_CSI_CONFIG;
+/* 816x612, 24MHz MCLK 96MHz PCLK */
+uint32_t OV7692_FULL_SIZE_WIDTH = 640;
+uint32_t OV7692_FULL_SIZE_HEIGHT = 480;
+
+uint32_t OV7692_QTR_SIZE_WIDTH = 640;
+uint32_t OV7692_QTR_SIZE_HEIGHT = 480;
+
+uint32_t OV7692_HRZ_FULL_BLK_PIXELS = 16;
+uint32_t OV7692_VER_FULL_BLK_LINES = 12;
+uint32_t OV7692_HRZ_QTR_BLK_PIXELS = 16;
+uint32_t OV7692_VER_QTR_BLK_LINES = 12;
+
+struct ov7692_work_t {
+ struct work_struct work;
+};
+static struct ov7692_work_t *ov7692_sensorw;
+static struct i2c_client *ov7692_client;
+struct ov7692_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t fps;
+ int32_t curr_lens_pos;
+ uint32_t curr_step_pos;
+ uint32_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint32_t total_lines_per_frame;
+ enum ov7692_resolution_t prev_res;
+ enum ov7692_resolution_t pict_res;
+ enum ov7692_resolution_t curr_res;
+ enum ov7692_test_mode_t set_test;
+ unsigned short imgaddr;
+};
+static struct ov7692_ctrl_t *ov7692_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(ov7692_wait_queue);
+DEFINE_MUTEX(ov7692_mut);
+
+/*=============================================================*/
+
+static int ov7692_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(ov7692_client->adapter, msgs, 2) < 0) {
+ CDBG("ov7692_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+static int32_t ov7692_i2c_txdata(unsigned short saddr,
+ unsigned char *txdata, int length)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 2,
+ .buf = txdata,
+ },
+ };
+ if (i2c_transfer(ov7692_client->adapter, msg, 1) < 0) {
+ CDBG("ov7692_i2c_txdata faild 0x%x\n", ov7692_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t ov7692_i2c_read(uint8_t raddr,
+ uint8_t *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[1];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = raddr;
+ rc = ov7692_i2c_rxdata(ov7692_client->addr >> 1, buf, rlen);
+ if (rc < 0) {
+ CDBG("ov7692_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = buf[0];
+ return rc;
+}
+static int32_t ov7692_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[2];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = waddr;
+ buf[1] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = ov7692_i2c_txdata(ov7692_client->addr >> 1, buf, 2);
+ if (rc < 0)
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ return rc;
+}
+
+static int32_t ov7692_sensor_setting(int update_type, int rt)
+{
+ int32_t i, array_length;
+ int32_t rc = 0;
+ struct msm_camera_csi_params ov7692_csi_params;
+ switch (update_type) {
+ case REG_INIT:
+ OV7692_CSI_CONFIG = 0;
+ ov7692_i2c_write_b_sensor(0x0e, 0x08);
+ return rc;
+ break;
+ case UPDATE_PERIODIC:
+ if (!OV7692_CSI_CONFIG) {
+ ov7692_csi_params.lane_cnt = 1;
+ ov7692_csi_params.data_format = CSI_8BIT;
+ ov7692_csi_params.lane_assign = 0xe4;
+ ov7692_csi_params.dpcm_scheme = 0;
+ ov7692_csi_params.settle_cnt = 0x14;
+
+ rc = msm_camio_csi_config(&ov7692_csi_params);
+ msleep(10);
+ array_length = sizeof(ov7692_init_settings_array) /
+ sizeof(ov7692_init_settings_array[0]);
+ for (i = 0; i < array_length; i++) {
+ rc = ov7692_i2c_write_b_sensor(
+ ov7692_init_settings_array[i].reg_addr,
+ ov7692_init_settings_array[i].reg_val);
+ if (rc < 0)
+ return rc;
+ }
+ OV7692_CSI_CONFIG = 1;
+ msleep(20);
+ return rc;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t ov7692_video_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ rt = RES_PREVIEW;
+
+ if (ov7692_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ ov7692_ctrl->curr_res = ov7692_ctrl->prev_res;
+ ov7692_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t ov7692_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = ov7692_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+static int32_t ov7692_power_down(void)
+{
+ return 0;
+}
+
+static int ov7692_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ uint8_t model_id_msb, model_id_lsb = 0;
+ uint16_t model_id;
+ int32_t rc = 0;
+ /*The reset pin is not physically connected to the sensor.
+ The standby pin will do the reset hence there is no need
+ to request the gpio reset*/
+
+ /* Read sensor Model ID: */
+ rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_MSB, &model_id_msb, 1);
+ if (rc < 0)
+ goto init_probe_fail;
+ rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_LSB, &model_id_lsb, 1);
+ if (rc < 0)
+ goto init_probe_fail;
+ model_id = (model_id_msb << 8) | ((model_id_lsb & 0x00FF)) ;
+ CDBG("ov7692 model_id = 0x%x, 0x%x, 0x%x\n",
+ model_id, model_id_msb, model_id_lsb);
+ /* 4. Compare sensor ID to OV7692 ID: */
+ if (model_id != OV7692_MODEL_ID) {
+ rc = -ENODEV;
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ pr_warning(" ov7692_probe_init_sensor fails\n");
+init_probe_done:
+ CDBG(" ov7692_probe_init_sensor finishes\n");
+ return rc;
+}
+
+int ov7692_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling ov7692_sensor_open_init\n");
+ ov7692_ctrl = kzalloc(sizeof(struct ov7692_ctrl_t), GFP_KERNEL);
+ if (!ov7692_ctrl) {
+ CDBG("ov7692_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ ov7692_ctrl->fps_divider = 1 * 0x00000400;
+ ov7692_ctrl->pict_fps_divider = 1 * 0x00000400;
+ ov7692_ctrl->fps = 30 * Q8;
+ ov7692_ctrl->set_test = TEST_OFF;
+ ov7692_ctrl->prev_res = QTR_SIZE;
+ ov7692_ctrl->pict_res = FULL_SIZE;
+ ov7692_ctrl->curr_res = INVALID_SIZE;
+
+ if (data)
+ ov7692_ctrl->sensordata = data;
+
+ /* enable mclk first */
+
+ msm_camio_clk_rate_set(24000000);
+ msleep(20);
+
+ rc = ov7692_probe_init_sensor(data);
+ if (rc < 0) {
+ CDBG("Calling ov7692_sensor_open_init fail\n");
+ goto init_fail;
+ }
+
+ rc = ov7692_sensor_setting(REG_INIT, RES_PREVIEW);
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+
+init_fail:
+ CDBG(" ov7692_sensor_open_init fail\n");
+ kfree(ov7692_ctrl);
+init_done:
+ CDBG("ov7692_sensor_open_init done\n");
+ return rc;
+}
+
+static int ov7692_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&ov7692_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id ov7692_i2c_id[] = {
+ {"ov7692", 0},
+ { }
+};
+
+static int ov7692_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("ov7692_i2c_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ ov7692_sensorw = kzalloc(sizeof(struct ov7692_work_t), GFP_KERNEL);
+ if (!ov7692_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, ov7692_sensorw);
+ ov7692_init_client(client);
+ ov7692_client = client;
+
+ CDBG("ov7692_i2c_probe success! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("ov7692_i2c_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int __exit ov7692_remove(struct i2c_client *client)
+{
+ struct ov7692_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ ov7692_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver ov7692_i2c_driver = {
+ .id_table = ov7692_i2c_id,
+ .probe = ov7692_i2c_probe,
+ .remove = __exit_p(ov7692_i2c_remove),
+ .driver = {
+ .name = "ov7692",
+ },
+};
+
+int ov7692_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;
+ mutex_lock(&ov7692_mut);
+ CDBG("ov7692_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_SET_MODE:
+ rc = ov7692_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ rc = ov7692_power_down();
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&ov7692_mut);
+
+ return rc;
+}
+static int ov7692_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&ov7692_mut);
+ ov7692_power_down();
+ kfree(ov7692_ctrl);
+ ov7692_ctrl = NULL;
+ CDBG("ov7692_release completed\n");
+ mutex_unlock(&ov7692_mut);
+
+ return rc;
+}
+
+static int ov7692_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&ov7692_i2c_driver);
+ if (rc < 0 || ov7692_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(24000000);
+ rc = ov7692_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = ov7692_sensor_open_init;
+ s->s_release = ov7692_sensor_release;
+ s->s_config = ov7692_sensor_config;
+ s->s_camera_type = FRONT_CAMERA_2D;
+ s->s_mount_angle = 0;
+ return rc;
+
+probe_fail:
+ CDBG("ov7692_sensor_probe: SENSOR PROBE FAILS!\n");
+ i2c_del_driver(&ov7692_i2c_driver);
+ return rc;
+}
+
+static int __ov7692_probe(struct platform_device *pdev)
+{
+
+ return msm_camera_drv_start(pdev, ov7692_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __ov7692_probe,
+ .driver = {
+ .name = "msm_camera_ov7692",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ov7692_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov7692_init);
+
+MODULE_DESCRIPTION("OMNI VGA YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/ov7692.h b/drivers/media/video/msm/ov7692.h
new file mode 100644
index 0000000..e43a17d
--- /dev/null
+++ b/drivers/media/video/msm/ov7692.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef OV7692_H
+#define OV7692_H
+#include <linux/types.h>
+#include <mach/board.h>
+
+struct reg_addr_val_pair_struct {
+ uint8_t reg_addr;
+ uint8_t reg_val;
+};
+
+enum ov7692_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum ov7692_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum ov7692_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+enum ov7692_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
+};
+#endif
+
diff --git a/drivers/media/video/msm/ov9726.c b/drivers/media/video/msm/ov9726.c
new file mode 100644
index 0000000..fc04558
--- /dev/null
+++ b/drivers/media/video/msm/ov9726.c
@@ -0,0 +1,792 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "ov9726.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define OV9726_Q8 0x00000100
+#define OV9726_Q8Shift 8
+#define OV9726_Q10 0x00000400
+#define OV9726_Q10Shift 10
+
+/* Omnivision8810 product ID register address */
+#define OV9726_PIDH_REG 0x0000
+#define OV9726_PIDL_REG 0x0001
+/* Omnivision8810 product ID */
+#define OV9726_PID 0x97
+/* Omnivision8810 version */
+#define OV9726_VER 0x26
+/* Time in milisecs for waiting for the sensor to reset */
+#define OV9726_RESET_DELAY_MSECS 66
+#define OV9726_DEFAULT_CLOCK_RATE 24000000
+/* Registers*/
+#define OV9726_GAIN 0x3000
+#define OV9726_AEC_MSB 0x3002
+#define OV9726_AEC_LSB 0x3003
+
+/* Color bar pattern selection */
+#define OV9726_COLOR_BAR_PATTERN_SEL_REG 0x600
+/* Color bar enabling control */
+#define OV9726_COLOR_BAR_ENABLE_REG 0x601
+/* Time in milisecs for waiting for the sensor to reset*/
+#define OV9726_RESET_DELAY_MSECS 66
+/* I2C Address of the Sensor */
+/*============================================================================
+ DATA DECLARATIONS
+============================================================================*/
+#define OV9726_FULL_SIZE_DUMMY_PIXELS 0
+#define OV9726_FULL_SIZE_DUMMY_LINES 0
+#define OV9726_QTR_SIZE_DUMMY_PIXELS 0
+#define OV9726_QTR_SIZE_DUMMY_LINES 0
+
+#define OV9726_FULL_SIZE_WIDTH 1296
+#define OV9726_FULL_SIZE_HEIGHT 808
+
+#define OV9726_QTR_SIZE_WIDTH 1296
+#define OV9726_QTR_SIZE_HEIGHT 808
+
+#define OV9726_HRZ_FULL_BLK_PIXELS 368
+#define OV9726_VER_FULL_BLK_LINES 32
+#define OV9726_HRZ_QTR_BLK_PIXELS 368
+#define OV9726_VER_QTR_BLK_LINES 32
+
+#define OV9726_MSB_MASK 0xFF00
+#define OV9726_LSB_MASK 0x00FF
+
+struct ov9726_work_t {
+ struct work_struct work;
+};
+static struct ov9726_work_t *ov9726_sensorw;
+static struct i2c_client *ov9726_client;
+struct ov9726_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+ uint16_t fps;
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+ enum ov9726_resolution_t prev_res;
+ enum ov9726_resolution_t pict_res;
+ enum ov9726_resolution_t curr_res;
+ enum ov9726_test_mode_t set_test;
+ unsigned short imgaddr;
+};
+static struct ov9726_ctrl_t *ov9726_ctrl;
+static int8_t config_not_set = 1;
+static DECLARE_WAIT_QUEUE_HEAD(ov9726_wait_queue);
+DEFINE_MUTEX(ov9726_mut);
+
+/*=============================================================*/
+static int ov9726_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(ov9726_client->adapter, msgs, 2) < 0) {
+ CDBG("ov9726_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t ov9726_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(ov9726_client->adapter, msg, 1) < 0) {
+ CDBG("ov9726_i2c_txdata faild 0x%x\n", ov9726_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t ov9726_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+
+ if (!rdata)
+ return -EIO;
+
+ buf[0] = (raddr & OV9726_MSB_MASK) >> 8;
+ buf[1] = (raddr & OV9726_LSB_MASK);
+
+ rc = ov9726_i2c_rxdata(ov9726_client->addr, buf, rlen);
+
+ if (rc < 0) {
+ CDBG("ov9726_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ return rc;
+}
+
+static int32_t ov9726_i2c_write_b(unsigned short saddr,
+ unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+
+ buf[0] = (waddr & OV9726_MSB_MASK) >> 8;
+ buf[1] = (waddr & OV9726_LSB_MASK);
+ buf[2] = bdata;
+
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%xd\n", waddr, bdata);
+ rc = ov9726_i2c_txdata(saddr, buf, 3);
+
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+
+ return rc;
+}
+
+static void ov9726_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ uint32_t divider; /*Q10 */
+ uint32_t d1;
+ uint32_t d2;
+ uint16_t snapshot_height, preview_height, preview_width, snapshot_width;
+ if (ov9726_ctrl->prev_res == QTR_SIZE) {
+ preview_width = OV9726_QTR_SIZE_WIDTH +
+ OV9726_HRZ_QTR_BLK_PIXELS ;
+ preview_height = OV9726_QTR_SIZE_HEIGHT +
+ OV9726_VER_QTR_BLK_LINES ;
+ } else {
+ /* full size resolution used for preview. */
+ preview_width = OV9726_FULL_SIZE_WIDTH +
+ OV9726_HRZ_FULL_BLK_PIXELS ;
+ preview_height = OV9726_FULL_SIZE_HEIGHT +
+ OV9726_VER_FULL_BLK_LINES ;
+ }
+ if (ov9726_ctrl->pict_res == QTR_SIZE) {
+ snapshot_width = OV9726_QTR_SIZE_WIDTH +
+ OV9726_HRZ_QTR_BLK_PIXELS ;
+ snapshot_height = OV9726_QTR_SIZE_HEIGHT +
+ OV9726_VER_QTR_BLK_LINES ;
+ } else {
+ snapshot_width = OV9726_FULL_SIZE_WIDTH +
+ OV9726_HRZ_FULL_BLK_PIXELS;
+ snapshot_height = OV9726_FULL_SIZE_HEIGHT +
+ OV9726_VER_FULL_BLK_LINES;
+ }
+
+ d1 = (uint32_t)(((uint32_t)preview_height <<
+ OV9726_Q10Shift) /
+ snapshot_height);
+
+ d2 = (uint32_t)(((uint32_t)preview_width <<
+ OV9726_Q10Shift) /
+ snapshot_width);
+
+ divider = (uint32_t) (d1 * d2) >> OV9726_Q10Shift;
+ *pfps = (uint16_t)((uint32_t)(fps * divider) >> OV9726_Q10Shift);
+}
+
+static uint16_t ov9726_get_prev_lines_pf(void)
+{
+ if (ov9726_ctrl->prev_res == QTR_SIZE)
+ return OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES;
+ else
+ return OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES;
+}
+
+static uint16_t ov9726_get_prev_pixels_pl(void)
+{
+ if (ov9726_ctrl->prev_res == QTR_SIZE)
+ return OV9726_QTR_SIZE_WIDTH + OV9726_HRZ_QTR_BLK_PIXELS;
+ else
+ return OV9726_FULL_SIZE_WIDTH + OV9726_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t ov9726_get_pict_lines_pf(void)
+{
+ if (ov9726_ctrl->pict_res == QTR_SIZE)
+ return OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES;
+ else
+ return OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES;
+}
+
+static uint16_t ov9726_get_pict_pixels_pl(void)
+{
+ if (ov9726_ctrl->pict_res == QTR_SIZE)
+ return OV9726_QTR_SIZE_WIDTH + OV9726_HRZ_QTR_BLK_PIXELS;
+ else
+ return OV9726_FULL_SIZE_WIDTH + OV9726_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t ov9726_get_pict_max_exp_lc(void)
+{
+ if (ov9726_ctrl->pict_res == QTR_SIZE)
+ return (OV9726_QTR_SIZE_HEIGHT + OV9726_VER_QTR_BLK_LINES)*24;
+ else
+ return (OV9726_FULL_SIZE_HEIGHT + OV9726_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t ov9726_set_fps(struct fps_cfg *fps)
+{
+ int32_t rc = 0;
+ CDBG("%s: fps->fps_div = %d\n", __func__, fps->fps_div);
+ /* TODO: Passing of fps_divider from user space has issues. */
+ /* ov9726_ctrl->fps_divider = fps->fps_div; */
+ ov9726_ctrl->fps_divider = 1 * 0x400;
+ CDBG("%s: ov9726_ctrl->fps_divider = %d\n", __func__,
+ ov9726_ctrl->fps_divider);
+ ov9726_ctrl->pict_fps_divider = fps->pict_fps_div;
+ ov9726_ctrl->fps = fps->f_mult;
+ return rc;
+}
+
+static int32_t ov9726_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ static uint16_t max_legal_gain = 0x00FF;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_time_msb, intg_time_lsb;
+ uint8_t ov9726_offset = 6;
+ uint8_t line_length_pck_msb, line_length_pck_lsb;
+ uint16_t line_length_pck, frame_length_lines;
+ uint32_t line_length_ratio = 1 << OV9726_Q8Shift;
+ int32_t rc = -1;
+ CDBG("%s: gain = %d line = %d", __func__, gain, line);
+
+ if (ov9726_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ if (ov9726_ctrl->curr_res == QTR_SIZE) {
+ frame_length_lines = OV9726_QTR_SIZE_HEIGHT +
+ OV9726_VER_QTR_BLK_LINES;
+ line_length_pck = OV9726_QTR_SIZE_WIDTH +
+ OV9726_HRZ_QTR_BLK_PIXELS;
+ } else {
+ frame_length_lines = OV9726_FULL_SIZE_HEIGHT +
+ OV9726_VER_FULL_BLK_LINES;
+ line_length_pck = OV9726_FULL_SIZE_WIDTH +
+ OV9726_HRZ_FULL_BLK_PIXELS;
+ }
+ if (line > (frame_length_lines - ov9726_offset))
+ ov9726_ctrl->fps = (uint16_t) (((uint32_t)30 <<
+ OV9726_Q8Shift) *
+ (frame_length_lines - ov9726_offset) / line);
+ else
+ ov9726_ctrl->fps = (uint16_t) ((uint32_t)30 <<
+ OV9726_Q8Shift);
+ } else {
+ frame_length_lines = OV9726_FULL_SIZE_HEIGHT +
+ OV9726_VER_FULL_BLK_LINES;
+ line_length_pck = OV9726_FULL_SIZE_WIDTH +
+ OV9726_HRZ_FULL_BLK_PIXELS;
+ }
+
+ if (ov9726_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ line = (uint32_t) (line * ov9726_ctrl->fps_divider) >>
+ OV9726_Q10Shift;
+ } else {
+ line = (uint32_t) (line * ov9726_ctrl->pict_fps_divider) >>
+ OV9726_Q10Shift;
+ }
+
+ /* calculate line_length_ratio */
+ if (line > (frame_length_lines - ov9726_offset)) {
+ line_length_ratio = (line << OV9726_Q8Shift) /
+ (frame_length_lines - ov9726_offset);
+ line = frame_length_lines - ov9726_offset;
+ } else
+ line_length_ratio = (uint32_t)1 << OV9726_Q8Shift;
+
+ if (gain > max_legal_gain) {
+ /* range: 0 to 224 */
+ gain = max_legal_gain;
+ }
+ /* update gain registers */
+ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lsb = (uint8_t) (gain & 0x00FF);
+ /* linear AFR horizontal stretch */
+ line_length_pck = (uint16_t) ((line_length_pck *
+ line_length_ratio) >> OV9726_Q8Shift);
+ line_length_pck_msb = (uint8_t) ((line_length_pck & 0xFF00) >> 8);
+ line_length_pck_lsb = (uint8_t) (line_length_pck & 0x00FF);
+ /* update line count registers */
+ intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+ intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x104, 0x1);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x204, gain_msb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x205, gain_lsb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x342,
+ line_length_pck_msb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x343,
+ line_length_pck_lsb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x0202, intg_time_msb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x0203, intg_time_lsb);
+ if (rc < 0)
+ return rc;
+
+ rc = ov9726_i2c_write_b(ov9726_client->addr, 0x104, 0x0);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t ov9726_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = ov9726_write_exp_gain(gain, line);
+ return rc;
+}
+
+static int32_t initialize_ov9726_registers(void)
+{
+ int32_t i;
+ int32_t rc = 0;
+ ov9726_ctrl->sensormode = SENSOR_PREVIEW_MODE ;
+ /* Configure sensor for Preview mode and Snapshot mode */
+ CDBG("Initialize_ov9726_registers\n");
+ for (i = 0; i < ov9726_array_length; i++) {
+ rc = ov9726_i2c_write_b(ov9726_client->addr,
+ ov9726_init_settings_array[i].reg_addr,
+ ov9726_init_settings_array[i].reg_val);
+ if (rc < 0)
+ return rc;
+ }
+ return rc;
+}
+
+static int32_t ov9726_video_config(int mode)
+{
+ int32_t rc = 0;
+
+ ov9726_ctrl->sensormode = mode;
+
+ if (config_not_set) {
+ struct msm_camera_csi_params ov9726_csi_params;
+
+ /* sensor in standby */
+ ov9726_i2c_write_b(ov9726_client->addr, 0x100, 0);
+ msleep(5);
+ /* Initialize Sensor registers */
+ ov9726_csi_params.data_format = CSI_10BIT;
+ ov9726_csi_params.lane_cnt = 1;
+ ov9726_csi_params.lane_assign = 0xe4;
+ ov9726_csi_params.dpcm_scheme = 0;
+ ov9726_csi_params.settle_cnt = 7;
+
+ rc = msm_camio_csi_config(&ov9726_csi_params);
+ rc = initialize_ov9726_registers();
+ config_not_set = 0;
+ }
+ return rc;
+}
+
+static int32_t ov9726_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ ov9726_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t ov9726_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ ov9726_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t ov9726_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = ov9726_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = ov9726_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = ov9726_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int ov9726_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ uint16_t chipidl, chipidh;
+
+ if (data->sensor_reset_enable) {
+ rc = gpio_request(data->sensor_reset, "ov9726");
+ if (!rc) {
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(20);
+ } else
+ goto init_probe_done;
+ }
+ /* 3. Read sensor Model ID: */
+ rc = ov9726_i2c_read(OV9726_PIDH_REG, &chipidh, 1);
+ if (rc < 0)
+ goto init_probe_fail;
+ rc = ov9726_i2c_read(OV9726_PIDL_REG, &chipidl, 1);
+ if (rc < 0)
+ goto init_probe_fail;
+ CDBG("kov9726 model_id = 0x%x 0x%x\n", chipidh, chipidl);
+ /* 4. Compare sensor ID to OV9726 ID: */
+ if (chipidh != OV9726_PID) {
+ rc = -ENODEV;
+ printk(KERN_INFO "Probeinit fail\n");
+ goto init_probe_fail;
+ }
+ CDBG("chipidh == OV9726_PID\n");
+ msleep(OV9726_RESET_DELAY_MSECS);
+ CDBG("after delay\n");
+ goto init_probe_done;
+
+init_probe_fail:
+ if (data->sensor_reset_enable) {
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ }
+init_probe_done:
+ printk(KERN_INFO " ov9726_probe_init_sensor finishes\n");
+ return rc;
+}
+
+int ov9726_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+
+ CDBG("Calling ov9726_sensor_open_init\n");
+ ov9726_ctrl = kzalloc(sizeof(struct ov9726_ctrl_t), GFP_KERNEL);
+ if (!ov9726_ctrl) {
+ CDBG("ov9726_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ ov9726_ctrl->curr_lens_pos = -1;
+ ov9726_ctrl->fps_divider = 1 << OV9726_Q10Shift;
+ ov9726_ctrl->pict_fps_divider = 1 << OV9726_Q10Shift;
+ ov9726_ctrl->set_test = TEST_OFF;
+ ov9726_ctrl->prev_res = FULL_SIZE;
+ ov9726_ctrl->pict_res = FULL_SIZE;
+ ov9726_ctrl->curr_res = INVALID_SIZE;
+ config_not_set = 1;
+ if (data)
+ ov9726_ctrl->sensordata = data;
+ /* enable mclk first */
+ msm_camio_clk_rate_set(OV9726_DEFAULT_CLOCK_RATE);
+ msleep(20);
+ rc = ov9726_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ ov9726_ctrl->fps = (uint16_t)(30 << OV9726_Q8Shift);
+ /* generate test pattern */
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+ /* reset the driver state */
+init_fail:
+ CDBG(" init_fail\n");
+ kfree(ov9726_ctrl);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+}
+
+static int ov9726_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&ov9726_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id ov9726_i2c_id[] = {
+ { "ov9726", 0},
+ { }
+};
+
+static int ov9726_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("ov9726_probe called!\n");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+ ov9726_sensorw = kzalloc(sizeof(struct ov9726_work_t), GFP_KERNEL);
+ if (!ov9726_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+ i2c_set_clientdata(client, ov9726_sensorw);
+ ov9726_init_client(client);
+ ov9726_client = client;
+ msleep(50);
+ CDBG("ov9726_probe successed! rc = %d\n", rc);
+ return 0;
+probe_failure:
+ CDBG("ov9726_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int __exit ov9726_remove(struct i2c_client *client)
+{
+ struct ov9726_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ ov9726_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver ov9726_i2c_driver = {
+ .id_table = ov9726_i2c_id,
+ .probe = ov9726_i2c_probe,
+ .remove = __exit_p(ov9726_i2c_remove),
+ .driver = {
+ .name = "ov9726",
+ },
+};
+
+int ov9726_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;
+ mutex_lock(&ov9726_mut);
+ CDBG("ov9726_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ ov9726_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 = ov9726_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 = ov9726_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 = ov9726_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 =
+ ov9726_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 = ov9726_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 = ov9726_set_fps(&(cdata.cfg.fps));
+ break;
+ case CFG_SET_EXP_GAIN:
+ rc = ov9726_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = ov9726_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_MODE:
+ rc = ov9726_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ case CFG_MOVE_FOCUS:
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = 0;
+ break;
+ case CFG_SET_EFFECT:
+ default:
+ rc = -EFAULT;
+ break;
+ }
+ mutex_unlock(&ov9726_mut);
+ return rc;
+}
+
+static int ov9726_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ if (data->sensor_reset_enable) {
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ }
+ return 0;
+}
+
+static int ov9726_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&ov9726_mut);
+ gpio_direction_output(ov9726_ctrl->sensordata->sensor_reset,
+ 0);
+ gpio_free(ov9726_ctrl->sensordata->sensor_reset);
+ kfree(ov9726_ctrl);
+ ov9726_ctrl = NULL;
+ CDBG("ov9726_release completed\n");
+ mutex_unlock(&ov9726_mut);
+ return rc;
+}
+
+static int ov9726_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+
+ rc = i2c_add_driver(&ov9726_i2c_driver);
+ if (rc < 0 || ov9726_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(24000000);
+ msleep(20);
+ rc = ov9726_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+
+ s->s_init = ov9726_sensor_open_init;
+ s->s_release = ov9726_sensor_release;
+ s->s_config = ov9726_sensor_config;
+ s->s_camera_type = FRONT_CAMERA_2D;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+ ov9726_probe_init_done(info);
+
+ return rc;
+
+probe_fail:
+ CDBG("SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __ov9726_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, ov9726_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __ov9726_probe,
+ .driver = {
+ .name = "msm_camera_ov9726",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ov9726_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(ov9726_init);
+void ov9726_exit(void)
+{
+ i2c_del_driver(&ov9726_i2c_driver);
+}
+
+MODULE_DESCRIPTION("OMNI VGA Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/msm/ov9726.h b/drivers/media/video/msm/ov9726.h
new file mode 100644
index 0000000..56d3da6
--- /dev/null
+++ b/drivers/media/video/msm/ov9726.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef OV9726_H
+#define OV9726_H
+#include <linux/types.h>
+#include <mach/board.h>
+
+/* 16bit address - 8 bit context register structure */
+struct reg_struct_type {
+ uint16_t reg_addr;
+ unsigned char reg_val;
+};
+
+enum ov9726_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum ov9726_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+extern struct reg_struct_type ov9726_init_settings_array[];
+extern int32_t ov9726_array_length;
+#endif
+
diff --git a/drivers/media/video/msm/ov9726_reg.c b/drivers/media/video/msm/ov9726_reg.c
new file mode 100644
index 0000000..54afbe8
--- /dev/null
+++ b/drivers/media/video/msm/ov9726_reg.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "ov9726.h"
+struct reg_struct_type ov9726_init_settings_array[] = {
+ {0x0103, 0x01}, /* SOFTWARE_RESET */
+ {0x3026, 0x00}, /* OUTPUT_SELECT01 */
+ {0x3027, 0x00}, /* OUTPUT_SELECT02 */
+ {0x3002, 0xe8}, /* IO_CTRL00 */
+ {0x3004, 0x03}, /* IO_CTRL01 */
+ {0x3005, 0xff}, /* IO_CTRL02 */
+ {0x3703, 0x42},
+ {0x3704, 0x10},
+ {0x3705, 0x45},
+ {0x3603, 0xaa},
+ {0x3632, 0x2f},
+ {0x3620, 0x66},
+ {0x3621, 0xc0},
+ {0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+ {0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+ {0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+ {0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+ {0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+ {0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+ {0x3833, 0x04},
+ {0x3835, 0x02},
+ {0x4702, 0x04},
+ {0x4704, 0x00}, /* DVP_CTRL01 */
+ {0x4706, 0x08},
+ {0x5052, 0x01},
+ {0x3819, 0x6e},
+ {0x3817, 0x94},
+ {0x3a18, 0x00}, /* AEC_GAIN_CEILING_HI */
+ {0x3a19, 0x7f}, /* AEC_GAIN_CEILING_LO */
+ {0x404e, 0x7e},
+ {0x3631, 0x52},
+ {0x3633, 0x50},
+ {0x3630, 0xd2},
+ {0x3604, 0x08},
+ {0x3601, 0x40},
+ {0x3602, 0x14},
+ {0x3610, 0xa0},
+ {0x3612, 0x20},
+ {0x034c, 0x05}, /* X_OUTPUT_SIZE_HI */
+ {0x034d, 0x10}, /* X_OUTPUT_SIZE_LO */
+ {0x034e, 0x03}, /* Y_OUTPUT_SIZE_HI */
+ {0x034f, 0x28}, /* Y_OUTPUT_SIZE_LO */
+ {0x0340, 0x03}, /* FRAME_LENGTH_LINES_HI */
+ {0x0341, 0xC1}, /* FRAME_LENGTH_LINES_LO */
+ {0x0342, 0x06}, /* LINE_LENGTH_PCK_HI */
+ {0x0343, 0x80}, /* LINE_LENGTH_PCK_LO */
+ {0x0202, 0x03}, /* COARSE_INTEGRATION_TIME_HI */
+ {0x0203, 0x43}, /* COARSE_INTEGRATION_TIME_LO */
+ {0x0303, 0x01}, /* VT_SYS_CLK_DIV_LO */
+ {0x3002, 0x00}, /* IO_CTRL00 */
+ {0x3004, 0x00}, /* IO_CTRL01 */
+ {0x3005, 0x00}, /* IO_CTRL02 */
+ {0x4801, 0x0f}, /* MIPI_CTRL01 */
+ {0x4803, 0x05}, /* MIPI_CTRL03 */
+ {0x4601, 0x16}, /* VFIFO_READ_CONTROL */
+ {0x3014, 0x05}, /* SC_CMMN_MIPI / SC_CTRL00 */
+ {0x3104, 0x80},
+ {0x0305, 0x04}, /* PRE_PLL_CLK_DIV_LO */
+ {0x0307, 0x64}, /* PLL_MULTIPLIER_LO */
+ {0x300c, 0x02},
+ {0x300d, 0x20},
+ {0x300e, 0x01},
+ {0x3010, 0x01},
+ {0x460e, 0x81}, /* VFIFO_CONTROL00 */
+ {0x0101, 0x01}, /* IMAGE_ORIENTATION */
+ {0x3707, 0x14},
+ {0x3622, 0x9f},
+ {0x5047, 0x3D}, /* ISP_CTRL47 */
+ {0x4002, 0x45}, /* BLC_CTRL02 */
+ {0x5000, 0x06}, /* ISP_CTRL0 */
+ {0x5001, 0x00}, /* ISP_CTRL1 */
+ {0x3406, 0x00}, /* AWB_MANUAL_CTRL */
+ {0x3503, 0x13}, /* AEC_ENABLE */
+ {0x4005, 0x18}, /* BLC_CTRL05 */
+ {0x4837, 0x21},
+ {0x0100, 0x01}, /* MODE_SELECT */
+ {0x3a0f, 0x64}, /* AEC_CTRL0F */
+ {0x3a10, 0x54}, /* AEC_CTRL10 */
+ {0x3a11, 0xc2}, /* AEC_CTRL11 */
+ {0x3a1b, 0x64}, /* AEC_CTRL1B */
+ {0x3a1e, 0x54}, /* AEC_CTRL1E */
+ {0x3a1a, 0x05}, /* AEC_DIFF_MAX */
+};
+int32_t ov9726_array_length = sizeof(ov9726_init_settings_array) /
+ sizeof(ov9726_init_settings_array[0]);
+
diff --git a/drivers/media/video/msm/qs_s5k4e1.c b/drivers/media/video/msm/qs_s5k4e1.c
new file mode 100644
index 0000000..d6bb51d
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1.c
@@ -0,0 +1,1663 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include "qs_s5k4e1.h"
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME 0x0202
+/* Gain */
+#define REG_GLOBAL_GAIN 0x0204
+#define REG_GR_GAIN 0x020E
+#define REG_R_GAIN 0x0210
+#define REG_B_GAIN 0x0212
+#define REG_GB_GAIN 0x0214
+/* PLL registers */
+#define REG_FRAME_LENGTH_LINES 0x0340
+#define REG_LINE_LENGTH_PCK 0x0342
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+#define REG_VCM_NEW_CODE 0x30F2
+#define AF_ADDR 0x18
+#define BRIDGE_ADDR 0x80
+/*============================================================================
+ TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define Q8 0x00000100
+#define Q10 0x00000400
+#define QS_S5K4E1_MASTER_CLK_RATE 24000000
+#define QS_S5K4E1_OFFSET 8
+
+/* AF Total steps parameters */
+#define QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR 32
+
+uint16_t qs_s5k4e1_step_position_table[QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR+1];
+uint16_t qs_s5k4e1_nl_region_boundary1;
+uint16_t qs_s5k4e1_nl_region_code_per_step1 = 190;
+uint16_t qs_s5k4e1_l_region_code_per_step = 8;
+uint16_t qs_s5k4e1_damping_threshold = 10;
+uint16_t qs_s5k4e1_sw_damping_time_wait = 8;
+uint16_t qs_s5k4e1_af_mode = 4;
+int16_t qs_s5k4e1_af_initial_code = 190;
+int16_t qs_s5k4e1_af_right_adjust;
+
+struct qs_s5k4e1_work_t {
+ struct work_struct work;
+};
+
+static struct qs_s5k4e1_work_t *qs_s5k4e1_sensorw;
+static struct i2c_client *qs_s5k4e1_client;
+static char lens_eeprom_data[864];
+static bool cali_data_status;
+struct qs_s5k4e1_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ uint16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum qs_s5k4e1_resolution_t prev_res;
+ enum qs_s5k4e1_resolution_t pict_res;
+ enum qs_s5k4e1_resolution_t curr_res;
+ enum qs_s5k4e1_test_mode_t set_test;
+ enum qs_s5k4e1_cam_mode_t cam_mode;
+};
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static bool CSI_CONFIG, LENS_SHADE_CONFIG, default_lens_shade;
+static struct qs_s5k4e1_ctrl_t *qs_s5k4e1_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(qs_s5k4e1_wait_queue);
+DEFINE_MUTEX(qs_s5k4e1_mut);
+
+static int cam_debug_init(void);
+static struct dentry *debugfs_base;
+/*=============================================================*/
+
+static int qs_s5k4e1_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(qs_s5k4e1_client->adapter, msgs, 2) < 0) {
+ CDBG("qs_s5k4e1_i2c_rxdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t qs_s5k4e1_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(qs_s5k4e1_client->adapter, msg, 1) < 0) {
+ CDBG("qs_s5k4e1_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t qs_s5k4e1_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = qs_s5k4e1_i2c_rxdata(qs_s5k4e1_client->addr>>1, buf, rlen);
+ if (rc < 0) {
+ CDBG("qs_s5k4e1_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("qs_s5k4e1_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+ return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_w_sensor(unsigned short waddr,
+ uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+ rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, 4);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_b_table(struct qs_s5k4e1_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = qs_s5k4e1_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_i2c_write_seq_sensor(unsigned short waddr,
+ unsigned char *seq_data, int len)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[len+2];
+ int i = 0;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ for (i = 0; i < len; i++)
+ buf[i+2] = seq_data[i];
+ rc = qs_s5k4e1_i2c_txdata(qs_s5k4e1_client->addr>>1, buf, len+2);
+ return rc;
+}
+
+static int32_t af_i2c_write_b_sensor(unsigned short baddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[2];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", baddr, bdata);
+ rc = qs_s5k4e1_i2c_txdata(AF_ADDR>>1, buf, 2);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ baddr, bdata);
+ }
+ return rc;
+}
+
+static int32_t bridge_i2c_write_w(unsigned short waddr, uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("bridge_i2c_write_w addr = 0x%x, val = 0x%x\n", waddr, wdata);
+ rc = qs_s5k4e1_i2c_txdata(BRIDGE_ADDR>>1, buf, 4);
+ if (rc < 0) {
+ CDBG("bridge_i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+ return rc;
+}
+
+static int32_t bridge_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = qs_s5k4e1_i2c_rxdata(BRIDGE_ADDR>>1, buf, rlen);
+ if (rc < 0) {
+ CDBG("bridge_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("bridge_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+ return rc;
+}
+
+static int32_t qs_s5k4e1_eeprom_i2c_read(unsigned short raddr,
+ unsigned char *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned short i2caddr = 0xA0 >> 1;
+ unsigned char buf[rlen];
+ int i = 0;
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = qs_s5k4e1_i2c_rxdata(i2caddr, buf, rlen);
+ if (rc < 0) {
+ CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ for (i = 0; i < rlen; i++) {
+ rdata[i] = buf[i];
+ CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x index: %d val = 0x%x!\n",
+ raddr, i, buf[i]);
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_eeprom_i2c_read_b(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ rc = qs_s5k4e1_eeprom_i2c_read(raddr, &buf[0], rlen);
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("qs_s5k4e1_eeprom_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+ return rc;
+}
+
+static int32_t qs_s5k4e1_get_calibration_data(
+ struct sensor_3d_cali_data_t *cdata)
+{
+ int32_t rc = 0;
+ cali_data_status = 1;
+ rc = qs_s5k4e1_eeprom_i2c_read(0x0,
+ &(cdata->left_p_matrix[0][0][0]), 96);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read(0x60,
+ &(cdata->right_p_matrix[0][0][0]), 96);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read(0xC0, &(cdata->square_len[0]), 8);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read(0xC8, &(cdata->focal_len[0]), 8);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read(0xD0, &(cdata->pixel_pitch[0]), 8);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x100, &(cdata->left_r), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x101, &(cdata->right_r), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x102, &(cdata->left_b), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x103, &(cdata->right_b), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x104, &(cdata->left_gb), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x105, &(cdata->right_gb), 1);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x110, &(cdata->left_af_far), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x112, &(cdata->right_af_far), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x114, &(cdata->left_af_mid), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x116, &(cdata->right_af_mid), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x118, &(cdata->left_af_short), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x11A, &(cdata->right_af_short), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x11C, &(cdata->left_af_5um), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x11E, &(cdata->right_af_5um), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x120, &(cdata->left_af_50up), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x122, &(cdata->right_af_50up), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x124, &(cdata->left_af_50down), 2);
+ if (rc < 0)
+ goto fail;
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x126, &(cdata->right_af_50down), 2);
+ if (rc < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ cali_data_status = 0;
+ return -EIO;
+
+}
+static int32_t qs_s5k4e1_write_left_lsc(char *left_lsc, int rt)
+{
+ struct qs_s5k4e1_i2c_reg_conf *ptr = (struct qs_s5k4e1_i2c_reg_conf *)
+ (qs_s5k4e1_regs.reg_lens + rt);
+ bridge_i2c_write_w(0x06, 0x02);
+ if (!LENS_SHADE_CONFIG) {
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+ qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+ if (default_lens_shade)
+ qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.
+ reg_default_lens, qs_s5k4e1_regs.reg_default_lens_size);
+ else {
+ qs_s5k4e1_i2c_write_seq_sensor(0x3200,
+ &left_lsc[0], 216);
+ qs_s5k4e1_i2c_write_seq_sensor(0x32D8,
+ &left_lsc[216], 216);
+ }
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x60);
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+ } else
+ qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_write_right_lsc(char *right_lsc, int rt)
+{
+ struct qs_s5k4e1_i2c_reg_conf *ptr = (struct qs_s5k4e1_i2c_reg_conf *)
+ (qs_s5k4e1_regs.reg_lens + rt);
+ bridge_i2c_write_w(0x06, 0x01);
+ if (!LENS_SHADE_CONFIG) {
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+ qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+ if (default_lens_shade)
+ qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.
+ reg_default_lens, qs_s5k4e1_regs.reg_default_lens_size);
+ else {
+ qs_s5k4e1_i2c_write_seq_sensor(0x3200,
+ &right_lsc[0], 216);
+ qs_s5k4e1_i2c_write_seq_sensor(0x32D8,
+ &right_lsc[216], 216);
+ }
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x60);
+ qs_s5k4e1_i2c_write_b_sensor(0x3096, 0x40);
+ } else
+ qs_s5k4e1_i2c_write_b_table(ptr, qs_s5k4e1_regs.reg_lens_size);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_write_lsc(char *lsc, int rt)
+{
+ if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+ qs_s5k4e1_write_left_lsc(&lsc[0], rt);
+ qs_s5k4e1_write_right_lsc(&lsc[432], rt);
+ bridge_i2c_write_w(0x06, 0x03);
+ } else if (qs_s5k4e1_ctrl->cam_mode == MODE_2D_LEFT)
+ qs_s5k4e1_write_left_lsc(&lsc[0], rt);
+ else if (qs_s5k4e1_ctrl->cam_mode == MODE_2D_RIGHT)
+ qs_s5k4e1_write_right_lsc(&lsc[432], rt);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_read_left_lsc(char *left_lsc)
+{
+ qs_s5k4e1_eeprom_i2c_read(0x200, &left_lsc[0], 216);
+ qs_s5k4e1_eeprom_i2c_read(0x2D8, &left_lsc[216], 216);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_read_right_lsc(char *right_lsc)
+{
+ qs_s5k4e1_eeprom_i2c_read(0x3B0, &right_lsc[0], 216);
+ qs_s5k4e1_eeprom_i2c_read(0x488, &right_lsc[216], 216);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_read_lsc(char *lsc)
+{
+ qs_s5k4e1_read_left_lsc(&lsc[0]);
+ qs_s5k4e1_read_right_lsc(&lsc[432]);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_bridge_reset(void){
+ unsigned short RegData = 0, GPIOInState = 0;
+ int32_t rc = 0;
+ rc = bridge_i2c_write_w(0x50, 0x00);
+ if (rc < 0)
+ goto bridge_fail;
+ rc = bridge_i2c_write_w(0x53, 0x00);
+ if (rc < 0)
+ goto bridge_fail;
+ msleep(30);
+ rc = bridge_i2c_write_w(0x53, 0x01);
+ if (rc < 0)
+ goto bridge_fail;
+ msleep(30);
+ rc = bridge_i2c_write_w(0x0E, 0xFFFF);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_read(0x54, &RegData, 2);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_write_w(0x54, (RegData | 0x1));
+ if (rc < 0)
+ goto err;
+ msleep(30);
+ rc = bridge_i2c_read(0x54, &RegData, 2);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_write_w(0x54, (RegData | 0x4));
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_write_w(0x55, (GPIOInState | 0x1));
+ if (rc < 0)
+ goto err;
+ msleep(30);
+ rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_write_w(0x55, (GPIOInState | 0x4));
+ if (rc < 0)
+ goto err;
+ msleep(30);
+ rc = bridge_i2c_read(0x55, &GPIOInState, 2);
+ if (rc < 0)
+ goto err;
+ GPIOInState = ((GPIOInState >> 4) & 0x1);
+
+ rc = bridge_i2c_read(0x08, &GPIOInState, 2);
+ if (rc < 0)
+ goto err;
+ rc = bridge_i2c_write_w(0x08, GPIOInState | 0x4000);
+ if (rc < 0)
+ goto err;
+ return rc;
+
+err:
+ bridge_i2c_write_w(0x53, 0x00);
+ msleep(30);
+
+bridge_fail:
+ return rc;
+
+}
+
+static void qs_s5k4e1_bridge_config(int mode, int rt)
+{
+ unsigned short RegData = 0;
+ if (mode == MODE_3D) {
+ bridge_i2c_read(0x54, &RegData, 2);
+ bridge_i2c_write_w(0x54, (RegData | 0x2));
+ bridge_i2c_write_w(0x54, (RegData | 0xa));
+ bridge_i2c_read(0x55, &RegData, 2);
+ bridge_i2c_write_w(0x55, (RegData | 0x2));
+ bridge_i2c_write_w(0x55, (RegData | 0xa));
+ bridge_i2c_write_w(0x14, 0x0C);
+ msleep(20);
+ bridge_i2c_write_w(0x16, 0x00);
+ bridge_i2c_write_w(0x51, 0x3);
+ bridge_i2c_write_w(0x52, 0x1);
+ bridge_i2c_write_w(0x06, 0x03);
+ bridge_i2c_write_w(0x04, 0x2018);
+ bridge_i2c_write_w(0x50, 0x00);
+ } else if (mode == MODE_2D_RIGHT) {
+ bridge_i2c_read(0x54, &RegData, 2);
+ RegData |= 0x2;
+ bridge_i2c_write_w(0x54, RegData);
+ bridge_i2c_write_w(0x54, (RegData & ~(0x8)));
+ bridge_i2c_read(0x55, &RegData, 2);
+ RegData |= 0x2;
+ bridge_i2c_write_w(0x55, RegData);
+ bridge_i2c_write_w(0x55, (RegData & ~(0x8)));
+ bridge_i2c_write_w(0x14, 0x04);
+ msleep(20);
+ bridge_i2c_write_w(0x51, 0x3);
+ bridge_i2c_write_w(0x06, 0x01);
+ bridge_i2c_write_w(0x04, 0x2018);
+ bridge_i2c_write_w(0x50, 0x01);
+ } else if (mode == MODE_2D_LEFT) {
+ bridge_i2c_read(0x54, &RegData, 2);
+ RegData |= 0x8;
+ bridge_i2c_write_w(0x54, RegData);
+ bridge_i2c_write_w(0x54, (RegData & ~(0x2)));
+ bridge_i2c_read(0x55, &RegData, 2);
+ RegData |= 0x8;
+ bridge_i2c_write_w(0x55, RegData);
+ bridge_i2c_write_w(0x55, (RegData & ~(0x2)));
+ bridge_i2c_write_w(0x14, 0x08);
+ msleep(20);
+ bridge_i2c_write_w(0x51, 0x3);
+ bridge_i2c_write_w(0x06, 0x02);
+ bridge_i2c_write_w(0x04, 0x2018);
+ bridge_i2c_write_w(0x50, 0x02);
+ }
+}
+
+static void qs_s5k4e1_group_hold_on(void)
+{
+ qs_s5k4e1_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+}
+
+static void qs_s5k4e1_group_hold_off(void)
+{
+ qs_s5k4e1_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+}
+
+static void qs_s5k4e1_start_stream(void)
+{
+ qs_s5k4e1_i2c_write_b_sensor(0x0100, 0x01);
+}
+
+static void qs_s5k4e1_stop_stream(void)
+{
+ qs_s5k4e1_i2c_write_b_sensor(0x0100, 0x00);
+}
+
+static void qs_s5k4e1_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider, d1, d2;
+
+ d1 = prev_frame_length_lines * 0x00000400 / snap_frame_length_lines;
+ d2 = prev_line_length_pck * 0x00000400 / snap_line_length_pck;
+ divider = d1 * d2 / 0x400;
+
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+ /* 2 is the ratio of no.of snapshot channels
+ to number of preview channels */
+}
+
+static uint16_t qs_s5k4e1_get_prev_lines_pf(void)
+{
+
+ return prev_frame_length_lines;
+
+}
+
+static uint16_t qs_s5k4e1_get_prev_pixels_pl(void)
+{
+ return prev_line_length_pck;
+
+}
+
+static uint16_t qs_s5k4e1_get_pict_lines_pf(void)
+{
+ return snap_frame_length_lines;
+}
+
+static uint16_t qs_s5k4e1_get_pict_pixels_pl(void)
+{
+ return snap_line_length_pck;
+}
+
+
+static uint32_t qs_s5k4e1_get_pict_max_exp_lc(void)
+{
+ return snap_frame_length_lines * 24;
+}
+
+static int32_t qs_s5k4e1_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_line_length_pclk;
+ int32_t rc = 0;
+ qs_s5k4e1_ctrl->fps_divider = fps->fps_div;
+ qs_s5k4e1_ctrl->pict_fps_divider = fps->pict_fps_div;
+ if (qs_s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ total_line_length_pclk = (uint16_t)
+ ((prev_line_length_pck) * qs_s5k4e1_ctrl->fps_divider/0x400);
+ } else {
+ total_line_length_pclk = (uint16_t)
+ ((snap_line_length_pck) *
+ qs_s5k4e1_ctrl->pict_fps_divider/0x400);
+ }
+ qs_s5k4e1_group_hold_on();
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_LINE_LENGTH_PCK,
+ total_line_length_pclk);
+ qs_s5k4e1_group_hold_off();
+ return rc;
+}
+
+static int32_t qs_s5k4e1_write_exp_gain(struct sensor_3d_exp_cfg exp_cfg)
+{
+ uint16_t max_legal_gain = 0x0200;
+ uint16_t min_ll_pck = 0x0AB2;
+ uint32_t ll_pck, fl_lines;
+ uint16_t gain = exp_cfg.gain;
+ uint32_t line = exp_cfg.line;
+ uint32_t ll_ratio;
+ int32_t rc = 0;
+ if (gain > max_legal_gain) {
+ CDBG("Max legal gain Line:%d\n", __LINE__);
+ gain = max_legal_gain;
+ }
+ CDBG("qs_s5k4e1_write_exp_gain : gain = %d line = %d\n", gain, line);
+
+ if (qs_s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ qs_s5k4e1_ctrl->my_reg_gain = gain;
+ qs_s5k4e1_ctrl->my_reg_line_count = (uint16_t) line;
+ ll_ratio = (uint32_t)(qs_s5k4e1_ctrl->fps_divider);
+ fl_lines = prev_frame_length_lines;
+ ll_pck = prev_line_length_pck;
+ } else {
+ ll_ratio = (uint32_t)(qs_s5k4e1_ctrl->pict_fps_divider);
+ fl_lines = snap_frame_length_lines;
+ ll_pck = snap_line_length_pck;
+ }
+ if (((fl_lines * ll_ratio / 0x400) - QS_S5K4E1_OFFSET) < line) {
+ ll_ratio = ll_ratio * line / (fl_lines - QS_S5K4E1_OFFSET);
+ line = fl_lines - QS_S5K4E1_OFFSET;
+ }
+ ll_pck = ll_pck * ll_ratio / 0x400;
+ if (ll_pck < min_ll_pck)
+ ll_pck = min_ll_pck;
+ qs_s5k4e1_group_hold_on();
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_GLOBAL_GAIN, gain);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_LINE_LENGTH_PCK, ll_pck);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_COARSE_INTEGRATION_TIME, line);
+ if ((qs_s5k4e1_ctrl->cam_mode == MODE_3D) && (cali_data_status == 1)) {
+ bridge_i2c_write_w(0x06, 0x01);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_GLOBAL_GAIN,
+ exp_cfg.gain_adjust);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_GR_GAIN, exp_cfg.gr_gain);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_R_GAIN,
+ exp_cfg.r_gain);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_B_GAIN,
+ exp_cfg.b_gain);
+ rc = qs_s5k4e1_i2c_write_w_sensor(REG_GB_GAIN,
+ exp_cfg.gb_gain);
+ bridge_i2c_write_w(0x06, 0x03);
+ }
+ qs_s5k4e1_group_hold_off();
+ return rc;
+}
+
+static int32_t qs_s5k4e1_set_pict_exp_gain(struct sensor_3d_exp_cfg exp_cfg)
+{
+ int32_t rc = 0;
+ rc = qs_s5k4e1_write_exp_gain(exp_cfg);
+ return rc;
+}
+
+static int32_t qs_s5k4e1_write_focus_value(uint16_t code_value)
+{
+ uint8_t code_val_msb, code_val_lsb;
+ if ((qs_s5k4e1_ctrl->cam_mode == MODE_2D_LEFT) ||
+ (qs_s5k4e1_ctrl->cam_mode == MODE_3D)) {
+ /* Left */
+ bridge_i2c_write_w(0x06, 0x02);
+ CDBG("%s: Left Lens Position: %d\n", __func__,
+ code_value);
+ code_val_msb = code_value >> 4;
+ code_val_lsb = (code_value & 0x000F) << 4;
+ code_val_lsb |= qs_s5k4e1_af_mode;
+ if (af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+ CDBG("move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+ }
+
+ if ((qs_s5k4e1_ctrl->cam_mode == MODE_2D_RIGHT) ||
+ (qs_s5k4e1_ctrl->cam_mode == MODE_3D)) {
+ /* Right */
+ bridge_i2c_write_w(0x06, 0x01);
+ code_value += qs_s5k4e1_af_right_adjust;
+ CDBG("%s: Right Lens Position: %d\n", __func__,
+ code_value);
+ code_val_msb = code_value >> 4;
+ code_val_lsb = (code_value & 0x000F) << 4;
+ code_val_lsb |= qs_s5k4e1_af_mode;
+ if (af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+ CDBG("move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+ }
+
+ if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+ /* 3D Mode */
+ bridge_i2c_write_w(0x06, 0x03);
+ }
+ usleep(qs_s5k4e1_sw_damping_time_wait*50);
+ return 0;
+}
+
+static int32_t qs_s5k4e1_move_focus(int direction,
+ int32_t num_steps)
+{
+ int16_t step_direction, actual_step, dest_lens_position,
+ dest_step_position;
+ CDBG("Inside %s\n", __func__);
+ if (direction == MOVE_NEAR)
+ step_direction = 1;
+ else
+ step_direction = -1;
+
+ actual_step = (int16_t) (step_direction * (int16_t) num_steps);
+ dest_step_position = (int16_t) (qs_s5k4e1_ctrl->curr_step_pos +
+ actual_step);
+
+ if (dest_step_position > QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+ else if (dest_step_position < 0)
+ dest_step_position = 0;
+
+ if (dest_step_position == qs_s5k4e1_ctrl->curr_step_pos) {
+ CDBG("%s cur and dest pos are same\n", __func__);
+ CDBG("%s cur_step_pos:%d\n", __func__,
+ qs_s5k4e1_ctrl->curr_step_pos);
+ return 0;
+ }
+
+ dest_lens_position = qs_s5k4e1_step_position_table[dest_step_position];
+ CDBG("%s: Step Position: %d\n", __func__, dest_step_position);
+
+ if (step_direction < 0) {
+ if (num_steps >= 20) {
+ /* sweeping towards all the way in infinity direction */
+ qs_s5k4e1_af_mode = 2;
+ qs_s5k4e1_sw_damping_time_wait = 8;
+ } else if (num_steps <= 4) {
+ /* reverse search during macro mode */
+ qs_s5k4e1_af_mode = 4;
+ qs_s5k4e1_sw_damping_time_wait = 16;
+ } else {
+ qs_s5k4e1_af_mode = 3;
+ qs_s5k4e1_sw_damping_time_wait = 12;
+ }
+ } else {
+ /* coarse search towards macro direction */
+ qs_s5k4e1_af_mode = 4;
+ qs_s5k4e1_sw_damping_time_wait = 16;
+ }
+
+ if (qs_s5k4e1_ctrl->curr_lens_pos != dest_lens_position) {
+ if (qs_s5k4e1_write_focus_value(dest_lens_position) < 0) {
+ CDBG("move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+ }
+
+ qs_s5k4e1_ctrl->curr_step_pos = dest_step_position;
+ qs_s5k4e1_ctrl->curr_lens_pos = dest_lens_position;
+ return 0;
+}
+
+static int32_t qs_s5k4e1_set_default_focus(uint8_t af_step)
+{
+ int32_t rc = 0;
+ if (qs_s5k4e1_ctrl->curr_step_pos) {
+ rc = qs_s5k4e1_move_focus(MOVE_FAR,
+ qs_s5k4e1_ctrl->curr_step_pos);
+ if (rc < 0)
+ return rc;
+ } else {
+ rc = qs_s5k4e1_write_focus_value(
+ qs_s5k4e1_step_position_table[0]);
+ if (rc < 0)
+ return rc;
+ qs_s5k4e1_ctrl->curr_lens_pos =
+ qs_s5k4e1_step_position_table[0];
+ }
+ CDBG("%s\n", __func__);
+ return 0;
+}
+
+static void qs_s5k4e1_init_focus(void)
+{
+ uint8_t i;
+ int32_t rc = 0;
+ int16_t af_far_data = 0;
+ qs_s5k4e1_af_initial_code = 190;
+ /* Read the calibration data from left and right sensors if available */
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x110, &af_far_data, 2);
+ if (rc == 0) {
+ CDBG("%s: Left Far data - %d\n", __func__, af_far_data);
+ qs_s5k4e1_af_initial_code = af_far_data;
+ }
+
+ rc = qs_s5k4e1_eeprom_i2c_read_b(0x112, &af_far_data, 2);
+ if (rc == 0) {
+ CDBG("%s: Right Far data - %d\n", __func__, af_far_data);
+ qs_s5k4e1_af_right_adjust = af_far_data -
+ qs_s5k4e1_af_initial_code;
+ }
+
+ qs_s5k4e1_step_position_table[0] = qs_s5k4e1_af_initial_code;
+ for (i = 1; i <= QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ if (i <= qs_s5k4e1_nl_region_boundary1) {
+ qs_s5k4e1_step_position_table[i] =
+ qs_s5k4e1_step_position_table[i-1]
+ + qs_s5k4e1_nl_region_code_per_step1;
+ } else {
+ qs_s5k4e1_step_position_table[i] =
+ qs_s5k4e1_step_position_table[i-1]
+ + qs_s5k4e1_l_region_code_per_step;
+ }
+
+ if (qs_s5k4e1_step_position_table[i] > 1023)
+ qs_s5k4e1_step_position_table[i] = 1023;
+ }
+ qs_s5k4e1_ctrl->curr_step_pos = 0;
+}
+
+static int32_t qs_s5k4e1_test(enum qs_s5k4e1_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+ 1: Bypass Signal Processing
+ REG_0x30D8[5] is EBDMASK: 0:
+ Output Embedded data, 1: No output embedded data */
+ if (qs_s5k4e1_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ struct msm_camera_csi_params qs_s5k4e1_csi_params;
+
+ qs_s5k4e1_stop_stream();
+ msleep(80);
+ bridge_i2c_write_w(0x53, 0x00);
+ msleep(80);
+ if (update_type == REG_INIT) {
+ CSI_CONFIG = 0;
+ LENS_SHADE_CONFIG = 0;
+ default_lens_shade = 1;
+ bridge_i2c_write_w(0x53, 0x01);
+ msleep(30);
+ qs_s5k4e1_bridge_config(qs_s5k4e1_ctrl->cam_mode, rt);
+ msleep(30);
+ qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.rec_settings,
+ qs_s5k4e1_regs.rec_size);
+ msleep(30);
+ } else if (update_type == UPDATE_PERIODIC) {
+ qs_s5k4e1_write_lsc(lens_eeprom_data, rt);
+ msleep(100);
+ if (!CSI_CONFIG) {
+ if (qs_s5k4e1_ctrl->cam_mode == MODE_3D) {
+ qs_s5k4e1_csi_params.lane_cnt = 4;
+ qs_s5k4e1_csi_params.data_format = CSI_8BIT;
+ } else {
+ qs_s5k4e1_csi_params.lane_cnt = 2;
+ qs_s5k4e1_csi_params.data_format = CSI_10BIT;
+ }
+ qs_s5k4e1_csi_params.lane_assign = 0xe4;
+ qs_s5k4e1_csi_params.dpcm_scheme = 0;
+ qs_s5k4e1_csi_params.settle_cnt = 24;
+ rc = msm_camio_csi_config(&qs_s5k4e1_csi_params);
+ msleep(10);
+ cam_debug_init();
+ CSI_CONFIG = 1;
+ }
+ bridge_i2c_write_w(0x53, 0x01);
+ msleep(50);
+ qs_s5k4e1_i2c_write_b_table(qs_s5k4e1_regs.conf_array[rt].conf,
+ qs_s5k4e1_regs.conf_array[rt].size);
+ msleep(50);
+ qs_s5k4e1_start_stream();
+ msleep(80);
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ /* change sensor resolution if needed */
+ if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+ qs_s5k4e1_ctrl->prev_res) < 0)
+ return rc;
+ if (qs_s5k4e1_ctrl->set_test) {
+ if (qs_s5k4e1_test(qs_s5k4e1_ctrl->set_test) < 0)
+ return rc;
+ }
+
+ qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->prev_res;
+ qs_s5k4e1_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t qs_s5k4e1_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /*change sensor resolution if needed */
+ if (qs_s5k4e1_ctrl->curr_res != qs_s5k4e1_ctrl->pict_res) {
+ if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+ qs_s5k4e1_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->pict_res;
+ qs_s5k4e1_ctrl->sensormode = mode;
+ return rc;
+} /*end of qs_s5k4e1_snapshot_config*/
+
+static int32_t qs_s5k4e1_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ /* change sensor resolution if needed */
+ if (qs_s5k4e1_ctrl->curr_res != qs_s5k4e1_ctrl->pict_res) {
+ if (qs_s5k4e1_sensor_setting(UPDATE_PERIODIC,
+ qs_s5k4e1_ctrl->pict_res) < 0)
+ return rc;
+ }
+
+ qs_s5k4e1_ctrl->curr_res = qs_s5k4e1_ctrl->pict_res;
+ qs_s5k4e1_ctrl->sensormode = mode;
+ return rc;
+} /*end of qs_s5k4e1_raw_snapshot_config*/
+
+static int32_t qs_s5k4e1_mode_init(int mode, struct sensor_init_cfg init_info)
+{
+ int32_t rc = 0;
+ if (mode != qs_s5k4e1_ctrl->cam_mode) {
+ qs_s5k4e1_ctrl->prev_res = init_info.prev_res;
+ qs_s5k4e1_ctrl->pict_res = init_info.pict_res;
+ qs_s5k4e1_ctrl->cam_mode = mode;
+
+ prev_frame_length_lines =
+ ((qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+ .conf[QS_S5K4E1_FRAME_LENGTH_LINES_H].wdata << 8)
+ | qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+ .conf[QS_S5K4E1_FRAME_LENGTH_LINES_L].wdata);
+ prev_line_length_pck =
+ (qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+ .conf[QS_S5K4E1_LINE_LENGTH_PCK_H].wdata << 8)
+ | qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->prev_res]\
+ .conf[QS_S5K4E1_LINE_LENGTH_PCK_L].wdata;
+ snap_frame_length_lines =
+ (qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+ .conf[QS_S5K4E1_FRAME_LENGTH_LINES_H].wdata << 8)
+ | qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+ .conf[QS_S5K4E1_FRAME_LENGTH_LINES_L].wdata;
+ snap_line_length_pck =
+ (qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+ .conf[QS_S5K4E1_LINE_LENGTH_PCK_H].wdata << 8)
+ | qs_s5k4e1_regs.conf_array[qs_s5k4e1_ctrl->pict_res]\
+ .conf[QS_S5K4E1_LINE_LENGTH_PCK_L].wdata;
+
+ rc = qs_s5k4e1_sensor_setting(REG_INIT,
+ qs_s5k4e1_ctrl->prev_res);
+ }
+ return rc;
+}
+static int32_t qs_s5k4e1_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ qs_s5k4e1_ctrl->prev_res = res;
+ rc = qs_s5k4e1_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ qs_s5k4e1_ctrl->pict_res = res;
+ rc = qs_s5k4e1_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ qs_s5k4e1_ctrl->pict_res = res;
+ rc = qs_s5k4e1_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t qs_s5k4e1_power_down(void)
+{
+ qs_s5k4e1_stop_stream();
+ msleep(30);
+ qs_s5k4e1_af_mode = 2;
+ qs_s5k4e1_af_right_adjust = 0;
+ qs_s5k4e1_write_focus_value(0);
+ msleep(100);
+ /* Set AF actutator to PowerDown */
+ af_i2c_write_b_sensor(0x80, 00);
+ return 0;
+}
+
+static int qs_s5k4e1_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ CDBG("probe done\n");
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int
+ qs_s5k4e1_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "qs_s5k4e1");
+ CDBG(" qs_s5k4e1_probe_init_sensor\n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(50);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(13);
+ } else {
+ goto init_probe_done;
+ }
+ msleep(70);
+ rc = qs_s5k4e1_bridge_reset();
+ if (rc < 0)
+ goto init_probe_fail;
+ qs_s5k4e1_bridge_config(MODE_3D, RES_PREVIEW);
+ msleep(30);
+
+ CDBG(" qs_s5k4e1_probe_init_sensor is called\n");
+ rc = qs_s5k4e1_i2c_read(0x0000, &chipid, 2);
+ CDBG("ID: %d\n", chipid);
+ /* 4. Compare sensor ID to QS_S5K4E1 ID: */
+ if (chipid != 0x4e10) {
+ rc = -ENODEV;
+ CDBG("qs_s5k4e1_probe_init_sensor fail chip id mismatch\n");
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" qs_s5k4e1_probe_init_sensor fails\n");
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ qs_s5k4e1_probe_init_done(data);
+init_probe_done:
+ CDBG(" qs_s5k4e1_probe_init_sensor finishes\n");
+ return rc;
+}
+/* camsensor_qs_s5k4e1_reset */
+
+int qs_s5k4e1_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling qs_s5k4e1_sensor_open_init\n");
+
+ qs_s5k4e1_ctrl = kzalloc(sizeof(struct qs_s5k4e1_ctrl_t), GFP_KERNEL);
+ if (!qs_s5k4e1_ctrl) {
+ CDBG("qs_s5k4e1_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ qs_s5k4e1_ctrl->fps_divider = 1 * 0x00000400;
+ qs_s5k4e1_ctrl->pict_fps_divider = 1 * 0x00000400;
+ qs_s5k4e1_ctrl->set_test = TEST_OFF;
+ qs_s5k4e1_ctrl->cam_mode = MODE_INVALID;
+
+ if (data)
+ qs_s5k4e1_ctrl->sensordata = data;
+ if (rc < 0) {
+ CDBG("Calling qs_s5k4e1_sensor_open_init fail1\n");
+ return rc;
+ }
+ CDBG("%s: %d\n", __func__, __LINE__);
+ /* enable mclk first */
+ msm_camio_clk_rate_set(QS_S5K4E1_MASTER_CLK_RATE);
+ rc = qs_s5k4e1_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+/*Default mode is 3D*/
+ memcpy(lens_eeprom_data, data->eeprom_data, 864);
+ qs_s5k4e1_ctrl->fps = 30*Q8;
+ qs_s5k4e1_init_focus();
+ if (rc < 0) {
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ goto init_fail;
+ } else
+ goto init_done;
+init_fail:
+ CDBG("init_fail\n");
+ qs_s5k4e1_probe_init_done(data);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+} /*endof qs_s5k4e1_sensor_open_init*/
+
+static int qs_s5k4e1_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&qs_s5k4e1_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id qs_s5k4e1_i2c_id[] = {
+ {"qs_s5k4e1", 0},
+ { }
+};
+
+static int qs_s5k4e1_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("qs_s5k4e1_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ qs_s5k4e1_sensorw = kzalloc(sizeof(struct qs_s5k4e1_work_t),
+ GFP_KERNEL);
+ if (!qs_s5k4e1_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, qs_s5k4e1_sensorw);
+ qs_s5k4e1_init_client(client);
+ qs_s5k4e1_client = client;
+
+ msleep(50);
+
+ CDBG("qs_s5k4e1_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("qs_s5k4e1_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int qs_s5k4e1_send_wb_info(struct wb_info_cfg *wb)
+{
+ return 0;
+
+} /*end of qs_s5k4e1_snapshot_config*/
+
+static int __exit qs_s5k4e1_remove(struct i2c_client *client)
+{
+ struct qs_s5k4e1_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ qs_s5k4e1_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver qs_s5k4e1_i2c_driver = {
+ .id_table = qs_s5k4e1_i2c_id,
+ .probe = qs_s5k4e1_i2c_probe,
+ .remove = __exit_p(qs_s5k4e1_i2c_remove),
+ .driver = {
+ .name = "qs_s5k4e1",
+ },
+};
+
+int qs_s5k4e1_3D_sensor_config(void __user *argp)
+{
+ struct sensor_large_data cdata;
+ long rc;
+ if (copy_from_user(&cdata,
+ (void *)argp,
+ sizeof(struct sensor_large_data)))
+ return -EFAULT;
+ mutex_lock(&qs_s5k4e1_mut);
+ rc = qs_s5k4e1_get_calibration_data
+ (&cdata.data.sensor_3d_cali_data);
+ if (rc < 0)
+ goto fail;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_large_data)))
+ rc = -EFAULT;
+fail:
+ mutex_unlock(&qs_s5k4e1_mut);
+ return rc;
+}
+
+int qs_s5k4e1_2D_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;
+ mutex_lock(&qs_s5k4e1_mut);
+ CDBG("qs_s5k4e1_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ qs_s5k4e1_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 =
+ qs_s5k4e1_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 =
+ qs_s5k4e1_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 =
+ qs_s5k4e1_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 =
+ qs_s5k4e1_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 =
+ qs_s5k4e1_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 = qs_s5k4e1_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ qs_s5k4e1_write_exp_gain(
+ cdata.cfg.sensor_3d_exp);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ qs_s5k4e1_set_pict_exp_gain(
+ cdata.cfg.sensor_3d_exp);
+ break;
+
+ case CFG_SET_MODE:
+ rc = qs_s5k4e1_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = qs_s5k4e1_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc =
+ qs_s5k4e1_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ qs_s5k4e1_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = qs_s5k4e1_set_default_focus(
+ cdata.cfg.effect);
+ break;
+
+
+ case CFG_SEND_WB_INFO:
+ rc = qs_s5k4e1_send_wb_info(
+ &(cdata.cfg.wb_info));
+ break;
+
+ case CFG_SENSOR_INIT:
+ rc = qs_s5k4e1_mode_init(cdata.mode,
+ cdata.cfg.init_info);
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&qs_s5k4e1_mut);
+
+ return rc;
+}
+
+int qs_s5k4e1_sensor_config(void __user *argp)
+{
+ int cfgtype;
+ long rc;
+ if (copy_from_user(&cfgtype,
+ (void *)argp,
+ sizeof(int)))
+ return -EFAULT;
+ if (cfgtype != CFG_GET_3D_CALI_DATA)
+ rc = qs_s5k4e1_2D_sensor_config(argp);
+ else
+ rc = qs_s5k4e1_3D_sensor_config(argp);
+ return rc;
+}
+
+static int qs_s5k4e1_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&qs_s5k4e1_mut);
+ qs_s5k4e1_power_down();
+ bridge_i2c_write_w(0x53, 0x00);
+ msleep(20);
+ gpio_set_value_cansleep(qs_s5k4e1_ctrl->sensordata->sensor_reset, 0);
+ msleep(5);
+ gpio_free(qs_s5k4e1_ctrl->sensordata->sensor_reset);
+ kfree(qs_s5k4e1_ctrl);
+ qs_s5k4e1_ctrl = NULL;
+ CDBG("qs_s5k4e1_release completed\n");
+ mutex_unlock(&qs_s5k4e1_mut);
+
+ return rc;
+}
+
+static int qs_s5k4e1_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&qs_s5k4e1_i2c_driver);
+ if (rc < 0 || qs_s5k4e1_client == NULL) {
+ rc = -ENOTSUPP;
+ CDBG("I2C add driver failed");
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(QS_S5K4E1_MASTER_CLK_RATE);
+ rc = qs_s5k4e1_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ qs_s5k4e1_read_lsc(info->eeprom_data); /*Default mode is 3D*/
+ s->s_init = qs_s5k4e1_sensor_open_init;
+ s->s_release = qs_s5k4e1_sensor_release;
+ s->s_config = qs_s5k4e1_sensor_config;
+ s->s_mount_angle = 0;
+ s->s_camera_type = BACK_CAMERA_3D;
+ s->s_video_packing = SIDE_BY_SIDE_HALF;
+ s->s_snap_packing = SIDE_BY_SIDE_FULL;
+ bridge_i2c_write_w(0x53, 0x00);
+ msleep(20);
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ qs_s5k4e1_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("qs_s5k4e1_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static bool streaming = 1;
+
+static int qs_s5k4e1_focus_test(void *data, u64 *val)
+{
+ int i = 0;
+ qs_s5k4e1_set_default_focus(0);
+
+ for (i = 0; i < QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR; i++) {
+ qs_s5k4e1_move_focus(MOVE_NEAR, 1);
+ msleep(2000);
+ }
+ msleep(5000);
+ for ( ; i > 0; i--) {
+ qs_s5k4e1_move_focus(MOVE_FAR, 1);
+ msleep(2000);
+ }
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_focus, qs_s5k4e1_focus_test,
+ NULL, "%lld\n");
+
+static int qs_s5k4e1_step_test(void *data, u64 *val)
+{
+ int rc = 0;
+ struct sensor_large_data cdata;
+ rc = qs_s5k4e1_get_calibration_data
+ (&cdata.data.sensor_3d_cali_data);
+ if (rc < 0)
+ CDBG("%s: Calibration data read fail.\n", __func__);
+
+ return 0;
+}
+
+static int qs_s5k4e1_set_step(void *data, u64 val)
+{
+ qs_s5k4e1_l_region_code_per_step = val & 0xFF;
+ qs_s5k4e1_af_mode = (val >> 8) & 0xFF;
+ qs_s5k4e1_nl_region_code_per_step1 = (val >> 16) & 0xFFFF;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_step, qs_s5k4e1_step_test,
+ qs_s5k4e1_set_step, "%lld\n");
+
+static int cam_debug_stream_set(void *data, u64 val)
+{
+ int rc = 0;
+
+ if (val) {
+ qs_s5k4e1_start_stream();
+ streaming = 1;
+ } else {
+ qs_s5k4e1_stop_stream();
+ streaming = 0;
+ }
+
+ return rc;
+}
+
+static int cam_debug_stream_get(void *data, u64 *val)
+{
+ *val = streaming;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cam_stream, cam_debug_stream_get,
+ cam_debug_stream_set, "%llu\n");
+
+static uint16_t qs_s5k4e1_step_val = QS_S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+static uint8_t qs_s5k4e1_step_dir = MOVE_NEAR;
+static int qs_s5k4e1_af_step_config(void *data, u64 val)
+{
+ qs_s5k4e1_step_val = val & 0xFFFF;
+ qs_s5k4e1_step_dir = (val >> 16) & 0x1;
+ CDBG("%s\n", __func__);
+ return 0;
+}
+
+static int qs_s5k4e1_af_step(void *data, u64 *val)
+{
+ int i = 0;
+ int dir = MOVE_NEAR;
+ CDBG("%s\n", __func__);
+ qs_s5k4e1_set_default_focus(0);
+ msleep(5000);
+ if (qs_s5k4e1_step_dir == 1)
+ dir = MOVE_FAR;
+
+ for (i = 0; i < qs_s5k4e1_step_val; i += 4) {
+ qs_s5k4e1_move_focus(dir, 4);
+ msleep(1000);
+ }
+ qs_s5k4e1_set_default_focus(0);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(af_step, qs_s5k4e1_af_step,
+ qs_s5k4e1_af_step_config, "%llu\n");
+
+static int cam_debug_init(void)
+{
+ struct dentry *cam_dir;
+ debugfs_base = debugfs_create_dir("sensor", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ cam_dir = debugfs_create_dir("qs_s5k4e1", debugfs_base);
+ if (!cam_dir)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("focus", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_focus))
+ return -ENOMEM;
+ if (!debugfs_create_file("step", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_step))
+ return -ENOMEM;
+ if (!debugfs_create_file("stream", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &cam_stream))
+ return -ENOMEM;
+ if (!debugfs_create_file("af_step", S_IRUGO | S_IWUSR, cam_dir,
+ NULL, &af_step))
+ return -ENOMEM;
+ return 0;
+}
+
+static int __qs_s5k4e1_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, qs_s5k4e1_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __qs_s5k4e1_probe,
+ .driver = {
+ .name = "msm_camera_qs_s5k4e1",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init qs_s5k4e1_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(qs_s5k4e1_init);
+void qs_s5k4e1_exit(void)
+{
+ i2c_del_driver(&qs_s5k4e1_i2c_driver);
+}
+MODULE_DESCRIPTION("Samsung 5MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/qs_s5k4e1.h b/drivers/media/video/msm/qs_s5k4e1.h
new file mode 100644
index 0000000..f9c4c3f
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef QS_S5K4E1_H
+#define QS_S5K4E1_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct qs_s5k4e1_reg qs_s5k4e1_regs;
+
+#define LENS_SHADE_TABLE 16
+
+struct qs_s5k4e1_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+struct qs_s5k4e1_i2c_conf_array {
+ struct qs_s5k4e1_i2c_reg_conf *conf;
+ unsigned short size;
+};
+
+enum qs_s5k4e1_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum qs_s5k4e1_resolution_t {
+ QTR_2D_SIZE,
+ FULL_2D_SIZE,
+ QTR_3D_SIZE,
+ FULL_3D_SIZE,
+ INVALID_SIZE
+};
+enum qs_s5k4e1_setting {
+ RES_PREVIEW,
+ RES_CAPTURE,
+ RES_3D_PREVIEW,
+ RES_3D_CAPTURE
+};
+enum qs_s5k4e1_cam_mode_t {
+ MODE_2D_RIGHT,
+ MODE_2D_LEFT,
+ MODE_3D,
+ MODE_INVALID
+};
+enum qs_s5k4e1_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 qs_s5k4e1_reg_mode {
+ QS_S5K4E1_FRAME_LENGTH_LINES_H = 1,
+ QS_S5K4E1_FRAME_LENGTH_LINES_L,
+ QS_S5K4E1_LINE_LENGTH_PCK_H,
+ QS_S5K4E1_LINE_LENGTH_PCK_L,
+};
+
+struct qs_s5k4e1_reg {
+ const struct qs_s5k4e1_i2c_reg_conf *rec_settings;
+ const unsigned short rec_size;
+ const struct qs_s5k4e1_i2c_reg_conf *reg_prev;
+ const unsigned short reg_prev_size;
+ const struct qs_s5k4e1_i2c_reg_conf *reg_snap;
+ const unsigned short reg_snap_size;
+ const struct qs_s5k4e1_i2c_reg_conf (*reg_lens)[LENS_SHADE_TABLE];
+ const unsigned short reg_lens_size;
+ const struct qs_s5k4e1_i2c_reg_conf *reg_default_lens;
+ const unsigned short reg_default_lens_size;
+ const struct qs_s5k4e1_i2c_conf_array *conf_array;
+};
+#endif /* QS_S5K4E1_H */
diff --git a/drivers/media/video/msm/qs_s5k4e1_reg.c b/drivers/media/video/msm/qs_s5k4e1_reg.c
new file mode 100644
index 0000000..22876de
--- /dev/null
+++ b/drivers/media/video/msm/qs_s5k4e1_reg.c
@@ -0,0 +1,799 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+
+#include "qs_s5k4e1.h"
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_prev_settings_3d[] = {
+ {0x0100, 0x00},
+ /*Frame Length*/
+ {0x0340, 0x04},
+ {0x0341, 0x90},
+ /*Line Length*/
+ {0x0342, 0x0A},
+ {0x0343, 0xB2},
+ {0x3030, 0x06},
+ {0x3017, 0xA4},
+ {0x301B, 0x88},
+ {0x30BC, 0x90},
+ {0x301C, 0x04},
+ {0x0202, 0x04},
+ {0x0203, 0x12},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+ {0x0306, 0x00},
+ {0x0307, 0x60},
+ {0x30F1, 0x70},
+/*MIPI Size Setting*/
+ {0x30A9, 0x02},
+ {0x300E, 0xE8},
+ {0x0387, 0x01},
+ {0x0344, 0x01},
+ {0x0345, 0x18},
+ {0x0348, 0x09},
+ {0x0349, 0x17},
+ {0x0346, 0x01},
+ {0x0347, 0x94},
+ {0x034A, 0x06},
+ {0x034B, 0x13},
+ {0x0380, 0x00},
+ {0x0381, 0x01},
+ {0x0382, 0x00},
+ {0x0383, 0x01},
+ {0x0384, 0x00},
+ {0x0385, 0x01},
+ {0x0386, 0x00},
+ {0x0387, 0x01},
+ {0x034C, 0x04},
+ {0x034D, 0x00},
+ {0x034E, 0x04},
+ {0x034F, 0x80},
+ {0x30BF, 0xAA},
+ {0x30C0, 0x40},
+ {0x30C8, 0x04},
+ {0x30C9, 0x00},
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_prev_settings_2d[] = {
+ {0x0100, 0x00},
+ {0x0340, 0x03},
+ {0x0341, 0xe0},
+ {0x0342, 0x0A},
+ {0x0343, 0xB2},
+ {0x3030, 0x06},
+ {0x301B, 0x83},
+ {0x30BC, 0x98},
+ {0x301C, 0x04},
+ {0x0202, 0x01},
+ {0x0203, 0xFD},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+ {0x0306, 0x00},
+ {0x0307, 0x64},
+ {0x30F1, 0xa0},
+ {0x30A9, 0x02},
+ {0x300E, 0xEB},
+ {0x0387, 0x03},
+ {0x0344, 0x00},
+ {0x0345, 0x00},
+ {0x0348, 0x0A},
+ {0x0349, 0x2F},
+ {0x0346, 0x00},
+ {0x0347, 0x00},
+ {0x034A, 0x07},
+ {0x034B, 0xA7},
+ {0x0380, 0x00},
+ {0x0381, 0x01},
+ {0x0382, 0x00},
+ {0x0383, 0x01},
+ {0x0384, 0x00},
+ {0x0385, 0x01},
+ {0x0386, 0x00},
+ {0x0387, 0x03},
+ {0x034C, 0x05},
+ {0x034D, 0x10},
+ {0x034E, 0x03},
+ {0x034F, 0xd4},
+ {0x30BF, 0xAB},
+ {0x30C0, 0xc0},
+ {0x30C8, 0x06},
+ {0x30C9, 0x54},
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_snap_settings_2d[] = {
+ {0x0100, 0x00},
+ {0x0340, 0x07},
+ {0x0341, 0xb4},
+ {0x0342, 0x0A},
+ {0x0343, 0xB2},
+ {0x3030, 0x06}, /*shut streaming off*/
+ {0x300E, 0xE8},
+ {0x301B, 0x75},
+ {0x301C, 0x04},
+ {0x30BC, 0x98},
+ {0x0202, 0x04},
+ {0x0203, 0x12},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+ {0x0306, 0x00},
+ {0x0307, 0x64},
+ {0x30F1, 0xa0},
+ {0x30A9, 0x03},/*Horizontal Binning Off*/
+ {0x300E, 0xE8},/*Vertical Binning Off*/
+ {0x0387, 0x01},/*y_odd_inc*/
+ {0x034C, 0x0A},/*x_output size*/
+ {0x034D, 0x30},
+ {0x034E, 0x07},/*y_output size*/
+ {0x034F, 0xA8},
+ {0x30BF, 0xAB},/*outif_enable[7], data_type[5:0](2Bh = bayer 10bit)*/
+ {0x30C0, 0x86},/*video_offset[7:4] 3260%12*/
+ {0x30C8, 0x0C},/*video_data_length 3260 = 2608 * 1.25*/
+ {0x30C9, 0xBC},
+
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_snap_settings_3d[] = {
+ {0x0100, 0x00},
+
+/* Frame Length*/
+ {0x0340, 0x09},
+ {0x0341, 0x20},
+/* Line Length*/
+ {0x0342, 0x0A},
+ {0x0343, 0xB2},
+ {0x3030, 0x06},/*shut streaming off*/
+/*Analog Setting*/
+ {0x3017, 0xA4},
+ {0x301B, 0x88},
+ {0x30BC, 0x90},
+ {0x301C, 0x04},
+/*Integration setting ... */
+ {0x0202, 0x04},
+ {0x0203, 0x12},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+/*PLL setting ...*/
+ {0x0306, 0x00},
+ {0x0307, 0x60},
+ {0x30F1, 0x70},
+/*MIPI Size Setting*/
+ {0x30A9, 0x01},
+ {0x300E, 0xE8},
+ {0x0387, 0x01},
+ {0x0344, 0x01},/*x_addr_start*/
+ {0x0345, 0x14},
+ {0x0348, 0x09},/*x_addr_end*/
+ {0x0349, 0x17},
+ {0x0346, 0x01},/*y_addr_start*/
+ {0x0347, 0x94},
+ {0x034A, 0x06},/*y_addr_end*/
+ {0x034B, 0x13},
+ {0x0380, 0x00},/*x_even_inc 1*/
+ {0x0381, 0x01},
+ {0x0382, 0x00},/*x_odd_inc 1*/
+ {0x0383, 0x01},
+ {0x0384, 0x00},/*y_even_inc 1*/
+ {0x0385, 0x01},
+ {0x0386, 0x00},/*y_odd_inc 1*/
+ {0x0387, 0x01},
+ {0x034C, 0x08},/*x_output size*/
+ {0x034D, 0x00},
+ {0x034E, 0x04},/*y_output size*/
+ {0x034F, 0x80},
+ {0x30BF, 0xAA},/*outif_enable[7], data_type[5:0](2Bh = bayer 8bit)*/
+ {0x30C0, 0x80},/*video_offset[7:4]*/
+ {0x30C8, 0x08},/*video_data_length*/
+ {0x30C9, 0x00},
+
+};
+
+struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_recommend_settings[] = {
+ {0x0100, 0x00},
+
+ {0x3030, 0x06},/*shut streaming*/
+/*Analog Setting*/
+ {0x3000, 0x05},
+ {0x3001, 0x03},
+ {0x3002, 0x08},
+ {0x3003, 0x09},
+ {0x3004, 0x2E},
+ {0x3005, 0x06},
+ {0x3006, 0x34},
+ {0x3007, 0x00},
+ {0x3008, 0x3C},
+ {0x3009, 0x3C},
+ {0x300A, 0x28},
+ {0x300B, 0x04},
+ {0x300C, 0x0A},
+ {0x300D, 0x02},
+ {0x300F, 0x82},
+ {0x3010, 0x00},
+ {0x3011, 0x4C},
+ {0x3012, 0x30},
+ {0x3013, 0xC0},
+ {0x3014, 0x00},
+ {0x3015, 0x00},
+ {0x3016, 0x2C},
+ {0x3017, 0x94},
+ {0x3018, 0x78},
+ {0x301D, 0xD4},
+ {0x3021, 0x02},
+ {0x3022, 0x24},
+ {0x3024, 0x40},
+ {0x3027, 0x08},
+ {0x3029, 0xC6},
+ {0x302B, 0x01},
+ {0x30D8, 0x3F},
+/* ADLC setting ...*/
+ {0x3070, 0x5F},
+ {0x3071, 0x00},
+ {0x3080, 0x04},
+ {0x3081, 0x38},
+
+/*MIPI setting*/
+ {0x30BD, 0x00},/*SEL_CCP[0]*/
+ {0x3084, 0x15},/*SYNC Mode*/
+ {0x30BE, 0x1A},/*M_PCLKDIV_AUTO[4], M_DIV_PCLK[3:0]*/
+ {0x30C1, 0x01},/*pack video enable [0]*/
+ {0x30EE, 0x02},/*DPHY enable [1]*/
+ {0x3111, 0x86},/*Embedded data off [5]*/
+/*For MIPI T8 T9*/
+ {0x30E3, 0x38},
+ {0x30E4, 0x40},
+ {0x3113, 0x70},
+ {0x3114, 0x80},
+ {0x3115, 0x7B},
+ {0x3116, 0xC0},
+ {0x30EE, 0x12},
+
+/*PLL setting ...*/
+ {0x0305, 0x06},
+ {0x30B5, 0x01},
+ {0x30E2, 0x02},/*num lanes[1:0] = 1*/
+
+};
+static struct qs_s5k4e1_i2c_reg_conf qs_s5k4e1_default_lenshading_settings[] = {
+
+ {0x3200, 0x00},
+ {0x3201, 0x9a},
+ {0x3202, 0x56},
+ {0x3203, 0xf },
+ {0x3204, 0xd8},
+ {0x3205, 0x94},
+ {0x3206, 0x0 },
+ {0x3207, 0x10},
+ {0x3208, 0x71},
+ {0x3209, 0x0 },
+ {0x320a, 0x9 },
+ {0x320b, 0xc1},
+ {0x320c, 0xf },
+ {0x320d, 0xf1},
+ {0x320e, 0x3d},
+ {0x320f, 0x0 },
+ {0x3210, 0xa },
+ {0x3211, 0x93},
+ {0x3212, 0xf },
+ {0x3213, 0xc9},
+ {0x3214, 0xa1},
+ {0x3215, 0x0 },
+ {0x3216, 0x10},
+ {0x3217, 0x89},
+ {0x3218, 0xf },
+ {0x3219, 0xfb},
+ {0x321a, 0xf3},
+ {0x321b, 0xf },
+ {0x321c, 0xf8},
+ {0x321d, 0xfc},
+ {0x321e, 0x0 },
+ {0x321f, 0x4 },
+ {0x3220, 0xe3},
+ {0x3221, 0xf },
+ {0x3222, 0xfe},
+ {0x3223, 0x94},
+ {0x3224, 0x0 },
+ {0x3225, 0x24},
+ {0x3226, 0x59},
+ {0x3227, 0xf },
+ {0x3228, 0xe9},
+ {0x3229, 0x68},
+ {0x322a, 0xf },
+ {0x322b, 0xfa},
+ {0x322c, 0x7f},
+ {0x322d, 0x0 },
+ {0x322e, 0x13},
+ {0x322f, 0xe1},
+ {0x3230, 0x0 },
+ {0x3231, 0x3 },
+ {0x3232, 0xbc},
+ {0x3233, 0xf },
+ {0x3234, 0xf0},
+ {0x3235, 0xa1},
+ {0x3236, 0xf },
+ {0x3237, 0xf4},
+ {0x3238, 0xc9},
+ {0x3239, 0x0 },
+ {0x323a, 0x11},
+ {0x323b, 0x4b},
+ {0x323c, 0x0 },
+ {0x323d, 0x12},
+ {0x323e, 0xc5},
+ {0x323f, 0xf },
+ {0x3240, 0xe3},
+ {0x3241, 0xb },
+ {0x3242, 0xf },
+ {0x3243, 0xf8},
+ {0x3244, 0x4f},
+ {0x3245, 0x0 },
+ {0x3246, 0x13},
+ {0x3247, 0xac},
+ {0x3248, 0x0 },
+ {0x3249, 0x0 },
+ {0x324a, 0x7c},
+ {0x324b, 0xf },
+ {0x324c, 0xfe},
+ {0x324d, 0xdd},
+ {0x324e, 0xf },
+ {0x324f, 0xf2},
+ {0x3250, 0x96},
+ {0x3251, 0x0 },
+ {0x3252, 0x8 },
+ {0x3253, 0xef},
+ {0x3254, 0x0 },
+ {0x3255, 0x6 },
+ {0x3256, 0xa4},
+ {0x3257, 0x0 },
+ {0x3258, 0x2 },
+ {0x3259, 0x4b},
+ {0x325a, 0x0 },
+ {0x325b, 0x6 },
+ {0x325c, 0x85},
+ {0x325d, 0xf },
+ {0x325e, 0xf8},
+ {0x325f, 0x6a},
+ {0x3260, 0xf },
+ {0x3261, 0xfd},
+ {0x3262, 0x70},
+ {0x3263, 0x0 },
+ {0x3264, 0xd },
+ {0x3265, 0xa9},
+ {0x3266, 0xf },
+ {0x3267, 0xfd},
+ {0x3268, 0xf8},
+ {0x3269, 0xf },
+ {0x326a, 0xec},
+ {0x326b, 0xfc},
+ {0x326c, 0x0 },
+ {0x326d, 0xa7},
+ {0x326e, 0x5 },
+ {0x326f, 0xf },
+ {0x3270, 0xd6},
+ {0x3271, 0x19},
+ {0x3272, 0x0 },
+ {0x3273, 0xa },
+ {0x3274, 0xe8},
+ {0x3275, 0x0 },
+ {0x3276, 0x17},
+ {0x3277, 0x1 },
+ {0x3278, 0xf },
+ {0x3279, 0xe7},
+ {0x327a, 0xa0},
+ {0x327b, 0x0 },
+ {0x327c, 0xb },
+ {0x327d, 0xc3},
+ {0x327e, 0xf },
+ {0x327f, 0xc0},
+ {0x3280, 0xe3},
+ {0x3281, 0x0 },
+ {0x3282, 0x15},
+ {0x3283, 0x5a},
+ {0x3284, 0xf },
+ {0x3285, 0xf9},
+ {0x3286, 0xa0},
+ {0x3287, 0xf },
+ {0x3288, 0xf4},
+ {0x3289, 0xce},
+ {0x328a, 0x0 },
+ {0x328b, 0xb },
+ {0x328c, 0x72},
+ {0x328d, 0xf },
+ {0x328e, 0xfb},
+ {0x328f, 0xb5},
+ {0x3290, 0x0 },
+ {0x3291, 0x2f},
+ {0x3292, 0xb },
+ {0x3293, 0xf },
+ {0x3294, 0xde},
+ {0x3295, 0xc0},
+ {0x3296, 0x0 },
+ {0x3297, 0x0 },
+ {0x3298, 0x58},
+ {0x3299, 0x0 },
+ {0x329a, 0x1b},
+ {0x329b, 0x5 },
+ {0x329c, 0xf },
+ {0x329d, 0xf9},
+ {0x329e, 0x23},
+ {0x329f, 0xf },
+ {0x32a0, 0xf3},
+ {0x32a1, 0x94},
+ {0x32a2, 0xf },
+ {0x32a3, 0xe7},
+ {0x32a4, 0xc2},
+ {0x32a5, 0x0 },
+ {0x32a6, 0x1d},
+ {0x32a7, 0xe5},
+ {0x32a8, 0x0 },
+ {0x32a9, 0x5 },
+ {0x32aa, 0xaf},
+ {0x32ab, 0xf },
+ {0x32ac, 0xe3},
+ {0x32ad, 0xb7},
+ {0x32ae, 0xf },
+ {0x32af, 0xf8},
+ {0x32b0, 0x34},
+ {0x32b1, 0x0 },
+ {0x32b2, 0x1c},
+ {0x32b3, 0x3d},
+ {0x32b4, 0x0 },
+ {0x32b5, 0x10},
+ {0x32b6, 0x4a},
+ {0x32b7, 0xf },
+ {0x32b8, 0xfa},
+ {0x32b9, 0x7 },
+ {0x32ba, 0xf },
+ {0x32bb, 0xff},
+ {0x32bc, 0x16},
+ {0x32bd, 0x0 },
+ {0x32be, 0x5 },
+ {0x32bf, 0x4e},
+ {0x32c0, 0x0 },
+ {0x32c1, 0xc },
+ {0x32c2, 0x1b},
+ {0x32c3, 0xf },
+ {0x32c4, 0xf1},
+ {0x32c5, 0xdb},
+ {0x32c6, 0xf },
+ {0x32c7, 0xfc},
+ {0x32c8, 0xf8},
+ {0x32c9, 0xf },
+ {0x32ca, 0xf4},
+ {0x32cb, 0xad},
+ {0x32cc, 0xf },
+ {0x32cd, 0xfb},
+ {0x32ce, 0x59},
+ {0x32cf, 0x0 },
+ {0x32d0, 0x9 },
+ {0x32d1, 0xf7},
+ {0x32d2, 0x0 },
+ {0x32d3, 0x0 },
+ {0x32d4, 0xc1},
+ {0x32d5, 0xf },
+ {0x32d6, 0xf5},
+ {0x32d7, 0x30},
+ {0x32d8, 0x0 },
+ {0x32d9, 0x83},
+ {0x32da, 0x1d},
+ {0x32db, 0xf },
+ {0x32dc, 0xe3},
+ {0x32dd, 0x3c},
+ {0x32de, 0x0 },
+ {0x32df, 0xa },
+ {0x32e0, 0x10},
+ {0x32e1, 0x0 },
+ {0x32e2, 0x7 },
+ {0x32e3, 0x65},
+ {0x32e4, 0xf },
+ {0x32e5, 0xfe},
+ {0x32e6, 0x79},
+ {0x32e7, 0xf },
+ {0x32e8, 0xfd},
+ {0x32e9, 0x57},
+ {0x32ea, 0xf },
+ {0x32eb, 0xd6},
+ {0x32ec, 0x8f},
+ {0x32ed, 0x0 },
+ {0x32ee, 0x3 },
+ {0x32ef, 0x93},
+ {0x32f0, 0x0 },
+ {0x32f1, 0x6 },
+ {0x32f2, 0xa },
+ {0x32f3, 0xf },
+ {0x32f4, 0xfa},
+ {0x32f5, 0x6c},
+ {0x32f6, 0xf },
+ {0x32f7, 0xf1},
+ {0x32f8, 0x1e},
+ {0x32f9, 0x0 },
+ {0x32fa, 0x14},
+ {0x32fb, 0xe7},
+ {0x32fc, 0x0 },
+ {0x32fd, 0x1f},
+ {0x32fe, 0x2d},
+ {0x32ff, 0x0 },
+ {0x3300, 0x7 },
+ {0x3301, 0x5e},
+ {0x3302, 0xf },
+ {0x3303, 0xe0},
+ {0x3304, 0x55},
+ {0x3305, 0x0 },
+ {0x3306, 0x20},
+ {0x3307, 0x93},
+ {0x3308, 0x0 },
+ {0x3309, 0xf },
+ {0x330a, 0x20},
+ {0x330b, 0xf },
+ {0x330c, 0xd7},
+ {0x330d, 0xf5},
+ {0x330e, 0xf },
+ {0x330f, 0xef},
+ {0x3310, 0xb8},
+ {0x3311, 0xf },
+ {0x3312, 0xf0},
+ {0x3313, 0x29},
+ {0x3314, 0x0 },
+ {0x3315, 0x27},
+ {0x3316, 0x5e},
+ {0x3317, 0xf },
+ {0x3318, 0xda},
+ {0x3319, 0x14},
+ {0x331a, 0xf },
+ {0x331b, 0xef},
+ {0x331c, 0x93},
+ {0x331d, 0x0 },
+ {0x331e, 0x2c},
+ {0x331f, 0xdc},
+ {0x3320, 0x0 },
+ {0x3321, 0xe },
+ {0x3322, 0x2d},
+ {0x3323, 0x0 },
+ {0x3324, 0x6 },
+ {0x3325, 0xcf},
+ {0x3326, 0xf },
+ {0x3327, 0xfb},
+ {0x3328, 0x26},
+ {0x3329, 0x0 },
+ {0x332a, 0x3 },
+ {0x332b, 0x5 },
+ {0x332c, 0x0 },
+ {0x332d, 0x6 },
+ {0x332e, 0xa6},
+ {0x332f, 0xf },
+ {0x3330, 0xf7},
+ {0x3331, 0x7b},
+ {0x3332, 0xf },
+ {0x3333, 0xf9},
+ {0x3334, 0xb },
+ {0x3335, 0x0 },
+ {0x3336, 0x7 },
+ {0x3337, 0x5a},
+ {0x3338, 0xf },
+ {0x3339, 0xe4},
+ {0x333a, 0x7a},
+ {0x333b, 0x0 },
+ {0x333c, 0x1b},
+ {0x333d, 0xb0},
+ {0x333e, 0x0 },
+ {0x333f, 0x2 },
+ {0x3340, 0xa7},
+ {0x3341, 0xf },
+ {0x3342, 0xe9},
+ {0x3343, 0x3a},
+ {0x3344, 0x0 },
+ {0x3345, 0x95},
+ {0x3346, 0x42},
+ {0x3347, 0xf },
+ {0x3348, 0xda},
+ {0x3349, 0x45},
+ {0x334a, 0x0 },
+ {0x334b, 0x16},
+ {0x334c, 0x7a},
+ {0x334d, 0xf },
+ {0x334e, 0xfb},
+ {0x334f, 0x32},
+ {0x3350, 0x0 },
+ {0x3351, 0x6 },
+ {0x3352, 0x35},
+ {0x3353, 0xf },
+ {0x3354, 0xfc},
+ {0x3355, 0x8f},
+ {0x3356, 0xf },
+ {0x3357, 0xca},
+ {0x3358, 0xd5},
+ {0x3359, 0x0 },
+ {0x335a, 0x11},
+ {0x335b, 0x59},
+ {0x335c, 0xf },
+ {0x335d, 0xfa},
+ {0x335e, 0xaa},
+ {0x335f, 0xf },
+ {0x3360, 0xfe},
+ {0x3361, 0x84},
+ {0x3362, 0xf },
+ {0x3363, 0xf6},
+ {0x3364, 0x8f},
+ {0x3365, 0x0 },
+ {0x3366, 0xb },
+ {0x3367, 0x70},
+ {0x3368, 0x0 },
+ {0x3369, 0x25},
+ {0x336a, 0x83},
+ {0x336b, 0xf },
+ {0x336c, 0xe7},
+ {0x336d, 0x27},
+ {0x336e, 0xf },
+ {0x336f, 0xf1},
+ {0x3370, 0x72},
+ {0x3371, 0x0 },
+ {0x3372, 0x21},
+ {0x3373, 0x6d},
+ {0x3374, 0x0 },
+ {0x3375, 0x2 },
+ {0x3376, 0xc3},
+ {0x3377, 0xf },
+ {0x3378, 0xe8},
+ {0x3379, 0x5a},
+ {0x337a, 0xf },
+ {0x337b, 0xf2},
+ {0x337c, 0x73},
+ {0x337d, 0x0 },
+ {0x337e, 0x19},
+ {0x337f, 0xa5},
+ {0x3380, 0x0 },
+ {0x3381, 0x1a},
+ {0x3382, 0x81},
+ {0x3383, 0xf },
+ {0x3384, 0xd0},
+ {0x3385, 0x31},
+ {0x3386, 0xf },
+ {0x3387, 0xfb},
+ {0x3388, 0xff},
+ {0x3389, 0x0 },
+ {0x338a, 0x1e},
+ {0x338b, 0xe1},
+ {0x338c, 0x0 },
+ {0x338d, 0x5 },
+ {0x338e, 0xe1},
+ {0x338f, 0xf },
+ {0x3390, 0xee},
+ {0x3391, 0xe2},
+ {0x3392, 0xf },
+ {0x3393, 0xf6},
+ {0x3394, 0xcf},
+ {0x3395, 0x0 },
+ {0x3396, 0x13},
+ {0x3397, 0x8f},
+ {0x3398, 0x0 },
+ {0x3399, 0x3 },
+ {0x339a, 0x61},
+ {0x339b, 0xf },
+ {0x339c, 0xf8},
+ {0x339d, 0xf7},
+ {0x339e, 0x0 },
+ {0x339f, 0x0 },
+ {0x33a0, 0xb5},
+ {0x33a1, 0x0 },
+ {0x33a2, 0x5 },
+ {0x33a3, 0x78},
+ {0x33a4, 0xf },
+ {0x33a5, 0xf4},
+ {0x33a6, 0x5 },
+ {0x33a7, 0x0 },
+ {0x33a8, 0xc },
+ {0x33a9, 0xe },
+ {0x33aa, 0x0 },
+ {0x33ab, 0x3 },
+ {0x33ac, 0x53},
+ {0x33ad, 0xf },
+ {0x33ae, 0xec},
+ {0x33af, 0xbd},
+};
+
+const struct
+qs_s5k4e1_i2c_reg_conf qs_s5k4e1_lenshading_settings[4][LENS_SHADE_TABLE] = {
+ {/*2D Preview*/
+ {0x3097, 0x52},/*sh4ch_blk_width = 82*/
+ {0x3098, 0x3e},/*sh4ch_blk_height = 62*/
+ {0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+ {0x309a, 0x1f},/*sh4ch_step_x lsb*/
+ {0x309b, 0x04},/*sh4ch_step_y msb (sh4ch_step_y = 1057)*/
+ {0x309c, 0x21},/*sh4ch_step_y lsb*/
+ {0x309d, 0x00},/*sh4ch_start_blk_cnt_x = 0*/
+ {0x309e, 0x00},/*sh4ch_start_int_cnt_x = 0*/
+ {0x309f, 0x00},/*sh4ch_start_frac_cnt_x msb (0)*/
+ {0x30a0, 0x00},/*sh4ch_start_frac_cnt_x lsb*/
+ {0x30a1, 0x00},/*sh4ch_start_blk_cnt_y = 0*/
+ {0x30a2, 0x00},/*sh4ch_start_int_cnt_y = 0*/
+ {0x30a3, 0x00},/*sh4ch_start_frac_cnt_y msb (0)*/
+ {0x30a4, 0x00},/*sh4ch_start_frac_cnt_y lsb*/
+ {0x30a5, 0x01},
+ {0x30a6, 0x00},/*gs_pedestal = 64*/
+ },
+ {/*2D Snapshot*/
+ {0x3097, 0x52},/*sh4ch_blk_width = 82*/
+ {0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+ {0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+ {0x309a, 0x1f},/*sh4ch_step_x lsb*/
+ {0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+ {0x309c, 0x15},/*sh4ch_step_y lsb*/
+ {0x309d, 0x00},/*sh4ch_start_blk_cnt_x = 0*/
+ {0x309e, 0x00},/*sh4ch_start_int_cnt_x = 0*/
+ {0x309f, 0x00},/*sh4ch_start_frac_cnt_x msb (0)*/
+ {0x30a0, 0x00},/*sh4ch_start_frac_cnt_x lsb*/
+ {0x30a1, 0x00},/*sh4ch_start_blk_cnt_y = 0*/
+ {0x30a2, 0x00},/*sh4ch_start_int_cnt_y = 0*/
+ {0x30a3, 0x00},/*sh4ch_start_frac_cnt_y msb (0)*/
+ {0x30a4, 0x00},/*sh4ch_start_frac_cnt_y lsb*/
+ {0x30a5, 0x01},
+ {0x30a6, 0x00},/*gs_pedestal = 64*/
+ },
+
+ {/*3D Preview*/
+ {0x3097, 0x52},/*sh4ch_blk_width = 82*/
+ {0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+ {0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+ {0x309a, 0x1f},/*sh4ch_step_x lsb*/
+ {0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+ {0x309c, 0x15},/*sh4ch_step_y lsb*/
+ {0x309d, 0x3a},/*sh4ch_start_blk_cnt_x = 58*/
+ {0x309e, 0x01},/*sh4ch_start_int_cnt_x = 1*/
+ {0x309f, 0xb5},/*sh4ch_start_frac_cnt_x msb (46342)*/
+ {0x30a0, 0x06},/*sh4ch_start_frac_cnt_x lsb*/
+ {0x30a1, 0x23},/*sh4ch_start_blk_cnt_y = 35*/
+ {0x30a2, 0x03},/*sh4ch_start_int_cnt_y = 3*/
+ {0x30a3, 0x48},/*sh4ch_start_frac_cnt_y msb (46342)*/
+ {0x30a4, 0xdf},/*sh4ch_start_frac_cnt_y lsb*/
+ {0x30a5, 0x01},
+ {0x30a6, 0x00},/*gs_pedestal = 64*/
+ },
+
+ {/*3D Snapshot*/
+ {0x3097, 0x52},/*sh4ch_blk_width = 82*/
+ {0x3098, 0x7b},/*sh4ch_blk_height = 123*/
+ {0x3099, 0x03},/*sh4ch_step_x msb (sh4ch_step_x = 799)*/
+ {0x309a, 0x1f},/*sh4ch_step_x lsb*/
+ {0x309b, 0x02},/*sh4ch_step_y msb (sh4ch_step_y = 533)*/
+ {0x309c, 0x15},/*sh4ch_step_y lsb*/
+ {0x309d, 0x38},/*sh4ch_start_blk_cnt_x = 56*/
+ {0x309e, 0x01},/*sh4ch_start_int_cnt_x = 1*/
+ {0x309f, 0xae},/*sh4ch_start_frac_cnt_x msb (44744)*/
+ {0x30a0, 0xc8},/*sh4ch_start_frac_cnt_x lsb*/
+ {0x30a1, 0x23},/*sh4ch_start_blk_cnt_y = 35*/
+ {0x30a2, 0x03},/*sh4ch_start_int_cnt_y = 3*/
+ {0x30a3, 0x48},/*sh4ch_start_frac_cnt_y msb (44744)*/
+ {0x30a4, 0xdf},/*sh4ch_start_frac_cnt_y lsb*/
+ {0x30a5, 0x01},
+ {0x30a6, 0x00},/*gs_pedestal = 64*/
+ },
+
+};
+
+struct qs_s5k4e1_i2c_conf_array qs_s5k4e1_confs[] = {
+ {&qs_s5k4e1_prev_settings_2d[0], \
+ ARRAY_SIZE(qs_s5k4e1_prev_settings_2d)},
+ {&qs_s5k4e1_snap_settings_2d[0], \
+ ARRAY_SIZE(qs_s5k4e1_snap_settings_2d)},
+ {&qs_s5k4e1_prev_settings_3d[0], \
+ ARRAY_SIZE(qs_s5k4e1_prev_settings_3d)},
+ {&qs_s5k4e1_snap_settings_3d[0], \
+ ARRAY_SIZE(qs_s5k4e1_snap_settings_3d)},
+};
+struct qs_s5k4e1_reg qs_s5k4e1_regs = {
+ .rec_settings = &qs_s5k4e1_recommend_settings[0],
+ .rec_size = ARRAY_SIZE(qs_s5k4e1_recommend_settings),
+ .reg_lens = &qs_s5k4e1_lenshading_settings[0],
+ .reg_lens_size = ARRAY_SIZE(qs_s5k4e1_lenshading_settings[0]),
+ .reg_default_lens = &qs_s5k4e1_default_lenshading_settings[0],
+ .reg_default_lens_size =
+ ARRAY_SIZE(qs_s5k4e1_default_lenshading_settings),
+ .conf_array = &qs_s5k4e1_confs[0],
+};
diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c
new file mode 100644
index 0000000..178a080
--- /dev/null
+++ b/drivers/media/video/msm/s5k3e2fx.c
@@ -0,0 +1,1386 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/slab.h>
+#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);
+DEFINE_MUTEX(s5k3e2fx_mutex);
+
+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++) {
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ reg_cfg_tbl->waddr, reg_cfg_tbl->bdata);
+ 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 */
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3146, 0x3A);
+ if (rc < 0)
+ goto init_fail1;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ 0x3130, 0x03);
+ if (rc < 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;
+
+ mutex_lock(&s5k3e2fx_mutex);
+
+ 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");
+
+ mutex_unlock(&s5k3e2fx_mutex);
+ 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;
+ enum msm_s_setting setting;
+
+ s5k3e2fx_ctrl->fps_divider = fps->fps_div;
+
+ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE)
+ setting = S_RES_PREVIEW;
+ else
+ setting = S_RES_CAPTURE;
+
+ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr,
+ REG_FRAME_LENGTH_LINES_MSB,
+ (((s5k3e2fx_reg_pat[setting].size_h +
+ s5k3e2fx_reg_pat[setting].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[setting].size_h +
+ s5k3e2fx_reg_pat[setting].blk_l) *
+ s5k3e2fx_ctrl->fps_divider / 0x400) & 0x00FF));
+
+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 */
+ uint32_t ll_pck, fl_lines;
+ uint16_t offset = 4;
+ uint32_t gain_msb, gain_lsb;
+ uint32_t intg_t_msb, intg_t_lsb;
+ uint32_t ll_pck_msb, ll_pck_lsb;
+
+ 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_PREVIEW].blk_l;
+
+ ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w +
+ s5k3e2fx_reg_pat[S_RES_PREVIEW].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 = ll_pck_msb;
+ tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB;
+ tbl[1].bdata = ll_pck_lsb;
+ rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl));
+ if (rc < 0)
+ goto write_gain_done;
+
+ line = line / ll_ratio;
+ intg_t_msb = (line & 0xFF00) >> 8;
+ intg_t_lsb = (line & 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;
+
+ mutex_lock(&s5k3e2fx_mutex);
+
+ 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;
+ }
+
+ mutex_unlock(&s5k3e2fx_mutex);
+ 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;
+ s->s_mount_angle = 0;
+ 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/media/video/msm/s5k3e2fx.h b/drivers/media/video/msm/s5k3e2fx.h
new file mode 100644
index 0000000..cf3f881
--- /dev/null
+++ b/drivers/media/video/msm/s5k3e2fx.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef CAMSENSOR_S5K3E2FX
+#define CAMSENSOR_S5K3E2FX
+
+#include <mach/board.h>
+#endif /* CAMSENSOR_S5K3E2FX */
diff --git a/drivers/media/video/msm/s5k4e1.c b/drivers/media/video/msm/s5k4e1.c
new file mode 100644
index 0000000..9cdd44c
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1.c
@@ -0,0 +1,1086 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+#include <mach/camera.h>
+#include <media/msm_camera.h>
+#include "s5k4e1.h"
+
+/* 16bit address - 8 bit context register structure */
+#define Q8 0x00000100
+#define Q10 0x00000400
+
+/* MCLK */
+#define S5K4E1_MASTER_CLK_RATE 24000000
+
+/* AF Total steps parameters */
+#define S5K4E1_TOTAL_STEPS_NEAR_TO_FAR 32
+
+#define S5K4E1_REG_PREV_FRAME_LEN_1 31
+#define S5K4E1_REG_PREV_FRAME_LEN_2 32
+#define S5K4E1_REG_PREV_LINE_LEN_1 33
+#define S5K4E1_REG_PREV_LINE_LEN_2 34
+
+#define S5K4E1_REG_SNAP_FRAME_LEN_1 15
+#define S5K4E1_REG_SNAP_FRAME_LEN_2 16
+#define S5K4E1_REG_SNAP_LINE_LEN_1 17
+#define S5K4E1_REG_SNAP_LINE_LEN_2 18
+#define MSB 1
+#define LSB 0
+
+struct s5k4e1_work_t {
+ struct work_struct work;
+};
+
+static struct s5k4e1_work_t *s5k4e1_sensorw;
+static struct s5k4e1_work_t *s5k4e1_af_sensorw;
+static struct i2c_client *s5k4e1_af_client;
+static struct i2c_client *s5k4e1_client;
+
+struct s5k4e1_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ uint16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum s5k4e1_resolution_t prev_res;
+ enum s5k4e1_resolution_t pict_res;
+ enum s5k4e1_resolution_t curr_res;
+ enum s5k4e1_test_mode_t set_test;
+};
+
+static bool CSI_CONFIG;
+static struct s5k4e1_ctrl_t *s5k4e1_ctrl;
+
+static DECLARE_WAIT_QUEUE_HEAD(s5k4e1_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(s5k4e1_af_wait_queue);
+DEFINE_MUTEX(s5k4e1_mut);
+
+static uint16_t prev_line_length_pck;
+static uint16_t prev_frame_length_lines;
+static uint16_t snap_line_length_pck;
+static uint16_t snap_frame_length_lines;
+
+static int s5k4e1_i2c_rxdata(unsigned short saddr,
+ unsigned char *rxdata, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(s5k4e1_client->adapter, msgs, 2) < 0) {
+ CDBG("s5k4e1_i2c_rxdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int32_t s5k4e1_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(s5k4e1_client->adapter, msg, 1) < 0) {
+ CDBG("s5k4e1_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t s5k4e1_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = s5k4e1_i2c_rxdata(s5k4e1_client->addr, buf, rlen);
+ if (rc < 0) {
+ CDBG("s5k4e1_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ CDBG("s5k4e1_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata);
+
+ return rc;
+}
+
+static int32_t s5k4e1_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = s5k4e1_i2c_txdata(s5k4e1_client->addr, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+
+static int32_t s5k4e1_i2c_write_b_table(struct s5k4e1_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+
+ for (i = 0; i < num; i++) {
+ rc = s5k4e1_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static int32_t s5k4e1_af_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(s5k4e1_af_client->adapter, msg, 1) < 0) {
+ pr_err("s5k4e1_af_i2c_txdata faild 0x%x\n", saddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t s5k4e1_af_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = waddr;
+ buf[1] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = s5k4e1_af_i2c_txdata(s5k4e1_af_client->addr << 1, buf, 2);
+ if (rc < 0) {
+ pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+
+static void s5k4e1_start_stream(void)
+{
+ s5k4e1_i2c_write_b_sensor(0x0100, 0x01);/* streaming on */
+}
+
+static void s5k4e1_stop_stream(void)
+{
+ s5k4e1_i2c_write_b_sensor(0x0100, 0x00);/* streaming off */
+}
+
+static void s5k4e1_group_hold_on(void)
+{
+ s5k4e1_i2c_write_b_sensor(0x0104, 0x01);
+}
+
+static void s5k4e1_group_hold_off(void)
+{
+ s5k4e1_i2c_write_b_sensor(0x0104, 0x0);
+}
+
+static void s5k4e1_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint32_t divider, d1, d2;
+
+ d1 = (prev_frame_length_lines * 0x00000400) / snap_frame_length_lines;
+ d2 = (prev_line_length_pck * 0x00000400) / snap_line_length_pck;
+ divider = (d1 * d2) / 0x400;
+
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+}
+
+static uint16_t s5k4e1_get_prev_lines_pf(void)
+{
+ if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+ return prev_frame_length_lines;
+ else
+ return snap_frame_length_lines;
+}
+
+static uint16_t s5k4e1_get_prev_pixels_pl(void)
+{
+ if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+ return prev_line_length_pck;
+ else
+ return snap_line_length_pck;
+}
+
+static uint16_t s5k4e1_get_pict_lines_pf(void)
+{
+ if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+ return prev_frame_length_lines;
+ else
+ return snap_frame_length_lines;
+}
+
+static uint16_t s5k4e1_get_pict_pixels_pl(void)
+{
+ if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+ return prev_line_length_pck;
+ else
+ return snap_line_length_pck;
+}
+
+static uint32_t s5k4e1_get_pict_max_exp_lc(void)
+{
+ return snap_frame_length_lines * 24;
+}
+
+static int32_t s5k4e1_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+
+ s5k4e1_ctrl->fps_divider = fps->fps_div;
+ s5k4e1_ctrl->pict_fps_divider = fps->pict_fps_div;
+
+ if (s5k4e1_ctrl->sensormode == SENSOR_PREVIEW_MODE) {
+ total_lines_per_frame = (uint16_t)
+ ((prev_frame_length_lines * s5k4e1_ctrl->fps_divider) / 0x400);
+ } else {
+ total_lines_per_frame = (uint16_t)
+ ((snap_frame_length_lines * s5k4e1_ctrl->fps_divider) / 0x400);
+ }
+
+ s5k4e1_group_hold_on();
+ rc = s5k4e1_i2c_write_b_sensor(0x0340,
+ ((total_lines_per_frame & 0xFF00) >> 8));
+ rc = s5k4e1_i2c_write_b_sensor(0x0341,
+ (total_lines_per_frame & 0x00FF));
+ s5k4e1_group_hold_off();
+
+ return rc;
+}
+
+static inline uint8_t s5k4e1_byte(uint16_t word, uint8_t offset)
+{
+ return word >> (offset * BITS_PER_BYTE);
+}
+
+static int32_t s5k4e1_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0x0200;
+ int32_t rc = 0;
+ static uint32_t fl_lines;
+
+ if (gain > max_legal_gain) {
+ pr_debug("Max legal gain Line:%d\n", __LINE__);
+ gain = max_legal_gain;
+ }
+ /* Analogue Gain */
+ s5k4e1_i2c_write_b_sensor(0x0204, s5k4e1_byte(gain, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0205, s5k4e1_byte(gain, LSB));
+
+ if (line > (prev_frame_length_lines - 4)) {
+ fl_lines = line+4;
+ s5k4e1_group_hold_on();
+ s5k4e1_i2c_write_b_sensor(0x0340, s5k4e1_byte(fl_lines, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0341, s5k4e1_byte(fl_lines, LSB));
+ /* Coarse Integration Time */
+ s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+ s5k4e1_group_hold_off();
+ } else if (line < (fl_lines - 4)) {
+ fl_lines = line+4;
+ if (fl_lines < prev_frame_length_lines)
+ fl_lines = prev_frame_length_lines;
+
+ s5k4e1_group_hold_on();
+ /* Coarse Integration Time */
+ s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+ s5k4e1_i2c_write_b_sensor(0x0340, s5k4e1_byte(fl_lines, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0341, s5k4e1_byte(fl_lines, LSB));
+ s5k4e1_group_hold_off();
+ } else {
+ fl_lines = line+4;
+ s5k4e1_group_hold_on();
+ /* Coarse Integration Time */
+ s5k4e1_i2c_write_b_sensor(0x0202, s5k4e1_byte(line, MSB));
+ s5k4e1_i2c_write_b_sensor(0x0203, s5k4e1_byte(line, LSB));
+ s5k4e1_group_hold_off();
+ }
+ return rc;
+}
+
+static int32_t s5k4e1_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t max_legal_gain = 0x0200;
+ uint16_t min_ll_pck = 0x0AB2;
+ uint32_t ll_pck, fl_lines;
+ uint32_t ll_ratio;
+ int32_t rc = 0;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_time_msb, intg_time_lsb;
+ uint8_t ll_pck_msb, ll_pck_lsb;
+
+ if (gain > max_legal_gain) {
+ pr_debug("Max legal gain Line:%d\n", __LINE__);
+ gain = max_legal_gain;
+ }
+
+ pr_debug("s5k4e1_write_exp_gain : gain = %d line = %d\n", gain, line);
+ line = (uint32_t) (line * s5k4e1_ctrl->pict_fps_divider);
+ fl_lines = snap_frame_length_lines;
+ ll_pck = snap_line_length_pck;
+
+ if (fl_lines < (line / 0x400))
+ ll_ratio = (line / (fl_lines - 4));
+ else
+ ll_ratio = 0x400;
+
+ ll_pck = ll_pck * ll_ratio / 0x400;
+ line = line / ll_ratio;
+ if (ll_pck < min_ll_pck)
+ ll_pck = min_ll_pck;
+
+ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lsb = (uint8_t) (gain & 0x00FF);
+
+ intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8);
+ intg_time_lsb = (uint8_t) (line & 0x00FF);
+
+ ll_pck_msb = (uint8_t) ((ll_pck & 0xFF00) >> 8);
+ ll_pck_lsb = (uint8_t) (ll_pck & 0x00FF);
+
+ s5k4e1_group_hold_on();
+ s5k4e1_i2c_write_b_sensor(0x0204, gain_msb); /* Analogue Gain */
+ s5k4e1_i2c_write_b_sensor(0x0205, gain_lsb);
+
+ s5k4e1_i2c_write_b_sensor(0x0342, ll_pck_msb);
+ s5k4e1_i2c_write_b_sensor(0x0343, ll_pck_lsb);
+
+ /* Coarse Integration Time */
+ s5k4e1_i2c_write_b_sensor(0x0202, intg_time_msb);
+ s5k4e1_i2c_write_b_sensor(0x0203, intg_time_lsb);
+ s5k4e1_group_hold_off();
+
+ return rc;
+}
+
+static int32_t s5k4e1_move_focus(int direction,
+ int32_t num_steps)
+{
+ int16_t step_direction, actual_step, next_position;
+ uint8_t code_val_msb, code_val_lsb;
+
+ if (direction == MOVE_NEAR)
+ step_direction = 16;
+ else
+ step_direction = -16;
+
+ actual_step = (int16_t) (step_direction * num_steps);
+ next_position = (int16_t) (s5k4e1_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;
+
+ if (s5k4e1_af_i2c_write_b_sensor(code_val_msb, code_val_lsb) < 0) {
+ pr_err("move_focus failed at line %d ...\n", __LINE__);
+ return -EBUSY;
+ }
+
+ s5k4e1_ctrl->curr_lens_pos = next_position;
+ return 0;
+}
+
+static int32_t s5k4e1_set_default_focus(uint8_t af_step)
+{
+ int32_t rc = 0;
+
+ if (s5k4e1_ctrl->curr_step_pos != 0) {
+ rc = s5k4e1_move_focus(MOVE_FAR,
+ s5k4e1_ctrl->curr_step_pos);
+ } else {
+ s5k4e1_af_i2c_write_b_sensor(0x00, 0x00);
+ }
+
+ s5k4e1_ctrl->curr_lens_pos = 0;
+ s5k4e1_ctrl->curr_step_pos = 0;
+
+ return rc;
+}
+
+static int32_t s5k4e1_test(enum s5k4e1_test_mode_t mo)
+{
+ int32_t rc = 0;
+
+ if (mo != TEST_OFF)
+ rc = s5k4e1_i2c_write_b_sensor(0x0601, (uint8_t) mo);
+
+ return rc;
+}
+
+static void s5k4e1_reset_sensor(void)
+{
+ s5k4e1_i2c_write_b_sensor(0x103, 0x1);
+}
+
+static int32_t s5k4e1_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ struct msm_camera_csi_params s5k4e1_csi_params;
+
+ s5k4e1_stop_stream();
+ msleep(30);
+
+ if (update_type == REG_INIT) {
+ s5k4e1_reset_sensor();
+ s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_mipi,
+ s5k4e1_regs.reg_mipi_size);
+ s5k4e1_i2c_write_b_table(s5k4e1_regs.rec_settings,
+ s5k4e1_regs.rec_size);
+ s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_pll_p,
+ s5k4e1_regs.reg_pll_p_size);
+ CSI_CONFIG = 0;
+ } else if (update_type == UPDATE_PERIODIC) {
+ if (rt == RES_PREVIEW)
+ s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_prev,
+ s5k4e1_regs.reg_prev_size);
+ else
+ s5k4e1_i2c_write_b_table(s5k4e1_regs.reg_snap,
+ s5k4e1_regs.reg_snap_size);
+ msleep(20);
+ if (!CSI_CONFIG) {
+ msm_camio_vfe_clk_rate_set(192000000);
+ s5k4e1_csi_params.data_format = CSI_10BIT;
+ s5k4e1_csi_params.lane_cnt = 1;
+ s5k4e1_csi_params.lane_assign = 0xe4;
+ s5k4e1_csi_params.dpcm_scheme = 0;
+ s5k4e1_csi_params.settle_cnt = 24;
+ rc = msm_camio_csi_config(&s5k4e1_csi_params);
+ msleep(20);
+ CSI_CONFIG = 1;
+ }
+ s5k4e1_start_stream();
+ msleep(30);
+ }
+ return rc;
+}
+
+static int32_t s5k4e1_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+ CDBG("video config\n");
+ /* change sensor resolution if needed */
+ if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+ if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ if (s5k4e1_ctrl->set_test) {
+ if (s5k4e1_test(s5k4e1_ctrl->set_test) < 0)
+ return rc;
+ }
+
+ s5k4e1_ctrl->curr_res = s5k4e1_ctrl->prev_res;
+ s5k4e1_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t s5k4e1_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+
+ /*change sensor resolution if needed */
+ if (s5k4e1_ctrl->curr_res != s5k4e1_ctrl->pict_res) {
+ if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+ if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ s5k4e1_ctrl->curr_res = s5k4e1_ctrl->pict_res;
+ s5k4e1_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t s5k4e1_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+
+ /* change sensor resolution if needed */
+ if (s5k4e1_ctrl->curr_res != s5k4e1_ctrl->pict_res) {
+ if (s5k4e1_ctrl->pict_res == QTR_SIZE)
+ rt = RES_PREVIEW;
+ else
+ rt = RES_CAPTURE;
+ if (s5k4e1_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ s5k4e1_ctrl->curr_res = s5k4e1_ctrl->pict_res;
+ s5k4e1_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t s5k4e1_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = s5k4e1_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = s5k4e1_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = s5k4e1_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t s5k4e1_power_down(void)
+{
+ s5k4e1_stop_stream();
+ return 0;
+}
+
+static int s5k4e1_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ CDBG("probe done\n");
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+
+static int s5k4e1_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ uint16_t regaddress1 = 0x0000;
+ uint16_t regaddress2 = 0x0001;
+ uint16_t chipid1 = 0;
+ uint16_t chipid2 = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG(" s5k4e1_probe_init_sensor is called\n");
+
+ rc = gpio_request(data->sensor_reset, "s5k4e1");
+ CDBG(" s5k4e1_probe_init_sensor\n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(50);
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ msleep(20);
+ } else {
+ goto init_probe_done;
+ }
+ msleep(20);
+
+ s5k4e1_i2c_read(regaddress1, &chipid1, 1);
+ if (chipid1 != 0x4E) {
+ rc = -ENODEV;
+ CDBG("s5k4e1_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+
+ s5k4e1_i2c_read(regaddress2, &chipid2 , 1);
+ if (chipid2 != 0x10) {
+ rc = -ENODEV;
+ CDBG("s5k4e1_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+
+ CDBG("ID: %d\n", chipid1);
+ CDBG("ID: %d\n", chipid1);
+
+
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" s5k4e1_probe_init_sensor fails\n");
+ s5k4e1_probe_init_done(data);
+init_probe_done:
+ CDBG(" s5k4e1_probe_init_sensor finishes\n");
+ return rc;
+}
+
+int s5k4e1_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling s5k4e1_sensor_open_init\n");
+
+ s5k4e1_ctrl = kzalloc(sizeof(struct s5k4e1_ctrl_t), GFP_KERNEL);
+ if (!s5k4e1_ctrl) {
+ CDBG("s5k4e1_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ s5k4e1_ctrl->fps_divider = 1 * 0x00000400;
+ s5k4e1_ctrl->pict_fps_divider = 1 * 0x00000400;
+ s5k4e1_ctrl->set_test = TEST_OFF;
+ s5k4e1_ctrl->prev_res = QTR_SIZE;
+ s5k4e1_ctrl->pict_res = FULL_SIZE;
+
+ if (data)
+ s5k4e1_ctrl->sensordata = data;
+
+ prev_frame_length_lines =
+ ((s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_FRAME_LEN_1].wdata << 8) |
+ s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_FRAME_LEN_2].wdata);
+
+ prev_line_length_pck =
+ (s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_LINE_LEN_1].wdata << 8) |
+ s5k4e1_regs.reg_prev[S5K4E1_REG_PREV_LINE_LEN_2].wdata;
+
+ snap_frame_length_lines =
+ (s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_FRAME_LEN_1].wdata << 8) |
+ s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_FRAME_LEN_2].wdata;
+
+ snap_line_length_pck =
+ (s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_LINE_LEN_1].wdata << 8) |
+ s5k4e1_regs.reg_snap[S5K4E1_REG_SNAP_LINE_LEN_1].wdata;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(S5K4E1_MASTER_CLK_RATE);
+ rc = s5k4e1_probe_init_sensor(data);
+ if (rc < 0)
+ goto init_fail;
+
+ CDBG("init settings\n");
+ if (s5k4e1_ctrl->prev_res == QTR_SIZE)
+ rc = s5k4e1_sensor_setting(REG_INIT, RES_PREVIEW);
+ else
+ rc = s5k4e1_sensor_setting(REG_INIT, RES_CAPTURE);
+ s5k4e1_ctrl->fps = 30 * Q8;
+
+ /* enable AF actuator */
+ if (s5k4e1_ctrl->sensordata->vcm_enable) {
+ CDBG("enable AF actuator, gpio = %d\n",
+ s5k4e1_ctrl->sensordata->vcm_pwd);
+ rc = gpio_request(s5k4e1_ctrl->sensordata->vcm_pwd,
+ "s5k4e1_af");
+ if (!rc)
+ gpio_direction_output(
+ s5k4e1_ctrl->sensordata->vcm_pwd,
+ 1);
+ else {
+ pr_err("s5k4e1_ctrl gpio request failed!\n");
+ goto init_fail;
+ }
+ msleep(20);
+ rc = s5k4e1_set_default_focus(0);
+ if (rc < 0) {
+ gpio_direction_output(s5k4e1_ctrl->sensordata->vcm_pwd,
+ 0);
+ gpio_free(s5k4e1_ctrl->sensordata->vcm_pwd);
+ }
+ }
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+init_fail:
+ CDBG("init_fail\n");
+ s5k4e1_probe_init_done(data);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+}
+
+static int s5k4e1_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&s5k4e1_wait_queue);
+ return 0;
+}
+
+static int s5k4e1_af_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&s5k4e1_af_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id s5k4e1_af_i2c_id[] = {
+ {"s5k4e1_af", 0},
+ { }
+};
+
+static int s5k4e1_af_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("s5k4e1_af_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ s5k4e1_af_sensorw = kzalloc(sizeof(struct s5k4e1_work_t), GFP_KERNEL);
+ if (!s5k4e1_af_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, s5k4e1_af_sensorw);
+ s5k4e1_af_init_client(client);
+ s5k4e1_af_client = client;
+
+ msleep(50);
+
+ CDBG("s5k4e1_af_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("s5k4e1_af_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static const struct i2c_device_id s5k4e1_i2c_id[] = {
+ {"s5k4e1", 0},
+ { }
+};
+
+static int s5k4e1_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("s5k4e1_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ s5k4e1_sensorw = kzalloc(sizeof(struct s5k4e1_work_t), GFP_KERNEL);
+ if (!s5k4e1_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, s5k4e1_sensorw);
+ s5k4e1_init_client(client);
+ s5k4e1_client = client;
+
+ msleep(50);
+
+ CDBG("s5k4e1_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("s5k4e1_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int __devexit s5k4e1_remove(struct i2c_client *client)
+{
+ struct s5k4e1_work_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ s5k4e1_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static int __devexit s5k4e1_af_remove(struct i2c_client *client)
+{
+ struct s5k4e1_work_t *s5k4e1_af = i2c_get_clientdata(client);
+ free_irq(client->irq, s5k4e1_af);
+ s5k4e1_af_client = NULL;
+ kfree(s5k4e1_af);
+ return 0;
+}
+
+static struct i2c_driver s5k4e1_i2c_driver = {
+ .id_table = s5k4e1_i2c_id,
+ .probe = s5k4e1_i2c_probe,
+ .remove = __exit_p(s5k4e1_i2c_remove),
+ .driver = {
+ .name = "s5k4e1",
+ },
+};
+
+static struct i2c_driver s5k4e1_af_i2c_driver = {
+ .id_table = s5k4e1_af_i2c_id,
+ .probe = s5k4e1_af_i2c_probe,
+ .remove = __exit_p(s5k4e1_af_i2c_remove),
+ .driver = {
+ .name = "s5k4e1_af",
+ },
+};
+
+int s5k4e1_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;
+ mutex_lock(&s5k4e1_mut);
+ CDBG("s5k4e1_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ s5k4e1_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 =
+ s5k4e1_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 =
+ s5k4e1_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 =
+ s5k4e1_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 =
+ s5k4e1_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 =
+ s5k4e1_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 = s5k4e1_set_fps(&(cdata.cfg.fps));
+ break;
+ case CFG_SET_EXP_GAIN:
+ rc = s5k4e1_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = s5k4e1_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_MODE:
+ rc = s5k4e1_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+ case CFG_PWR_DOWN:
+ rc = s5k4e1_power_down();
+ break;
+ case CFG_MOVE_FOCUS:
+ rc = s5k4e1_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = s5k4e1_set_default_focus(cdata.cfg.focus.steps);
+ break;
+ case CFG_GET_AF_MAX_STEPS:
+ cdata.max_steps = S5K4E1_TOTAL_STEPS_NEAR_TO_FAR;
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+ case CFG_SET_EFFECT:
+ rc = s5k4e1_set_default_focus(cdata.cfg.effect);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+ mutex_unlock(&s5k4e1_mut);
+
+ return rc;
+}
+
+static int s5k4e1_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&s5k4e1_mut);
+ s5k4e1_power_down();
+ msleep(20);
+ gpio_set_value_cansleep(s5k4e1_ctrl->sensordata->sensor_reset, 0);
+ usleep_range(5000, 5100);
+ gpio_free(s5k4e1_ctrl->sensordata->sensor_reset);
+ if (s5k4e1_ctrl->sensordata->vcm_enable) {
+ gpio_direction_output(s5k4e1_ctrl->sensordata->vcm_pwd, 0);
+ gpio_free(s5k4e1_ctrl->sensordata->vcm_pwd);
+ }
+ kfree(s5k4e1_ctrl);
+ s5k4e1_ctrl = NULL;
+ CDBG("s5k4e1_release completed\n");
+ mutex_unlock(&s5k4e1_mut);
+
+ return rc;
+}
+
+static int s5k4e1_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+
+ rc = i2c_add_driver(&s5k4e1_i2c_driver);
+ if (rc < 0 || s5k4e1_client == NULL) {
+ rc = -ENOTSUPP;
+ CDBG("I2C add driver failed");
+ goto probe_fail_1;
+ }
+
+ rc = i2c_add_driver(&s5k4e1_af_i2c_driver);
+ if (rc < 0 || s5k4e1_af_client == NULL) {
+ rc = -ENOTSUPP;
+ CDBG("I2C add driver failed");
+ goto probe_fail_2;
+ }
+
+ msm_camio_clk_rate_set(S5K4E1_MASTER_CLK_RATE);
+
+ rc = s5k4e1_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail_3;
+
+ s->s_init = s5k4e1_sensor_open_init;
+ s->s_release = s5k4e1_sensor_release;
+ s->s_config = s5k4e1_sensor_config;
+ s->s_mount_angle = info->sensor_platform_info->mount_angle;
+ gpio_set_value_cansleep(info->sensor_reset, 0);
+ s5k4e1_probe_init_done(info);
+
+ return rc;
+
+probe_fail_3:
+ i2c_del_driver(&s5k4e1_af_i2c_driver);
+probe_fail_2:
+ i2c_del_driver(&s5k4e1_i2c_driver);
+probe_fail_1:
+ CDBG("s5k4e1_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __devinit s5k4e1_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, s5k4e1_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = s5k4e1_probe,
+ .driver = {
+ .name = "msm_camera_s5k4e1",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s5k4e1_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(s5k4e1_init);
+MODULE_DESCRIPTION("Samsung 5 MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/s5k4e1.h b/drivers/media/video/msm/s5k4e1.h
new file mode 100644
index 0000000..7f60332
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1.h
@@ -0,0 +1,94 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef S5K4E1_H
+#define S5K4E1_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct s5k4e1_reg s5k4e1_regs;
+
+struct s5k4e1_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+enum s5k4e1_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum s5k4e1_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+enum s5k4e1_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+enum s5k4e1_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 s5k4e1_reg_pll {
+ E013_VT_PIX_CLK_DIV,
+ E013_VT_SYS_CLK_DIV,
+ E013_PRE_PLL_CLK_DIV,
+ E013_PLL_MULTIPLIER,
+ E013_OP_PIX_CLK_DIV,
+ E013_OP_SYS_CLK_DIV
+};
+
+enum s5k4e1_reg_mode {
+ E013_X_ADDR_START,
+ E013_X_ADDR_END,
+ E013_Y_ADDR_START,
+ E013_Y_ADDR_END,
+ E013_X_OUTPUT_SIZE,
+ E013_Y_OUTPUT_SIZE,
+ E013_DATAPATH_SELECT,
+ E013_READ_MODE,
+ E013_ANALOG_CONTROL5,
+ E013_DAC_LD_4_5,
+ E013_SCALING_MODE,
+ E013_SCALE_M,
+ E013_LINE_LENGTH_PCK,
+ E013_FRAME_LENGTH_LINES,
+ E013_COARSE_INTEGRATION_TIME,
+ E013_FINE_INTEGRATION_TIME,
+ E013_FINE_CORRECTION
+};
+
+struct s5k4e1_reg {
+ const struct s5k4e1_i2c_reg_conf *reg_mipi;
+ const unsigned short reg_mipi_size;
+ const struct s5k4e1_i2c_reg_conf *rec_settings;
+ const unsigned short rec_size;
+ const struct s5k4e1_i2c_reg_conf *reg_pll_p;
+ const unsigned short reg_pll_p_size;
+ const struct s5k4e1_i2c_reg_conf *reg_pll_s;
+ const unsigned short reg_pll_s_size;
+ const struct s5k4e1_i2c_reg_conf *reg_prev;
+ const unsigned short reg_prev_size;
+ const struct s5k4e1_i2c_reg_conf *reg_snap;
+ const unsigned short reg_snap_size;
+};
+#endif /* S5K4E1_H */
diff --git a/drivers/media/video/msm/s5k4e1_reg.c b/drivers/media/video/msm/s5k4e1_reg.c
new file mode 100644
index 0000000..59bb1c8
--- /dev/null
+++ b/drivers/media/video/msm/s5k4e1_reg.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include "s5k4e1.h"
+
+struct s5k4e1_i2c_reg_conf s5k4e1_mipi_settings[] = {
+ {0x30BD, 0x00},/* SEL_CCP[0] */
+ {0x3084, 0x15},/* SYNC Mode */
+ {0x30BE, 0x1A},/* M_PCLKDIV_AUTO[4], M_DIV_PCLK[3:0] */
+ {0x30C1, 0x01},/* pack video enable [0] */
+ {0x30EE, 0x02},/* DPHY enable [ 1] */
+ {0x3111, 0x86},/* Embedded data off [5] */
+};
+
+/* PLL Configuration */
+struct s5k4e1_i2c_reg_conf s5k4e1_pll_preview_settings[] = {
+ {0x0305, 0x04},
+ {0x0306, 0x00},
+ {0x0307, 0x44},
+ {0x30B5, 0x00},
+ {0x30E2, 0x01},/* num lanes[1:0] = 2 */
+ {0x30F1, 0xB0},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_pll_snap_settings[] = {
+ {0x0305, 0x04},
+ {0x0306, 0x00},
+ {0x0307, 0x44},
+ {0x30B5, 0x00},
+ {0x30E2, 0x01},/* num lanes[1:0] = 2 */
+ {0x30F1, 0xB0},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_prev_settings[] = {
+ /* output size (1304 x 980) */
+ {0x30A9, 0x02},/* Horizontal Binning On */
+ {0x300E, 0xEB},/* Vertical Binning On */
+ {0x0387, 0x03},/* y_odd_inc 03(10b AVG) */
+ {0x0344, 0x00},/* x_addr_start 0 */
+ {0x0345, 0x00},
+ {0x0348, 0x0A},/* x_addr_end 2607 */
+ {0x0349, 0x2F},
+ {0x0346, 0x00},/* y_addr_start 0 */
+ {0x0347, 0x00},
+ {0x034A, 0x07},/* y_addr_end 1959 */
+ {0x034B, 0xA7},
+ {0x0380, 0x00},/* x_even_inc 1 */
+ {0x0381, 0x01},
+ {0x0382, 0x00},/* x_odd_inc 1 */
+ {0x0383, 0x01},
+ {0x0384, 0x00},/* y_even_inc 1 */
+ {0x0385, 0x01},
+ {0x0386, 0x00},/* y_odd_inc 3 */
+ {0x0387, 0x03},
+ {0x034C, 0x05},/* x_output_size 1304 */
+ {0x034D, 0x18},
+ {0x034E, 0x03},/* y_output_size 980 */
+ {0x034F, 0xd4},
+ {0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+ {0x30C0, 0xA0},/* video_offset[7:4] 3260%12 */
+ {0x30C8, 0x06},/* video_data_length 1600 = 1304 * 1.25 */
+ {0x30C9, 0x5E},
+ /* Timing Configuration */
+ {0x0202, 0x03},
+ {0x0203, 0x14},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+ {0x0340, 0x03},/* Frame Length */
+ {0x0341, 0xE0},
+ {0x0342, 0x0A},/* 2738 Line Length */
+ {0x0343, 0xB2},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_snap_settings[] = {
+ /*Output Size (2608x1960)*/
+ {0x30A9, 0x03},/* Horizontal Binning Off */
+ {0x300E, 0xE8},/* Vertical Binning Off */
+ {0x0387, 0x01},/* y_odd_inc */
+ {0x034C, 0x0A},/* x_output size */
+ {0x034D, 0x30},
+ {0x034E, 0x07},/* y_output size */
+ {0x034F, 0xA8},
+ {0x30BF, 0xAB},/* outif_enable[7], data_type[5:0](2Bh = bayer 10bit} */
+ {0x30C0, 0x80},/* video_offset[7:4] 3260%12 */
+ {0x30C8, 0x0C},/* video_data_length 3260 = 2608 * 1.25 */
+ {0x30C9, 0xBC},
+ /*Timing configuration*/
+ {0x0202, 0x06},
+ {0x0203, 0x28},
+ {0x0204, 0x00},
+ {0x0205, 0x80},
+ {0x0340, 0x07},/* Frame Length */
+ {0x0341, 0xB4},
+ {0x0342, 0x0A},/* 2738 Line Length */
+ {0x0343, 0xB2},
+};
+
+struct s5k4e1_i2c_reg_conf s5k4e1_recommend_settings[] = {
+ /*CDS timing setting ... */
+ {0x3000, 0x05},
+ {0x3001, 0x03},
+ {0x3002, 0x08},
+ {0x3003, 0x0A},
+ {0x3004, 0x50},
+ {0x3005, 0x0E},
+ {0x3006, 0x5E},
+ {0x3007, 0x00},
+ {0x3008, 0x78},
+ {0x3009, 0x78},
+ {0x300A, 0x50},
+ {0x300B, 0x08},
+ {0x300C, 0x14},
+ {0x300D, 0x00},
+ {0x300E, 0xE8},
+ {0x300F, 0x82},
+ {0x301B, 0x77},
+
+ /* CDS option setting ... */
+ {0x3010, 0x00},
+ {0x3011, 0x3A},
+ {0x3029, 0x04},
+ {0x3012, 0x30},
+ {0x3013, 0xA0},
+ {0x3014, 0x00},
+ {0x3015, 0x00},
+ {0x3016, 0x30},
+ {0x3017, 0x94},
+ {0x3018, 0x70},
+ {0x301D, 0xD4},
+ {0x3021, 0x02},
+ {0x3022, 0x24},
+ {0x3024, 0x40},
+ {0x3027, 0x08},
+
+ /* Pixel option setting ... */
+ {0x301C, 0x04},
+ {0x30D8, 0x3F},
+ {0x302B, 0x01},
+
+ {0x3070, 0x5F},
+ {0x3071, 0x00},
+ {0x3080, 0x04},
+ {0x3081, 0x38},
+};
+
+struct s5k4e1_reg s5k4e1_regs = {
+ .reg_mipi = &s5k4e1_mipi_settings[0],
+ .reg_mipi_size = ARRAY_SIZE(s5k4e1_mipi_settings),
+ .rec_settings = &s5k4e1_recommend_settings[0],
+ .rec_size = ARRAY_SIZE(s5k4e1_recommend_settings),
+ .reg_pll_p = &s5k4e1_pll_preview_settings[0],
+ .reg_pll_p_size = ARRAY_SIZE(s5k4e1_pll_preview_settings),
+ .reg_pll_s = &s5k4e1_pll_snap_settings[0],
+ .reg_pll_s_size = ARRAY_SIZE(s5k4e1_pll_snap_settings),
+ .reg_prev = &s5k4e1_prev_settings[0],
+ .reg_prev_size = ARRAY_SIZE(s5k4e1_prev_settings),
+ .reg_snap = &s5k4e1_snap_settings[0],
+ .reg_snap_size = ARRAY_SIZE(s5k4e1_snap_settings),
+};
diff --git a/drivers/media/video/msm/sn12m0pz.c b/drivers/media/video/msm/sn12m0pz.c
new file mode 100644
index 0000000..affa581
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz.c
@@ -0,0 +1,1850 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 <linux/slab.h>
+#include "sn12m0pz.h"
+
+
+#define Q8 0x00000100
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+#define REG_MODE_SELECT 0x0100
+#define MODE_SELECT_STANDBY_MODE 0x00
+#define MODE_SELECT_STREAM 0x01
+
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_MSB 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203
+
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205
+
+/* PLL Register Defines */
+#define REG_PLL_MULTIPLIER 0x0307
+#define REG_0x302B 0x302B
+
+/* MIPI Enable Settings */
+#define REG_0x30E5 0x30E5
+#define REG_0x3300 0x3300
+
+/* Global Setting */
+#define REG_IMAGE_ORIENTATION 0x0101
+
+#define REG_0x300A 0x300A
+#define REG_0x3014 0x3014
+#define REG_0x3015 0x3015
+#define REG_0x3017 0x3017
+#define REG_0x301C 0x301C
+#define REG_0x3031 0x3031
+#define REG_0x3040 0x3040
+#define REG_0x3041 0x3041
+#define REG_0x3051 0x3051
+#define REG_0x3053 0x3053
+#define REG_0x3055 0x3055
+#define REG_0x3057 0x3057
+#define REG_0x3060 0x3060
+#define REG_0x3065 0x3065
+#define REG_0x30AA 0x30AA
+#define REG_0x30AB 0x30AB
+#define REG_0x30B0 0x30B0
+#define REG_0x30B2 0x30B2
+#define REG_0x30D3 0x30D3
+
+#define REG_0x3106 0x3106
+#define REG_0x3108 0x3108
+#define REG_0x310A 0x310A
+#define REG_0x310C 0x310C
+#define REG_0x310E 0x310E
+#define REG_0x3126 0x3126
+#define REG_0x312E 0x312E
+#define REG_0x313C 0x313C
+#define REG_0x313E 0x313E
+#define REG_0x3140 0x3140
+#define REG_0x3142 0x3142
+#define REG_0x3144 0x3144
+#define REG_0x3148 0x3148
+#define REG_0x314A 0x314A
+#define REG_0x3166 0x3166
+#define REG_0x3168 0x3168
+#define REG_0x316F 0x316F
+#define REG_0x3171 0x3171
+#define REG_0x3173 0x3173
+#define REG_0x3175 0x3175
+#define REG_0x3177 0x3177
+#define REG_0x3179 0x3179
+#define REG_0x317B 0x317B
+#define REG_0x317D 0x317D
+#define REG_0x317F 0x317F
+#define REG_0x3181 0x3181
+#define REG_0x3184 0x3184
+#define REG_0x3185 0x3185
+#define REG_0x3187 0x3187
+
+#define REG_0x31A4 0x31A4
+#define REG_0x31A6 0x31A6
+#define REG_0x31AC 0x31AC
+#define REG_0x31AE 0x31AE
+#define REG_0x31B4 0x31B4
+#define REG_0x31B6 0x31B6
+
+#define REG_0x3254 0x3254
+#define REG_0x3256 0x3256
+#define REG_0x3258 0x3258
+#define REG_0x325A 0x325A
+#define REG_0x3260 0x3260
+#define REG_0x3262 0x3262
+
+
+#define REG_0x3304 0x3304
+#define REG_0x3305 0x3305
+#define REG_0x3306 0x3306
+#define REG_0x3307 0x3307
+#define REG_0x3308 0x3308
+#define REG_0x3309 0x3309
+#define REG_0x330A 0x330A
+#define REG_0x330B 0x330B
+#define REG_0x330C 0x330C
+#define REG_0x330D 0x330D
+
+/* Mode Setting */
+#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
+#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
+#define REG_X_EVEN_INC_LSB 0x0381
+#define REG_X_ODD_INC_LSB 0x0383
+#define REG_Y_EVEN_INC_LSB 0x0385
+#define REG_Y_ODD_INC_LSB 0x0387
+#define REG_0x3016 0x3016
+#define REG_0x30E8 0x30E8
+#define REG_0x3301 0x3301
+/* for 120fps support */
+#define REG_0x0344 0x0344
+#define REG_0x0345 0x0345
+#define REG_0x0346 0x0346
+#define REG_0x0347 0x0347
+#define REG_0x0348 0x0348
+#define REG_0x0349 0x0349
+#define REG_0x034A 0x034A
+#define REG_0x034B 0x034B
+
+/* Test Pattern */
+#define REG_0x30D8 0x30D8
+#define REG_TEST_PATTERN_MODE 0x0601
+
+/* Solid Color Test Pattern */
+#define REG_TEST_DATA_RED_MSB 0x0603
+#define REG_TEST_DATA_RED_LSB 0x0603
+#define REG_TEST_DATA_GREENR_MSB 0x0604
+#define REG_TEST_DATA_GREENR_LSB 0x0605
+#define REG_TEST_DATA_BLUE_MSB 0x0606
+#define REG_TEST_DATA_BLUE_LSB 0x0607
+#define REG_TEST_DATA_GREENB_MSB 0x0608
+#define REG_TEST_DATA_GREENB_LSB 0x0609
+#define SN12M0PZ_AF_I2C_SLAVE_ID 0xE4
+#define SN12M0PZ_STEPS_NEAR_TO_CLOSEST_INF 42
+#define SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR 42
+
+
+/* TYPE DECLARATIONS */
+
+
+enum mipi_config_type {
+ IU060F_SN12M0PZ_STMIPID01,
+ IU060F_SN12M0PZ_STMIPID02
+};
+
+enum sn12m0pz_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum sn12m0pz_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE,
+ QVGA_SIZE,
+};
+
+enum sn12m0pz_setting {
+ RES_PREVIEW,
+ RES_CAPTURE,
+ RES_VIDEO_120FPS,
+};
+
+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
+};
+
+/* 816x612, 24MHz MCLK 96MHz PCLK */
+#define IU060F_SN12M0PZ_OFFSET 3
+/* Time in milisecs for waiting for the sensor to reset.*/
+#define SN12M0PZ_RESET_DELAY_MSECS 66
+#define SN12M0PZ_WIDTH 4032
+#define SN12M0PZ_HEIGHT 3024
+#define SN12M0PZ_FULL_SIZE_WIDTH 4032
+#define SN12M0PZ_FULL_SIZE_HEIGHT 3024
+#define SN12M0PZ_HRZ_FULL_BLK_PIXELS 176
+#define SN12M0PZ_VER_FULL_BLK_LINES 50
+#define SN12M0PZ_QTR_SIZE_WIDTH 2016
+#define SN12M0PZ_QTR_SIZE_HEIGHT 1512
+#define SN12M0PZ_HRZ_QTR_BLK_PIXELS 2192
+#define SN12M0PZ_VER_QTR_BLK_LINES 26
+
+/* 120fps mode */
+#define SN12M0PZ_QVGA_SIZE_WIDTH 4032
+#define SN12M0PZ_QVGA_SIZE_HEIGHT 249
+#define SN12M0PZ_HRZ_QVGA_BLK_PIXELS 176
+#define SN12M0PZ_VER_QVGA_BLK_LINES 9
+#define SN12M0PZ_DEFAULT_CLOCK_RATE 24000000
+
+static uint32_t IU060F_SN12M0PZ_DELAY_MSECS = 30;
+static enum mipi_config_type mipi_config = IU060F_SN12M0PZ_STMIPID02;
+/* AF Tuning Parameters */
+static int16_t enable_single_D02_lane;
+static int16_t fullsize_cropped_at_8mp;
+
+struct sn12m0pz_work_t {
+ struct work_struct work;
+};
+
+static struct sn12m0pz_work_t *sn12m0pz_sensorw;
+static struct i2c_client *sn12m0pz_client;
+
+struct sn12m0pz_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+ uint32_t sensormode;
+ uint32_t fps_divider;/* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider;/* init to 1 * 0x00000400 */
+ uint16_t fps;
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+ enum sn12m0pz_resolution_t prev_res;
+ enum sn12m0pz_resolution_t pict_res;
+ enum sn12m0pz_resolution_t curr_res;
+ enum sn12m0pz_test_mode_t set_test;
+ unsigned short imgaddr;
+};
+
+static struct sn12m0pz_ctrl_t *sn12m0pz_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(sn12m0pz_wait_queue);
+DEFINE_MUTEX(sn12m0pz_mut);
+
+
+static int sn12m0pz_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 = 2,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(sn12m0pz_client->adapter, msgs, 2) < 0) {
+ CDBG("sn12m0pz_i2c_rxdata failed!");
+ return -EIO;
+ }
+
+ return 0;
+}
+static int32_t sn12m0pz_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(sn12m0pz_client->adapter, msg, 1) < 0) {
+ CDBG("sn12m0pz_i2c_txdata faild 0x%x", sn12m0pz_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t sn12m0pz_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = sn12m0pz_i2c_rxdata(sn12m0pz_client->addr, buf, rlen);
+
+ if (rc < 0) {
+ CDBG("sn12m0pz_i2c_read 0x%x failed!", raddr);
+ return rc;
+ }
+
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+
+ return rc;
+}
+
+static int32_t sn12m0pz_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc;
+ unsigned char buf[3];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ udelay(90);
+ CDBG("i2c_write_b addr = %x, val = %x\n", waddr, bdata);
+ rc = sn12m0pz_i2c_txdata(sn12m0pz_client->addr, buf, 3);
+
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!",
+ waddr, bdata);
+ }
+
+ return rc;
+}
+
+static int16_t sn12m0pz_i2c_write_b_af(unsigned short saddr,
+ unsigned short baddr, unsigned short bdata)
+{
+ int16_t rc;
+ unsigned char buf[2];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = baddr;
+ buf[1] = bdata;
+ rc = sn12m0pz_i2c_txdata(saddr, buf, 2);
+
+ if (rc < 0)
+ CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!",
+ saddr, baddr, bdata);
+
+ return rc;
+}
+
+static int32_t sn12m0pz_i2c_write_byte_bridge(unsigned short saddr,
+ unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc;
+ unsigned char buf[3];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+
+ CDBG("i2c_write_b addr = %x, val = %x", waddr, bdata);
+ rc = sn12m0pz_i2c_txdata(saddr, buf, 3);
+
+ if (rc < 0)
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!",
+ waddr, bdata);
+
+ return rc;
+}
+
+static int32_t sn12m0pz_stmipid01_config(void)
+{
+ int32_t rc = 0;
+ /* Initiate I2C for D01: */
+ /* MIPI Bridge configuration */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0002, 0x19) < 0)
+ return rc; /* enable clock lane*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0003, 0x00) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0004, 0x3E) < 0)
+ return rc; /* mipi mode clock*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0005, 0x01) < 0)
+ return rc; /* enable data line*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0006, 0x0F) < 0)
+ return rc; /* mipi mode data 0x01*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x00) < 0)
+ return rc; /* Data_Lane1_Reg1*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000D, 0x92) < 0)
+ return rc; /* CCPRxRegisters*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000E, 0x28) < 0)
+ return rc; /* 10 bits for pixel width input for CCP rx.*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC0) < 0)
+ return rc; /* no bypass, no decomp, 1Lane System,CSIstreaming*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0015, 0x48) < 0)
+ return rc; /* ModeControlRegisters-- Don't reset error flag*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0017, 0x2B) < 0)
+ return rc; /* Data_ID_Rreg*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0018, 0x2B) < 0)
+ return rc; /* Data_ID_Rreg_emb*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0019, 0x0C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001E, 0x0A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001F, 0x0A) < 0)
+ return rc;
+
+ return rc;
+}
+static int32_t sn12m0pz_stmipid02_config(void)
+{
+ int32_t rc = 0;
+
+ /* Main Camera Clock Lane 1 (CLHP1, CLKN1)*/
+ /* Enable Clock Lane 1 (CLHP1, CLKN1), 0x15 for 400MHz */
+ if (enable_single_D02_lane) {
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0002, 0x19) < 0)
+ return rc;
+ /* Main Camera Data Lane 1.1 (DATA2P1, DATA2N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x00) < 0)
+ return rc;/* Enable Data Lane 1.2 (DATA2P1, DATA2N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000A, 0x00) < 0)
+ return rc; /*CSIMode on Data Lane1.2(DATA2P1,DATA2N1)*/
+ /* Mode Control */
+ /* Enable single lane for qtr preview */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC0) < 0)
+ return rc; /*set 0xC0 - left justified on upper bits)*/
+ /* bit 1 set to 0 i.e. 1 lane system for qtr size preview */
+ } else {
+ if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1,
+ 0x0002, 0x19) < 0)
+ return rc;
+ } else {
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1,
+ 0x0002, 0x21) < 0)
+ return rc;
+ }
+ /* Main Camera Data Lane 1.1 (DATA2P1, DATA2N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0009, 0x01) < 0)
+ return rc; /* Enable Data Lane 1.2 (DATA2P1, DATA2N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x000A, 0x01) < 0)
+ return rc; /* CSI Mode Data Lane1.2(DATA2P1, DATA2N1)*/
+
+ /* Mode Control */
+ /* Enable two lanes for full size preview/ snapshot */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0014, 0xC2) < 0)
+ return rc; /* No decompression, CSI dual lane */
+ }
+
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0004, 0x1E) < 0)
+ return rc;
+
+ /* Main Camera Data Lane 1.1 (DATA1P1, DATA1N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0005, 0x03) < 0)
+ return rc; /* Enable Data Lane 1.1 (DATA1P1, DATA1N1) */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0006, 0x0f) < 0)
+ return rc; /* CSI Mode on Data Lane 1.1 (DATA1P1, DATA1N1) */
+
+ /* Tristated Output, continuous clock, */
+ /*polarity of clock is inverted and sync signals not inverted*/
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0015, 0x08) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0036, 0x20) < 0)
+ return rc; /* Enable compensation macro, main camera */
+
+ /* Data type: 0x2B Raw 10 */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0017, 0x2B) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0018, 0x2B) < 0)
+ return rc; /* Data type of embedded data: 0x2B Raw 10 */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x0019, 0x0C) < 0)
+ return rc; /* Data type and pixel width programmed 0x0C*/
+
+ /* Decompression Mode */
+
+ /* Pixel Width and Decompression ON/OFF */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001E, 0x0A) < 0)
+ return rc; /* Image data not compressed: 0x0A for 10 bits */
+ if (sn12m0pz_i2c_write_byte_bridge(0x28>>1, 0x001F, 0x0A) < 0)
+ return rc; /* Embedded data not compressed: 0x0A for 10 bits */
+ return rc;
+}
+
+static int16_t sn12m0pz_af_init(void)
+{
+ int16_t rc;
+ /* Initialize waveform */
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x01, 0xA9);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x02, 0xD2);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x03, 0x0C);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x04, 0x14);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x05, 0xB6);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x06, 0x4F);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x07, 0x00);
+
+ return rc;
+}
+
+static int32_t sn12m0pz_move_focus(int direction,
+ int32_t num_steps)
+{
+ int8_t step_direction, dest_step_position, bit_mask;
+ int32_t rc = 0;
+ uint16_t sn12m0pz_l_region_code_per_step = 3;
+
+ if (num_steps == 0)
+ return rc;
+
+ if (direction == MOVE_NEAR) {
+ step_direction = 1;
+ bit_mask = 0x80;
+ } else if (direction == MOVE_FAR) {
+ step_direction = -1;
+ bit_mask = 0x00;
+ } else {
+ CDBG("sn12m0pz_move_focus: Illegal focus direction");
+ return -EINVAL;
+ }
+
+ dest_step_position = sn12m0pz_ctrl->curr_step_pos +
+ (step_direction * num_steps);
+
+ if (dest_step_position < 0)
+ dest_step_position = 0;
+ else if (dest_step_position > SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR)
+ dest_step_position = SN12M0PZ_TOTAL_STEPS_NEAR_TO_FAR;
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00,
+ ((num_steps * sn12m0pz_l_region_code_per_step) | bit_mask));
+
+ sn12m0pz_ctrl->curr_step_pos = dest_step_position;
+
+ return rc;
+}
+static int32_t sn12m0pz_set_default_focus(uint8_t af_step)
+{
+ int32_t rc;
+
+ /* Initialize to infinity */
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00, 0x7F);
+
+ rc = sn12m0pz_i2c_write_b_af(SN12M0PZ_AF_I2C_SLAVE_ID >> 1, 0x00, 0x7F);
+
+ sn12m0pz_ctrl->curr_step_pos = 0;
+
+ return rc;
+}
+static void sn12m0pz_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+ uint16_t preview_line_length_pck, snapshot_line_length_pck;
+ uint32_t divider, pclk_mult, d1, d2;
+
+ /* Total frame_length_lines and line_length_pck for preview */
+ CDBG("sn12m0pz_get_pict_fps prev_res %d", sn12m0pz_ctrl->prev_res);
+ if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+ preview_frame_length_lines = SN12M0PZ_QVGA_SIZE_HEIGHT +
+ SN12M0PZ_VER_QVGA_BLK_LINES;
+ preview_line_length_pck = SN12M0PZ_QVGA_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QVGA_BLK_PIXELS;
+ } else {
+ preview_frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES;
+ preview_line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+ }
+ /* Total frame_length_lines and line_length_pck for snapshot */
+ snapshot_frame_length_lines = SN12M0PZ_FULL_SIZE_HEIGHT
+ + SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+ snapshot_line_length_pck = SN12M0PZ_FULL_SIZE_WIDTH
+ + SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+ d1 = preview_frame_length_lines *
+ 0x00000400 / snapshot_frame_length_lines;
+ d2 = preview_line_length_pck *
+ 0x00000400/snapshot_line_length_pck;
+ divider = d1 * d2 / 0x400;
+ pclk_mult =
+ (uint32_t)
+ (sn12m0pz_regs.reg_pat[RES_CAPTURE].pll_multiplier_lsb *
+ 0x400) / (uint32_t)
+ sn12m0pz_regs.reg_pat[RES_PREVIEW].pll_multiplier_lsb;
+ *pfps = (uint16_t) (((fps * divider) / 0x400 * pclk_mult) / 0x400);
+}
+
+static uint16_t sn12m0pz_get_prev_lines_pf(void)
+{
+ if (sn12m0pz_ctrl->prev_res == QTR_SIZE)
+ return SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES;
+ else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE)
+ return SN12M0PZ_QVGA_SIZE_HEIGHT +
+ SN12M0PZ_VER_QVGA_BLK_LINES;
+
+ else
+ return SN12M0PZ_FULL_SIZE_HEIGHT +
+ SN12M0PZ_VER_FULL_BLK_LINES;
+}
+
+static uint16_t sn12m0pz_get_prev_pixels_pl(void)
+{
+ if (sn12m0pz_ctrl->prev_res == QTR_SIZE)
+ return SN12M0PZ_QTR_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+ else
+ return SN12M0PZ_FULL_SIZE_WIDTH +
+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t sn12m0pz_get_pict_lines_pf(void)
+{
+ if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+ return SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES;
+ else
+ return SN12M0PZ_FULL_SIZE_HEIGHT +
+ SN12M0PZ_VER_FULL_BLK_LINES;
+}
+
+static uint16_t sn12m0pz_get_pict_pixels_pl(void)
+{
+ if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+ return SN12M0PZ_QTR_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+ else
+ return SN12M0PZ_FULL_SIZE_WIDTH +
+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t sn12m0pz_get_pict_max_exp_lc(void)
+{
+ if (sn12m0pz_ctrl->pict_res == QTR_SIZE)
+ return (SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES) * 24;
+ else
+ return (SN12M0PZ_FULL_SIZE_HEIGHT +
+ SN12M0PZ_VER_FULL_BLK_LINES) * 24;
+}
+
+static int32_t sn12m0pz_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+
+ total_lines_per_frame = (uint16_t)((SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES) *
+ sn12m0pz_ctrl->fps_divider / 0x400);
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_MSB,
+ ((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LSB,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t sn12m0pz_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ static uint16_t max_legal_gain = 0x00E0;
+ uint8_t gain_msb, gain_lsb;
+ uint8_t intg_time_msb, intg_time_lsb;
+ uint8_t line_length_pck_msb, line_length_pck_lsb;
+ uint16_t line_length_pck, frame_length_lines, temp_lines;
+ uint32_t line_length_ratio = 1 * Q8;
+ int32_t rc = 0;
+ CDBG("sn12m0pz_write_exp_gain : gain = %d line = %d", gain, line);
+
+ if (sn12m0pz_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+ frame_length_lines = SN12M0PZ_QVGA_SIZE_HEIGHT +
+ SN12M0PZ_VER_QVGA_BLK_LINES;
+ line_length_pck = SN12M0PZ_QVGA_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QVGA_BLK_PIXELS;
+ if (line > (frame_length_lines -
+ IU060F_SN12M0PZ_OFFSET))
+ line = frame_length_lines -
+ IU060F_SN12M0PZ_OFFSET;
+ sn12m0pz_ctrl->fps = (uint16_t) (120 * Q8);
+ } else {
+ if (sn12m0pz_ctrl->curr_res == QTR_SIZE) {
+ frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES;
+ line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+ } else {
+ frame_length_lines = SN12M0PZ_HEIGHT +
+ SN12M0PZ_VER_FULL_BLK_LINES;
+ line_length_pck = SN12M0PZ_WIDTH +
+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+ }
+ if (line > (frame_length_lines -
+ IU060F_SN12M0PZ_OFFSET))
+ sn12m0pz_ctrl->fps = (uint16_t) (30 * Q8 *
+ (frame_length_lines - IU060F_SN12M0PZ_OFFSET) / line);
+ else
+ sn12m0pz_ctrl->fps = (uint16_t) (30 * Q8);
+ }
+ } else {
+ if (sn12m0pz_ctrl->curr_res == QTR_SIZE) {
+ frame_length_lines = SN12M0PZ_QTR_SIZE_HEIGHT +
+ SN12M0PZ_VER_QTR_BLK_LINES;
+ line_length_pck = SN12M0PZ_QTR_SIZE_WIDTH +
+ SN12M0PZ_HRZ_QTR_BLK_PIXELS;
+ } else {
+ frame_length_lines = SN12M0PZ_HEIGHT +
+ SN12M0PZ_VER_FULL_BLK_LINES;
+ line_length_pck = SN12M0PZ_WIDTH +
+ SN12M0PZ_HRZ_FULL_BLK_PIXELS;
+ }
+ }
+ if (gain > max_legal_gain)
+ /* range: 0 to 224 */
+ gain = max_legal_gain;
+ temp_lines = line;
+ /* calculate line_length_ratio */
+ if (line > (frame_length_lines - IU060F_SN12M0PZ_OFFSET)) {
+ line_length_ratio = (line * Q8) / (frame_length_lines -
+ IU060F_SN12M0PZ_OFFSET);
+ temp_lines = frame_length_lines - IU060F_SN12M0PZ_OFFSET;
+ if (line_length_ratio == 0)
+ line_length_ratio = 1 * Q8;
+ } else
+ line_length_ratio = 1 * Q8;
+
+ line = (uint32_t) (line * sn12m0pz_ctrl->fps_divider/0x400);
+
+ /* update gain registers */
+ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lsb = (uint8_t) (gain & 0x00FF);
+
+ /* linear AFR horizontal stretch */
+ line_length_pck = (uint16_t) (line_length_pck * line_length_ratio / Q8);
+ line_length_pck_msb = (uint8_t) ((line_length_pck & 0xFF00) >> 8);
+ line_length_pck_lsb = (uint8_t) (line_length_pck & 0x00FF);
+
+ /* update line count registers */
+ intg_time_msb = (uint8_t) ((temp_lines & 0xFF00) >> 8);
+ intg_time_lsb = (uint8_t) (temp_lines & 0x00FF);
+
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB,
+ gain_msb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB,
+ gain_lsb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_MSB,
+ line_length_pck_msb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LSB,
+ line_length_pck_lsb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_MSB,
+ intg_time_msb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LSB,
+ intg_time_lsb) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF) < 0)
+ return rc;
+
+ return rc;
+}
+
+
+static int32_t sn12m0pz_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc;
+ rc = sn12m0pz_write_exp_gain(gain, line);
+ return rc;
+}
+
+static int32_t sn12m0pz_test(enum sn12m0pz_test_mode_t mo)
+{
+ uint8_t test_data_val_msb = 0x07;
+ uint8_t test_data_val_lsb = 0xFF;
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+ 1: Bypass Signal Processing. REG_0x30D8[5] is EBDMASK:
+ 0: Output Embedded data, 1: No output embedded data */
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30D8, 0x10) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0)
+ return rc;
+
+ /* Solid Color Test Pattern */
+
+ if (mo == TEST_1) {
+ if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_RED_MSB,
+ test_data_val_msb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_RED_LSB,
+ test_data_val_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_TEST_DATA_GREENR_MSB,
+ test_data_val_msb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_TEST_DATA_GREENR_LSB,
+ test_data_val_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_BLUE_MSB,
+ test_data_val_msb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_TEST_DATA_BLUE_LSB,
+ test_data_val_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_TEST_DATA_GREENB_MSB,
+ test_data_val_msb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_TEST_DATA_GREENB_LSB,
+ test_data_val_lsb) < 0)
+ return rc;
+ }
+
+ }
+
+ return rc;
+}
+
+static int32_t sn12m0pz_reset(void)
+{
+ int32_t rc = 0;
+ /* register 0x0002 is Port 2, CAM_XCLRO */
+ gpio_direction_output(sn12m0pz_ctrl->
+ sensordata->sensor_reset,
+ 0);
+ msleep(50);
+ gpio_direction_output(sn12m0pz_ctrl->
+ sensordata->sensor_reset,
+ 1);
+ msleep(13);
+ return rc;
+}
+
+static int32_t sn12m0pz_sensor_setting(int update_type, int rt)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+
+ switch (update_type) {
+ case UPDATE_PERIODIC:
+ /* Put Sensor into sofware standby mode */
+ if (sn12m0pz_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ msleep(5);
+ /* Hardware reset D02, lane config between full size/qtr size*/
+ rc = sn12m0pz_reset();
+ if (rc < 0)
+ return rc;
+
+ if (sn12m0pz_stmipid02_config() < 0)
+ return rc;
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE
+ || rt == RES_VIDEO_120FPS) {
+ /* reset fps_divider */
+ sn12m0pz_ctrl->fps_divider = 1 * 0x400;
+
+ /* PLL settings */
+ if (sn12m0pz_i2c_write_b_sensor(REG_PLL_MULTIPLIER,
+ sn12m0pz_regs.reg_pat[rt].pll_multiplier_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x302B,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x302B) < 0)
+ return rc;
+
+ /* MIPI Enable Settings */
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30E5,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30E5) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3300,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3300) < 0)
+ return rc;
+
+ /* Global Setting */
+ if (
+ sn12m0pz_i2c_write_b_sensor(
+ REG_IMAGE_ORIENTATION,
+ sn12m0pz_regs.reg_pat_init[0].image_orient) < 0)
+ return rc;
+ if (
+ sn12m0pz_i2c_write_b_sensor(
+ REG_COARSE_INTEGRATION_TIME_MSB,
+ sn12m0pz_regs.reg_pat[rt].coarse_integ_time_msb)
+ < 0)
+ return rc;
+ if (
+ sn12m0pz_i2c_write_b_sensor(
+ REG_COARSE_INTEGRATION_TIME_LSB,
+ sn12m0pz_regs.reg_pat[rt].coarse_integ_time_lsb)
+ < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x300A,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x300A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3014,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3014) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3015,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3015) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3017,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3017) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x301C,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x301C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3031,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3031) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3040,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3040) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3041,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3041) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3051,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3051) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3053,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3053) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3055,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3055) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3057,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3057) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3060,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3060) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3065,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3065) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30AA,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30AA) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30AB,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30AB) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30B0,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30B0) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30B2,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30B2) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30D3,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30D3) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30D8,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x30D8) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3106,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3106) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3108,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3108) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x310A,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x310A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x310C,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x310C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x310E,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x310E) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3126,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3126) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x312E,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x312E) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x313C,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x313C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x313E,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x313E) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3140,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3140) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3142,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3142) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3144,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3144) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3148,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3148) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x314A,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x314A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3166,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3166) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3168,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3168) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x316F,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x316F) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3171,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3171) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3173,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3173) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3175,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3175) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3177,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3177) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3179,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3179) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x317B,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x317B) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x317D,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x317D) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x317F,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x317F) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3181,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3181) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3184,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3184) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3185,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3185) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3187,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3187) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31A4,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31A4) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31A6,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31A6) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31AC,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31AC) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31AE,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31AE) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31B4,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31B4) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x31B6,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x31B6) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3254,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3254) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3256,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3256) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3258,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3258) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x325A,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x325A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3260,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3260) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3262,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3262) < 0)
+ return rc;
+
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3304,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3304) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3305,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3305) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3306,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3306) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3307,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3307) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3308,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3308) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3309,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x3309) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x330A,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x330A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x330B,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x330B) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x330C,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x330C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x330D,
+ sn12m0pz_regs.reg_pat_init[0].reg_0x330D) < 0)
+ return rc;
+
+ /* Mode setting */
+ /* Update registers with correct
+ frame_length_line value for AFR */
+ total_lines_per_frame = (uint16_t)(
+ (sn12m0pz_regs.reg_pat[rt].frame_length_lines_msb << 8)
+ & 0xFF00) +
+ sn12m0pz_regs.reg_pat[rt].frame_length_lines_lsb;
+ total_lines_per_frame = total_lines_per_frame *
+ sn12m0pz_ctrl->fps_divider / 0x400;
+
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_FRAME_LENGTH_LINES_MSB,
+ (total_lines_per_frame & 0xFF00) >> 8)
+ < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_FRAME_LENGTH_LINES_LSB,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_MSB,
+ sn12m0pz_regs.reg_pat[rt].line_length_pck_msb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LSB,
+ sn12m0pz_regs.reg_pat[rt].line_length_pck_lsb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_X_OUTPUT_SIZE_MSB,
+ sn12m0pz_regs.reg_pat[rt].x_output_size_msb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_X_OUTPUT_SIZE_LSB,
+ sn12m0pz_regs.reg_pat[rt].x_output_size_lsb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_Y_OUTPUT_SIZE_MSB,
+ sn12m0pz_regs.reg_pat[rt].y_output_size_msb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_Y_OUTPUT_SIZE_LSB,
+ sn12m0pz_regs.reg_pat[rt].y_output_size_lsb) <
+ 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_X_EVEN_INC_LSB,
+ sn12m0pz_regs.reg_pat[rt].x_even_inc_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_X_ODD_INC_LSB,
+ sn12m0pz_regs.reg_pat[rt].x_odd_inc_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_Y_EVEN_INC_LSB,
+ sn12m0pz_regs.reg_pat[rt].y_even_inc_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_Y_ODD_INC_LSB,
+ sn12m0pz_regs.reg_pat[rt].y_odd_inc_lsb) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3016,
+ sn12m0pz_regs.reg_pat[rt].reg_0x3016) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x30E8,
+ sn12m0pz_regs.reg_pat[rt].reg_0x30E8) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x3301,
+ sn12m0pz_regs.reg_pat[rt].reg_0x3301) < 0)
+ return rc;
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0344,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0344) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0345,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0345) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0346,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0346) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0347,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0347) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0348,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0348) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x0349,
+ sn12m0pz_regs.reg_pat[rt].reg_0x0349) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x034A,
+ sn12m0pz_regs.reg_pat[rt].reg_0x034A) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(REG_0x034B,
+ sn12m0pz_regs.reg_pat[rt].reg_0x034B) < 0)
+ return rc;
+
+ if ((rt == RES_CAPTURE) && fullsize_cropped_at_8mp) {
+ /* x address end */
+ if (sn12m0pz_i2c_write_b_sensor(0x0348,
+ 0x0C) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(0x0349,
+ 0x0CF) < 0)
+ return rc;
+ /* y address end */
+ if (sn12m0pz_i2c_write_b_sensor(0x034A,
+ 0x09) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(0x034B,
+ 0x9F) < 0)
+ return rc;
+ }
+
+ if (mipi_config == IU060F_SN12M0PZ_STMIPID01) {
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_PLL_MULTIPLIER, 0x43) < 0)
+ return rc;
+ if (rt == RES_CAPTURE) {
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_0x3301, 0x01) < 0)
+ return rc;
+ if (sn12m0pz_i2c_write_b_sensor(
+ REG_0x3017, 0xE0) < 0)
+ return rc;
+ }
+ }
+
+ if (sn12m0pz_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+
+ msleep(IU060F_SN12M0PZ_DELAY_MSECS);
+
+ if (sn12m0pz_test(sn12m0pz_ctrl->set_test) < 0)
+ return rc;
+
+ if (mipi_config == IU060F_SN12M0PZ_STMIPID02)
+ CDBG("%s,%d", __func__, __LINE__);
+ return rc;
+ }
+ default:
+ return rc;
+ }
+}
+
+
+static int32_t sn12m0pz_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+
+
+ if (mode == SENSOR_HFR_120FPS_MODE)
+ sn12m0pz_ctrl->prev_res = QVGA_SIZE;
+
+ /* change sensor resolution if needed */
+ if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->prev_res) {
+ if (sn12m0pz_ctrl->prev_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ IU060F_SN12M0PZ_DELAY_MSECS = 35; /*measured on scope*/
+ enable_single_D02_lane = 1;
+ } else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+ rt = RES_VIDEO_120FPS;
+ IU060F_SN12M0PZ_DELAY_MSECS = 35; /*measured on scope*/
+ enable_single_D02_lane = 0;
+ } else {
+ rt = RES_CAPTURE;
+ enable_single_D02_lane = 0;
+ }
+
+ if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->prev_res;
+ sn12m0pz_ctrl->sensormode = mode;
+
+ return rc;
+}
+static int32_t sn12m0pz_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->pict_res) {
+ if (sn12m0pz_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ enable_single_D02_lane = 1;
+ } else {
+ rt = RES_CAPTURE;
+ IU060F_SN12M0PZ_DELAY_MSECS = 100;/*measured on scope*/
+ enable_single_D02_lane = 0;
+ }
+
+ if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->pict_res;
+ sn12m0pz_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t sn12m0pz_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (sn12m0pz_ctrl->curr_res != sn12m0pz_ctrl->pict_res) {
+ if (sn12m0pz_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ enable_single_D02_lane = 1;
+ } else {
+ rt = RES_CAPTURE;
+ IU060F_SN12M0PZ_DELAY_MSECS = 100;/*measured on scope*/
+ enable_single_D02_lane = 0;
+ }
+ if (sn12m0pz_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+ sn12m0pz_ctrl->curr_res = sn12m0pz_ctrl->pict_res;
+ sn12m0pz_ctrl->sensormode = mode;
+ return rc;
+}
+static int32_t sn12m0pz_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ case SENSOR_HFR_120FPS_MODE:
+ rc = sn12m0pz_video_config(mode);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ rc = sn12m0pz_snapshot_config(mode);
+ break;
+
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = sn12m0pz_raw_snapshot_config(mode);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int32_t sn12m0pz_power_down(void)
+{
+ return 0;
+}
+
+
+static int sn12m0pz_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ gpio_direction_output(data->vcm_pwd, 0);
+ gpio_free(data->vcm_pwd);
+ return 0;
+}
+
+static int sn12m0pz_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+ unsigned short chipidl, chipidh;
+ CDBG("Requesting gpio");
+ rc = gpio_request(data->sensor_reset, "sn12m0pz");
+ CDBG(" sn12m0pz_probe_init_sensor");
+ if (!rc) {
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(20);
+ gpio_direction_output(data->sensor_reset, 1);
+ msleep(13);
+ } else {
+ goto init_probe_done;
+ }
+ CDBG("Requestion gpio");
+ rc = gpio_request(data->vcm_pwd, "sn12m0pz");
+ CDBG(" sn12m0pz_probe_init_sensor");
+
+ if (!rc) {
+ gpio_direction_output(data->vcm_pwd, 0);
+ msleep(20);
+ gpio_direction_output(data->vcm_pwd, 1);
+ msleep(13);
+ } else {
+ gpio_direction_output(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ goto init_probe_done;
+ }
+
+ msleep(20);
+
+ /* 3. Read sensor Model ID: */
+ rc = sn12m0pz_i2c_read(0x0000, &chipidh, 1);
+ if (rc < 0) {
+ CDBG(" sn12m0pz_probe_init_sensor3");
+ goto init_probe_fail;
+ }
+ rc = sn12m0pz_i2c_read(0x0001, &chipidl, 1);
+ if (rc < 0) {
+ CDBG(" sn12m0pz_probe_init_sensor4");
+ goto init_probe_fail;
+ }
+
+ /* 4. Compare sensor ID to SN12M0PZ ID: */
+ if (chipidh != 0x00 || chipidl != 0x60) {
+ rc = -ENODEV;
+ CDBG("sn12m0pz_probe_init_sensor fail chip id doesnot match");
+ goto init_probe_fail;
+ }
+
+ msleep(SN12M0PZ_RESET_DELAY_MSECS);
+
+ goto init_probe_done;
+
+init_probe_fail:
+ CDBG(" sn12m0pz_probe_init_sensor fails");
+ sn12m0pz_probe_init_done(data);
+
+init_probe_done:
+ CDBG(" sn12m0pz_probe_init_sensor finishes");
+ return rc;
+}
+
+int sn12m0pz_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ CDBG("Calling sn12m0pz_sensor_open_init");
+
+ sn12m0pz_ctrl = kzalloc(sizeof(struct sn12m0pz_ctrl_t), GFP_KERNEL);
+ if (!sn12m0pz_ctrl) {
+ CDBG("sn12m0pz_init failed!");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+
+ sn12m0pz_ctrl->fps_divider = 1 * 0x00000400;
+ sn12m0pz_ctrl->pict_fps_divider = 1 * 0x00000400;
+ sn12m0pz_ctrl->set_test = TEST_OFF;
+ sn12m0pz_ctrl->prev_res = QTR_SIZE;
+ sn12m0pz_ctrl->pict_res = FULL_SIZE;
+ sn12m0pz_ctrl->curr_res = INVALID_SIZE;
+ if (data)
+ sn12m0pz_ctrl->sensordata = data;
+
+ if (rc < 0)
+ return rc;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(SN12M0PZ_DEFAULT_CLOCK_RATE);
+ msleep(20);
+ msm_camio_camif_pad_reg_reset();
+ msleep(20);
+ CDBG("Calling sn12m0pz_sensor_open_init");
+ rc = sn12m0pz_probe_init_sensor(data);
+
+ if (rc < 0)
+ goto init_fail;
+ /* send reset signal */
+ if (mipi_config == IU060F_SN12M0PZ_STMIPID01) {
+ if (sn12m0pz_stmipid01_config() < 0) {
+ CDBG("Calling sn12m0pz_sensor_open_init fail");
+ return rc;
+ }
+ } else {
+ if (sn12m0pz_ctrl->prev_res == QTR_SIZE)
+ enable_single_D02_lane = 1;
+ else /* FULL_SIZE */
+ enable_single_D02_lane = 0;
+
+ if (sn12m0pz_stmipid02_config() < 0) {
+ CDBG("Calling sn12m0pz_sensor_open_init fail");
+ return rc;
+ }
+ }
+
+
+ if (sn12m0pz_ctrl->prev_res == QTR_SIZE) {
+ if (sn12m0pz_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+ return rc;
+ } else if (sn12m0pz_ctrl->prev_res == QVGA_SIZE) {
+ if (sn12m0pz_sensor_setting(REG_INIT, RES_VIDEO_120FPS) < 0)
+ return rc;
+ } else {
+ if (sn12m0pz_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+ return rc;
+ }
+
+ if (sn12m0pz_af_init() < 0)
+ return rc;
+ sn12m0pz_ctrl->fps = 30*Q8;
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+init_fail:
+ CDBG(" init_fail");
+ sn12m0pz_probe_init_done(data);
+ kfree(sn12m0pz_ctrl);
+init_done:
+ CDBG("init_done");
+ return rc;
+}
+static int __init sn12m0pz_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&sn12m0pz_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id sn12m0pz_i2c_id[] = {
+ { "sn12m0pz", 0},
+ { }
+};
+
+static int sn12m0pz_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("sn12m0pz_probe called!");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed");
+ goto probe_failure;
+ }
+
+ sn12m0pz_sensorw = kzalloc(sizeof(struct sn12m0pz_work_t), GFP_KERNEL);
+ if (!sn12m0pz_sensorw) {
+ CDBG("kzalloc failed");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, sn12m0pz_sensorw);
+ sn12m0pz_init_client(client);
+ sn12m0pz_client = client;
+
+ msleep(50);
+
+ CDBG("sn12m0pz_probe successed! rc = %d", rc);
+ return 0;
+
+probe_failure:
+ CDBG("sn12m0pz_probe failed! rc = %d", rc);
+ return rc;
+}
+
+static int __exit sn12m0pz_remove(struct i2c_client *client)
+{
+ struct sn12m0pz_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ sn12m0pz_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver sn12m0pz_i2c_driver = {
+ .id_table = sn12m0pz_i2c_id,
+ .probe = sn12m0pz_i2c_probe,
+ .remove = __exit_p(sn12m0pz_i2c_remove),
+ .driver = {
+ .name = "sn12m0pz",
+ },
+};
+
+int sn12m0pz_sensor_config(void __user *argp)
+{
+ struct sensor_cfg_data cdata;
+ int32_t rc = 0;
+ if (copy_from_user(&cdata,
+ (void *)argp,
+ sizeof(struct sensor_cfg_data)))
+ return -EFAULT;
+
+ mutex_lock(&sn12m0pz_mut);
+
+ CDBG("sn12m0pz_sensor_config: cfgtype = %d",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ sn12m0pz_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 =
+ sn12m0pz_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 =
+ sn12m0pz_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 =
+ sn12m0pz_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 =
+ sn12m0pz_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 =
+ sn12m0pz_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 = sn12m0pz_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ sn12m0pz_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ sn12m0pz_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = sn12m0pz_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = sn12m0pz_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = sn12m0pz_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = sn12m0pz_set_default_focus(cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = 0;
+ break;
+ case CFG_SET_LENS_SHADING:
+ rc = 0;
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&sn12m0pz_mut);
+
+ return rc;
+}
+
+static int sn12m0pz_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&sn12m0pz_mut);
+
+ sn12m0pz_power_down();
+
+ gpio_direction_output(sn12m0pz_ctrl->sensordata->sensor_reset,
+ 0);
+ gpio_free(sn12m0pz_ctrl->sensordata->sensor_reset);
+
+ gpio_direction_output(sn12m0pz_ctrl->sensordata->vcm_pwd,
+ 0);
+ gpio_free(sn12m0pz_ctrl->sensordata->vcm_pwd);
+
+ kfree(sn12m0pz_ctrl);
+ sn12m0pz_ctrl = NULL;
+
+ CDBG("sn12m0pz_release completed");
+
+
+ mutex_unlock(&sn12m0pz_mut);
+
+ return rc;
+}
+
+static int sn12m0pz_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc;
+
+ rc = i2c_add_driver(&sn12m0pz_i2c_driver);
+ if (rc < 0 || sn12m0pz_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+
+ msm_camio_clk_rate_set(SN12M0PZ_DEFAULT_CLOCK_RATE);
+ msleep(20);
+
+ rc = sn12m0pz_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+
+ s->s_init = sn12m0pz_sensor_open_init;
+ s->s_release = sn12m0pz_sensor_release;
+ s->s_config = sn12m0pz_sensor_config;
+ s->s_mount_angle = 0;
+ sn12m0pz_probe_init_done(info);
+
+ return rc;
+
+probe_fail:
+ CDBG("SENSOR PROBE FAILS!");
+ i2c_del_driver(&sn12m0pz_i2c_driver);
+ return rc;
+}
+
+static int __sn12m0pz_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, sn12m0pz_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __sn12m0pz_probe,
+ .driver = {
+ .name = "msm_camera_sn12m0pz",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sn12m0pz_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(sn12m0pz_init);
+
+MODULE_DESCRIPTION("Sony 12M MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/sn12m0pz.h b/drivers/media/video/msm/sn12m0pz.h
new file mode 100644
index 0000000..f2abc47
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz.h
@@ -0,0 +1,138 @@
+
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#ifndef SN12M0PZ_H
+#define SN12M0PZ_H
+
+#include <linux/types.h>
+extern struct sn12m0pz_reg sn12m0pz_regs; /* from mt9t013_reg.c */
+struct reg_struct{
+ uint8_t pll_multiplier_lsb; /* 0x0307*/
+ uint8_t coarse_integ_time_msb; /* 0x0202*/
+ uint8_t coarse_integ_time_lsb; /* 0x0203*/
+ 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 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_lsb; /* 0x0381*/
+ uint8_t x_odd_inc_lsb; /* 0x0383*/
+ uint8_t y_even_inc_lsb; /* 0x0385*/
+ uint8_t y_odd_inc_lsb; /* 0x0387*/
+ uint8_t reg_0x3016; /* 0x3016 VMODEADD*/
+ uint8_t reg_0x30E8; /* 0x30E8 HADDAVE*/
+ uint8_t reg_0x3301; /* 0x3301 RGLANESEL*/
+ /*added for 120fps support */
+ uint8_t reg_0x0344;
+ uint8_t reg_0x0345;
+ uint8_t reg_0x0346;
+ uint8_t reg_0x0347;
+ uint8_t reg_0x0348;
+ uint8_t reg_0x0349;
+ uint8_t reg_0x034A;
+ uint8_t reg_0x034B;
+};
+struct reg_struct_init{
+ uint8_t reg_0x302B;/* 0x302B*/
+
+ uint8_t reg_0x30E5;/* 0x30E5*/
+ uint8_t reg_0x3300; /* 0x3300*/
+
+ uint8_t image_orient; /* 0x0101*/
+
+ uint8_t reg_0x300A; /* 0x300A*/
+ uint8_t reg_0x3014; /* 0x3014*/
+ uint8_t reg_0x3015; /* 0x3015*/
+ uint8_t reg_0x3017; /* 0x3017*/
+ uint8_t reg_0x301C; /* 0x301C*/
+ uint8_t reg_0x3031; /* 0x3031*/
+ uint8_t reg_0x3040; /* 0x3040*/
+ uint8_t reg_0x3041; /* 0x3041*/
+ uint8_t reg_0x3051; /* 0x3051*/
+ uint8_t reg_0x3053; /* 0x3053*/
+ uint8_t reg_0x3055; /* 0x3055*/
+ uint8_t reg_0x3057; /* 0x3057*/
+ uint8_t reg_0x3060; /* 0x3060*/
+ uint8_t reg_0x3065; /* 0x3065*/
+ uint8_t reg_0x30AA; /* 0x30AA*/
+ uint8_t reg_0x30AB; /* 0x30AB*/
+ uint8_t reg_0x30B0; /* 0x30B0*/
+ uint8_t reg_0x30B2; /* 0x30B2*/
+
+ uint8_t reg_0x30D3; /* 0X30D3*/
+ uint8_t reg_0x30D8; /* 0X30D8*/
+
+ uint8_t reg_0x3106; /* 0x3106*/
+ uint8_t reg_0x3108; /* 0x3108*/
+ uint8_t reg_0x310A; /* 0x310A*/
+ uint8_t reg_0x310C; /* 0x310C*/
+ uint8_t reg_0x310E; /* 0x310E*/
+ uint8_t reg_0x3126; /* 0x3126*/
+ uint8_t reg_0x312E; /* 0x312E*/
+ uint8_t reg_0x313C; /* 0x313C*/
+ uint8_t reg_0x313E; /* 0x313E*/
+ uint8_t reg_0x3140; /* 0x3140*/
+ uint8_t reg_0x3142; /* 0x3142*/
+ uint8_t reg_0x3144; /* 0x3144*/
+ uint8_t reg_0x3148; /* 0x3148*/
+ uint8_t reg_0x314A; /* 0x314A*/
+ uint8_t reg_0x3166; /* 0x3166*/
+ uint8_t reg_0x3168; /* 0x3168*/
+ uint8_t reg_0x316F; /* 0x316F*/
+ uint8_t reg_0x3171; /* 0x3171*/
+ uint8_t reg_0x3173; /* 0x3173*/
+ uint8_t reg_0x3175; /* 0x3175*/
+ uint8_t reg_0x3177; /* 0x3177*/
+ uint8_t reg_0x3179; /* 0x3179*/
+ uint8_t reg_0x317B; /* 0x317B*/
+ uint8_t reg_0x317D; /* 0x317D*/
+ uint8_t reg_0x317F; /* 0x317F*/
+ uint8_t reg_0x3181; /* 0x3181*/
+ uint8_t reg_0x3184; /* 0x3184*/
+ uint8_t reg_0x3185; /* 0x3185*/
+ uint8_t reg_0x3187; /* 0x3187*/
+
+ uint8_t reg_0x31A4; /* 0x31A4*/
+ uint8_t reg_0x31A6; /* 0x31A6*/
+ uint8_t reg_0x31AC; /* 0x31AC*/
+ uint8_t reg_0x31AE; /* 0x31AE*/
+ uint8_t reg_0x31B4; /* 0x31B4*/
+ uint8_t reg_0x31B6; /* 0x31B6*/
+
+ uint8_t reg_0x3254; /* 0x3254*/
+ uint8_t reg_0x3256; /* 0x3256*/
+ uint8_t reg_0x3258; /* 0x3258*/
+ uint8_t reg_0x325A; /* 0x325A*/
+ uint8_t reg_0x3260; /* 0x3260*/
+ uint8_t reg_0x3262; /* 0x3262*/
+
+ uint8_t reg_0x3304; /* 0x3304*/
+ uint8_t reg_0x3305; /* 0x3305*/
+ uint8_t reg_0x3306; /* 0x3306*/
+ uint8_t reg_0x3307; /* 0x3307*/
+ uint8_t reg_0x3308; /* 0x3308*/
+ uint8_t reg_0x3309; /* 0x3309*/
+ uint8_t reg_0x330A; /* 0x330A*/
+ uint8_t reg_0x330B; /* 0x330B*/
+ uint8_t reg_0x330C; /* 0x330C*/
+ uint8_t reg_0x330D; /* 0x330D*/
+
+};
+struct sn12m0pz_reg{
+ const struct reg_struct *reg_pat;
+ const struct reg_struct_init *reg_pat_init;
+};
+#endif
diff --git a/drivers/media/video/msm/sn12m0pz_reg.c b/drivers/media/video/msm/sn12m0pz_reg.c
new file mode 100644
index 0000000..d21eac1
--- /dev/null
+++ b/drivers/media/video/msm/sn12m0pz_reg.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include "sn12m0pz.h"
+/* Initialisation settings */
+
+const struct reg_struct_init iu060f_reg_pat_init[1] = {{
+ /* PLL setting */
+ 0x4B, /* reg 0x302B*/
+ /* MIPI Enable Setting */
+ 0x04, /* reg 0x30E5*/
+ 0x00, /* reg 0x3300*/
+ /* Global Setting */
+ 0x00, /* image_orientation*/
+ 0x80, /* reg 0x300A*/
+ 0x08, /* reg 0x3014*/
+ 0x37, /* reg 0x3015*/
+ 0x60, /* reg 0x3017*/
+ 0x01, /* reg 0x301C*/
+ 0x28, /* reg 0x3031*/
+ 0x00, /* reg 0x3040*/
+ 0x60, /* reg 0x3041*/
+ 0x24, /* reg 0x3051*/
+ 0x34, /* reg 0x3053*/
+ 0x3B, /* reg 0x3055*/
+ 0xC0, /* reg 0x3057*/
+ 0x30, /* reg 0x3060*/
+ 0x00, /* reg 0x3065*/
+ 0x88, /* reg 0x30AA*/
+ 0x1C, /* reg 0x30AB*/
+ 0x32, /* reg 0x30B0*/
+ 0x83, /* reg 0x30B2*/
+ 0x04, /* reg 0x30D3*/
+ 0xC0, /* reg 0x30D8*/
+ 0x50, /* reg 0x3106*/
+ 0xA5, /* reg 0x3108*/
+ 0xA9, /* reg 0x310A*/
+ 0x0C, /* reg 0x310C*/
+ 0x55, /* reg 0x310E*/
+ 0xCC, /* reg 0x3126*/
+ 0x83, /* reg 0x312E*/
+ 0xC7, /* reg 0x313C*/
+ 0x07, /* reg 0x313E*/
+ 0x32, /* reg 0x3140*/
+ 0x35, /* reg 0x3142*/
+ 0x35, /* reg 0x3144*/
+ 0x73, /* reg 0x3148*/
+ 0x80, /* reg 0x314A*/
+ 0xBE, /* reg 0x3166*/
+ 0xBD, /* reg 0x3168*/
+ 0x82, /* reg 0x316F*/
+ 0xBC, /* reg 0x3171*/
+ 0x82, /* reg 0x3173*/
+ 0xBC, /* reg 0x3175*/
+ 0x0C, /* reg 0x3177*/
+ 0x2C, /* reg 0x3179*/
+ 0x83, /* reg 0x317B*/
+ 0xAF, /* reg 0x317D*/
+ 0x83, /* reg 0x317F*/
+ 0xAF, /* reg 0x3181*/
+ 0x06, /* reg 0x3184*/
+ 0xBA, /* reg 0x3185*/
+ 0xBE, /* reg 0x3187*/
+ 0xD8, /* reg 0x31A4*/
+ 0x17, /* reg 0x31A6*/
+ 0xCF, /* reg 0x31AC*/
+ 0xF1, /* reg 0x31AE*/
+ 0xD8, /* reg 0x31B4*/
+ 0x17, /* reg 0x31B6*/
+ 0x09, /* reg 0x3254 */
+ 0xC5, /* reg 0x3256 */
+ 0x84, /* reg 0x3258 */
+ 0x6C, /* reg 0x325A */
+ 0x0B, /* reg 0x3260 */
+ 0x09, /* reg 0x3262 */
+ 0x05, /* reg 0x3304*/
+ 0x04, /* reg 0x3305*/
+ 0x15, /* reg 0x3306*/
+ 0x03, /* reg 0x3307*/
+ 0x13, /* reg 0x3308*/
+ 0x05, /* reg 0x3309*/
+ 0x0B, /* reg 0x330A*/
+ 0x04, /* reg 0x330B*/
+ 0x0B, /* reg 0x330C*/
+ 0x06 /* reg 0x330D*/
+}
+};
+
+/* Preview / Snapshot register settings */
+const struct reg_struct iu060f_reg_pat[3] = {
+ { /* Preview */
+ 0x22, /*0x1b*/ /* fps*/
+
+ /* Global Setting */
+ 0x01, /* coarse_integration_time_msb*/
+ 0xFF, /* coarse_integration_time_lsb*/
+
+ /* Mode Setting */
+ /* V: 1/2 V-addition (1,3),
+ H: 1/2 H-averaging (1,3) */
+
+ 0x06, /* frame_length_lines_msb 0x0340*/
+ 0x02, /* frame_length_lines_lsb 0x0341*/
+ 0x10, /* line_length_pck_msb 0x0342*/
+ 0x70, /* line_length_pck_lsb 0x0343*/
+ 0x07, /* x_output_size_msb 0x034C*/
+ 0xe0, /* x_output_size_lsb 0x034D*/
+ 0x05, /* y_output_size_msb 0x034E*/
+ 0xe8, /* y_output_size_lsb 0x034F*/
+ 0x01, /* x_even_inc_lsb 0x0381*/
+ 0x03, /* x_odd_inc_lsb 0x0383*/
+ 0x01, /* y_even_inc_lsb 0x0385*/
+ 0x03, /* y_odd_inc_lsb 0x0387*/
+ 0x46, /* reg 0x3016 VMODEADD 0x3016*/
+ 0x86, /* reg 0x30E8 HADDAVE 0x30E8*/
+ 0x01, /* reg 0x3301 RGLANESEL 0x3301*/
+
+ 0x00, /* 0x0344 */
+ 0x00, /* 0x0345 */
+ 0x00, /* 0x0346 */
+ 0x00, /* 0x0347 */
+ 0x0F, /* 0x0348 */
+ 0xBF, /* 0x0349 */
+ 0x0B, /* 0x034A */
+ 0xCF, /* 0x034B */
+ },
+ { /* Snapshot */
+ 0x14, /* pll_multiplier_lsb // 20/10 fps*/
+ /* 0x14 for pclk 96MHz at 7.5 fps */
+
+ /* Global Setting */
+ 0x0B, /* coarse_integration_time_msb*/
+ 0xFF, /* coarse_integration_time_lsb*/
+
+ /* Mode Setting */
+ /* Full */
+ 0x0C,/*frame_length_lines_msb 0x0340*/
+ 0x02,/*frame_length_lines_lsb 0x0341*/
+ 0x10,/*line_length_pck_msb 0x0342*/
+ 0x70,/* line_length_pck_lsb 0x0343*/
+ 0x0F,/* x_output_size_msb 0x034C*/
+ 0xC0, /* x_output_size_lsb 0x034D*/
+ 0x0B, /* y_output_size_msb 0x034E*/
+ 0xD0, /* y_output_size_lsb 0x034F*/
+ 0x01, /* x_even_inc_lsb 0x0381*/
+ 0x01, /* x_odd_inc_lsb 0x0383*/
+ 0x01, /* y_even_inc_lsb 0x0385*/
+ 0x01, /* y_odd_inc_lsb 0x0387*/
+ 0x06, /* reg 0x3016 VMODEADD 0x3016*/
+ 0x06, /* reg 0x30E8 HADDAVE 0x30E8*/
+ 0x00, /* reg 0x3301 RGLANESEL 0x3301*/
+
+ 0x00, /* 0x0344 */
+ 0x00, /* 0x0345 */
+ 0x00, /* 0x0346 */
+ 0x00, /* 0x0347 */
+ 0x0F, /* 0x0348 */
+ 0xBF, /* 0x0349 */
+ 0x0B, /* 0x034A */
+ 0xCF, /* 0x034B */
+ },
+ /* 120 fps settings */
+ {
+ 0x1B, /*0x1B fps*/
+ /* Global Setting */
+ 0x00, /* coarse_integration_time_msb*/
+ 0xFE, /* coarse_integration_time_lsb*/
+
+ /* Mode Setting */
+ /* V: 1/8 V-addition (9,7),
+ H: Full */
+
+ 0x01, /* frame_length_lines_msb 0x0340*/
+ 0x01, /* frame_length_lines_lsb 0x0341*/
+ 0x10, /* line_length_pck_msb 0x0342*/
+ 0x70, /* line_length_pck_lsb 0x0343*/
+ 0x0f, /* x_output_size_msb 0x034C*/
+ 0xc0, /* x_output_size_lsb 0x034D*/
+ 0x00, /* y_output_size_msb 0x034E*/
+ 0xF8, /* y_output_size_lsb 0x034F*/
+ 0x01, /* x_even_inc_lsb 0x0381*/
+ 0x01, /* x_odd_inc_lsb 0x0383*/
+ 0x09, /* y_even_inc_lsb 0x0385*/
+ 0x07, /* y_odd_inc_lsb 0x0387*/
+ 0x46, /* reg 0x3016 VMODEADD 0x3016*/
+ 0x86, /* reg 0x30E8 HADDAVE 0x30E8*/
+ 0x00, /* reg 0x3301 RGLANESEL 0x3301*/
+ /* add for 120fps support */
+ 0x00, /* 0x0344*/
+ 0x00, /* 0x0345*/
+ 0x02, /* 0x0346*/
+ 0x10, /* 0x0347*/
+ 0x0F, /* 0x0348*/
+ 0xBF, /* 0x0349*/
+ 0x09, /* 0x034A*/
+ 0xCF, /* 0x034B*/
+ }
+};
+struct sn12m0pz_reg sn12m0pz_regs = {
+ .reg_pat = &iu060f_reg_pat[0],
+ .reg_pat_init = &iu060f_reg_pat_init[0],
+};
+
diff --git a/drivers/media/video/msm/vb6801.c b/drivers/media/video/msm/vb6801.c
new file mode 100644
index 0000000..fa82570
--- /dev/null
+++ b/drivers/media/video/msm/vb6801.c
@@ -0,0 +1,1616 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 "vb6801.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+enum {
+ REG_HOLD = 0x0104,
+ RELEASE_HOLD = 0x0000,
+ HOLD = 0x0001,
+ STANDBY_MODE = 0x0000,
+ REG_COARSE_INTEGRATION_TIME = 0x0202,
+ REG_ANALOGUE_GAIN_CODE_GLOBAL = 0x0204,
+ REG_RAMP_SCALE = 0x3116,
+ REG_POWER_MAN_ENABLE_3 = 0x3142,
+ REG_POWER_MAN_ENABLE_4 = 0x3143,
+ REG_POWER_MAN_ENABLE_5 = 0x3144,
+ REG_CCP2_DATA_FORMAT = 0x0112,
+ REG_PRE_PLL_CLK_DIV = 0x0304,
+ REG_PLL_MULTIPLIER = 0x0306,
+ REG_VT_SYS_CLK_DIV = 0x0302,
+ REG_VT_PIX_CLK_DIV = 0x0300,
+ REG_OP_SYS_CLK_DIV = 0x030A,
+ REG_OP_PIX_CLK_DIV = 0x0308,
+ REG_VT_LINE_LENGTH_PCK = 0x0342,
+ REG_X_OUTPUT_SIZE = 0x034C,
+ REG_Y_OUTPUT_SIZE = 0x034E,
+ REG_X_ODD_INC = 0x0382,
+ REG_Y_ODD_INC = 0x0386,
+ REG_VT_FRAME_LENGTH_LINES = 0x0340,
+ REG_ANALOG_TIMING_MODES_2 = 0x3113,
+ REG_BRUCE_ENABLE = 0x37B0,
+ REG_OP_CODER_SYNC_CLK_SETUP = 0x3400,
+ REG_OP_CODER_ENABLE = 0x3401,
+ REG_OP_CODER_SLOW_PAD_EN = 0x3402,
+ REG_OP_CODER_AUTO_STARTUP = 0x3414,
+ REG_SCYTHE_ENABLE = 0x3204,
+ REG_SCYTHE_WEIGHT = 0x3206,
+ REG_FRAME_COUNT = 0x0005,
+ REG_MODE_SELECT = 0x0100,
+ REG_CCP2_CHANNEL_IDENTIFIER = 0x0110,
+ REG_CCP2_SIGNALLING_MODE = 0x0111,
+ REG_BTL_LEVEL_SETUP = 0x311B,
+ REG_OP_CODER_AUTOMATIC_MODE_ENABLE = 0x3403,
+ REG_PLL_CTRL = 0x3801,
+ REG_VCM_DAC_CODE = 0x3860,
+ REG_VCM_DAC_STROBE = 0x3868,
+ REG_VCM_DAC_ENABLE = 0x386C,
+ REG_NVM_T1_ADDR_00 = 0x3600,
+ REG_NVM_T1_ADDR_01 = 0x3601,
+ REG_NVM_T1_ADDR_02 = 0x3602,
+ REG_NVM_T1_ADDR_03 = 0x3603,
+ REG_NVM_T1_ADDR_04 = 0x3604,
+ REG_NVM_T1_ADDR_05 = 0x3605,
+ REG_NVM_T1_ADDR_06 = 0x3606,
+ REG_NVM_T1_ADDR_07 = 0x3607,
+ REG_NVM_T1_ADDR_08 = 0x3608,
+ REG_NVM_T1_ADDR_09 = 0x3609,
+ REG_NVM_T1_ADDR_0A = 0x360A,
+ REG_NVM_T1_ADDR_0B = 0x360B,
+ REG_NVM_T1_ADDR_0C = 0x360C,
+ REG_NVM_T1_ADDR_0D = 0x360D,
+ REG_NVM_T1_ADDR_0E = 0x360E,
+ REG_NVM_T1_ADDR_0F = 0x360F,
+ REG_NVM_T1_ADDR_10 = 0x3610,
+ REG_NVM_T1_ADDR_11 = 0x3611,
+ REG_NVM_T1_ADDR_12 = 0x3612,
+ REG_NVM_T1_ADDR_13 = 0x3613,
+ REG_NVM_CTRL = 0x3680,
+ REG_NVM_PDN = 0x3681,
+ REG_NVM_PULSE_WIDTH = 0x368B,
+};
+
+#define VB6801_LINES_PER_FRAME_PREVIEW 800
+#define VB6801_LINES_PER_FRAME_SNAPSHOT 1600
+#define VB6801_PIXELS_PER_LINE_PREVIEW 2500
+#define VB6801_PIXELS_PER_LINE_SNAPSHOT 2500
+
+/* AF constant */
+#define VB6801_TOTAL_STEPS_NEAR_TO_FAR 25
+#define VB6801_STEPS_NEAR_TO_CLOSEST_INF 25
+
+/* for 30 fps preview */
+#define VB6801_DEFAULT_CLOCK_RATE 12000000
+
+enum vb6801_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum vb6801_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+
+enum vb6801_setting_t {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+
+struct vb6801_work_t {
+ struct work_struct work;
+};
+
+struct sensor_dynamic_params_t {
+ uint16_t preview_pixelsPerLine;
+ uint16_t preview_linesPerFrame;
+ uint16_t snapshot_pixelsPerLine;
+ uint16_t snapshot_linesPerFrame;
+ uint8_t snapshot_changed_fps;
+ uint32_t pclk;
+};
+
+struct vb6801_sensor_info {
+ /* Sensor Configuration Input Parameters */
+ uint32_t ext_clk_freq_mhz;
+ uint32_t target_frame_rate_fps;
+ uint32_t target_vt_pix_clk_freq_mhz;
+ uint32_t sub_sampling_factor;
+ uint32_t analog_binning_allowed;
+ uint32_t raw_mode;
+ uint32_t capture_mode;
+
+ /* Image Readout Registers */
+ uint32_t x_odd_inc; /* x pixel array addressing odd increment */
+ uint32_t y_odd_inc; /* y pixel array addressing odd increment */
+ uint32_t x_output_size; /* width of output image */
+ uint32_t y_output_size; /* height of output image */
+
+ /* Declare data format */
+ uint32_t ccp2_data_format;
+
+ /* Clock Tree Registers */
+ uint32_t pre_pll_clk_div;
+ uint32_t pll_multiplier;
+ uint32_t vt_sys_clk_div;
+ uint32_t vt_pix_clk_div;
+ uint32_t op_sys_clk_div;
+ uint32_t op_pix_clk_div;
+
+ /* Video Timing Registers */
+ uint32_t vt_line_length_pck;
+ uint32_t vt_frame_length_lines;
+
+ /* Analogue Binning Registers */
+ uint8_t vtiming_major;
+ uint8_t analog_timing_modes_4;
+
+ /* Fine (pixel) Integration Time Registers */
+ uint32_t fine_integration_time;
+
+ /* Coarse (lines) Integration Time Limit Registers */
+ uint32_t coarse_integration_time_max;
+
+ /* Coarse (lines) Integration Timit Register (16-bit) */
+ uint32_t coarse_integration_time;
+
+ /* Analogue Gain Code Global Registers */
+ uint32_t analogue_gain_code_global;
+
+ /* Digital Gain Code Registers */
+ uint32_t digital_gain_code;
+
+ /* Overall gain (analogue & digital) code
+ * Note that this is not a real register but just
+ * an abstraction for the combination of analogue
+ * and digital gain */
+ uint32_t gain_code;
+
+ /* FMT Test Information */
+ uint32_t pass_fail;
+ uint32_t day;
+ uint32_t month;
+ uint32_t year;
+ uint32_t tester;
+ uint32_t part_number;
+
+ /* Autofocus controls */
+ uint32_t vcm_dac_code;
+ int vcm_max_dac_code_step;
+ int vcm_proportional_factor;
+ int vcm_dac_code_spacing_ms;
+
+ /* VCM NVM Characterisation Information */
+ uint32_t vcm_dac_code_infinity_dn;
+ uint32_t vcm_dac_code_macro_up;
+ uint32_t vcm_dac_code_up_dn_delta;
+
+ /* Internal Variables */
+ uint32_t min_vt_frame_length_lines;
+};
+
+struct vb6801_work_t *vb6801_sensorw;
+struct i2c_client *vb6801_client;
+
+struct vb6801_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ int sensormode;
+ uint32_t factor_fps; /* init to 1 * 0x00000400 */
+ uint16_t curr_fps;
+ uint16_t max_fps;
+ int8_t pict_exp_update;
+ int8_t reducel;
+ uint16_t curr_lens_pos;
+ uint16_t init_curr_lens_pos;
+ enum vb6801_resolution_t prev_res;
+ enum vb6801_resolution_t pict_res;
+ enum vb6801_resolution_t curr_res;
+ enum vb6801_test_mode_t set_test;
+
+ struct vb6801_sensor_info s_info;
+ struct sensor_dynamic_params_t s_dynamic_params;
+};
+
+static struct vb6801_ctrl_t *vb6801_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vb6801_wait_queue);
+DEFINE_MUTEX(vb6801_mut);
+
+static int vb6801_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 = 2,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(vb6801_client->adapter, msgs, 2) < 0) {
+ CDBG("vb6801_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t vb6801_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+
+ if (!rdata)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+
+ rc = vb6801_i2c_rxdata(vb6801_client->addr, buf, rlen);
+
+ if (rc < 0) {
+ CDBG("vb6801_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+
+ return rc;
+}
+
+static int32_t vb6801_i2c_read_table(struct vb6801_i2c_reg_conf_t *regs,
+ int items)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ for (i = 0; i < items; i++) {
+ unsigned short *buf =
+ regs->dlen == D_LEN_BYTE ?
+ (unsigned short *)®s->bdata :
+ (unsigned short *)®s->wdata;
+ rc = vb6801_i2c_read(regs->waddr, buf, regs->dlen + 1);
+
+ if (rc < 0) {
+ CDBG("vb6801_i2c_read_table Failed!!!\n");
+ break;
+ }
+
+ regs++;
+ }
+
+ return rc;
+}
+
+static int32_t vb6801_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(vb6801_client->adapter, msg, 1) < 0) {
+ CDBG("vb6801_i2c_txdata faild 0x%x\n", vb6801_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int32_t vb6801_i2c_write_b(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+
+ CDBG("i2c_write_b addr = %d, val = %d\n", waddr, bdata);
+ rc = vb6801_i2c_txdata(vb6801_client->addr, buf, 3);
+
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+
+ return rc;
+}
+
+static int32_t vb6801_i2c_write_w(unsigned short waddr, unsigned short wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+
+ CDBG("i2c_write_w addr = %d, val = %d, buf[2] = 0x%x, buf[3] = 0x%x\n",
+ waddr, wdata, buf[2], buf[3]);
+
+ rc = vb6801_i2c_txdata(vb6801_client->addr, buf, 4);
+ if (rc < 0) {
+ CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+
+ return rc;
+}
+
+static int32_t vb6801_i2c_write_table(struct vb6801_i2c_reg_conf_t *regs,
+ int items)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ for (i = 0; i < items; i++) {
+ rc = ((regs->dlen == D_LEN_BYTE) ?
+ vb6801_i2c_write_b(regs->waddr, regs->bdata) :
+ vb6801_i2c_write_w(regs->waddr, regs->wdata));
+
+ if (rc < 0) {
+ CDBG("vb6801_i2c_write_table Failed!!!\n");
+ break;
+ }
+
+ regs++;
+ }
+
+ return rc;
+}
+
+static int32_t vb6801_reset(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+
+ rc = gpio_request(data->sensor_reset, "vb6801");
+ if (!rc) {
+ CDBG("sensor_reset SUcceeded\n");
+ gpio_direction_output(data->sensor_reset, 0);
+ mdelay(50);
+ gpio_direction_output(data->sensor_reset, 1);
+ mdelay(13);
+ } else
+ CDBG("sensor_reset FAiled\n");
+
+ return rc;
+}
+
+static int32_t vb6801_set_default_focus(void)
+{
+ int32_t rc = 0;
+
+ /* FIXME: Default focus not supported */
+
+ return rc;
+}
+
+static void vb6801_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 */
+ uint32_t d1;
+ uint32_t d2;
+
+ d1 =
+ (uint32_t)(
+ (vb6801_ctrl->s_dynamic_params.preview_linesPerFrame *
+ 0x00000400) /
+ vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame);
+
+ d2 =
+ (uint32_t)(
+ (vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine *
+ 0x00000400) /
+ vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine);
+
+
+ divider = (uint32_t) (d1 * d2) / 0x00000400;
+
+ pclk_mult = (48 * 0x400) / 60;
+
+ /* Verify PCLK settings and frame sizes. */
+ *pfps = (uint16_t)((((fps * pclk_mult) / 0x00000400) * divider)/
+ 0x00000400);
+}
+
+static uint16_t vb6801_get_prev_lines_pf(void)
+{
+ if (vb6801_ctrl->prev_res == QTR_SIZE)
+ return vb6801_ctrl->s_dynamic_params.preview_linesPerFrame;
+ else
+ return vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame;
+}
+
+static uint16_t vb6801_get_prev_pixels_pl(void)
+{
+ if (vb6801_ctrl->prev_res == QTR_SIZE)
+ return vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine;
+ else
+ return vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine;
+}
+
+static uint16_t vb6801_get_pict_lines_pf(void)
+{
+ return vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame;
+}
+
+static uint16_t vb6801_get_pict_pixels_pl(void)
+{
+ return vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine;
+}
+
+static uint32_t vb6801_get_pict_max_exp_lc(void)
+{
+ uint16_t snapshot_lines_per_frame;
+
+ if (vb6801_ctrl->pict_res == QTR_SIZE) {
+ snapshot_lines_per_frame =
+ vb6801_ctrl->s_dynamic_params.preview_linesPerFrame - 3;
+ } else {
+ snapshot_lines_per_frame =
+ vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame - 3;
+ }
+
+ return snapshot_lines_per_frame;
+}
+
+static int32_t vb6801_set_fps(struct fps_cfg *fps)
+{
+ int32_t rc = 0;
+
+ /* input is new fps in Q8 format */
+ switch (fps->fps_div) {
+ case 7680: /* 30 * Q8 */
+ vb6801_ctrl->factor_fps = 1;
+ break;
+
+ case 3840: /* 15 * Q8 */
+ vb6801_ctrl->factor_fps = 2;
+ break;
+
+ case 2560: /* 10 * Q8 */
+ vb6801_ctrl->factor_fps = 3;
+ break;
+
+ case 1920: /* 7.5 * Q8 */
+ vb6801_ctrl->factor_fps = 4;
+ break;
+
+ default:
+ rc = -ENODEV;
+ break;
+ }
+
+ return rc;
+}
+
+static int32_t vb6801_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ uint16_t lpf;
+
+ if (vb6801_ctrl->curr_res == SENSOR_FULL_SIZE)
+ lpf = VB6801_LINES_PER_FRAME_SNAPSHOT;
+ else
+ lpf = VB6801_LINES_PER_FRAME_PREVIEW;
+
+ /* hold */
+ rc = vb6801_i2c_write_w(REG_HOLD, HOLD);
+ if (rc < 0)
+ goto exp_gain_done;
+
+ if ((vb6801_ctrl->curr_fps <
+ vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps) &&
+ (!vb6801_ctrl->pict_exp_update)) {
+
+ if (vb6801_ctrl->reducel) {
+
+ rc = vb6801_i2c_write_w(REG_VT_FRAME_LENGTH_LINES,
+ lpf * vb6801_ctrl->factor_fps);
+
+ vb6801_ctrl->curr_fps =
+ vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps;
+
+ } else if (!vb6801_ctrl->reducel) {
+
+ rc = vb6801_i2c_write_w(REG_COARSE_INTEGRATION_TIME,
+ line * vb6801_ctrl->factor_fps);
+
+ vb6801_ctrl->reducel = 1;
+ }
+ } else if ((vb6801_ctrl->curr_fps >
+ vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps) &&
+ (!vb6801_ctrl->pict_exp_update)) {
+
+ rc = vb6801_i2c_write_w(REG_VT_FRAME_LENGTH_LINES,
+ lpf * vb6801_ctrl->factor_fps);
+
+ vb6801_ctrl->curr_fps =
+ vb6801_ctrl->max_fps / vb6801_ctrl->factor_fps;
+
+ } else {
+ /* analogue_gain_code_global */
+ rc = vb6801_i2c_write_w(REG_ANALOGUE_GAIN_CODE_GLOBAL, gain);
+ if (rc < 0)
+ goto exp_gain_done;
+
+ /* coarse_integration_time */
+ rc = vb6801_i2c_write_w(REG_COARSE_INTEGRATION_TIME,
+ line * vb6801_ctrl->factor_fps);
+ if (rc < 0)
+ goto exp_gain_done;
+
+ vb6801_ctrl->pict_exp_update = 1;
+ }
+
+ rc = vb6801_i2c_write_w(REG_HOLD, RELEASE_HOLD);
+
+exp_gain_done:
+ return rc;
+}
+
+static int32_t vb6801_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ vb6801_ctrl->pict_exp_update = 1;
+ return vb6801_write_exp_gain(gain, line);
+}
+
+static int32_t vb6801_power_down(void)
+{
+ int32_t rc = 0;
+ rc = vb6801_i2c_write_b(REG_NVM_PDN, 0);
+
+ mdelay(5);
+ return rc;
+}
+
+static int32_t vb6801_go_to_position(uint32_t target_vcm_dac_code,
+ struct vb6801_sensor_info *ps)
+{
+ /* Prior to running this function the following values must
+ * be initialised in the sensor data structure, PS
+ * ps->vcm_dac_code
+ * ps->vcm_max_dac_code_step
+ * ps->vcm_dac_code_spacing_ms */
+
+ int32_t rc = 0;
+
+ ps->vcm_dac_code = target_vcm_dac_code;
+
+ /* Restore Strobe to zero state */
+ rc = vb6801_i2c_write_b(REG_VCM_DAC_STROBE, 0x00);
+ if (rc < 0)
+ return rc;
+
+ /* Write 9-bit VCM DAC Code */
+ rc = vb6801_i2c_write_w(REG_VCM_DAC_CODE, ps->vcm_dac_code);
+ if (rc < 0)
+ return rc;
+
+ /* Generate a rising edge on the dac_strobe to latch
+ * new DAC value */
+
+ rc = vb6801_i2c_write_w(REG_VCM_DAC_STROBE, 0x01);
+
+ return rc;
+}
+
+static int32_t vb6801_move_focus(int direction, int32_t num_steps)
+{
+ int16_t step_direction;
+ int16_t actual_step;
+ int16_t next_position;
+ uint32_t step_size;
+ int16_t small_move[4];
+ uint16_t i;
+ int32_t rc = 0;
+
+ step_size = (vb6801_ctrl->s_info.vcm_dac_code_macro_up -
+ vb6801_ctrl->s_info.vcm_dac_code_infinity_dn) /
+ VB6801_TOTAL_STEPS_NEAR_TO_FAR;
+
+ if (num_steps > VB6801_TOTAL_STEPS_NEAR_TO_FAR)
+ num_steps = VB6801_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;
+
+ /* need to decide about default position and power supplied
+ * at start up and reset */
+ if (vb6801_ctrl->curr_lens_pos < vb6801_ctrl->init_curr_lens_pos)
+ vb6801_ctrl->curr_lens_pos = vb6801_ctrl->init_curr_lens_pos;
+
+ actual_step = (step_direction * num_steps);
+
+ next_position = vb6801_ctrl->curr_lens_pos;
+
+ for (i = 0; i < 4; i++) {
+ if (actual_step >= 0)
+ small_move[i] =
+ (i + 1) * actual_step / 4 - i * actual_step / 4;
+
+ if (actual_step < 0)
+ small_move[i] =
+ (i + 1) * actual_step / 4 - i * actual_step / 4;
+ }
+
+ if (next_position > 511)
+ next_position = 511;
+ else if (next_position < 0)
+ next_position = 0;
+
+ /* for damping */
+ for (i = 0; i < 4; i++) {
+ next_position =
+ (int16_t) (vb6801_ctrl->curr_lens_pos + small_move[i]);
+
+ /* Writing the digital code for current to the actuator */
+ CDBG("next_position in damping mode = %d\n", next_position);
+
+ rc = vb6801_go_to_position(next_position, &vb6801_ctrl->s_info);
+ if (rc < 0) {
+ CDBG("go_to_position Failed!!!\n");
+ return rc;
+ }
+
+ vb6801_ctrl->curr_lens_pos = next_position;
+ if (i < 3)
+ mdelay(5);
+ }
+
+ return rc;
+}
+
+static int vb6801_read_nvm_data(struct vb6801_sensor_info *ps)
+{
+ /* +--------+------+------+----------------+---------------+
+ * | Index | NVM | NVM | Name | Description |
+ * | | Addr | Byte | | |
+ * +--------+------+------+----------------+---------------+
+ * | 0x3600 | 0 | 3 | nvm_t1_addr_00 | {PF[2:0]:Day[4:0]} |
+ * | 0x3601 | 0 | 2 | nvm_t1_addr_01 | {Month[3:0]:Year[3:0]} |
+ * | 0x3602 | 0 | 1 | nvm_t1_addr_02 | Tester[7:0] |
+ * | 0x3603 | 0 | 0 | nvm_t1_addr_03 | Part[15:8] |
+ * +--------+------+------+----------------+---------------+
+ * | 0x3604 | 1 | 3 | nvm_t1_addr_04 | Part[7:0] |
+ * | 0x3605 | 1 | 2 | nvm_t1_addr_05 | StartWPM[7:0] |
+ * | 0x3606 | 1 | 1 | nvm_t1_addr_06 | Infinity[7:0] |
+ * | 0x3607 | 1 | 0 | nvm_t1_addr_07 | Macro[7:0] |
+ * +--------+------+------+----------------+---------------+
+ * | 0x3608 | 2 | 3 | nvm_t1_addr_08 | Reserved |
+ * | 0x3609 | 2 | 2 | nvm_t1_addr_09 | Reserved |
+ * | 0x360A | 2 | 1 | nvm_t1_addr_0A | UpDown[7:0] |
+ * | 0x360B | 2 | 0 | nvm_t1_addr_0B | Reserved |
+ * +--------+------+------+----------------+---------------+
+ * | 0x360C | 3 | 3 | nvm_t1_addr_0C | Reserved |
+ * | 0x360D | 3 | 2 | nvm_t1_addr_0D | Reserved |
+ * | 0x360E | 3 | 1 | nvm_t1_addr_0E | Reserved |
+ * | 0x360F | 3 | 0 | nvm_t1_addr_0F | Reserved |
+ * +--------+------+------+----------------+---------------+
+ * | 0x3610 | 4 | 3 | nvm_t1_addr_10 | Reserved |
+ * | 0x3611 | 4 | 2 | nvm_t1_addr_11 | Reserved |
+ * | 0x3612 | 4 | 1 | nvm_t1_addr_12 | Reserved |
+ * | 0x3613 | 4 | 0 | nvm_t1_addr_13 | Reserved |
+ * +--------+------+------+----------------+---------------+*/
+
+ int32_t rc;
+ struct vb6801_i2c_reg_conf_t rreg[] = {
+ {REG_NVM_T1_ADDR_00, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_01, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_02, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_03, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_04, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_05, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_06, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_07, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_08, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_09, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0A, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0B, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0C, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0D, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0E, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_0F, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_10, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_11, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_12, 0, 0, D_LEN_BYTE},
+ {REG_NVM_T1_ADDR_13, 0, 0, D_LEN_BYTE},
+ };
+
+ struct vb6801_i2c_reg_conf_t wreg[] = {
+ /* Enable NVM for Direct Reading */
+ {REG_NVM_CTRL, 0, 2, D_LEN_BYTE},
+
+ /* Power up NVM */
+ {REG_NVM_PDN, 0, 1, D_LEN_BYTE},
+ };
+
+ rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ return rc;
+ }
+
+ /* NVM Read Pulse Width
+ * ====================
+ * nvm_pulse_width_us = nvm_pulse_width_ext_clk / ext_clk_freq_mhz
+ * Valid Range for Read Pulse Width = 400ns -> 3.0us
+ * Min ext_clk_freq_mhz = 6MHz => 3.0 * 6 = 18
+ * Max ext_clk_freq_mhz = 27MHz => 0.4 * 27 = 10.8
+ * Choose 15 as a common value
+ * - 15 / 6.0 = 2.5000us
+ * - 15 / 12.0 = 1.2500us
+ * - 15 / 27.0 = 0.5555us */
+ rc = vb6801_i2c_write_w(REG_NVM_PULSE_WIDTH, 15);
+ if (rc < 0) {
+ rc = -EBUSY;
+ goto nv_shutdown;
+ }
+
+ rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+ if (rc < 0) {
+ CDBG("I2C Read Table FAILED!!!\n");
+ rc = -EBUSY;
+ goto nv_shutdown;
+ }
+
+ /* Decode and Save FMT Info */
+ ps->pass_fail = (rreg[0].bdata & 0x00E0) >> 5;
+ ps->day = (rreg[0].bdata & 0x001F);
+ ps->month = (rreg[1].bdata & 0x00F0) >> 4;
+ ps->year = (rreg[1].bdata & 0x000F) + 2000;
+ ps->tester = rreg[2].bdata;
+ ps->part_number = (rreg[3].bdata << 8) + rreg[4].bdata;
+
+ /* Decode and Save VCM Dac Values in data structure */
+ ps->vcm_dac_code_infinity_dn = rreg[6].bdata;
+ ps->vcm_dac_code_macro_up = rreg[7].bdata << 1;
+ ps->vcm_dac_code_up_dn_delta = rreg[10].bdata;
+
+nv_shutdown:
+ /* Power Down NVM to extend life time */
+ rc = vb6801_i2c_write_b(REG_NVM_PDN, 0);
+
+ return rc;
+}
+
+static int vb6801_config_sensor(int32_t ext_clk_freq_mhz,
+ int32_t target_frame_rate_fps,
+ int32_t target_vt_pix_clk_freq_mhz,
+ uint32_t sub_sampling_factor,
+ uint32_t analog_binning_allowed,
+ uint32_t raw_mode, int capture_mode,
+ enum vb6801_resolution_t res)
+{
+ uint32_t rc;
+ /* ext_clk_freq_mhz = 6.0 -> 27.0 MHz
+ * target_frame_rate_fps = 15 fps
+ * target_vt_pix_clk_freq_mhz = 24.0 -> 64.0MHz
+ * sub_sampling factor = 1, 2, 3, or 4
+ * raw_mode factor = 10
+ *
+ * capture_mode, 0 = CCP1
+ * capture_mode, 1 = CCP2
+ * capture_mode, 2 = 10-bit parallel + hsync + vsync */
+
+ /* Declare data format */
+ uint32_t ccp2_data_format = 0x0A0A;
+
+ /* Declare clock tree variables */
+ int32_t min_pll_ip_freq_mhz = 6;
+ int32_t max_pll_op_freq_mhz = 640;
+ uint32_t pre_pll_clk_div = 1;
+ int32_t pll_ip_freq_mhz = 6;
+ uint32_t pll_multiplier = 100;
+ int32_t pll_op_freq_mhz = 600;
+ uint32_t vt_sys_clk_div = 1;
+ int32_t vt_sys_clk_freq_mhz = 600;
+ uint32_t vt_pix_clk_div = 10;
+ int32_t vt_pix_clk_freq_mhz = 60;
+ uint32_t op_sys_clk_div = 1;
+ int32_t op_sys_clk_freq_mhz = 60;
+ uint32_t op_pix_clk_div = 10;
+ int32_t op_pix_clk_freq_mhz = 60;
+
+ /* Declare pixel array and frame timing variables */
+ uint32_t x_pixel_array = 2064;
+ uint32_t y_pixel_array = 1544;
+ uint32_t x_even_inc = 1;
+ uint32_t x_odd_inc = 1;
+ uint32_t y_even_inc = 1;
+ uint32_t y_odd_inc = 1;
+ uint32_t x_output_size = 2064;
+ uint32_t y_output_size = 1544;
+ uint32_t additional_rows = 2;
+ uint32_t min_vt_frame_blanking_lines = 16;
+ uint32_t vt_line_length_pck = 2500;
+ uint32_t vt_line_length_us = 0;
+ uint32_t min_vt_frame_length_lines = 1562;
+ uint32_t vt_frame_length_lines = 1600;
+ uint32_t target_vt_frame_length_ms; /* 200 * 0x0001000 / 3; */
+ uint32_t vt_frame_length_ms; /* 200 * 0x0001000 / 3; */
+ uint32_t frame_rate_fps = 15;
+
+ /* Coarse intergration time */
+ uint32_t coarse_integration_time = 1597;
+ uint32_t coarse_integration_time_max_margin = 3;
+ uint16_t frame_count;
+ int timeout;
+
+ struct vb6801_sensor_info *pinfo = &vb6801_ctrl->s_info;
+
+ struct vb6801_i2c_reg_conf_t rreg[] = {
+ {REG_PRE_PLL_CLK_DIV, 0, 0, D_LEN_WORD},
+ {REG_PLL_MULTIPLIER, 0, 0, D_LEN_WORD},
+ {REG_VT_SYS_CLK_DIV, 0, 0, D_LEN_WORD},
+ {REG_VT_PIX_CLK_DIV, 0, 0, D_LEN_WORD},
+ {REG_OP_SYS_CLK_DIV, 0, 0, D_LEN_WORD},
+ {REG_OP_PIX_CLK_DIV, 0, 0, D_LEN_WORD},
+ {REG_FRAME_COUNT, 0, 0, D_LEN_BYTE},
+ };
+
+ struct vb6801_i2c_reg_conf_t wreg2[] = {
+ {REG_POWER_MAN_ENABLE_3, 0, 95, D_LEN_BYTE},
+ {REG_POWER_MAN_ENABLE_4, 0, 142, D_LEN_BYTE},
+ {REG_POWER_MAN_ENABLE_5, 0, 7, D_LEN_BYTE},
+ };
+
+ /* VIDEO TIMING CALCULATIONS
+ * ========================= */
+
+ /* Pixel Array Size */
+ x_pixel_array = 2064;
+ y_pixel_array = 1544;
+
+ /* set current resolution */
+ vb6801_ctrl->curr_res = res;
+
+ /* Analogue binning setup */
+ if (pinfo->analog_binning_allowed > 0 &&
+ pinfo->sub_sampling_factor == 4) {
+
+ pinfo->vtiming_major = 1;
+ pinfo->analog_timing_modes_4 = 32;
+ } else if (pinfo->analog_binning_allowed > 0 &&
+ pinfo->sub_sampling_factor == 2) {
+
+ pinfo->vtiming_major = 1;
+ pinfo->analog_timing_modes_4 = 0;
+ } else {
+
+ pinfo->vtiming_major = 0;
+ pinfo->analog_timing_modes_4 = 0;
+ }
+
+ /* Sub-Sampling X & Y Odd Increments: valid values 1, 3, 5, 7 */
+ x_even_inc = 1;
+ y_even_inc = 1;
+ x_odd_inc = (sub_sampling_factor << 1) - x_even_inc;
+ y_odd_inc = (sub_sampling_factor << 1) - y_even_inc;
+
+ /* Output image size
+ * Must always be a multiple of 2 - round down */
+ x_output_size = ((x_pixel_array / sub_sampling_factor) >> 1) << 1;
+ y_output_size = ((y_pixel_array / sub_sampling_factor) >> 1) << 1;
+
+ /* Output data format */
+ ccp2_data_format = (raw_mode << 8) + raw_mode;
+
+ /* Pre PLL clock divider : valid values 1, 2 or 4
+ * The 1st step is to ensure that PLL input frequency is as close
+ * as possible to the min allowed PLL input frequency.
+ * This yields the smallest step size in the PLL output frequency. */
+ pre_pll_clk_div =
+ ((int)(ext_clk_freq_mhz / min_pll_ip_freq_mhz) >> 1) << 1;
+ if (pre_pll_clk_div < 2)
+ pre_pll_clk_div = 1;
+
+ pll_ip_freq_mhz = ext_clk_freq_mhz / pre_pll_clk_div;
+
+ /* Video Timing System Clock divider: valid values 1, 2, 4
+ * Now need to work backwards through the clock tree to determine the
+ * 1st pass estimates for vt_sys_clk_freq_mhz and then the PLL output
+ * frequency.*/
+ vt_sys_clk_freq_mhz = vt_pix_clk_div * target_vt_pix_clk_freq_mhz;
+ vt_sys_clk_div = max_pll_op_freq_mhz / vt_sys_clk_freq_mhz;
+ if (vt_sys_clk_div < 2)
+ vt_sys_clk_div = 1;
+
+ /* PLL Mulitplier: min , max 106 */
+ pll_op_freq_mhz = vt_sys_clk_div * vt_sys_clk_freq_mhz;
+ pll_multiplier = (pll_op_freq_mhz * 0x0001000) / pll_ip_freq_mhz;
+
+ /* Calculate the acutal pll output frequency
+ * - the pll_multiplier calculation introduces a quantisation error
+ * due the integer nature of the pll multiplier */
+ pll_op_freq_mhz = (pll_ip_freq_mhz * pll_multiplier) / 0x0001000;
+
+ /* Re-calculate video timing clock frequencies based
+ * on actual PLL freq */
+ vt_sys_clk_freq_mhz = pll_op_freq_mhz / vt_sys_clk_div;
+ vt_pix_clk_freq_mhz = ((vt_sys_clk_freq_mhz * 0x0001000) /
+ vt_pix_clk_div)/0x0001000;
+
+ /* Output System Clock Divider: valid value 1, 2, 4, 6, 8
+ * op_sys_clk_div = vt_sys_clk_div;*/
+ op_sys_clk_div = (vt_sys_clk_div * sub_sampling_factor);
+ if (op_sys_clk_div < 2)
+ op_sys_clk_div = 1;
+
+ /* Calculate output timing clock frequencies */
+ op_sys_clk_freq_mhz = pll_op_freq_mhz / op_sys_clk_div;
+ op_pix_clk_freq_mhz =
+ (op_sys_clk_freq_mhz * 0x0001000) / (op_pix_clk_div * 0x0001000);
+
+ /* Line length in pixels and us */
+ vt_line_length_pck = 2500;
+ vt_line_length_us =
+ vt_line_length_pck * 0x0001000 / vt_pix_clk_freq_mhz;
+
+ /* Target vt_frame_length_ms */
+ target_vt_frame_length_ms = (1000 * 0x0001000 / target_frame_rate_fps);
+
+ /* Frame length in lines */
+ min_vt_frame_length_lines =
+ additional_rows + y_output_size + min_vt_frame_blanking_lines;
+
+ vt_frame_length_lines =
+ ((1000 * target_vt_frame_length_ms) / vt_line_length_us);
+
+ if (vt_frame_length_lines <= min_vt_frame_length_lines)
+ vt_frame_length_lines = min_vt_frame_length_lines;
+
+ /* Calcuate the actual frame length in ms */
+ vt_frame_length_ms = (vt_frame_length_lines * vt_line_length_us / 1000);
+
+ /* Frame Rate in fps */
+ frame_rate_fps = (1000 * 0x0001000 / vt_frame_length_ms);
+
+ /* Set coarse integration to max */
+ coarse_integration_time =
+ vt_frame_length_lines - coarse_integration_time_max_margin;
+
+ CDBG("SENSOR VIDEO TIMING SUMMARY:\n");
+ CDBG(" ============================\n");
+ CDBG("ext_clk_freq_mhz = %d\n", ext_clk_freq_mhz);
+ CDBG("pre_pll_clk_div = %d\n", pre_pll_clk_div);
+ CDBG("pll_ip_freq_mhz = %d\n", pll_ip_freq_mhz);
+ CDBG("pll_multiplier = %d\n", pll_multiplier);
+ CDBG("pll_op_freq_mhz = %d\n", pll_op_freq_mhz);
+ CDBG("vt_sys_clk_div = %d\n", vt_sys_clk_div);
+ CDBG("vt_sys_clk_freq_mhz = %d\n", vt_sys_clk_freq_mhz);
+ CDBG("vt_pix_clk_div = %d\n", vt_pix_clk_div);
+ CDBG("vt_pix_clk_freq_mhz = %d\n", vt_pix_clk_freq_mhz);
+ CDBG("op_sys_clk_div = %d\n", op_sys_clk_div);
+ CDBG("op_sys_clk_freq_mhz = %d\n", op_sys_clk_freq_mhz);
+ CDBG("op_pix_clk_div = %d\n", op_pix_clk_div);
+ CDBG("op_pix_clk_freq_mhz = %d\n", op_pix_clk_freq_mhz);
+ CDBG("vt_line_length_pck = %d\n", vt_line_length_pck);
+ CDBG("vt_line_length_us = %d\n", vt_line_length_us/0x0001000);
+ CDBG("vt_frame_length_lines = %d\n", vt_frame_length_lines);
+ CDBG("vt_frame_length_ms = %d\n", vt_frame_length_ms/0x0001000);
+ CDBG("frame_rate_fps = %d\n", frame_rate_fps);
+ CDBG("ccp2_data_format = %d\n", ccp2_data_format);
+ CDBG("x_output_size = %d\n", x_output_size);
+ CDBG("y_output_size = %d\n", y_output_size);
+ CDBG("x_odd_inc = %d\n", x_odd_inc);
+ CDBG("y_odd_inc = %d\n", y_odd_inc);
+ CDBG("(vt_frame_length_lines * frame_rate_factor ) = %d\n",
+ (vt_frame_length_lines * vb6801_ctrl->factor_fps));
+ CDBG("coarse_integration_time = %d\n", coarse_integration_time);
+ CDBG("pinfo->vcm_dac_code = %d\n", pinfo->vcm_dac_code);
+ CDBG("capture_mode = %d\n", capture_mode);
+
+ /* RE-CONFIGURE SENSOR WITH NEW TIMINGS
+ * ====================================
+ * Enter Software Standby Mode */
+ rc = vb6801_i2c_write_b(REG_MODE_SELECT, 0);
+ if (rc < 0) {
+ CDBG("I2C vb6801_i2c_write_b FAILED!!!\n");
+ return rc;
+ }
+
+ /* Wait 100ms */
+ mdelay(100);
+
+ if (capture_mode == 0) {
+
+ rc = vb6801_i2c_write_b(REG_CCP2_CHANNEL_IDENTIFIER, 0);
+ rc = vb6801_i2c_write_b(REG_CCP2_SIGNALLING_MODE, 0);
+ } else if (capture_mode == 1) {
+
+ rc = vb6801_i2c_write_b(REG_CCP2_CHANNEL_IDENTIFIER, 0);
+ rc = vb6801_i2c_write_b(REG_CCP2_SIGNALLING_MODE, 1);
+ }
+
+ {
+ struct vb6801_i2c_reg_conf_t wreg[] = {
+ /* Re-configure Sensor */
+ {REG_CCP2_DATA_FORMAT, ccp2_data_format, 0,
+ D_LEN_WORD},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL, 128, 0, D_LEN_WORD},
+ {REG_PRE_PLL_CLK_DIV, pre_pll_clk_div, 0, D_LEN_WORD},
+ {REG_VT_SYS_CLK_DIV, vt_sys_clk_div, 0, D_LEN_WORD},
+ {REG_VT_PIX_CLK_DIV, vt_pix_clk_div, 0, D_LEN_WORD},
+ {REG_OP_SYS_CLK_DIV, vt_sys_clk_div, 0, D_LEN_WORD},
+ {REG_OP_PIX_CLK_DIV, vt_pix_clk_div, 0, D_LEN_WORD},
+ {REG_VT_LINE_LENGTH_PCK, vt_line_length_pck, 0,
+ D_LEN_WORD},
+ {REG_X_OUTPUT_SIZE, x_output_size, 0, D_LEN_WORD},
+ {REG_Y_OUTPUT_SIZE, y_output_size, 0, D_LEN_WORD},
+ {REG_X_ODD_INC, x_odd_inc, 0, D_LEN_WORD},
+ {REG_Y_ODD_INC, y_odd_inc, 0, D_LEN_WORD},
+ {REG_VT_FRAME_LENGTH_LINES,
+ vt_frame_length_lines * vb6801_ctrl->factor_fps, 0,
+ D_LEN_WORD},
+ {REG_COARSE_INTEGRATION_TIME,
+ coarse_integration_time, 0, D_LEN_WORD},
+ /* Analogue Settings */
+ {REG_ANALOG_TIMING_MODES_2, 0, 132, D_LEN_BYTE},
+ {REG_RAMP_SCALE, 0, 5, D_LEN_BYTE},
+ {REG_BTL_LEVEL_SETUP, 0, 11, D_LEN_BYTE},
+ /* Enable Defect Correction */
+ {REG_SCYTHE_ENABLE, 0, 1, D_LEN_BYTE},
+ {REG_SCYTHE_WEIGHT, 0, 16, D_LEN_BYTE},
+ {REG_BRUCE_ENABLE, 0, 1, D_LEN_BYTE},
+ /* Auto Focus Configuration
+ * Please note that the DAC Code is a written as a
+ * 16-bit value 0 = infinity (no DAC current) */
+ {REG_VCM_DAC_CODE, pinfo->vcm_dac_code, 0, D_LEN_WORD},
+ {REG_VCM_DAC_STROBE, 0, 0, D_LEN_BYTE},
+ {REG_VCM_DAC_ENABLE, 0, 1, D_LEN_BYTE},
+ };
+
+ rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ return rc;
+ }
+ }
+ /* Parallel Interface Configuration */
+ if (capture_mode >= 2) {
+ struct vb6801_i2c_reg_conf_t wreg1[] = {
+ {REG_OP_CODER_SYNC_CLK_SETUP, 0, 15, D_LEN_BYTE},
+ {REG_OP_CODER_ENABLE, 0, 3, D_LEN_BYTE},
+ {REG_OP_CODER_SLOW_PAD_EN, 0, 1, D_LEN_BYTE},
+ {REG_OP_CODER_AUTOMATIC_MODE_ENABLE, 0, 3, D_LEN_BYTE},
+ {REG_OP_CODER_AUTO_STARTUP, 0, 2, D_LEN_BYTE},
+ };
+
+ rc = vb6801_i2c_write_table(wreg1, ARRAY_SIZE(wreg1));
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ return rc;
+ }
+ }
+
+ /* Enter Streaming Mode */
+ rc = vb6801_i2c_write_b(REG_MODE_SELECT, 1);
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ return rc;
+ }
+
+ /* Wait until the sensor starts streaming
+ * Poll until the reported frame_count value is != 0xFF */
+ frame_count = 0xFF;
+ timeout = 2000;
+ while (frame_count == 0xFF && timeout > 0) {
+ rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+ if (rc < 0)
+ return rc;
+
+ CDBG("REG_FRAME_COUNT = 0x%x\n", frame_count);
+ timeout--;
+ }
+
+ /* Post Streaming Configuration */
+
+ rc = vb6801_i2c_write_table(wreg2, ARRAY_SIZE(wreg2));
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ return rc;
+ }
+
+ rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+ if (rc < 0) {
+ CDBG("I2C Read Table FAILED!!!\n");
+ return rc;
+ }
+
+ CDBG("REG_PRE_PLL_CLK_DIV = 0x%x\n", rreg[0].wdata);
+ CDBG("REG_PLL_MULTIPLIER = 0x%x\n", rreg[1].wdata);
+ CDBG("REG_VT_SYS_CLK_DIV = 0x%x\n", rreg[2].wdata);
+ CDBG("REG_VT_PIX_CLK_DIV = 0x%x\n", rreg[3].wdata);
+ CDBG("REG_OP_SYS_CLK_DIV = 0x%x\n", rreg[4].wdata);
+ CDBG("REG_OP_PIX_CLK_DIV = 0x%x\n", rreg[5].wdata);
+ CDBG("REG_FRAME_COUNT = 0x%x\n", rreg[6].bdata);
+
+ mdelay(50);
+ frame_count = 0;
+ rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+ CDBG("REG_FRAME_COUNT1 = 0x%x\n", frame_count);
+
+ mdelay(150);
+ frame_count = 0;
+ rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+ CDBG("REG_FRAME_COUNT2 = 0x%x\n", frame_count);
+
+ mdelay(100);
+ frame_count = 0;
+ rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+ CDBG("REG_FRAME_COUNT3 = 0x%x\n", frame_count);
+
+ mdelay(250);
+ frame_count = 0;
+ rc = vb6801_i2c_read(REG_FRAME_COUNT, &frame_count, 1);
+ CDBG("REG_FRAME_COUNT4 = 0x%x\n", frame_count);
+
+ return rc;
+}
+
+static int vb6801_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 vb6801_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&vb6801_wait_queue);
+ return 0;
+}
+
+static int32_t vb6801_video_config(int mode, int res)
+{
+ int32_t rc = 0;
+
+ vb6801_ctrl->prev_res = res;
+ vb6801_ctrl->curr_res = res;
+ vb6801_ctrl->sensormode = mode;
+
+ rc = vb6801_config_sensor(12, 30, 60, 2, 1, 10, 2, RES_PREVIEW);
+ if (rc < 0)
+ return rc;
+
+ rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+ &vb6801_ctrl->s_dynamic_params.
+ preview_pixelsPerLine, 2);
+ if (rc < 0)
+ return rc;
+
+ rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+ &vb6801_ctrl->s_dynamic_params.
+ preview_linesPerFrame, 2);
+
+ return rc;
+}
+
+static int32_t vb6801_snapshot_config(int mode, int res)
+{
+ int32_t rc = 0;
+
+ vb6801_ctrl->curr_res = vb6801_ctrl->pict_res;
+ vb6801_ctrl->sensormode = mode;
+
+ rc = vb6801_config_sensor(12, 12, 48, 1, 1, 10, 2, RES_CAPTURE);
+ if (rc < 0)
+ return rc;
+
+ rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+ &vb6801_ctrl->s_dynamic_params.
+ snapshot_pixelsPerLine, 2);
+ if (rc < 0)
+ return rc;
+
+ rc = vb6801_i2c_read(REG_VT_LINE_LENGTH_PCK,
+ &vb6801_ctrl->s_dynamic_params.
+ snapshot_linesPerFrame, 2);
+
+ return rc;
+}
+
+static int32_t vb6801_set_sensor_mode(int mode, int res)
+{
+ int32_t rc = 0;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = vb6801_video_config(mode, res);
+ break;
+
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = vb6801_snapshot_config(mode, res);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int vb6801_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;
+
+ mutex_lock(&vb6801_mut);
+
+ CDBG("vb6801_sensor_config, cfgtype = %d\n", cdata.cfgtype);
+
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ vb6801_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 = vb6801_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 = vb6801_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 = vb6801_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 = vb6801_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 = vb6801_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 = vb6801_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc = vb6801_write_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc = vb6801_set_pict_exp_gain(cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = vb6801_set_sensor_mode(cdata.mode, cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = vb6801_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = vb6801_move_focus(cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = vb6801_set_default_focus();
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&vb6801_mut);
+
+ return rc;
+}
+
+static int vb6801_sensor_release(void)
+{
+ int rc = -EBADF;
+
+ mutex_lock(&vb6801_mut);
+
+ vb6801_power_down();
+ vb6801_sensor_init_done(vb6801_ctrl->sensordata);
+ kfree(vb6801_ctrl);
+ mutex_unlock(&vb6801_mut);
+
+ return rc;
+}
+
+static int vb6801_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;
+ }
+
+ vb6801_sensorw = kzalloc(sizeof(struct vb6801_work_t), GFP_KERNEL);
+
+ if (!vb6801_sensorw) {
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, vb6801_sensorw);
+ vb6801_init_client(client);
+ vb6801_client = client;
+ vb6801_client->addr = vb6801_client->addr >> 1;
+
+ return 0;
+
+probe_failure:
+ if (vb6801_sensorw != NULL) {
+ kfree(vb6801_sensorw);
+ vb6801_sensorw = NULL;
+ }
+ return rc;
+}
+
+static int __exit vb6801_i2c_remove(struct i2c_client *client)
+{
+ struct vb6801_work_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ vb6801_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static const struct i2c_device_id vb6801_i2c_id[] = {
+ {"vb6801", 0},
+ {}
+};
+
+static struct i2c_driver vb6801_i2c_driver = {
+ .id_table = vb6801_i2c_id,
+ .probe = vb6801_i2c_probe,
+ .remove = __exit_p(vb6801_i2c_remove),
+ .driver = {
+ .name = "vb6801",
+ },
+};
+
+static int vb6801_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int rc;
+
+ struct vb6801_i2c_reg_conf_t rreg[] = {
+ {0x0000, 0, 0, D_LEN_BYTE},
+ {0x0001, 0, 0, D_LEN_BYTE},
+ };
+
+ rc = vb6801_reset(data);
+ if (rc < 0)
+ goto init_probe_done;
+
+ mdelay(20);
+
+ rc = vb6801_i2c_read_table(rreg, ARRAY_SIZE(rreg));
+ if (rc < 0) {
+ CDBG("I2C Read Table FAILED!!!\n");
+ goto init_probe_fail;
+ }
+
+ /* 4. Compare sensor ID to VB6801 ID: */
+ if (rreg[0].bdata != 0x03 || rreg[1].bdata != 0x53) {
+ CDBG("vb6801_sensor_init: sensor ID don't match!\n");
+ goto init_probe_fail;
+ }
+
+ goto init_probe_done;
+
+init_probe_fail:
+ vb6801_sensor_init_done(data);
+init_probe_done:
+ return rc;
+}
+
+int vb6801_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc;
+ struct vb6801_i2c_reg_conf_t wreg[] = {
+ {REG_MODE_SELECT, 0, STANDBY_MODE, D_LEN_BYTE},
+ {0x0113, 0, 0x0A, D_LEN_BYTE},
+ };
+
+ vb6801_ctrl = kzalloc(sizeof(struct vb6801_ctrl_t), GFP_KERNEL);
+ if (!vb6801_ctrl) {
+ rc = -ENOMEM;
+ goto open_init_fail1;
+ }
+
+ vb6801_ctrl->factor_fps = 1 /** 0x00000400*/ ;
+ vb6801_ctrl->curr_fps = 7680; /* 30 * Q8 */ ;
+ vb6801_ctrl->max_fps = 7680; /* 30 * Q8 */ ;
+ vb6801_ctrl->pict_exp_update = 0; /* 30 * Q8 */ ;
+ vb6801_ctrl->reducel = 0; /* 30 * Q8 */ ;
+
+ vb6801_ctrl->set_test = TEST_OFF;
+ vb6801_ctrl->prev_res = QTR_SIZE;
+ vb6801_ctrl->pict_res = FULL_SIZE;
+
+ vb6801_ctrl->s_dynamic_params.preview_linesPerFrame =
+ VB6801_LINES_PER_FRAME_PREVIEW;
+ vb6801_ctrl->s_dynamic_params.preview_pixelsPerLine =
+ VB6801_PIXELS_PER_LINE_PREVIEW;
+ vb6801_ctrl->s_dynamic_params.snapshot_linesPerFrame =
+ VB6801_LINES_PER_FRAME_SNAPSHOT;
+ vb6801_ctrl->s_dynamic_params.snapshot_pixelsPerLine =
+ VB6801_PIXELS_PER_LINE_SNAPSHOT;
+
+ if (data)
+ vb6801_ctrl->sensordata = data;
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(VB6801_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = vb6801_reset(data);
+ if (rc < 0)
+ goto open_init_fail1;
+
+ rc = vb6801_i2c_write_table(wreg, ARRAY_SIZE(wreg));
+ if (rc < 0) {
+ CDBG("I2C Write Table FAILED!!!\n");
+ goto open_init_fail2;
+ }
+
+ rc = vb6801_read_nvm_data(&vb6801_ctrl->s_info);
+ if (rc < 0) {
+ CDBG("vb6801_read_nvm_data FAILED!!!\n");
+ goto open_init_fail2;
+ }
+ mdelay(66);
+
+ rc = vb6801_config_sensor(12, 30, 60, 2, 1, 10, 2, RES_PREVIEW);
+ if (rc < 0)
+ goto open_init_fail2;
+
+ goto open_init_done;
+
+open_init_fail2:
+ vb6801_sensor_init_done(data);
+open_init_fail1:
+ kfree(vb6801_ctrl);
+open_init_done:
+ return rc;
+}
+
+static int vb6801_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = i2c_add_driver(&vb6801_i2c_driver);
+ if (rc < 0 || vb6801_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_done;
+ }
+
+ /* enable mclk first */
+ msm_camio_clk_rate_set(VB6801_DEFAULT_CLOCK_RATE);
+ mdelay(20);
+
+ rc = vb6801_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_done;
+
+ s->s_init = vb6801_sensor_open_init;
+ s->s_release = vb6801_sensor_release;
+ s->s_config = vb6801_sensor_config;
+ s->s_mount_angle = 0;
+ vb6801_sensor_init_done(info);
+
+probe_done:
+ return rc;
+}
+
+static int __vb6801_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, vb6801_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __vb6801_probe,
+ .driver = {
+ .name = "msm_camera_vb6801",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vb6801_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vb6801_init);
+void vb6801_exit(void)
+{
+ i2c_del_driver(&vb6801_i2c_driver);
+}
diff --git a/drivers/media/video/msm/vb6801.h b/drivers/media/video/msm/vb6801.h
new file mode 100644
index 0000000..8248f8d
--- /dev/null
+++ b/drivers/media/video/msm/vb6801.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef VB6801_H
+#define VB6801_H
+
+#include <mach/board.h>
+
+extern struct vb6801_reg_t vb6801_regs; /* from vb6801_reg.c */
+
+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 */
+};
+
+enum i2c_data_len {
+ D_LEN_BYTE,
+ D_LEN_WORD
+};
+
+struct vb6801_i2c_reg_conf_t {
+ unsigned short waddr;
+ unsigned short wdata;
+ uint8_t bdata;
+ enum i2c_data_len dlen;
+};
+
+struct vb6801_reg_t {
+ struct reg_struct const *reg_pat;
+ uint16_t reg_pat_size;
+ struct vb6801_i2c_reg_conf_t const *ttbl;
+ uint16_t ttbl_size;
+ struct vb6801_i2c_reg_conf_t const *lctbl;
+ uint16_t lctbl_size;
+ struct vb6801_i2c_reg_conf_t const *rftbl;
+ uint16_t rftbl_size;
+};
+
+#endif /* VB6801_H */
diff --git a/drivers/media/video/msm/vx6953.c b/drivers/media/video/msm/vx6953.c
new file mode 100644
index 0000000..17e5e2e
--- /dev/null
+++ b/drivers/media/video/msm/vx6953.c
@@ -0,0 +1,3666 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#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 <linux/slab.h>
+#include "vx6953.h"
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+#define REG_MODE_SELECT 0x0100
+#define MODE_SELECT_STANDBY_MODE 0x00
+#define MODE_SELECT_STREAM 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+/* Digital Gain */
+#define REG_DIGITAL_GAIN_GREEN_R_HI 0x020E
+#define REG_DIGITAL_GAIN_GREEN_R_LO 0x020F
+#define REG_DIGITAL_GAIN_RED_HI 0x0210
+#define REG_DIGITAL_GAIN_RED_LO 0x0211
+#define REG_DIGITAL_GAIN_BLUE_HI 0x0212
+#define REG_DIGITAL_GAIN_BLUE_LO 0x0213
+#define REG_DIGITAL_GAIN_GREEN_B_HI 0x0214
+#define REG_DIGITAL_GAIN_GREEN_B_LO 0x0215
+/* output bits setting */
+#define REG_0x0112 0x0112
+#define REG_0x0113 0x0113
+/* PLL registers */
+#define REG_VT_PIX_CLK_DIV 0x0301
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLL_MULTIPLIER 0x0307
+#define REG_OP_PIX_CLK_DIV 0x0309
+#define REG_0x034c 0x034c
+#define REG_0x034d 0x034d
+#define REG_0x034e 0x034e
+#define REG_0x034f 0x034f
+#define REG_0x0387 0x0387
+#define REG_0x0383 0x0383
+#define REG_FRAME_LENGTH_LINES_HI 0x0340
+#define REG_FRAME_LENGTH_LINES_LO 0x0341
+#define REG_LINE_LENGTH_PCK_HI 0x0342
+#define REG_LINE_LENGTH_PCK_LO 0x0343
+#define REG_0x3030 0x3030
+#define REG_0x0111 0x0111
+#define REG_0x0136 0x0136
+#define REG_0x0137 0x0137
+#define REG_0x0b00 0x0b00
+#define REG_0x3001 0x3001
+#define REG_0x3004 0x3004
+#define REG_0x3007 0x3007
+#define REG_0x301a 0x301a
+#define REG_0x3101 0x3101
+#define REG_0x3364 0x3364
+#define REG_0x3365 0x3365
+#define REG_0x0b83 0x0b83
+#define REG_0x0b84 0x0b84
+#define REG_0x0b85 0x0b85
+#define REG_0x0b88 0x0b88
+#define REG_0x0b89 0x0b89
+#define REG_0x0b8a 0x0b8a
+#define REG_0x3005 0x3005
+#define REG_0x3010 0x3010
+#define REG_0x3036 0x3036
+#define REG_0x3041 0x3041
+#define REG_0x0b80 0x0b80
+#define REG_0x0900 0x0900
+#define REG_0x0901 0x0901
+#define REG_0x0902 0x0902
+#define REG_0x3016 0x3016
+#define REG_0x301d 0x301d
+#define REG_0x317e 0x317e
+#define REG_0x317f 0x317f
+#define REG_0x3400 0x3400
+#define REG_0x303a 0x303a
+#define REG_0x1716 0x1716
+#define REG_0x1717 0x1717
+#define REG_0x1718 0x1718
+#define REG_0x1719 0x1719
+#define REG_0x3006 0x3006
+#define REG_0x301b 0x301b
+#define REG_0x3098 0x3098
+#define REG_0x309d 0x309d
+#define REG_0x3011 0x3011
+#define REG_0x3035 0x3035
+#define REG_0x3045 0x3045
+#define REG_0x3210 0x3210
+#define REG_0x0111 0x0111
+#define REG_0x3410 0x3410
+#define REG_0x0b06 0x0b06
+#define REG_0x0b07 0x0b07
+#define REG_0x0b08 0x0b08
+#define REG_0x0b09 0x0b09
+#define REG_0x3640 0x3640
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+
+/*============================================================================
+ TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define VX6953_STM5M0EDOF_OFFSET 9
+#define Q8 0x00000100
+#define Q10 0x00000400
+#define VX6953_STM5M0EDOF_MAX_SNAPSHOT_EXPOSURE_LINE_COUNT 2922
+#define VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE 24000000
+#define VX6953_STM5M0EDOF_OP_PIXEL_CLOCK_RATE 79800000
+#define VX6953_STM5M0EDOF_VT_PIXEL_CLOCK_RATE 88670000
+/* Full Size */
+#define VX6953_FULL_SIZE_WIDTH 2608
+#define VX6953_FULL_SIZE_HEIGHT 1960
+#define VX6953_FULL_SIZE_DUMMY_PIXELS 1
+#define VX6953_FULL_SIZE_DUMMY_LINES 0
+/* Quarter Size */
+#define VX6953_QTR_SIZE_WIDTH 1304
+#define VX6953_QTR_SIZE_HEIGHT 980
+#define VX6953_QTR_SIZE_DUMMY_PIXELS 1
+#define VX6953_QTR_SIZE_DUMMY_LINES 0
+/* Blanking as measured on the scope */
+/* Full Size */
+#define VX6953_HRZ_FULL_BLK_PIXELS 348
+#define VX6953_VER_FULL_BLK_LINES 40
+/* Quarter Size */
+#define VX6953_HRZ_QTR_BLK_PIXELS 1628
+#define VX6953_VER_QTR_BLK_LINES 28
+#define MAX_LINE_LENGTH_PCK 8190
+#define MAX_FRAME_LENGTH_LINES 16383
+#define VX6953_REVISION_NUMBER_CUT2 0x10/*revision number for Cut2.0*/
+#define VX6953_REVISION_NUMBER_CUT3 0x20/*revision number for Cut3.0*/
+/* FIXME: Changes from here */
+struct vx6953_work_t {
+ struct work_struct work;
+};
+
+static struct vx6953_work_t *vx6953_sensorw;
+static struct i2c_client *vx6953_client;
+
+struct vx6953_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum vx6953_resolution_t prev_res;
+ enum vx6953_resolution_t pict_res;
+ enum vx6953_resolution_t curr_res;
+ enum vx6953_test_mode_t set_test;
+ enum sensor_revision_t sensor_type;
+
+ enum edof_mode_t edof_mode;
+
+ unsigned short imgaddr;
+};
+
+
+static uint8_t vx6953_stm5m0edof_delay_msecs_stdby;
+static uint16_t vx6953_stm5m0edof_delay_msecs_stream = 20;
+static uint8_t count;
+static struct vx6953_ctrl_t *vx6953_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vx6953_wait_queue);
+DEFINE_MUTEX(vx6953_mut);
+static struct vx6953_i2c_reg_conf patch_tbl_cut2[] = {
+ {0xFB94, 0}, /*intialise Data Xfer Status reg*/
+ {0xFB95, 0}, /*gain 1 (0x00)*/
+ {0xFB96, 0}, /*gain 1.07 (0x10)*/
+ {0xFB97, 0}, /*gain 1.14 (0x20)*/
+ {0xFB98, 0}, /*gain 1.23 (0x30)*/
+ {0xFB99, 0}, /*gain 1.33 (0x40)*/
+ {0xFB9A, 0}, /*gain 1.45 (0x50)*/
+ {0xFB9B, 0}, /*gain 1.6 (0x60)*/
+ {0xFB9C, 0}, /*gain 1.78 (0x70)*/
+ {0xFB9D, 2}, /*gain 2 (0x80)*/
+ {0xFB9E, 2}, /*gain 2.29 (0x90)*/
+ {0xFB9F, 3}, /*gain 2.67 (0xA0)*/
+ {0xFBA0, 3}, /*gain 3.2 (0xB0)*/
+ {0xFBA1, 4}, /*gain 4 (0xC0)*/
+ {0xFBA2, 7}, /*gain 5.33 (0xD0)*/
+ {0xFBA3, 10}, /*gain 8 (0xE0)*/
+ {0xFBA4, 11}, /*gain 9.14 (0xE4)*/
+ {0xFBA5, 13}, /*gain 10.67 (0xE8)*/
+ {0xFBA6, 15}, /*gain 12.8 (0xEC)*/
+ {0xFBA7, 19}, /*gain 16 (0xF0)*/
+ {0xF800, 0x12},
+ {0xF801, 0x06},
+ {0xF802, 0xf7},
+ {0xF803, 0x90},
+ {0xF804, 0x02},
+ {0xF805, 0x05},
+ {0xF806, 0xe0},
+ {0xF807, 0xff},
+ {0xF808, 0x65},
+ {0xF809, 0x7d},
+ {0xF80A, 0x70},
+ {0xF80B, 0x03},
+ {0xF80C, 0x02},
+ {0xF80D, 0xf9},
+ {0xF80E, 0x1c},
+ {0xF80F, 0x8f},
+ {0xF810, 0x7d},
+ {0xF811, 0xe4},
+ {0xF812, 0xf5},
+ {0xF813, 0x7a},
+ {0xF814, 0x75},
+ {0xF815, 0x78},
+ {0xF816, 0x30},
+ {0xF817, 0x75},
+ {0xF818, 0x79},
+ {0xF819, 0x53},
+ {0xF81A, 0x85},
+ {0xF81B, 0x79},
+ {0xF81C, 0x82},
+ {0xF81D, 0x85},
+ {0xF81E, 0x78},
+ {0xF81F, 0x83},
+ {0xF820, 0xe0},
+ {0xF821, 0xc3},
+ {0xF822, 0x95},
+ {0xF823, 0x7b},
+ {0xF824, 0xf0},
+ {0xF825, 0x74},
+ {0xF826, 0x02},
+ {0xF827, 0x25},
+ {0xF828, 0x79},
+ {0xF829, 0xf5},
+ {0xF82A, 0x79},
+ {0xF82B, 0xe4},
+ {0xF82C, 0x35},
+ {0xF82D, 0x78},
+ {0xF82E, 0xf5},
+ {0xF82F, 0x78},
+ {0xF830, 0x05},
+ {0xF831, 0x7a},
+ {0xF832, 0xe5},
+ {0xF833, 0x7a},
+ {0xF834, 0xb4},
+ {0xF835, 0x08},
+ {0xF836, 0xe3},
+ {0xF837, 0xe5},
+ {0xF838, 0x7d},
+ {0xF839, 0x70},
+ {0xF83A, 0x04},
+ {0xF83B, 0xff},
+ {0xF83C, 0x02},
+ {0xF83D, 0xf8},
+ {0xF83E, 0xe4},
+ {0xF83F, 0xe5},
+ {0xF840, 0x7d},
+ {0xF841, 0xb4},
+ {0xF842, 0x10},
+ {0xF843, 0x05},
+ {0xF844, 0x7f},
+ {0xF845, 0x01},
+ {0xF846, 0x02},
+ {0xF847, 0xf8},
+ {0xF848, 0xe4},
+ {0xF849, 0xe5},
+ {0xF84A, 0x7d},
+ {0xF84B, 0xb4},
+ {0xF84C, 0x20},
+ {0xF84D, 0x05},
+ {0xF84E, 0x7f},
+ {0xF84F, 0x02},
+ {0xF850, 0x02},
+ {0xF851, 0xf8},
+ {0xF852, 0xe4},
+ {0xF853, 0xe5},
+ {0xF854, 0x7d},
+ {0xF855, 0xb4},
+ {0xF856, 0x30},
+ {0xF857, 0x05},
+ {0xF858, 0x7f},
+ {0xF859, 0x03},
+ {0xF85A, 0x02},
+ {0xF85B, 0xf8},
+ {0xF85C, 0xe4},
+ {0xF85D, 0xe5},
+ {0xF85E, 0x7d},
+ {0xF85F, 0xb4},
+ {0xF860, 0x40},
+ {0xF861, 0x04},
+ {0xF862, 0x7f},
+ {0xF863, 0x04},
+ {0xF864, 0x80},
+ {0xF865, 0x7e},
+ {0xF866, 0xe5},
+ {0xF867, 0x7d},
+ {0xF868, 0xb4},
+ {0xF869, 0x50},
+ {0xF86A, 0x04},
+ {0xF86B, 0x7f},
+ {0xF86C, 0x05},
+ {0xF86D, 0x80},
+ {0xF86E, 0x75},
+ {0xF86F, 0xe5},
+ {0xF870, 0x7d},
+ {0xF871, 0xb4},
+ {0xF872, 0x60},
+ {0xF873, 0x04},
+ {0xF874, 0x7f},
+ {0xF875, 0x06},
+ {0xF876, 0x80},
+ {0xF877, 0x6c},
+ {0xF878, 0xe5},
+ {0xF879, 0x7d},
+ {0xF87A, 0xb4},
+ {0xF87B, 0x70},
+ {0xF87C, 0x04},
+ {0xF87D, 0x7f},
+ {0xF87E, 0x07},
+ {0xF87F, 0x80},
+ {0xF880, 0x63},
+ {0xF881, 0xe5},
+ {0xF882, 0x7d},
+ {0xF883, 0xb4},
+ {0xF884, 0x80},
+ {0xF885, 0x04},
+ {0xF886, 0x7f},
+ {0xF887, 0x08},
+ {0xF888, 0x80},
+ {0xF889, 0x5a},
+ {0xF88A, 0xe5},
+ {0xF88B, 0x7d},
+ {0xF88C, 0xb4},
+ {0xF88D, 0x90},
+ {0xF88E, 0x04},
+ {0xF88F, 0x7f},
+ {0xF890, 0x09},
+ {0xF891, 0x80},
+ {0xF892, 0x51},
+ {0xF893, 0xe5},
+ {0xF894, 0x7d},
+ {0xF895, 0xb4},
+ {0xF896, 0xa0},
+ {0xF897, 0x04},
+ {0xF898, 0x7f},
+ {0xF899, 0x0a},
+ {0xF89A, 0x80},
+ {0xF89B, 0x48},
+ {0xF89C, 0xe5},
+ {0xF89D, 0x7d},
+ {0xF89E, 0xb4},
+ {0xF89F, 0xb0},
+ {0xF8A0, 0x04},
+ {0xF8A1, 0x7f},
+ {0xF8A2, 0x0b},
+ {0xF8A3, 0x80},
+ {0xF8A4, 0x3f},
+ {0xF8A5, 0xe5},
+ {0xF8A6, 0x7d},
+ {0xF8A7, 0xb4},
+ {0xF8A8, 0xc0},
+ {0xF8A9, 0x04},
+ {0xF8AA, 0x7f},
+ {0xF8AB, 0x0c},
+ {0xF8AC, 0x80},
+ {0xF8AD, 0x36},
+ {0xF8AE, 0xe5},
+ {0xF8AF, 0x7d},
+ {0xF8B0, 0xb4},
+ {0xF8B1, 0xd0},
+ {0xF8B2, 0x04},
+ {0xF8B3, 0x7f},
+ {0xF8B4, 0x0d},
+ {0xF8B5, 0x80},
+ {0xF8B6, 0x2d},
+ {0xF8B7, 0xe5},
+ {0xF8B8, 0x7d},
+ {0xF8B9, 0xb4},
+ {0xF8BA, 0xe0},
+ {0xF8BB, 0x04},
+ {0xF8BC, 0x7f},
+ {0xF8BD, 0x0e},
+ {0xF8BE, 0x80},
+ {0xF8BF, 0x24},
+ {0xF8C0, 0xe5},
+ {0xF8C1, 0x7d},
+ {0xF8C2, 0xb4},
+ {0xF8C3, 0xe4},
+ {0xF8C4, 0x04},
+ {0xF8C5, 0x7f},
+ {0xF8C6, 0x0f},
+ {0xF8C7, 0x80},
+ {0xF8C8, 0x1b},
+ {0xF8C9, 0xe5},
+ {0xF8CA, 0x7d},
+ {0xF8CB, 0xb4},
+ {0xF8CC, 0xe8},
+ {0xF8CD, 0x04},
+ {0xF8CE, 0x7f},
+ {0xF8CF, 0x10},
+ {0xF8D0, 0x80},
+ {0xF8D1, 0x12},
+ {0xF8D2, 0xe5},
+ {0xF8D3, 0x7d},
+ {0xF8D4, 0xb4},
+ {0xF8D5, 0xec},
+ {0xF8D6, 0x04},
+ {0xF8D7, 0x7f},
+ {0xF8D8, 0x11},
+ {0xF8D9, 0x80},
+ {0xF8DA, 0x09},
+ {0xF8DB, 0xe5},
+ {0xF8DC, 0x7d},
+ {0xF8DD, 0x7f},
+ {0xF8DE, 0x00},
+ {0xF8DF, 0xb4},
+ {0xF8E0, 0xf0},
+ {0xF8E1, 0x02},
+ {0xF8E2, 0x7f},
+ {0xF8E3, 0x12},
+ {0xF8E4, 0x8f},
+ {0xF8E5, 0x7c},
+ {0xF8E6, 0xef},
+ {0xF8E7, 0x24},
+ {0xF8E8, 0x95},
+ {0xF8E9, 0xff},
+ {0xF8EA, 0xe4},
+ {0xF8EB, 0x34},
+ {0xF8EC, 0xfb},
+ {0xF8ED, 0x8f},
+ {0xF8EE, 0x82},
+ {0xF8EF, 0xf5},
+ {0xF8F0, 0x83},
+ {0xF8F1, 0xe4},
+ {0xF8F2, 0x93},
+ {0xF8F3, 0xf5},
+ {0xF8F4, 0x7c},
+ {0xF8F5, 0xf5},
+ {0xF8F6, 0x7b},
+ {0xF8F7, 0xe4},
+ {0xF8F8, 0xf5},
+ {0xF8F9, 0x7a},
+ {0xF8FA, 0x75},
+ {0xF8FB, 0x78},
+ {0xF8FC, 0x30},
+ {0xF8FD, 0x75},
+ {0xF8FE, 0x79},
+ {0xF8FF, 0x53},
+ {0xF900, 0x85},
+ {0xF901, 0x79},
+ {0xF902, 0x82},
+ {0xF903, 0x85},
+ {0xF904, 0x78},
+ {0xF905, 0x83},
+ {0xF906, 0xe0},
+ {0xF907, 0x25},
+ {0xF908, 0x7c},
+ {0xF909, 0xf0},
+ {0xF90A, 0x74},
+ {0xF90B, 0x02},
+ {0xF90C, 0x25},
+ {0xF90D, 0x79},
+ {0xF90E, 0xf5},
+ {0xF90F, 0x79},
+ {0xF910, 0xe4},
+ {0xF911, 0x35},
+ {0xF912, 0x78},
+ {0xF913, 0xf5},
+ {0xF914, 0x78},
+ {0xF915, 0x05},
+ {0xF916, 0x7a},
+ {0xF917, 0xe5},
+ {0xF918, 0x7a},
+ {0xF919, 0xb4},
+ {0xF91A, 0x08},
+ {0xF91B, 0xe4},
+ {0xF91C, 0x02},
+ {0xF91D, 0x18},
+ {0xF91E, 0x32},
+ {0xF91F, 0x22},
+ {0xF920, 0xf0},
+ {0xF921, 0x90},
+ {0xF922, 0xa0},
+ {0xF923, 0xf8},
+ {0xF924, 0xe0},
+ {0xF925, 0x70},
+ {0xF926, 0x02},
+ {0xF927, 0xa3},
+ {0xF928, 0xe0},
+ {0xF929, 0x70},
+ {0xF92A, 0x0a},
+ {0xF92B, 0x90},
+ {0xF92C, 0xa1},
+ {0xF92D, 0x10},
+ {0xF92E, 0xe0},
+ {0xF92F, 0xfe},
+ {0xF930, 0xa3},
+ {0xF931, 0xe0},
+ {0xF932, 0xff},
+ {0xF933, 0x80},
+ {0xF934, 0x04},
+ {0xF935, 0x7e},
+ {0xF936, 0x00},
+ {0xF937, 0x7f},
+ {0xF938, 0x00},
+ {0xF939, 0x8e},
+ {0xF93A, 0x7e},
+ {0xF93B, 0x8f},
+ {0xF93C, 0x7f},
+ {0xF93D, 0x90},
+ {0xF93E, 0x36},
+ {0xF93F, 0x0d},
+ {0xF940, 0xe0},
+ {0xF941, 0x44},
+ {0xF942, 0x02},
+ {0xF943, 0xf0},
+ {0xF944, 0x90},
+ {0xF945, 0x36},
+ {0xF946, 0x0e},
+ {0xF947, 0xe5},
+ {0xF948, 0x7e},
+ {0xF949, 0xf0},
+ {0xF94A, 0xa3},
+ {0xF94B, 0xe5},
+ {0xF94C, 0x7f},
+ {0xF94D, 0xf0},
+ {0xF94E, 0xe5},
+ {0xF94F, 0x3a},
+ {0xF950, 0x60},
+ {0xF951, 0x0c},
+ {0xF952, 0x90},
+ {0xF953, 0x36},
+ {0xF954, 0x09},
+ {0xF955, 0xe0},
+ {0xF956, 0x70},
+ {0xF957, 0x06},
+ {0xF958, 0x90},
+ {0xF959, 0x36},
+ {0xF95A, 0x08},
+ {0xF95B, 0xf0},
+ {0xF95C, 0xf5},
+ {0xF95D, 0x3a},
+ {0xF95E, 0x02},
+ {0xF95F, 0x03},
+ {0xF960, 0x94},
+ {0xF961, 0x22},
+ {0xF962, 0x78},
+ {0xF963, 0x07},
+ {0xF964, 0xe6},
+ {0xF965, 0xd3},
+ {0xF966, 0x94},
+ {0xF967, 0x00},
+ {0xF968, 0x40},
+ {0xF969, 0x16},
+ {0xF96A, 0x16},
+ {0xF96B, 0xe6},
+ {0xF96C, 0x90},
+ {0xF96D, 0x30},
+ {0xF96E, 0xa1},
+ {0xF96F, 0xf0},
+ {0xF970, 0x90},
+ {0xF971, 0x43},
+ {0xF972, 0x83},
+ {0xF973, 0xe0},
+ {0xF974, 0xb4},
+ {0xF975, 0x01},
+ {0xF976, 0x0f},
+ {0xF977, 0x90},
+ {0xF978, 0x43},
+ {0xF979, 0x87},
+ {0xF97A, 0xe0},
+ {0xF97B, 0xb4},
+ {0xF97C, 0x01},
+ {0xF97D, 0x08},
+ {0xF97E, 0x80},
+ {0xF97F, 0x00},
+ {0xF980, 0x90},
+ {0xF981, 0x30},
+ {0xF982, 0xa0},
+ {0xF983, 0x74},
+ {0xF984, 0x01},
+ {0xF985, 0xf0},
+ {0xF986, 0x22},
+ {0xF987, 0xf0},
+ {0xF988, 0x90},
+ {0xF989, 0x35},
+ {0xF98A, 0xba},
+ {0xF98B, 0xe0},
+ {0xF98C, 0xb4},
+ {0xF98D, 0x0a},
+ {0xF98E, 0x0d},
+ {0xF98F, 0xa3},
+ {0xF990, 0xe0},
+ {0xF991, 0xb4},
+ {0xF992, 0x01},
+ {0xF993, 0x08},
+ {0xF994, 0x90},
+ {0xF995, 0xfb},
+ {0xF996, 0x94},
+ {0xF997, 0xe0},
+ {0xF998, 0x90},
+ {0xF999, 0x35},
+ {0xF99A, 0xb8},
+ {0xF99B, 0xf0},
+ {0xF99C, 0xd0},
+ {0xF99D, 0xd0},
+ {0xF99E, 0xd0},
+ {0xF99F, 0x82},
+ {0xF9A0, 0xd0},
+ {0xF9A1, 0x83},
+ {0xF9A2, 0xd0},
+ {0xF9A3, 0xe0},
+ {0xF9A4, 0x32},
+ {0xF9A5, 0x22},
+ {0xF9A6, 0xe5},
+ {0xF9A7, 0x7f},
+ {0xF9A8, 0x45},
+ {0xF9A9, 0x7e},
+ {0xF9AA, 0x60},
+ {0xF9AB, 0x15},
+ {0xF9AC, 0x90},
+ {0xF9AD, 0x01},
+ {0xF9AE, 0x00},
+ {0xF9AF, 0xe0},
+ {0xF9B0, 0x70},
+ {0xF9B1, 0x0f},
+ {0xF9B2, 0x90},
+ {0xF9B3, 0xa0},
+ {0xF9B4, 0xf8},
+ {0xF9B5, 0xe5},
+ {0xF9B6, 0x7e},
+ {0xF9B7, 0xf0},
+ {0xF9B8, 0xa3},
+ {0xF9B9, 0xe5},
+ {0xF9BA, 0x7f},
+ {0xF9BB, 0xf0},
+ {0xF9BC, 0xe4},
+ {0xF9BD, 0xf5},
+ {0xF9BE, 0x7e},
+ {0xF9BF, 0xf5},
+ {0xF9C0, 0x7f},
+ {0xF9C1, 0x22},
+ {0xF9C2, 0x02},
+ {0xF9C3, 0x0e},
+ {0xF9C4, 0x79},
+ {0xF9C5, 0x22},
+ /* Offsets:*/
+ {0x35C6, 0x00},/* FIDDLEDARKCAL*/
+ {0x35C7, 0x00},
+ {0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+ {0x35C9, 0x20},
+ {0x35CA, 0x01},/*BRUCEFIX*/
+ {0x35CB, 0x62},
+ {0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+ {0x35CD, 0x87},
+ {0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+ {0x35CF, 0xA6},
+ {0x35D0, 0x01},/*SKIPEDOFRESET*/
+ {0x35D1, 0xC2},
+ {0x35D2, 0x00},
+ {0x35D3, 0xFB},
+ {0x35D4, 0x00},
+ {0x35D5, 0x94},
+ {0x35D6, 0x00},
+ {0x35D7, 0xFB},
+ {0x35D8, 0x00},
+ {0x35D9, 0x94},
+ {0x35DA, 0x00},
+ {0x35DB, 0xFB},
+ {0x35DC, 0x00},
+ {0x35DD, 0x94},
+ {0x35DE, 0x00},
+ {0x35DF, 0xFB},
+ {0x35E0, 0x00},
+ {0x35E1, 0x94},
+ {0x35E6, 0x18},/* FIDDLEDARKCAL*/
+ {0x35E7, 0x2F},
+ {0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+ {0x35E9, 0x93},
+ {0x35EA, 0x18},/* BRUCEFIX*/
+ {0x35EB, 0x99},
+ {0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+ {0x35ED, 0xA3},
+ {0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+ {0x35EF, 0x5B},
+ {0x35F0, 0x0E},/* SKIPEDOFRESET*/
+ {0x35F1, 0x74},
+ {0x35F2, 0x04},
+ {0x35F3, 0x64},
+ {0x35F4, 0x04},
+ {0x35F5, 0x65},
+ {0x35F6, 0x04},
+ {0x35F7, 0x7B},
+ {0x35F8, 0x04},
+ {0x35F9, 0x7C},
+ {0x35FA, 0x04},
+ {0x35FB, 0xDD},
+ {0x35FC, 0x04},
+ {0x35FD, 0xDE},
+ {0x35FE, 0x04},
+ {0x35FF, 0xEF},
+ {0x3600, 0x04},
+ {0x3601, 0xF0},
+ /*Jump/Data:*/
+ {0x35C2, 0x3F},/* Jump Reg*/
+ {0x35C3, 0xFF},/* Jump Reg*/
+ {0x35C4, 0x3F},/* Data Reg*/
+ {0x35C5, 0xC0},/* Data Reg*/
+ {0x35C0, 0x01},/* Enable*/
+
+};
+
+static struct vx6953_i2c_reg_conf cut3_cali_data[] = {
+ {0x360A, 0x07 },
+ {0x3530, 0x07 },
+ {0x35B5, 0x00 },
+ {0x35BC, 0x00 },
+ {0xAFF8, 0x00 },
+ {0xAFF9, 0x01 },
+ {0xF800, 0x90 },
+ {0xF801, 0x30 },
+ {0xF802, 0x31 },
+ {0xF803, 0xe0 },
+ {0xF804, 0xf5 },
+ {0xF805, 0x7d },
+ {0xF806, 0xb4 },
+ {0xF807, 0x01 },
+ {0xF808, 0x06 },
+ {0xF809, 0x75 },
+ {0xF80A, 0x7d },
+ {0xF80B, 0x03 },
+ {0xF80C, 0x74 },
+ {0xF80D, 0x03 },
+ {0xF80E, 0xf0 },
+ {0xF80F, 0x90 },
+ {0xF810, 0x30 },
+ {0xF811, 0x04 },
+ {0xF812, 0x74 },
+ {0xF813, 0x33 },
+ {0xF814, 0xf0 },
+ {0xF815, 0x90 },
+ {0xF816, 0x30 },
+ {0xF817, 0x06 },
+ {0xF818, 0xe4 },
+ {0xF819, 0xf0 },
+ {0xF81A, 0xa3 },
+ {0xF81B, 0x74 },
+ {0xF81C, 0x08 },
+ {0xF81D, 0xf0 },
+ {0xF81E, 0x90 },
+ {0xF81F, 0x30 },
+ {0xF820, 0x10 },
+ {0xF821, 0xe4 },
+ {0xF822, 0xf0 },
+ {0xF823, 0xa3 },
+ {0xF824, 0xf0 },
+ {0xF825, 0x90 },
+ {0xF826, 0x30 },
+ {0xF827, 0x16 },
+ {0xF828, 0x74 },
+ {0xF829, 0x1e },
+ {0xF82A, 0xf0 },
+ {0xF82B, 0x90 },
+ {0xF82C, 0x30 },
+ {0xF82D, 0x1a },
+ {0xF82E, 0x74 },
+ {0xF82F, 0x6a },
+ {0xF830, 0xf0 },
+ {0xF831, 0x90 },
+ {0xF832, 0x30 },
+ {0xF833, 0x30 },
+ {0xF834, 0x74 },
+ {0xF835, 0x08 },
+ {0xF836, 0xf0 },
+ {0xF837, 0x90 },
+ {0xF838, 0x30 },
+ {0xF839, 0x36 },
+ {0xF83A, 0x74 },
+ {0xF83B, 0x2c },
+ {0xF83C, 0xf0 },
+ {0xF83D, 0x90 },
+ {0xF83E, 0x30 },
+ {0xF83F, 0x41 },
+ {0xF840, 0xe4 },
+ {0xF841, 0xf0 },
+ {0xF842, 0xa3 },
+ {0xF843, 0x74 },
+ {0xF844, 0x24 },
+ {0xF845, 0xf0 },
+ {0xF846, 0x90 },
+ {0xF847, 0x30 },
+ {0xF848, 0x45 },
+ {0xF849, 0x74 },
+ {0xF84A, 0x81 },
+ {0xF84B, 0xf0 },
+ {0xF84C, 0x90 },
+ {0xF84D, 0x30 },
+ {0xF84E, 0x98 },
+ {0xF84F, 0x74 },
+ {0xF850, 0x01 },
+ {0xF851, 0xf0 },
+ {0xF852, 0x90 },
+ {0xF853, 0x30 },
+ {0xF854, 0x9d },
+ {0xF855, 0x74 },
+ {0xF856, 0x05 },
+ {0xF857, 0xf0 },
+ {0xF858, 0xe5 },
+ {0xF859, 0x7d },
+ {0xF85A, 0x70 },
+ {0xF85B, 0x22 },
+ {0xF85C, 0x90 },
+ {0xF85D, 0x02 },
+ {0xF85E, 0x00 },
+ {0xF85F, 0x74 },
+ {0xF860, 0x02 },
+ {0xF861, 0xf0 },
+ {0xF862, 0xa3 },
+ {0xF863, 0x74 },
+ {0xF864, 0x54 },
+ {0xF865, 0xf0 },
+ {0xF866, 0x90 },
+ {0xF867, 0x30 },
+ {0xF868, 0x05 },
+ {0xF869, 0x74 },
+ {0xF86A, 0x01 },
+ {0xF86B, 0xf0 },
+ {0xF86C, 0x90 },
+ {0xF86D, 0x30 },
+ {0xF86E, 0x1b },
+ {0xF86F, 0x74 },
+ {0xF870, 0x29 },
+ {0xF871, 0xf0 },
+ {0xF872, 0x90 },
+ {0xF873, 0x30 },
+ {0xF874, 0x30 },
+ {0xF875, 0xe4 },
+ {0xF876, 0xf0 },
+ {0xF877, 0x90 },
+ {0xF878, 0x30 },
+ {0xF879, 0x35 },
+ {0xF87A, 0x04 },
+ {0xF87B, 0xf0 },
+ {0xF87C, 0x80 },
+ {0xF87D, 0x69 },
+ {0xF87E, 0xe5 },
+ {0xF87F, 0x7d },
+ {0xF880, 0x64 },
+ {0xF881, 0x02 },
+ {0xF882, 0x70 },
+ {0xF883, 0x3c },
+ {0xF884, 0x90 },
+ {0xF885, 0x02 },
+ {0xF886, 0x00 },
+ {0xF887, 0x74 },
+ {0xF888, 0x04 },
+ {0xF889, 0xf0 },
+ {0xF88A, 0xa3 },
+ {0xF88B, 0x74 },
+ {0xF88C, 0x10 },
+ {0xF88D, 0xf0 },
+ {0xF88E, 0x90 },
+ {0xF88F, 0x30 },
+ {0xF890, 0x04 },
+ {0xF891, 0x74 },
+ {0xF892, 0x34 },
+ {0xF893, 0xf0 },
+ {0xF894, 0xa3 },
+ {0xF895, 0x74 },
+ {0xF896, 0x07 },
+ {0xF897, 0xf0 },
+ {0xF898, 0x90 },
+ {0xF899, 0x30 },
+ {0xF89A, 0x10 },
+ {0xF89B, 0x74 },
+ {0xF89C, 0x10 },
+ {0xF89D, 0xf0 },
+ {0xF89E, 0x90 },
+ {0xF89F, 0x30 },
+ {0xF8A0, 0x16 },
+ {0xF8A1, 0x74 },
+ {0xF8A2, 0x1f },
+ {0xF8A3, 0xf0 },
+ {0xF8A4, 0x90 },
+ {0xF8A5, 0x30 },
+ {0xF8A6, 0x1a },
+ {0xF8A7, 0x74 },
+ {0xF8A8, 0x62 },
+ {0xF8A9, 0xf0 },
+ {0xF8AA, 0xa3 },
+ {0xF8AB, 0x74 },
+ {0xF8AC, 0x2a },
+ {0xF8AD, 0xf0 },
+ {0xF8AE, 0x90 },
+ {0xF8AF, 0x30 },
+ {0xF8B0, 0x35 },
+ {0xF8B1, 0x74 },
+ {0xF8B2, 0x04 },
+ {0xF8B3, 0xf0 },
+ {0xF8B4, 0x90 },
+ {0xF8B5, 0x30 },
+ {0xF8B6, 0x41 },
+ {0xF8B7, 0x74 },
+ {0xF8B8, 0x60 },
+ {0xF8B9, 0xf0 },
+ {0xF8BA, 0xa3 },
+ {0xF8BB, 0x74 },
+ {0xF8BC, 0x64 },
+ {0xF8BD, 0xf0 },
+ {0xF8BE, 0x80 },
+ {0xF8BF, 0x27 },
+ {0xF8C0, 0xe5 },
+ {0xF8C1, 0x7d },
+ {0xF8C2, 0xb4 },
+ {0xF8C3, 0x03 },
+ {0xF8C4, 0x22 },
+ {0xF8C5, 0x90 },
+ {0xF8C6, 0x02 },
+ {0xF8C7, 0x00 },
+ {0xF8C8, 0x74 },
+ {0xF8C9, 0x02 },
+ {0xF8CA, 0xf0 },
+ {0xF8CB, 0xa3 },
+ {0xF8CC, 0x74 },
+ {0xF8CD, 0x26 },
+ {0xF8CE, 0xf0 },
+ {0xF8CF, 0x90 },
+ {0xF8D0, 0x30 },
+ {0xF8D1, 0x05 },
+ {0xF8D2, 0x74 },
+ {0xF8D3, 0x03 },
+ {0xF8D4, 0xf0 },
+ {0xF8D5, 0x90 },
+ {0xF8D6, 0x30 },
+ {0xF8D7, 0x11 },
+ {0xF8D8, 0x74 },
+ {0xF8D9, 0x01 },
+ {0xF8DA, 0xf0 },
+ {0xF8DB, 0x90 },
+ {0xF8DC, 0x30 },
+ {0xF8DD, 0x1b },
+ {0xF8DE, 0x74 },
+ {0xF8DF, 0x2a },
+ {0xF8E0, 0xf0 },
+ {0xF8E1, 0x90 },
+ {0xF8E2, 0x30 },
+ {0xF8E3, 0x35 },
+ {0xF8E4, 0x74 },
+ {0xF8E5, 0x03 },
+ {0xF8E6, 0xf0 },
+ {0xF8E7, 0x90 },
+ {0xF8E8, 0x41 },
+ {0xF8E9, 0x01 },
+ {0xF8EA, 0xe0 },
+ {0xF8EB, 0xf5 },
+ {0xF8EC, 0x79 },
+ {0xF8ED, 0x90 },
+ {0xF8EE, 0x43 },
+ {0xF8EF, 0x87 },
+ {0xF8F0, 0xe0 },
+ {0xF8F1, 0xf5 },
+ {0xF8F2, 0x7a },
+ {0xF8F3, 0x90 },
+ {0xF8F4, 0x42 },
+ {0xF8F5, 0x05 },
+ {0xF8F6, 0xe0 },
+ {0xF8F7, 0xf5 },
+ {0xF8F8, 0x7b },
+ {0xF8F9, 0x22 },
+ {0xF8FA, 0x78 },
+ {0xF8FB, 0x07 },
+ {0xF8FC, 0xe6 },
+ {0xF8FD, 0xf5 },
+ {0xF8FE, 0x7c },
+ {0xF8FF, 0xe5 },
+ {0xF900, 0x7c },
+ {0xF901, 0x60 },
+ {0xF902, 0x1e },
+ {0xF903, 0x90 },
+ {0xF904, 0x43 },
+ {0xF905, 0x83 },
+ {0xF906, 0xe0 },
+ {0xF907, 0xb4 },
+ {0xF908, 0x01 },
+ {0xF909, 0x17 },
+ {0xF90A, 0x90 },
+ {0xF90B, 0x43 },
+ {0xF90C, 0x87 },
+ {0xF90D, 0xe0 },
+ {0xF90E, 0xb4 },
+ {0xF90F, 0x01 },
+ {0xF910, 0x10 },
+ {0xF911, 0x15 },
+ {0xF912, 0x7c },
+ {0xF913, 0x90 },
+ {0xF914, 0x30 },
+ {0xF915, 0xa1 },
+ {0xF916, 0xe5 },
+ {0xF917, 0x7c },
+ {0xF918, 0xf0 },
+ {0xF919, 0x90 },
+ {0xF91A, 0x30 },
+ {0xF91B, 0xa0 },
+ {0xF91C, 0x74 },
+ {0xF91D, 0x01 },
+ {0xF91E, 0xf0 },
+ {0xF91F, 0x80 },
+ {0xF920, 0x05 },
+ {0xF921, 0xe4 },
+ {0xF922, 0x90 },
+ {0xF923, 0x30 },
+ {0xF924, 0xa0 },
+ {0xF925, 0xf0 },
+ {0xF926, 0x90 },
+ {0xF927, 0x41 },
+ {0xF928, 0x01 },
+ {0xF929, 0xe0 },
+ {0xF92A, 0xfc },
+ {0xF92B, 0x54 },
+ {0xF92C, 0x02 },
+ {0xF92D, 0xfe },
+ {0xF92E, 0xe5 },
+ {0xF92F, 0x79 },
+ {0xF930, 0x54 },
+ {0xF931, 0x02 },
+ {0xF932, 0xb5 },
+ {0xF933, 0x06 },
+ {0xF934, 0x0f },
+ {0xF935, 0x90 },
+ {0xF936, 0x43 },
+ {0xF937, 0x87 },
+ {0xF938, 0xe0 },
+ {0xF939, 0xb5 },
+ {0xF93A, 0x7a },
+ {0xF93B, 0x08 },
+ {0xF93C, 0x90 },
+ {0xF93D, 0x42 },
+ {0xF93E, 0x05 },
+ {0xF93F, 0xe0 },
+ {0xF940, 0x65 },
+ {0xF941, 0x7b },
+ {0xF942, 0x60 },
+ {0xF943, 0x0b },
+ {0xF944, 0x90 },
+ {0xF945, 0x30 },
+ {0xF946, 0x50 },
+ {0xF947, 0xe0 },
+ {0xF948, 0x54 },
+ {0xF949, 0xf9 },
+ {0xF94A, 0x44 },
+ {0xF94B, 0x02 },
+ {0xF94C, 0xf0 },
+ {0xF94D, 0x80 },
+ {0xF94E, 0x09 },
+ {0xF94F, 0x90 },
+ {0xF950, 0x30 },
+ {0xF951, 0x50 },
+ {0xF952, 0xe0 },
+ {0xF953, 0x54 },
+ {0xF954, 0xf9 },
+ {0xF955, 0x44 },
+ {0xF956, 0x04 },
+ {0xF957, 0xf0 },
+ {0xF958, 0x8c },
+ {0xF959, 0x79 },
+ {0xF95A, 0x90 },
+ {0xF95B, 0x43 },
+ {0xF95C, 0x87 },
+ {0xF95D, 0xe0 },
+ {0xF95E, 0xf5 },
+ {0xF95F, 0x7a },
+ {0xF960, 0x90 },
+ {0xF961, 0x42 },
+ {0xF962, 0x05 },
+ {0xF963, 0xe0 },
+ {0xF964, 0xf5 },
+ {0xF965, 0x7b },
+ {0xF966, 0x22 },
+ {0xF967, 0xc3 },
+ {0xF968, 0x90 },
+ {0xF969, 0x0b },
+ {0xF96A, 0x89 },
+ {0xF96B, 0xe0 },
+ {0xF96C, 0x94 },
+ {0xF96D, 0x1e },
+ {0xF96E, 0x90 },
+ {0xF96F, 0x0b },
+ {0xF970, 0x88 },
+ {0xF971, 0xe0 },
+ {0xF972, 0x94 },
+ {0xF973, 0x00 },
+ {0xF974, 0x50 },
+ {0xF975, 0x06 },
+ {0xF976, 0x7e },
+ {0xF977, 0x00 },
+ {0xF978, 0x7f },
+ {0xF979, 0x01 },
+ {0xF97A, 0x80 },
+ {0xF97B, 0x3d },
+ {0xF97C, 0xc3 },
+ {0xF97D, 0x90 },
+ {0xF97E, 0x0b },
+ {0xF97F, 0x89 },
+ {0xF980, 0xe0 },
+ {0xF981, 0x94 },
+ {0xF982, 0x3c },
+ {0xF983, 0x90 },
+ {0xF984, 0x0b },
+ {0xF985, 0x88 },
+ {0xF986, 0xe0 },
+ {0xF987, 0x94 },
+ {0xF988, 0x00 },
+ {0xF989, 0x50 },
+ {0xF98A, 0x06 },
+ {0xF98B, 0x7e },
+ {0xF98C, 0x00 },
+ {0xF98D, 0x7f },
+ {0xF98E, 0x02 },
+ {0xF98F, 0x80 },
+ {0xF990, 0x28 },
+ {0xF991, 0xc3 },
+ {0xF992, 0x90 },
+ {0xF993, 0x0b },
+ {0xF994, 0x89 },
+ {0xF995, 0xe0 },
+ {0xF996, 0x94 },
+ {0xF997, 0xfa },
+ {0xF998, 0x90 },
+ {0xF999, 0x0b },
+ {0xF99A, 0x88 },
+ {0xF99B, 0xe0 },
+ {0xF99C, 0x94 },
+ {0xF99D, 0x00 },
+ {0xF99E, 0x50 },
+ {0xF99F, 0x06 },
+ {0xF9A0, 0x7e },
+ {0xF9A1, 0x00 },
+ {0xF9A2, 0x7f },
+ {0xF9A3, 0x03 },
+ {0xF9A4, 0x80 },
+ {0xF9A5, 0x13 },
+ {0xF9A6, 0xc3 },
+ {0xF9A7, 0x90 },
+ {0xF9A8, 0x0b },
+ {0xF9A9, 0x88 },
+ {0xF9AA, 0xe0 },
+ {0xF9AB, 0x94 },
+ {0xF9AC, 0x80 },
+ {0xF9AD, 0x50 },
+ {0xF9AE, 0x06 },
+ {0xF9AF, 0x7e },
+ {0xF9B0, 0x00 },
+ {0xF9B1, 0x7f },
+ {0xF9B2, 0x04 },
+ {0xF9B3, 0x80 },
+ {0xF9B4, 0x04 },
+ {0xF9B5, 0xae },
+ {0xF9B6, 0x7e },
+ {0xF9B7, 0xaf },
+ {0xF9B8, 0x7f },
+ {0xF9B9, 0x90 },
+ {0xF9BA, 0xa0 },
+ {0xF9BB, 0xf8 },
+ {0xF9BC, 0xee },
+ {0xF9BD, 0xf0 },
+ {0xF9BE, 0xa3 },
+ {0xF9BF, 0xef },
+ {0xF9C0, 0xf0 },
+ {0xF9C1, 0x22 },
+ {0xF9C2, 0x90 },
+ {0xF9C3, 0x33 },
+ {0xF9C4, 0x82 },
+ {0xF9C5, 0xe0 },
+ {0xF9C6, 0xff },
+ {0xF9C7, 0x64 },
+ {0xF9C8, 0x01 },
+ {0xF9C9, 0x70 },
+ {0xF9CA, 0x30 },
+ {0xF9CB, 0xe5 },
+ {0xF9CC, 0x7f },
+ {0xF9CD, 0x64 },
+ {0xF9CE, 0x02 },
+ {0xF9CF, 0x45 },
+ {0xF9D0, 0x7e },
+ {0xF9D1, 0x70 },
+ {0xF9D2, 0x04 },
+ {0xF9D3, 0x7d },
+ {0xF9D4, 0x1e },
+ {0xF9D5, 0x80 },
+ {0xF9D6, 0x1d },
+ {0xF9D7, 0xe5 },
+ {0xF9D8, 0x7f },
+ {0xF9D9, 0x64 },
+ {0xF9DA, 0x03 },
+ {0xF9DB, 0x45 },
+ {0xF9DC, 0x7e },
+ {0xF9DD, 0x70 },
+ {0xF9DE, 0x04 },
+ {0xF9DF, 0x7d },
+ {0xF9E0, 0x3c },
+ {0xF9E1, 0x80 },
+ {0xF9E2, 0x11 },
+ {0xF9E3, 0xe5 },
+ {0xF9E4, 0x7f },
+ {0xF9E5, 0x64 },
+ {0xF9E6, 0x04 },
+ {0xF9E7, 0x45 },
+ {0xF9E8, 0x7e },
+ {0xF9E9, 0x70 },
+ {0xF9EA, 0x04 },
+ {0xF9EB, 0x7d },
+ {0xF9EC, 0xfa },
+ {0xF9ED, 0x80 },
+ {0xF9EE, 0x05 },
+ {0xF9EF, 0x90 },
+ {0xF9F0, 0x33 },
+ {0xF9F1, 0x81 },
+ {0xF9F2, 0xe0 },
+ {0xF9F3, 0xfd },
+ {0xF9F4, 0xae },
+ {0xF9F5, 0x05 },
+ {0xF9F6, 0x90 },
+ {0xF9F7, 0x33 },
+ {0xF9F8, 0x81 },
+ {0xF9F9, 0xed },
+ {0xF9FA, 0xf0 },
+ {0xF9FB, 0xef },
+ {0xF9FC, 0xb4 },
+ {0xF9FD, 0x01 },
+ {0xF9FE, 0x10 },
+ {0xF9FF, 0x90 },
+ {0xFA00, 0x01 },
+ {0xFA01, 0x00 },
+ {0xFA02, 0xe0 },
+ {0xFA03, 0x60 },
+ {0xFA04, 0x0a },
+ {0xFA05, 0x90 },
+ {0xFA06, 0xa1 },
+ {0xFA07, 0x10 },
+ {0xFA08, 0xe0 },
+ {0xFA09, 0xf5 },
+ {0xFA0A, 0x7e },
+ {0xFA0B, 0xa3 },
+ {0xFA0C, 0xe0 },
+ {0xFA0D, 0xf5 },
+ {0xFA0E, 0x7f },
+ {0xFA0F, 0x22 },
+ {0xFA10, 0x12 },
+ {0xFA11, 0x2f },
+ {0xFA12, 0x4d },
+ {0xFA13, 0x90 },
+ {0xFA14, 0x35 },
+ {0xFA15, 0x38 },
+ {0xFA16, 0xe0 },
+ {0xFA17, 0x70 },
+ {0xFA18, 0x05 },
+ {0xFA19, 0x12 },
+ {0xFA1A, 0x00 },
+ {0xFA1B, 0x0e },
+ {0xFA1C, 0x80 },
+ {0xFA1D, 0x03 },
+ {0xFA1E, 0x12 },
+ {0xFA1F, 0x07 },
+ {0xFA20, 0xc9 },
+ {0xFA21, 0x90 },
+ {0xFA22, 0x40 },
+ {0xFA23, 0x06 },
+ {0xFA24, 0xe0 },
+ {0xFA25, 0xf4 },
+ {0xFA26, 0x54 },
+ {0xFA27, 0x02 },
+ {0xFA28, 0xff },
+ {0xFA29, 0xe0 },
+ {0xFA2A, 0x54 },
+ {0xFA2B, 0x01 },
+ {0xFA2C, 0x4f },
+ {0xFA2D, 0x90 },
+ {0xFA2E, 0x31 },
+ {0xFA2F, 0x32 },
+ {0xFA30, 0xf0 },
+ {0xFA31, 0x90 },
+ {0xFA32, 0xfa },
+ {0xFA33, 0x9d },
+ {0xFA34, 0xe0 },
+ {0xFA35, 0x70 },
+ {0xFA36, 0x03 },
+ {0xFA37, 0x12 },
+ {0xFA38, 0x27 },
+ {0xFA39, 0x27 },
+ {0xFA3A, 0x02 },
+ {0xFA3B, 0x05 },
+ {0xFA3C, 0xac },
+ {0xFA3D, 0x22 },
+ {0xFA3E, 0xf0 },
+ {0xFA3F, 0xe5 },
+ {0xFA40, 0x3a },
+ {0xFA41, 0xb4 },
+ {0xFA42, 0x06 },
+ {0xFA43, 0x06 },
+ {0xFA44, 0x63 },
+ {0xFA45, 0x3e },
+ {0xFA46, 0x02 },
+ {0xFA47, 0x12 },
+ {0xFA48, 0x03 },
+ {0xFA49, 0xea },
+ {0xFA4A, 0x02 },
+ {0xFA4B, 0x17 },
+ {0xFA4C, 0x4a },
+ {0xFA4D, 0x22 },
+ {0x35C9, 0xFA },
+ {0x35CA, 0x01 },
+ {0x35CB, 0x67 },
+ {0x35CC, 0x01 },
+ {0x35CD, 0xC2 },
+ {0x35CE, 0x02 },
+ {0x35CF, 0x10 },
+ {0x35D0, 0x02 },
+ {0x35D1, 0x3E },
+ {0x35D3, 0xF6 },
+ {0x35D5, 0x07 },
+ {0x35D7, 0xA3 },
+ {0x35DB, 0x02 },
+ {0x35DD, 0x06 },
+ {0x35DF, 0x27 },
+ {0x35E6, 0x28 },
+ {0x35E7, 0x76 },
+ {0x35E8, 0x2A },
+ {0x35E9, 0x15 },
+ {0x35EA, 0x2D },
+ {0x35EB, 0x07 },
+ {0x35EC, 0x04 },
+ {0x35ED, 0x43 },
+ {0x35EE, 0x05 },
+ {0x35EF, 0xA9 },
+ {0x35F0, 0x17 },
+ {0x35F1, 0x41 },
+ {0x35F2, 0x24 },
+ {0x35F3, 0x88 },
+ {0x35F4, 0x01 },
+ {0x35F5, 0x54 },
+ {0x35F6, 0x01 },
+ {0x35F7, 0x55 },
+ {0x35F8, 0x2E },
+ {0x35F9, 0xF2 },
+ {0x35FA, 0x06 },
+ {0x35FB, 0x02 },
+ {0x35FC, 0x06 },
+ {0x35FD, 0x03 },
+ {0x35FE, 0x06 },
+ {0x35FF, 0x04 },
+ {0x3600, 0x0F },
+ {0x3601, 0x48 },
+ {0x3602, 0x0F },
+ {0x3603, 0x49 },
+ {0x3604, 0x0F },
+ {0x3605, 0x4A },
+ {0x35C2, 0xFF },
+ {0x35C3, 0xFF },
+ {0x35C4, 0xFF },
+ {0x35C5, 0xC0 },
+ {0x35C0, 0x01 },
+
+
+ {0xa098, 0x02 },
+ {0xa099, 0x87 },
+ {0xa09c, 0x00 },
+ {0xa09d, 0xc5 },
+ {0xa4ec, 0x05 },
+ {0xa4ed, 0x05 },
+ {0xa4f0, 0x04 },
+ {0xa4f1, 0x04 },
+ {0xa4f4, 0x04 },
+ {0xa4f5, 0x05 },
+ {0xa4f8, 0x05 },
+ {0xa4f9, 0x07 },
+ {0xa4fc, 0x07 },
+ {0xa4fd, 0x07 },
+ {0xa500, 0x07 },
+ {0xa501, 0x07 },
+ {0xa504, 0x08 },
+ {0xa505, 0x08 },
+ {0xa518, 0x01 },
+ {0xa519, 0x02 },
+ {0xa51c, 0x01 },
+ {0xa51d, 0x00 },
+ {0xa534, 0x00 },
+ {0xa535, 0x04 },
+ {0xa538, 0x04 },
+ {0xa539, 0x03 },
+ {0xa53c, 0x05 },
+ {0xa53d, 0x07 },
+ {0xa540, 0x07 },
+ {0xa541, 0x06 },
+ {0xa544, 0x07 },
+ {0xa545, 0x06 },
+ {0xa548, 0x05 },
+ {0xa549, 0x06 },
+ {0xa54c, 0x06 },
+ {0xa54d, 0x07 },
+ {0xa550, 0x07 },
+ {0xa551, 0x04 },
+ {0xa554, 0x04 },
+ {0xa555, 0x04 },
+ {0xa558, 0x05 },
+ {0xa559, 0x06 },
+ {0xa55c, 0x07 },
+ {0xa55d, 0x07 },
+ {0xa56c, 0x00 },
+ {0xa56d, 0x0a },
+ {0xa570, 0x08 },
+ {0xa571, 0x05 },
+ {0xa574, 0x04 },
+ {0xa575, 0x03 },
+ {0xa578, 0x04 },
+ {0xa579, 0x04 },
+ {0xa58c, 0x1f },
+ {0xa58d, 0x1b },
+ {0xa590, 0x17 },
+ {0xa591, 0x13 },
+ {0xa594, 0x10 },
+ {0xa595, 0x0d },
+ {0xa598, 0x0f },
+ {0xa599, 0x11 },
+ {0xa59c, 0x03 },
+ {0xa59d, 0x03 },
+ {0xa5a0, 0x03 },
+ {0xa5a1, 0x03 },
+ {0xa5a4, 0x03 },
+ {0xa5a5, 0x04 },
+ {0xa5a8, 0x05 },
+ {0xa5a9, 0x00 },
+ {0xa5ac, 0x00 },
+ {0xa5ad, 0x00 },
+ {0xa5b0, 0x00 },
+ {0xa5b1, 0x00 },
+ {0xa5b4, 0x00 },
+ {0xa5b5, 0x00 },
+ {0xa5c4, 0x1f },
+ {0xa5c5, 0x13 },
+ {0xa5c8, 0x14 },
+ {0xa5c9, 0x14 },
+ {0xa5cc, 0x14 },
+ {0xa5cd, 0x13 },
+ {0xa5d0, 0x17 },
+ {0xa5d1, 0x1a },
+ {0xa5f4, 0x05 },
+ {0xa5f5, 0x05 },
+ {0xa5f8, 0x05 },
+ {0xa5f9, 0x06 },
+ {0xa5fc, 0x06 },
+ {0xa5fd, 0x06 },
+ {0xa600, 0x06 },
+ {0xa601, 0x06 },
+ {0xa608, 0x07 },
+ {0xa609, 0x08 },
+ {0xa60c, 0x08 },
+ {0xa60d, 0x07 },
+ {0xa63c, 0x00 },
+ {0xa63d, 0x02 },
+ {0xa640, 0x02 },
+ {0xa641, 0x02 },
+ {0xa644, 0x02 },
+ {0xa645, 0x02 },
+ {0xa648, 0x03 },
+ {0xa649, 0x04 },
+ {0xa64c, 0x0a },
+ {0xa64d, 0x09 },
+ {0xa650, 0x08 },
+ {0xa651, 0x09 },
+ {0xa654, 0x09 },
+ {0xa655, 0x0a },
+ {0xa658, 0x0a },
+ {0xa659, 0x0a },
+ {0xa65c, 0x0a },
+ {0xa65d, 0x09 },
+ {0xa660, 0x09 },
+ {0xa661, 0x09 },
+ {0xa664, 0x09 },
+ {0xa665, 0x08 },
+ {0xa680, 0x01 },
+ {0xa681, 0x02 },
+ {0xa694, 0x1f },
+ {0xa695, 0x10 },
+ {0xa698, 0x0e },
+ {0xa699, 0x0c },
+ {0xa69c, 0x0d },
+ {0xa69d, 0x0d },
+ {0xa6a0, 0x0f },
+ {0xa6a1, 0x11 },
+ {0xa6a4, 0x00 },
+ {0xa6a5, 0x00 },
+ {0xa6a8, 0x00 },
+ {0xa6a9, 0x00 },
+ {0xa6ac, 0x00 },
+ {0xa6ad, 0x00 },
+ {0xa6b0, 0x00 },
+ {0xa6b1, 0x04 },
+ {0xa6b4, 0x04 },
+ {0xa6b5, 0x04 },
+ {0xa6b8, 0x04 },
+ {0xa6b9, 0x04 },
+ {0xa6bc, 0x05 },
+ {0xa6bd, 0x05 },
+ {0xa6c0, 0x1f },
+ {0xa6c1, 0x1f },
+ {0xa6c4, 0x1f },
+ {0xa6c5, 0x1f },
+ {0xa6c8, 0x1f },
+ {0xa6c9, 0x1f },
+ {0xa6cc, 0x1f },
+ {0xa6cd, 0x0b },
+ {0xa6d0, 0x0c },
+ {0xa6d1, 0x0d },
+ {0xa6d4, 0x0d },
+ {0xa6d5, 0x0d },
+ {0xa6d8, 0x11 },
+ {0xa6d9, 0x14 },
+ {0xa6fc, 0x02 },
+ {0xa6fd, 0x03 },
+ {0xa700, 0x03 },
+ {0xa701, 0x03 },
+ {0xa704, 0x03 },
+ {0xa705, 0x04 },
+ {0xa708, 0x05 },
+ {0xa709, 0x02 },
+ {0xa70c, 0x02 },
+ {0xa70d, 0x02 },
+ {0xa710, 0x03 },
+ {0xa711, 0x04 },
+ {0xa714, 0x04 },
+ {0xa715, 0x04 },
+ {0xa744, 0x00 },
+ {0xa745, 0x03 },
+ {0xa748, 0x04 },
+ {0xa749, 0x04 },
+ {0xa74c, 0x05 },
+ {0xa74d, 0x06 },
+ {0xa750, 0x07 },
+ {0xa751, 0x07 },
+ {0xa754, 0x05 },
+ {0xa755, 0x05 },
+ {0xa758, 0x05 },
+ {0xa759, 0x05 },
+ {0xa75c, 0x05 },
+ {0xa75d, 0x06 },
+ {0xa760, 0x07 },
+ {0xa761, 0x07 },
+ {0xa764, 0x06 },
+ {0xa765, 0x05 },
+ {0xa768, 0x05 },
+ {0xa769, 0x05 },
+ {0xa76c, 0x06 },
+ {0xa76d, 0x07 },
+ {0xa77c, 0x00 },
+ {0xa77d, 0x05 },
+ {0xa780, 0x05 },
+ {0xa781, 0x05 },
+ {0xa784, 0x05 },
+ {0xa785, 0x04 },
+ {0xa788, 0x05 },
+ {0xa789, 0x06 },
+ {0xa79c, 0x1f },
+ {0xa79d, 0x15 },
+ {0xa7a0, 0x13 },
+ {0xa7a1, 0x10 },
+ {0xa7a4, 0x0f },
+ {0xa7a5, 0x0d },
+ {0xa7a8, 0x11 },
+ {0xa7a9, 0x14 },
+ {0xa7ac, 0x02 },
+ {0xa7ad, 0x02 },
+ {0xa7b0, 0x02 },
+ {0xa7b1, 0x02 },
+ {0xa7b4, 0x02 },
+ {0xa7b5, 0x03 },
+ {0xa7b8, 0x03 },
+ {0xa7b9, 0x00 },
+ {0xa7bc, 0x00 },
+ {0xa7bd, 0x00 },
+ {0xa7c0, 0x00 },
+ {0xa7c1, 0x00 },
+ {0xa7c4, 0x00 },
+ {0xa7c5, 0x00 },
+ {0xa7d4, 0x1f },
+ {0xa7d5, 0x0d },
+ {0xa7d8, 0x0f },
+ {0xa7d9, 0x10 },
+ {0xa7dc, 0x10 },
+ {0xa7dd, 0x10 },
+ {0xa7e0, 0x13 },
+ {0xa7e1, 0x16 },
+ {0xa7f4, 0x00 },
+ {0xa7f5, 0x03 },
+ {0xa7f8, 0x04 },
+ {0xa7f9, 0x04 },
+ {0xa7fc, 0x04 },
+ {0xa7fd, 0x03 },
+ {0xa800, 0x03 },
+ {0xa801, 0x03 },
+ {0xa804, 0x03 },
+ {0xa805, 0x03 },
+ {0xa808, 0x03 },
+ {0xa809, 0x03 },
+ {0xa80c, 0x03 },
+ {0xa80d, 0x04 },
+ {0xa810, 0x04 },
+ {0xa811, 0x0a },
+ {0xa814, 0x0a },
+ {0xa815, 0x0a },
+ {0xa818, 0x0f },
+ {0xa819, 0x14 },
+ {0xa81c, 0x14 },
+ {0xa81d, 0x14 },
+ {0xa82c, 0x00 },
+ {0xa82d, 0x04 },
+ {0xa830, 0x02 },
+ {0xa831, 0x00 },
+ {0xa834, 0x00 },
+ {0xa835, 0x00 },
+ {0xa838, 0x00 },
+ {0xa839, 0x00 },
+ {0xa840, 0x1f },
+ {0xa841, 0x1f },
+ {0xa848, 0x1f },
+ {0xa849, 0x1f },
+ {0xa84c, 0x1f },
+ {0xa84d, 0x0c },
+ {0xa850, 0x0c },
+ {0xa851, 0x0c },
+ {0xa854, 0x0c },
+ {0xa855, 0x0c },
+ {0xa858, 0x0c },
+ {0xa859, 0x0c },
+ {0xa85c, 0x0c },
+ {0xa85d, 0x0c },
+ {0xa860, 0x0c },
+ {0xa861, 0x0c },
+ {0xa864, 0x0c },
+ {0xa865, 0x0c },
+ {0xa868, 0x0c },
+ {0xa869, 0x0c },
+ {0xa86c, 0x0c },
+ {0xa86d, 0x0c },
+ {0xa870, 0x0c },
+ {0xa871, 0x0c },
+ {0xa874, 0x0c },
+ {0xa875, 0x0c },
+ {0xa878, 0x1f },
+ {0xa879, 0x1f },
+ {0xa87c, 0x1f },
+ {0xa87d, 0x1f },
+ {0xa880, 0x1f },
+ {0xa881, 0x1f },
+ {0xa884, 0x1f },
+ {0xa885, 0x0c },
+ {0xa888, 0x0c },
+ {0xa889, 0x0c },
+ {0xa88c, 0x0c },
+ {0xa88d, 0x0c },
+ {0xa890, 0x0c },
+ {0xa891, 0x0c },
+ {0xa898, 0x1f },
+ {0xa899, 0x1f },
+ {0xa8a0, 0x1f },
+ {0xa8a1, 0x1f },
+ {0xa8a4, 0x1f },
+ {0xa8a5, 0x0c },
+ {0xa8a8, 0x0c },
+ {0xa8a9, 0x0c },
+ {0xa8ac, 0x0c },
+ {0xa8ad, 0x0c },
+ {0xa8b0, 0x0c },
+ {0xa8b1, 0x0c },
+ {0xa8b4, 0x0c },
+ {0xa8b5, 0x0c },
+ {0xa8b8, 0x0c },
+ {0xa8b9, 0x0c },
+ {0xa8bc, 0x0c },
+ {0xa8bd, 0x0c },
+ {0xa8c0, 0x0c },
+ {0xa8c1, 0x0c },
+ {0xa8c4, 0x0c },
+ {0xa8c5, 0x0c },
+ {0xa8c8, 0x0c },
+ {0xa8c9, 0x0c },
+ {0xa8cc, 0x0c },
+ {0xa8cd, 0x0c },
+ {0xa8d0, 0x1f },
+ {0xa8d1, 0x1f },
+ {0xa8d4, 0x1f },
+ {0xa8d5, 0x1f },
+ {0xa8d8, 0x1f },
+ {0xa8d9, 0x1f },
+ {0xa8dc, 0x1f },
+ {0xa8dd, 0x0c },
+ {0xa8e0, 0x0c },
+ {0xa8e1, 0x0c },
+ {0xa8e4, 0x0c },
+ {0xa8e5, 0x0c },
+ {0xa8e8, 0x0c },
+ {0xa8e9, 0x0c },
+ {0xa8f0, 0x1f },
+ {0xa8f1, 0x1f },
+ {0xa8f8, 0x1f },
+ {0xa8f9, 0x1f },
+ {0xa8fc, 0x1f },
+ {0xa8fd, 0x0c },
+ {0xa900, 0x0c },
+ {0xa901, 0x0c },
+ {0xa904, 0x0c },
+ {0xa905, 0x0c },
+ {0xa908, 0x0c },
+ {0xa909, 0x0c },
+ {0xa90c, 0x0c },
+ {0xa90d, 0x0c },
+ {0xa910, 0x0c },
+ {0xa911, 0x0c },
+ {0xa914, 0x0c },
+ {0xa915, 0x0c },
+ {0xa918, 0x0c },
+ {0xa919, 0x0c },
+ {0xa91c, 0x0c },
+ {0xa91d, 0x0c },
+ {0xa920, 0x0c },
+ {0xa921, 0x0c },
+ {0xa924, 0x0c },
+ {0xa925, 0x0c },
+ {0xa928, 0x1f },
+ {0xa929, 0x1f },
+ {0xa92c, 0x1f },
+ {0xa92d, 0x1f },
+ {0xa930, 0x1f },
+ {0xa931, 0x1f },
+ {0xa934, 0x1f },
+ {0xa935, 0x0c },
+ {0xa938, 0x0c },
+ {0xa939, 0x0c },
+ {0xa93c, 0x0c },
+ {0xa93d, 0x0c },
+ {0xa940, 0x0c },
+ {0xa941, 0x0c },
+ {0xa96c, 0x0d },
+ {0xa96d, 0x16 },
+ {0xa970, 0x19 },
+ {0xa971, 0x0e },
+ {0xa974, 0x16 },
+ {0xa975, 0x1a },
+ {0xa978, 0x0d },
+ {0xa979, 0x15 },
+ {0xa97c, 0x19 },
+ {0xa97d, 0x0d },
+ {0xa980, 0x15 },
+ {0xa981, 0x1a },
+ {0xa984, 0x0d },
+ {0xa985, 0x15 },
+ {0xa988, 0x1a },
+ {0xa989, 0x0d },
+ {0xa98c, 0x15 },
+ {0xa98d, 0x1a },
+ {0xa990, 0x0b },
+ {0xa991, 0x11 },
+ {0xa994, 0x02 },
+ {0xa995, 0x0e },
+ {0xa998, 0x16 },
+ {0xa999, 0x02 },
+ {0xa99c, 0x0c },
+ {0xa99d, 0x13 },
+ {0xa9a0, 0x02 },
+ {0xa9a1, 0x0c },
+ {0xa9a4, 0x12 },
+ {0xa9a5, 0x02 },
+ {0xa9a8, 0x0c },
+ {0xa9a9, 0x12 },
+ {0xa9ac, 0x02 },
+ {0xa9ad, 0x0c },
+ {0xa9b0, 0x12 },
+ {0xa9b1, 0x02 },
+ {0xa9b4, 0x10 },
+ {0xa9b5, 0x1e },
+ {0xa9b8, 0x0f },
+ {0xa9b9, 0x13 },
+ {0xa9bc, 0x20 },
+ {0xa9bd, 0x10 },
+ {0xa9c0, 0x11 },
+ {0xa9c1, 0x1e },
+ {0xa9c4, 0x10 },
+ {0xa9c5, 0x11 },
+ {0xa9c8, 0x1e },
+ {0xa9c9, 0x10 },
+ {0xa9cc, 0x11 },
+ {0xa9cd, 0x20 },
+ {0xa9d0, 0x10 },
+ {0xa9d1, 0x13 },
+ {0xa9d4, 0x24 },
+ {0xa9d5, 0x10 },
+ {0xa9f0, 0x02 },
+ {0xa9f1, 0x01 },
+ {0xa9f8, 0x19 },
+ {0xa9f9, 0x0b },
+ {0xa9fc, 0x0a },
+ {0xa9fd, 0x07 },
+ {0xaa00, 0x0c },
+ {0xaa01, 0x0e },
+ {0xaa08, 0x0c },
+ {0xaa09, 0x06 },
+ {0xaa0c, 0x0c },
+ {0xaa0d, 0x0a },
+ {0xaa24, 0x10 },
+ {0xaa25, 0x12 },
+ {0xaa28, 0x0b },
+ {0xaa29, 0x07 },
+ {0xaa2c, 0x10 },
+ {0xaa2d, 0x14 },
+ {0xaa34, 0x0e },
+ {0xaa35, 0x0e },
+ {0xaa38, 0x07 },
+ {0xaa39, 0x07 },
+ {0xaa3c, 0x0e },
+ {0xaa3d, 0x0c },
+ {0xaa48, 0x09 },
+ {0xaa49, 0x0c },
+ {0xaa4c, 0x0c },
+ {0xaa4d, 0x07 },
+ {0xaa54, 0x08 },
+ {0xaa55, 0x06 },
+ {0xaa58, 0x04 },
+ {0xaa59, 0x05 },
+ {0xaa5c, 0x06 },
+ {0xaa5d, 0x06 },
+ {0xaa68, 0x05 },
+ {0xaa69, 0x05 },
+ {0xaa6c, 0x04 },
+ {0xaa6d, 0x05 },
+ {0xaa74, 0x06 },
+ {0xaa75, 0x04 },
+ {0xaa78, 0x05 },
+ {0xaa79, 0x05 },
+ {0xaa7c, 0x04 },
+ {0xaa7d, 0x06 },
+ {0xac18, 0x14 },
+ {0xac19, 0x00 },
+ {0xac1c, 0x14 },
+ {0xac1d, 0x00 },
+ {0xac20, 0x14 },
+ {0xac21, 0x00 },
+ {0xac24, 0x14 },
+ {0xac25, 0x00 },
+ {0xac28, 0x14 },
+ {0xac29, 0x00 },
+ {0xac2c, 0x14 },
+ {0xac2d, 0x00 },
+ {0xac34, 0x16 },
+ {0xac35, 0x00 },
+ {0xac38, 0x16 },
+ {0xac39, 0x00 },
+ {0xac3c, 0x16 },
+ {0xac3d, 0x00 },
+ {0xac40, 0x16 },
+ {0xac41, 0x00 },
+ {0xac44, 0x16 },
+ {0xac45, 0x00 },
+ {0xac48, 0x16 },
+ {0xac49, 0x00 },
+ {0xac50, 0x1b },
+ {0xac51, 0x00 },
+ {0xac54, 0x1b },
+ {0xac55, 0x00 },
+ {0xac58, 0x1b },
+ {0xac59, 0x00 },
+ {0xac5c, 0x1b },
+ {0xac5d, 0x00 },
+ {0xac60, 0x1b },
+ {0xac61, 0x00 },
+ {0xac64, 0x1b },
+ {0xac65, 0x00 },
+ {0xac74, 0x09 },
+ {0xac75, 0x0c },
+ {0xac78, 0x0f },
+ {0xac79, 0x11 },
+ {0xac7c, 0x12 },
+ {0xac7d, 0x14 },
+ {0xac80, 0x09 },
+ {0xac81, 0x0c },
+ {0xac84, 0x0f },
+ {0xac85, 0x11 },
+ {0xac88, 0x12 },
+ {0xac89, 0x14 },
+ {0xac8c, 0x09 },
+ {0xac8d, 0x0c },
+ {0xac90, 0x0f },
+ {0xac91, 0x11 },
+ {0xac94, 0x12 },
+ {0xac95, 0x14 },
+ {0xac98, 0x09 },
+ {0xac99, 0x0c },
+ {0xac9c, 0x0f },
+ {0xac9d, 0x11 },
+ {0xaca0, 0x12 },
+ {0xaca1, 0x14 },
+ {0xaca4, 0x09 },
+ {0xaca5, 0x0c },
+ {0xaca8, 0x0f },
+ {0xaca9, 0x11 },
+ {0xacac, 0x12 },
+ {0xacad, 0x14 },
+ {0xacb0, 0x07 },
+ {0xacb1, 0x09 },
+ {0xacb4, 0x0c },
+ {0xacb5, 0x0d },
+ {0xacb8, 0x0d },
+ {0xacb9, 0x0e },
+ {0xacbc, 0x05 },
+ {0xacbd, 0x07 },
+ {0xacc0, 0x0a },
+ {0xacc1, 0x0b },
+ {0xacc4, 0x0b },
+ {0xacc5, 0x0c },
+ {0xacc8, 0x03 },
+ {0xacc9, 0x04 },
+ {0xaccc, 0x07 },
+ {0xaccd, 0x08 },
+ {0xacd0, 0x09 },
+ {0xacd1, 0x09 },
+ {0x35B5, 0x01 },
+ {0x35BC, 0x01 },
+ {0x360A, 0x02 },
+ {0xFA9B, 0x01 },
+};
+
+#define NUM_LSC_CAST_REGS 33
+
+enum LSC_Cast_t{
+ cast_H = 0,
+ cast_U30,
+ cast_CW,
+ cast_D,
+ cast_MAX
+};
+
+static short int LSC_CorrectionForCast[cast_MAX][NUM_LSC_CAST_REGS] = {
+ {-30, -20, 8, 11, -16, -26, -35, -53, -9, -10, 44, 57, -39,
+ -14, 50, -173, -38, -32, -1, 9, 39, 51, -33, -49, -28,
+ -22, 7, 11, -21, 17, -62, -56, 0},
+ {-29, -18, 6, 1, 17, -35, -77, 0, 5, -17, -6, -22, -41, -1,
+ -37, 83, -38, -32, 1, -2, 15, 25, -67, 19, -28, -22, 5,
+ 2, -18, 21, -86, 0, 0},
+ {-10, -15, -4, -6, -8, -3, -63, 8, 25, -9, -39, -51, -9,
+ 0, -21, 112, -10, -23, -7, -9, 10, 18, -11, 23, -10,
+ -15, -4, -6, -10, -3, -52, 7, 0},
+ { 5, 3, -4, -5, -1, 3, 4, 8, 12, 3, -22, -21, 7, 17,
+ 2, 35, 8, 2, -3, -2, -9, -5, 10, 4, 9, 2, -4, -5,
+ -2, 0, -6, 9, 0}
+};
+
+static unsigned short LSC_CastRegs[] = {
+ 0xFB7E, /* H */
+ 0xFB3C, /* U30 */
+ 0xFAFA, /* CW */
+ 0xFAB8 /* D65 */
+};
+
+/*=============================================================*/
+
+static int vx6953_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 = 2,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(vx6953_client->adapter, msgs, 2) < 0) {
+ CDBG("vx6953_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+static int32_t vx6953_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(vx6953_client->adapter, msg, 1) < 0) {
+ CDBG("vx6953_i2c_txdata faild 0x%x\n", vx6953_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int32_t vx6953_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = vx6953_i2c_rxdata(vx6953_client->addr>>1, buf, rlen);
+ if (rc < 0) {
+ CDBG("vx6953_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ return rc;
+}
+static int32_t vx6953_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+static int32_t vx6953_i2c_write_w_sensor(unsigned short waddr, uint16_t wdata)
+{
+ int32_t rc = -EFAULT;
+ 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);
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, wdata);
+ rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 4);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, wdata);
+ }
+ return rc;
+}
+static int32_t vx6953_i2c_write_seq_sensor(unsigned short waddr,
+ uint8_t *bdata, uint16_t len)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[len+2];
+ int i;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ for (i = 2; i < len+2; i++)
+ buf[i] = *bdata++;
+ rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, len+2);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata[0]);
+ }
+ return rc;
+}
+
+static int32_t vx6953_i2c_write_w_table(struct vx6953_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = vx6953_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static void vx6953_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+ uint16_t preview_line_length_pck, snapshot_line_length_pck;
+ uint32_t divider, d1, d2;
+ /* Total frame_length_lines and line_length_pck for preview */
+ preview_frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ preview_line_length_pck = VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ /* Total frame_length_lines and line_length_pck for snapshot */
+ snapshot_frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+ snapshot_line_length_pck = VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+ d1 = preview_frame_length_lines * 0x00000400/
+ snapshot_frame_length_lines;
+ d2 = preview_line_length_pck * 0x00000400/
+ snapshot_line_length_pck;
+ divider = d1 * d2 / 0x400;
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+ /* 2 is the ratio of no.of snapshot channels
+ to number of preview channels */
+
+}
+
+static uint16_t vx6953_get_prev_lines_pf(void)
+{
+ if (vx6953_ctrl->prev_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_HEIGHT + VX6953_VER_QTR_BLK_LINES;
+ else
+ return VX6953_FULL_SIZE_HEIGHT + VX6953_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t vx6953_get_prev_pixels_pl(void)
+{
+ if (vx6953_ctrl->prev_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_WIDTH + VX6953_HRZ_QTR_BLK_PIXELS;
+ else
+ return VX6953_FULL_SIZE_WIDTH + VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t vx6953_get_pict_lines_pf(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ else
+ return VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+}
+
+static uint16_t vx6953_get_pict_pixels_pl(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ else
+ return VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t vx6953_get_pict_max_exp_lc(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return (VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES)*24;
+ else
+ return (VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t vx6953_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ total_lines_per_frame = (uint16_t)((VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES) * vx6953_ctrl->fps_divider/0x400);
+
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ ((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+ return rc;
+ if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+ return rc;
+}
+
+static int32_t vx6953_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t line_length_pck, frame_length_lines;
+ uint8_t gain_hi, gain_lo;
+ uint8_t intg_time_hi, intg_time_lo;
+ uint8_t frame_length_lines_hi = 0, frame_length_lines_lo = 0;
+ int32_t rc = 0;
+ if (vx6953_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ line_length_pck = VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ if (line > (frame_length_lines -
+ VX6953_STM5M0EDOF_OFFSET)) {
+ vx6953_ctrl->fps = (uint16_t) (30 * Q8 *
+ (frame_length_lines - VX6953_STM5M0EDOF_OFFSET)/
+ line);
+ } else {
+ vx6953_ctrl->fps = (uint16_t) (30 * Q8);
+ }
+ } else {
+ frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+ line_length_pck = VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+ }
+
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ if ((line + VX6953_STM5M0EDOF_OFFSET) > MAX_FRAME_LENGTH_LINES) {
+ frame_length_lines = MAX_FRAME_LENGTH_LINES;
+ line = MAX_FRAME_LENGTH_LINES - VX6953_STM5M0EDOF_OFFSET;
+ } else if ((line + VX6953_STM5M0EDOF_OFFSET) > frame_length_lines) {
+ frame_length_lines = line + VX6953_STM5M0EDOF_OFFSET;
+ line = frame_length_lines;
+ }
+
+ frame_length_lines_hi = (uint8_t) ((frame_length_lines &
+ 0xFF00) >> 8);
+ frame_length_lines_lo = (uint8_t) (frame_length_lines &
+ 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ frame_length_lines_hi);
+ vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ frame_length_lines_lo);
+
+ /* update analogue gain registers */
+ gain_hi = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lo = (uint8_t) (gain & 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ gain_lo);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_R_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_RED_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_BLUE_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_B_LO, gain_hi);
+ CDBG("%s, gain_hi 0x%x, gain_lo 0x%x\n", __func__,
+ gain_hi, gain_lo);
+ /* update line count registers */
+ intg_time_hi = (uint8_t) (((uint16_t)line & 0xFF00) >> 8);
+ intg_time_lo = (uint8_t) ((uint16_t)line & 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+ intg_time_hi);
+ vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+ intg_time_lo);
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+
+ return rc;
+}
+
+static int32_t vx6953_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = vx6953_write_exp_gain(gain, line);
+ return rc;
+} /* endof vx6953_set_pict_exp_gain*/
+
+static int32_t vx6953_move_focus(int direction,
+ int32_t num_steps)
+{
+ return 0;
+}
+
+
+static int32_t vx6953_set_default_focus(uint8_t af_step)
+{
+ return 0;
+}
+
+static int32_t vx6953_test(enum vx6953_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+ 1: Bypass Signal Processing
+ REG_0x30D8[5] is EBDMASK: 0:
+ Output Embedded data, 1: No output embedded data */
+ if (vx6953_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int vx6953_enable_edof(enum edof_mode_t edof_mode)
+{
+ int rc = 0;
+ if (edof_mode == VX6953_EDOF_ESTIMATION) {
+ /* EDof Estimation mode for preview */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x02) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_ESTIMATION");
+ } else if (edof_mode == VX6953_EDOF_APPLICATION) {
+ /* EDof Application mode for Capture */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x01) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_APPLICATION");
+ } else {
+ /* EDOF disabled */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x00) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_DISABLE");
+ }
+ return rc;
+}
+
+static int32_t vx6953_patch_for_cut2(void)
+{
+ int32_t rc = 0;
+ rc = vx6953_i2c_write_w_table(patch_tbl_cut2,
+ ARRAY_SIZE(patch_tbl_cut2));
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int32_t vx6953_lsc_patch(void)
+{
+ int32_t rc = 0;
+ int i, j;
+ short int v;
+ unsigned short version = 0;
+ unsigned short LSC_Raw[NUM_LSC_CAST_REGS];
+ unsigned short LSC_Fixed[NUM_LSC_CAST_REGS];
+
+ vx6953_i2c_read(0x10, &version, 1);
+ CDBG("Cut 3 Version %d\n", version);
+ if (version != 1)
+ return 0;
+
+ vx6953_i2c_write_b_sensor(0x3640, 0x00);
+ for (j = cast_H; j < cast_MAX; j++) {
+ for (i = 0; i < NUM_LSC_CAST_REGS; i++) {
+ rc = vx6953_i2c_read(LSC_CastRegs[cast_D]+(2*i),
+ &LSC_Raw[i], 2);
+ if (rc < 0)
+ return rc;
+ v = LSC_Raw[i];
+ v += LSC_CorrectionForCast[j][i];
+ LSC_Fixed[i] = (unsigned short) v;
+ }
+ for (i = 0; i < NUM_LSC_CAST_REGS; i++) {
+ rc = vx6953_i2c_write_w_sensor(LSC_CastRegs[j]+(2*i),
+ LSC_Fixed[i]);
+ if (rc < 0)
+ return rc;
+ }
+ }
+ CDBG("vx6953_lsc_patch done\n");
+ return rc;
+}
+
+static int32_t vx6953_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ unsigned short frame_cnt;
+ struct msm_camera_csi_params vx6953_csi_params;
+ if (vx6953_ctrl->sensor_type != VX6953_STM5M0EDOF_CUT_2) {
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {0x303, 0x01},
+ {0x30b, 0x01},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_0x3210, 0x01},
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b00},
+ {REG_0x0136,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0136},
+ {REG_0x0137,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0137},
+ {REG_0x0b06,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b06},
+ {REG_0x0b07,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b07},
+ {REG_0x0b08,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b08},
+ {REG_0x0b09,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b09},
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b84},
+ {REG_0x0b85,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b85},
+ {REG_0x0b88,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b88},
+ {REG_0x0b89,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].
+ reg_0x0b8a},
+ {0x3393, 0x06},
+ {0x3394, 0x07},
+ {0x338d, 0x08},
+ {0x338e, 0x08},
+ {0x338f, 0x00},
+ };
+ /* reset fps_divider */
+ vx6953_ctrl->fps = 30 * Q8;
+ /* stop streaming */
+
+ count = 0;
+ CDBG("Init vx6953_sensor_setting standby\n");
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+
+ rc = vx6953_i2c_write_w_table(cut3_cali_data,
+ ARRAY_SIZE(cut3_cali_data));
+
+ vx6953_lsc_patch();
+
+ vx6953_i2c_write_w_sensor(0x100A, 0x07A3);
+ vx6953_i2c_write_w_sensor(0x114A, 0x002A);
+ vx6953_i2c_write_w_sensor(0x1716, 0x0204);
+ vx6953_i2c_write_w_sensor(0x1718, 0x0880);
+
+ rc = vx6953_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(10);
+
+ }
+ return rc;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf preview_mode_tbl[] = {
+ {0x200, 0x02},
+ {0x201, 0x26},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0387},
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034f},
+ {REG_0x3640, 0x00},
+ };
+
+ struct vx6953_i2c_reg_conf snapshot_mode_tbl[] = {
+ {0x0200, 0x02},
+ {0x0201, 0x54},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].
+ reg_0x0387},
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].
+ reg_0x034f},
+ {0x3140, 0x01},
+ {REG_0x3640, 0x00},
+ };
+ /* stop streaming */
+
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ if (count == 0) {
+ vx6953_csi_params.data_format = CSI_8BIT;
+ vx6953_csi_params.lane_cnt = 1;
+ vx6953_csi_params.lane_assign = 0xe4;
+ vx6953_csi_params.dpcm_scheme = 0;
+ vx6953_csi_params.settle_cnt = 7;
+ rc = msm_camio_csi_config(&vx6953_csi_params);
+ if (rc < 0)
+ CDBG("config csi controller failed\n");
+
+ msleep(20);
+ count = 1;
+ }
+
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+
+ if (rt == RES_PREVIEW) {
+ rc = vx6953_i2c_write_w_table(
+ &preview_mode_tbl[0],
+ ARRAY_SIZE(preview_mode_tbl));
+ if (rc < 0)
+ return rc;
+ }
+ if (rt == RES_CAPTURE) {
+ rc = vx6953_i2c_write_w_table(
+ &snapshot_mode_tbl[0],
+ ARRAY_SIZE(snapshot_mode_tbl));
+ if (rc < 0)
+ return rc;
+ }
+
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+
+ /* Start sensor streaming */
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+ msleep(10);
+
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+
+ while (frame_cnt == 0xFF) {
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+ CDBG("frame_cnt=%d\n", frame_cnt);
+ msleep(2);
+ }
+ }
+ return rc;
+ default:
+ return rc;
+ }
+ } else {
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {REG_0x3016,
+ vx6953_regs.reg_pat_init[0].reg_0x3016},
+ {REG_0x301d,
+ vx6953_regs.reg_pat_init[0].reg_0x301d},
+ {REG_0x317e,
+ vx6953_regs.reg_pat_init[0].reg_0x317e},
+ {REG_0x317f,
+ vx6953_regs.reg_pat_init[0].reg_0x317f},
+ {REG_0x3400,
+ vx6953_regs.reg_pat_init[0].reg_0x3400},
+ /* DEFCOR settings */
+ /*Single Defect Correction Weight DISABLE*/
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ {REG_0x1716,
+ vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717,
+ vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718,
+ vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719,
+ vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ /* reset fps_divider */
+ vx6953_ctrl->fps = 30 * Q8;
+ /* stop streaming */
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ CDBG("Init vx6953_sensor_setting standby\n");
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+ vx6953_patch_for_cut2();
+ rc = vx6953_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+ }
+ return rc;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_mode_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {REG_0x3016,
+ vx6953_regs.reg_pat_init[0].reg_0x3016},
+ {REG_0x301d,
+ vx6953_regs.reg_pat_init[0].reg_0x301d},
+ {REG_0x317e,
+ vx6953_regs.reg_pat_init[0].reg_0x317e},
+ {REG_0x317f,
+ vx6953_regs.reg_pat_init[0].reg_0x317f},
+ {REG_0x3400,
+ vx6953_regs.reg_pat_init[0].reg_0x3400},
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ {REG_0x1716,
+ vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717,
+ vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718,
+ vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719,
+ vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ struct vx6953_i2c_reg_conf mode_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].line_length_pck_lo},
+ {REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036, vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture
+ mode(standard settings - Not tuned) */
+ {REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c, vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d, vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e, vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f, vx6953_regs.reg_pat[rt].reg_0x034f},
+ /*{0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+ {0x201, vx6953_regs.reg_pat[rt].reg_0x0201},*/
+ {REG_0x1716, vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717, vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718, vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719, vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ /* stop streaming */
+ msleep(5);
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+
+ vx6953_csi_params.data_format = CSI_8BIT;
+ vx6953_csi_params.lane_cnt = 1;
+ vx6953_csi_params.lane_assign = 0xe4;
+ vx6953_csi_params.dpcm_scheme = 0;
+ vx6953_csi_params.settle_cnt = 7;
+ rc = msm_camio_csi_config(&vx6953_csi_params);
+ if (rc < 0)
+ CDBG(" config csi controller failed \n");
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_patch_for_cut2();
+ rc = vx6953_i2c_write_w_table(&init_mode_tbl[0],
+ ARRAY_SIZE(init_mode_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ rc = vx6953_i2c_write_w_table(&mode_tbl[0],
+ ARRAY_SIZE(mode_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ /* Start sensor streaming */
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stream);
+
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+
+ while (frame_cnt == 0xFF) {
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+ CDBG("frame_cnt=%d", frame_cnt);
+ msleep(10);
+ }
+ }
+ return rc;
+ default:
+ return rc;
+ }
+ }
+ return rc;
+}
+
+
+static int32_t vx6953_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (vx6953_ctrl->curr_res != vx6953_ctrl->prev_res) {
+ if (vx6953_ctrl->prev_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+ if (vx6953_ctrl->set_test) {
+ if (vx6953_test(vx6953_ctrl->set_test) < 0)
+ return rc;
+ }
+ vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+ rc = vx6953_enable_edof(vx6953_ctrl->edof_mode);
+ if (rc < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->prev_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t vx6953_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /*change sensor resolution if needed */
+ if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+ if (vx6953_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+ if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int32_t vx6953_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+ if (vx6953_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider)/
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider)/
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+ vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+ if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+} /*end of vx6953_raw_snapshot_config*/
+static int32_t vx6953_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = vx6953_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = vx6953_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = vx6953_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+static int32_t vx6953_power_down(void)
+{
+ return 0;
+}
+
+
+static int vx6953_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_set_value_cansleep(data->sensor_reset, 0);
+ gpio_free(data->sensor_reset);
+ return 0;
+}
+static int vx6953_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ int32_t rc = 0;
+ unsigned short chipidl, chipidh;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "vx6953");
+ CDBG(" vx6953_probe_init_sensor \n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ CDBG(" vx6953_probe_init_sensor 1\n");
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(10);
+ CDBG(" vx6953_probe_init_sensor 1\n");
+ gpio_set_value_cansleep(data->sensor_reset, 1);
+ } else {
+ CDBG(" vx6953_probe_init_sensor 2\n");
+ goto init_probe_done;
+ }
+ msleep(20);
+ CDBG(" vx6953_probe_init_sensor is called\n");
+ /* 3. Read sensor Model ID: */
+ rc = vx6953_i2c_read(0x0000, &chipidh, 1);
+ if (rc < 0) {
+ CDBG(" vx6953_probe_init_sensor 3\n");
+ goto init_probe_fail;
+ }
+ rc = vx6953_i2c_read(0x0001, &chipidl, 1);
+ if (rc < 0) {
+ CDBG(" vx6953_probe_init_sensor4\n");
+ goto init_probe_fail;
+ }
+ CDBG("vx6953 model_id = 0x%x 0x%x\n", chipidh, chipidl);
+ /* 4. Compare sensor ID to VX6953 ID: */
+ if (chipidh != 0x03 || chipidl != 0xB9) {
+ rc = -ENODEV;
+ CDBG("vx6953_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" vx6953_probe_init_sensor fails\n");
+ vx6953_probe_init_done(data);
+init_probe_done:
+ CDBG(" vx6953_probe_init_sensor finishes\n");
+ return rc;
+ }
+/* camsensor_iu060f_vx6953_reset */
+int vx6953_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ unsigned short revision_number;
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling vx6953_sensor_open_init\n");
+ vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+ if (!vx6953_ctrl) {
+ CDBG("vx6953_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ vx6953_ctrl->fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->set_test = TEST_OFF;
+ vx6953_ctrl->prev_res = QTR_SIZE;
+ vx6953_ctrl->pict_res = FULL_SIZE;
+ vx6953_ctrl->curr_res = INVALID_SIZE;
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+ if (data)
+ vx6953_ctrl->sensordata = data;
+ if (rc < 0) {
+ CDBG("Calling vx6953_sensor_open_init fail1\n");
+ return rc;
+ }
+ CDBG("%s: %d\n", __func__, __LINE__);
+ /* enable mclk first */
+ msm_camio_clk_rate_set(VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE);
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = vx6953_probe_init_sensor(data);
+ if (rc < 0) {
+ CDBG("Calling vx6953_sensor_open_init fail3\n");
+ goto init_fail;
+ }
+ if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number major = 0x%x\n", revision_number);
+ if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number = 0x%x\n", revision_number);
+ if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+ CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+ } else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+ } else {/* Cut1.0 reads 0x00 for register 0x0018*/
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+ CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+ }
+ if (vx6953_ctrl->prev_res == QTR_SIZE) {
+ if (vx6953_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+ return rc;
+ } else {
+ if (vx6953_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+ return rc;
+ }
+ vx6953_ctrl->fps = 30*Q8;
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+init_fail:
+ CDBG("init_fail\n");
+ vx6953_probe_init_done(data);
+ kfree(vx6953_ctrl);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+} /*endof vx6953_sensor_open_init*/
+
+static int vx6953_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&vx6953_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id vx6953_i2c_id[] = {
+ {"vx6953", 0},
+ { }
+};
+
+static int vx6953_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("vx6953_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ vx6953_sensorw = kzalloc(sizeof(struct vx6953_work_t), GFP_KERNEL);
+ if (!vx6953_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, vx6953_sensorw);
+ vx6953_init_client(client);
+ vx6953_client = client;
+
+ msleep(50);
+
+ CDBG("vx6953_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("vx6953_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int vx6953_send_wb_info(struct wb_info_cfg *wb)
+{
+ unsigned short read_data;
+ uint8_t temp[8];
+ int rc = 0;
+ int i = 0;
+
+ /* red_gain */
+ temp[2] = wb->red_gain >> 8;
+ temp[3] = wb->red_gain & 0xFF;
+
+ /* green_gain */
+ temp[0] = wb->green_gain >> 8;
+ temp[1] = wb->green_gain & 0xFF;
+ temp[6] = temp[0];
+ temp[7] = temp[1];
+
+ /* blue_gain */
+ temp[4] = wb->blue_gain >> 8;
+ temp[5] = wb->blue_gain & 0xFF;
+ rc = vx6953_i2c_write_seq_sensor(0x0B8E, &temp[0], 8);
+
+ for (i = 0; i < 6; i++) {
+ rc = vx6953_i2c_read(0x0B8E + i, &read_data, 1);
+ CDBG("%s addr 0x%x val %d \n", __func__, 0x0B8E + i, read_data);
+ }
+ rc = vx6953_i2c_read(0x0B82, &read_data, 1);
+ CDBG("%s addr 0x%x val %d \n", __func__, 0x0B82, read_data);
+ if (rc < 0)
+ return rc;
+ return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int __exit vx6953_remove(struct i2c_client *client)
+{
+ struct vx6953_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ vx6953_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver vx6953_i2c_driver = {
+ .id_table = vx6953_i2c_id,
+ .probe = vx6953_i2c_probe,
+ .remove = __exit_p(vx6953_i2c_remove),
+ .driver = {
+ .name = "vx6953",
+ },
+};
+
+int vx6953_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;
+ mutex_lock(&vx6953_mut);
+ CDBG("vx6953_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 = vx6953_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ vx6953_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ vx6953_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = vx6953_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = vx6953_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc =
+ vx6953_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ vx6953_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = vx6953_set_default_focus(
+ cdata.cfg.effect);
+ break;
+
+
+ case CFG_SEND_WB_INFO:
+ rc = vx6953_send_wb_info(
+ &(cdata.cfg.wb_info));
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&vx6953_mut);
+
+ return rc;
+}
+
+
+
+
+static int vx6953_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&vx6953_mut);
+ vx6953_power_down();
+ gpio_direction_output(vx6953_ctrl->sensordata->sensor_reset, 0);
+ gpio_free(vx6953_ctrl->sensordata->sensor_reset);
+ kfree(vx6953_ctrl);
+ vx6953_ctrl = NULL;
+ CDBG("vx6953_release completed\n");
+ mutex_unlock(&vx6953_mut);
+
+ return rc;
+}
+
+static int vx6953_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&vx6953_i2c_driver);
+ if (rc < 0 || vx6953_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(24000000);
+ rc = vx6953_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = vx6953_sensor_open_init;
+ s->s_release = vx6953_sensor_release;
+ s->s_config = vx6953_sensor_config;
+ s->s_mount_angle = 0;
+ vx6953_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("vx6953_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+static int __vx6953_probe(struct platform_device *pdev)
+{
+ return msm_camera_drv_start(pdev, vx6953_sensor_probe);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __vx6953_probe,
+ .driver = {
+ .name = "msm_camera_vx6953",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vx6953_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vx6953_init);
+void vx6953_exit(void)
+{
+ i2c_del_driver(&vx6953_i2c_driver);
+}
+
+
diff --git a/drivers/media/video/msm/vx6953.h b/drivers/media/video/msm/vx6953.h
new file mode 100644
index 0000000..0e12063
--- /dev/null
+++ b/drivers/media/video/msm/vx6953.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef VX6953_H
+#define VX6953_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct vx6953_reg vx6953_regs;
+struct reg_struct_init {
+ uint8_t reg_0x0112; /* 0x0112*/
+ uint8_t reg_0x0113; /* 0x0113*/
+ uint8_t vt_pix_clk_div; /* 0x0301*/
+ uint8_t pre_pll_clk_div; /* 0x0305*/
+ uint8_t pll_multiplier; /* 0x0307*/
+ uint8_t op_pix_clk_div; /* 0x0309*/
+ uint8_t reg_0x3030; /*0x3030*/
+ uint8_t reg_0x0111; /*0x0111*/
+ uint8_t reg_0x0b00; /*0x0b00*/
+ uint8_t reg_0x3001; /*0x3001*/
+ uint8_t reg_0x3004; /*0x3004*/
+ uint8_t reg_0x3007; /*0x3007*/
+ uint8_t reg_0x3016; /*0x3016*/
+ uint8_t reg_0x301d; /*0x301d*/
+ uint8_t reg_0x317e; /*0x317E*/
+ uint8_t reg_0x317f; /*0x317F*/
+ uint8_t reg_0x3400; /*0x3400*/
+ uint8_t reg_0x0b06; /*0x0b06*/
+ uint8_t reg_0x0b07; /*0x0b07*/
+ uint8_t reg_0x0b08; /*0x0b08*/
+ uint8_t reg_0x0b09; /*0x0b09*/
+ uint8_t reg_0x0136;
+ uint8_t reg_0x0137;
+ /* Edof */
+ uint8_t reg_0x0b83; /*0x0b83*/
+ uint8_t reg_0x0b84; /*0x0b84*/
+ uint8_t reg_0x0b85; /*0x0b85*/
+ uint8_t reg_0x0b88; /*0x0b88*/
+ uint8_t reg_0x0b89; /*0x0b89*/
+ uint8_t reg_0x0b8a; /*0x0b8a*/
+ };
+struct reg_struct {
+ uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+ uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+ uint8_t analogue_gain_code_global;
+ uint8_t frame_length_lines_hi; /* 0x0340*/
+ uint8_t frame_length_lines_lo; /* 0x0341*/
+ uint8_t line_length_pck_hi; /* 0x0342*/
+ uint8_t line_length_pck_lo; /* 0x0343*/
+ uint8_t reg_0x3005; /* 0x3005*/
+ uint8_t reg_0x3010; /* 0x3010*/
+ uint8_t reg_0x3011; /* 0x3011*/
+ uint8_t reg_0x301a; /* 0x301a*/
+ uint8_t reg_0x3035; /* 0x3035*/
+ uint8_t reg_0x3036; /* 0x3036*/
+ uint8_t reg_0x3041; /*0x3041*/
+ uint8_t reg_0x3042; /*0x3042*/
+ uint8_t reg_0x3045; /*0x3045*/
+ uint8_t reg_0x0b80; /* 0x0b80*/
+ uint8_t reg_0x0900; /*0x0900*/
+ uint8_t reg_0x0901; /* 0x0901*/
+ uint8_t reg_0x0902; /*0x0902*/
+ uint8_t reg_0x0383; /*0x0383*/
+ uint8_t reg_0x0387; /* 0x0387*/
+ uint8_t reg_0x034c; /* 0x034c*/
+ uint8_t reg_0x034d; /*0x034d*/
+ uint8_t reg_0x034e; /* 0x034e*/
+ uint8_t reg_0x034f; /* 0x034f*/
+ uint8_t reg_0x1716; /*0x1716*/
+ uint8_t reg_0x1717; /*0x1717*/
+ uint8_t reg_0x1718; /*0x1718*/
+ uint8_t reg_0x1719; /*0x1719*/
+ uint8_t reg_0x3210;/*0x3210*/
+ uint8_t reg_0x111; /*0x111*/
+ uint8_t reg_0x3410; /*0x3410*/
+ uint8_t reg_0x3098;
+ uint8_t reg_0x309D;
+ uint8_t reg_0x0200;
+ uint8_t reg_0x0201;
+ };
+struct vx6953_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+enum vx6953_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum vx6953_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+enum vx6953_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+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 sensor_revision_t {
+ VX6953_STM5M0EDOF_CUT_1,
+ VX6953_STM5M0EDOF_CUT_2,
+ VX6953_STM5M0EDOF_CUT_3
+};
+enum edof_mode_t {
+ VX6953_EDOF_DISABLE, /* 0x00 */
+ VX6953_EDOF_APPLICATION, /* 0x01 */
+ VX6953_EDOF_ESTIMATION /* 0x02 */
+};
+struct vx6953_reg {
+ const struct reg_struct_init *reg_pat_init;
+ const struct reg_struct *reg_pat;
+};
+#endif /* VX6953_H */
diff --git a/drivers/media/video/msm/vx6953_reg.c b/drivers/media/video/msm/vx6953_reg.c
new file mode 100644
index 0000000..48fc71f
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_reg.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+
+#include "vx6953.h"
+const struct reg_struct_init vx6953_reg_init[1] = {
+ {
+ 10, /*REG = 0x0112 , 10 bit */
+ 10, /*REG = 0x0113*/
+ 9, /*REG = 0x0301 vt_pix_clk_div*/
+ 4, /*REG = 0x0305 pre_pll_clk_div*/
+ 133, /*REG = 0x0307 pll_multiplier*/
+ 10, /*REG = 0x0309 op_pix_clk_div*/
+ 0x08, /*REG = 0x3030*/
+ 0x02, /*REG = 0x0111*/
+ 0x01, /*REG = 0x0b00 ,lens shading off */
+ 0x30, /*REG = 0x3001*/
+ 0x33, /*REG = 0x3004*/
+ 0x09, /*REG = 0x3007*/
+ 0x1F, /*REG = 0x3016*/
+ 0x03, /*REG = 0x301d*/
+ 0x11, /*REG = 0x317E*/
+ 0x09, /*REG = 0x317F*/
+ 0x38, /*REG = 0x3400*/
+ 0x00, /*REG_0x0b06*/
+ 0x80, /*REG_0x0b07*/
+ 0x01, /*REG_0x0b08*/
+ 0x4F, /*REG_0x0b09*/
+ 0x18, /*REG_0x0136*/
+ 0x00, /*/REG_0x0137*/
+ 0x20, /*REG = 0x0b83*/
+ 0x90, /*REG = 0x0b84*/
+ 0x20, /*REG = 0x0b85*/
+ 0x80, /*REG = 0x0b88*/
+ 0x00, /*REG = 0x0b89*/
+ 0x00, /*REG = 0x0b8a*/
+ }
+};
+const struct reg_struct vx6953_reg_pat[2] = {
+ {/* Preview */
+ 0x03, /*REG = 0x0202 coarse integration_time_hi*/
+ 0xd0, /*REG = 0x0203 coarse_integration_time_lo*/
+ 0xc0, /*REG = 0x0205 analogue_gain_code_global*/
+ 0x03, /*REG = 0x0340 frame_length_lines_hi*/
+ 0xf0, /*REG = 0x0341 frame_length_lines_lo*/
+ 0x0b, /*REG = 0x0342 line_length_pck_hi*/
+ 0x74, /*REG = 0x0343 line_length_pck_lo*/
+ 0x03, /*REG = 0x3005*/
+ 0x00, /*REG = 0x3010*/
+ 0x01, /*REG = 0x3011*/
+ 0x6a, /*REG = 0x301a*/
+ 0x03, /*REG = 0x3035*/
+ 0x2c, /*REG = 0x3036*/
+ 0x00, /*REG = 0x3041*/
+ 0x24, /*REG = 0x3042*/
+ 0x81, /*REG = 0x3045*/
+ 0x02, /*REG = 0x0b80 edof estimate*/
+ 0x01, /*REG = 0x0900*/
+ 0x22, /*REG = 0x0901*/
+ 0x04, /*REG = 0x0902*/
+ 0x03, /*REG = 0x0383*/
+ 0x03, /*REG = 0x0387*/
+ 0x05, /*REG = 0x034c*/
+ 0x18, /*REG = 0x034d*/
+ 0x03, /*REG = 0x034e*/
+ 0xd4, /*REG = 0x034f*/
+ 0x02, /*0x1716*/
+ 0x04, /*0x1717*/
+ 0x08, /*0x1718*/
+ 0x2c, /*0x1719*/
+ 0x01, /*0x3210*/
+ 0x02, /*0x111*/
+ 0x01, /*0x3410*/
+ 0x01, /*0x3098*/
+ 0x05, /*0x309D*/
+ 0x02,
+ 0x04,
+ },
+ { /* Snapshot */
+ 0x07,/*REG = 0x0202 coarse_integration_time_hi*/
+ 0x00,/*REG = 0x0203 coarse_integration_time_lo*/
+ 0xc0,/*REG = 0x0205 analogue_gain_code_global*/
+ 0x07,/*REG = 0x0340 frame_length_lines_hi*/
+ 0xd0,/*REG = 0x0341 frame_length_lines_lo*/
+ 0x0b,/*REG = 0x0342 line_length_pck_hi*/
+ 0x8c,/*REG = 0x0343 line_length_pck_lo*/
+ 0x01,/*REG = 0x3005*/
+ 0x00,/*REG = 0x3010*/
+ 0x00,/*REG = 0x3011*/
+ 0x55,/*REG = 0x301a*/
+ 0x01,/*REG = 0x3035*/
+ 0x23,/*REG = 0x3036*/
+ 0x00,/*REG = 0x3041*/
+ 0x24,/*REG = 0x3042*/
+ 0xb7,/*REG = 0x3045*/
+ 0x01,/*REG = 0x0b80 edof application*/
+ 0x00,/*REG = 0x0900*/
+ 0x00,/*REG = 0x0901*/
+ 0x00,/*REG = 0x0902*/
+ 0x01,/*REG = 0x0383*/
+ 0x01,/*REG = 0x0387*/
+ 0x0A,/*REG = 0x034c*/
+ 0x30,/*REG = 0x034d*/
+ 0x07,/*REG = 0x034e*/
+ 0xA8,/*REG = 0x034f*/
+ 0x02,/*0x1716*/
+ 0x0d,/*0x1717*/
+ 0x07,/*0x1718*/
+ 0x7d,/*0x1719*/
+ 0x01,/*0x3210*/
+ 0x02,/*0x111*/
+ 0x01,/*0x3410*/
+ 0x01,/*0x3098*/
+ 0x05, /*0x309D*/
+ 0x02,
+ 0x00,
+ }
+};
+
+
+
+struct vx6953_reg vx6953_regs = {
+ .reg_pat_init = &vx6953_reg_init[0],
+ .reg_pat = &vx6953_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/vx6953_reg_v4l2.c b/drivers/media/video/msm/vx6953_reg_v4l2.c
new file mode 100644
index 0000000..f16054b
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_reg_v4l2.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+
+#include "vx6953_v4l2.h"
+const struct reg_struct_init vx6953_reg_init[1] = {
+ {
+ 10, /*REG = 0x0112 , 10 bit */
+ 10, /*REG = 0x0113*/
+ 9, /*REG = 0x0301 vt_pix_clk_div*/
+ 4, /*REG = 0x0305 pre_pll_clk_div*/
+ 133, /*REG = 0x0307 pll_multiplier*/
+ 10, /*REG = 0x0309 op_pix_clk_div*/
+ 0x08, /*REG = 0x3030*/
+ 0x02, /*REG = 0x0111*/
+ 0x01, /*REG = 0x0b00 ,lens shading off */
+ 0x30, /*REG = 0x3001*/
+ 0x33, /*REG = 0x3004*/
+ 0x09, /*REG = 0x3007*/
+ 0x1F, /*REG = 0x3016*/
+ 0x03, /*REG = 0x301d*/
+ 0x11, /*REG = 0x317E*/
+ 0x09, /*REG = 0x317F*/
+ 0x38, /*REG = 0x3400*/
+ 0x00, /*REG_0x0b06*/
+ 0x80, /*REG_0x0b07*/
+ 0x01, /*REG_0x0b08*/
+ 0x4F, /*REG_0x0b09*/
+ 0x18, /*REG_0x0136*/
+ 0x00, /*/REG_0x0137*/
+ 0x20, /*REG = 0x0b83*/
+ 0x90, /*REG = 0x0b84*/
+ 0x20, /*REG = 0x0b85*/
+ 0x80, /*REG = 0x0b88*/
+ 0x00, /*REG = 0x0b89*/
+ 0x00, /*REG = 0x0b8a*/
+ }
+};
+const struct reg_struct vx6953_reg_pat[2] = {
+ {/* Preview */
+ 0x03, /*REG = 0x0202 coarse integration_time_hi*/
+ 0xd0, /*REG = 0x0203 coarse_integration_time_lo*/
+ 0xc0, /*REG = 0x0205 analogue_gain_code_global*/
+ 0x03, /*REG = 0x0340 frame_length_lines_hi*/
+ 0xf0, /*REG = 0x0341 frame_length_lines_lo*/
+ 0x0b, /*REG = 0x0342 line_length_pck_hi*/
+ 0xa5, /*REG = 0x0343 line_length_pck_lo*/
+ 0x03, /*REG = 0x3005*/
+ 0x00, /*REG = 0x3010*/
+ 0x01, /*REG = 0x3011*/
+ 0x6a, /*REG = 0x301a*/
+ 0x03, /*REG = 0x3035*/
+ 0x2c, /*REG = 0x3036*/
+ 0x00, /*REG = 0x3041*/
+ 0x24, /*REG = 0x3042*/
+ 0x81, /*REG = 0x3045*/
+ 0x02, /*REG = 0x0b80 edof estimate*/
+ 0x01, /*REG = 0x0900*/
+ 0x22, /*REG = 0x0901*/
+ 0x04, /*REG = 0x0902*/
+ 0x03, /*REG = 0x0383*/
+ 0x03, /*REG = 0x0387*/
+ 0x05, /*REG = 0x034c*/
+ 0x18, /*REG = 0x034d*/
+ 0x03, /*REG = 0x034e*/
+ 0xd4, /*REG = 0x034f*/
+ 0x02, /*0x1716*/
+ 0x04, /*0x1717*/
+ 0x08, /*0x1718*/
+ 0x80, /*0x1719*/
+ 0x01, /*0x3210*/
+ 0x02, /*0x111*/
+ 0x01, /*0x3410*/
+ 0x01, /*0x3098*/
+ 0x05, /*0x309D*/
+ 0x02,
+ 0x04,
+ },
+ { /* Snapshot */
+ 0x07,/*REG = 0x0202 coarse_integration_time_hi*/
+ 0x00,/*REG = 0x0203 coarse_integration_time_lo*/
+ 0xc0,/*REG = 0x0205 analogue_gain_code_global*/
+ 0x07,/*REG = 0x0340 frame_length_lines_hi*/
+ 0xd0,/*REG = 0x0341 frame_length_lines_lo*/
+ 0x0b,/*REG = 0x0342 line_length_pck_hi*/
+ 0x8c,/*REG = 0x0343 line_length_pck_lo*/
+ 0x01,/*REG = 0x3005*/
+ 0x00,/*REG = 0x3010*/
+ 0x00,/*REG = 0x3011*/
+ 0x55,/*REG = 0x301a*/
+ 0x01,/*REG = 0x3035*/
+ 0x23,/*REG = 0x3036*/
+ 0x00,/*REG = 0x3041*/
+ 0x24,/*REG = 0x3042*/
+ 0xb7,/*REG = 0x3045*/
+ 0x01,/*REG = 0x0b80 edof application*/
+ 0x00,/*REG = 0x0900*/
+ 0x00,/*REG = 0x0901*/
+ 0x00,/*REG = 0x0902*/
+ 0x01,/*REG = 0x0383*/
+ 0x01,/*REG = 0x0387*/
+ 0x0A,/*REG = 0x034c*/
+ 0x30,/*REG = 0x034d*/
+ 0x07,/*REG = 0x034e*/
+ 0xA8,/*REG = 0x034f*/
+ 0x02,/*0x1716*/
+ 0x0d,/*0x1717*/
+ 0x07,/*0x1718*/
+ 0x7d,/*0x1719*/
+ 0x01,/*0x3210*/
+ 0x02,/*0x111*/
+ 0x01,/*0x3410*/
+ 0x01,/*0x3098*/
+ 0x05, /*0x309D*/
+ 0x02,
+ 0x00,
+ }
+};
+
+
+
+struct vx6953_reg vx6953_regs = {
+ .reg_pat_init = &vx6953_reg_init[0],
+ .reg_pat = &vx6953_reg_pat[0],
+};
diff --git a/drivers/media/video/msm/vx6953_v4l2.c b/drivers/media/video/msm/vx6953_v4l2.c
new file mode 100644
index 0000000..2e5e39b
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_v4l2.c
@@ -0,0 +1,4149 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_camera.h>
+#include <mach/gpio.h>
+#include <mach/camera.h>
+#include <linux/slab.h>
+#include "vx6953_v4l2.h"
+#include "msm.h"
+
+#define V4L2_IDENT_VX6953 50000
+
+/*=============================================================
+ SENSOR REGISTER DEFINES
+==============================================================*/
+
+#define REG_GROUPED_PARAMETER_HOLD 0x0104
+#define GROUPED_PARAMETER_HOLD_OFF 0x00
+#define GROUPED_PARAMETER_HOLD 0x01
+#define REG_MODE_SELECT 0x0100
+#define MODE_SELECT_STANDBY_MODE 0x00
+#define MODE_SELECT_STREAM 0x01
+/* Integration Time */
+#define REG_COARSE_INTEGRATION_TIME_HI 0x0202
+#define REG_COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+/* Digital Gain */
+#define REG_DIGITAL_GAIN_GREEN_R_HI 0x020E
+#define REG_DIGITAL_GAIN_GREEN_R_LO 0x020F
+#define REG_DIGITAL_GAIN_RED_HI 0x0210
+#define REG_DIGITAL_GAIN_RED_LO 0x0211
+#define REG_DIGITAL_GAIN_BLUE_HI 0x0212
+#define REG_DIGITAL_GAIN_BLUE_LO 0x0213
+#define REG_DIGITAL_GAIN_GREEN_B_HI 0x0214
+#define REG_DIGITAL_GAIN_GREEN_B_LO 0x0215
+/* output bits setting */
+#define REG_0x0112 0x0112
+#define REG_0x0113 0x0113
+/* PLL registers */
+#define REG_VT_PIX_CLK_DIV 0x0301
+#define REG_PRE_PLL_CLK_DIV 0x0305
+#define REG_PLL_MULTIPLIER 0x0307
+#define REG_OP_PIX_CLK_DIV 0x0309
+#define REG_0x034c 0x034c
+#define REG_0x034d 0x034d
+#define REG_0x034e 0x034e
+#define REG_0x034f 0x034f
+#define REG_0x0387 0x0387
+#define REG_0x0383 0x0383
+#define REG_FRAME_LENGTH_LINES_HI 0x0340
+#define REG_FRAME_LENGTH_LINES_LO 0x0341
+#define REG_LINE_LENGTH_PCK_HI 0x0342
+#define REG_LINE_LENGTH_PCK_LO 0x0343
+#define REG_0x3030 0x3030
+#define REG_0x0111 0x0111
+#define REG_0x0136 0x0136
+#define REG_0x0137 0x0137
+#define REG_0x0b00 0x0b00
+#define REG_0x3001 0x3001
+#define REG_0x3004 0x3004
+#define REG_0x3007 0x3007
+#define REG_0x301a 0x301a
+#define REG_0x3101 0x3101
+#define REG_0x3364 0x3364
+#define REG_0x3365 0x3365
+#define REG_0x0b83 0x0b83
+#define REG_0x0b84 0x0b84
+#define REG_0x0b85 0x0b85
+#define REG_0x0b88 0x0b88
+#define REG_0x0b89 0x0b89
+#define REG_0x0b8a 0x0b8a
+#define REG_0x3005 0x3005
+#define REG_0x3010 0x3010
+#define REG_0x3036 0x3036
+#define REG_0x3041 0x3041
+#define REG_0x0b80 0x0b80
+#define REG_0x0900 0x0900
+#define REG_0x0901 0x0901
+#define REG_0x0902 0x0902
+#define REG_0x3016 0x3016
+#define REG_0x301d 0x301d
+#define REG_0x317e 0x317e
+#define REG_0x317f 0x317f
+#define REG_0x3400 0x3400
+#define REG_0x303a 0x303a
+#define REG_0x1716 0x1716
+#define REG_0x1717 0x1717
+#define REG_0x1718 0x1718
+#define REG_0x1719 0x1719
+#define REG_0x3006 0x3006
+#define REG_0x301b 0x301b
+#define REG_0x3098 0x3098
+#define REG_0x309d 0x309d
+#define REG_0x3011 0x3011
+#define REG_0x3035 0x3035
+#define REG_0x3045 0x3045
+#define REG_0x3210 0x3210
+#define REG_0x0111 0x0111
+#define REG_0x3410 0x3410
+/* Test Pattern */
+#define REG_TEST_PATTERN_MODE 0x0601
+
+/*============================================================================
+ TYPE DECLARATIONS
+============================================================================*/
+
+/* 16bit address - 8 bit context register structure */
+#define VX6953_STM5M0EDOF_OFFSET 9
+#define Q8 0x00000100
+#define Q10 0x00000400
+#define VX6953_STM5M0EDOF_MAX_SNAPSHOT_EXPOSURE_LINE_COUNT 2922
+#define VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE 24000000
+#define VX6953_STM5M0EDOF_OP_PIXEL_CLOCK_RATE 79800000
+#define VX6953_STM5M0EDOF_VT_PIXEL_CLOCK_RATE 88670000
+/* Full Size */
+#define VX6953_FULL_SIZE_WIDTH 2608
+#define VX6953_FULL_SIZE_HEIGHT 1960
+#define VX6953_FULL_SIZE_DUMMY_PIXELS 1
+#define VX6953_FULL_SIZE_DUMMY_LINES 0
+/* Quarter Size */
+#define VX6953_QTR_SIZE_WIDTH 1304
+#define VX6953_QTR_SIZE_HEIGHT 980
+#define VX6953_QTR_SIZE_DUMMY_PIXELS 1
+#define VX6953_QTR_SIZE_DUMMY_LINES 0
+/* Blanking as measured on the scope */
+/* Full Size */
+#define VX6953_HRZ_FULL_BLK_PIXELS 348
+#define VX6953_VER_FULL_BLK_LINES 40
+/* Quarter Size */
+#define VX6953_HRZ_QTR_BLK_PIXELS 1628
+#define VX6953_VER_QTR_BLK_LINES 28
+#define MAX_LINE_LENGTH_PCK 8190
+#define VX6953_REVISION_NUMBER_CUT2 0x10/*revision number for Cut2.0*/
+#define VX6953_REVISION_NUMBER_CUT3 0x20/*revision number for Cut3.0*/
+/* FIXME: Changes from here */
+struct vx6953_work_t {
+ struct work_struct work;
+};
+
+static struct vx6953_work_t *vx6953_sensorw;
+static struct i2c_client *vx6953_client;
+
+struct vx6953_ctrl_t {
+ const struct msm_camera_sensor_info *sensordata;
+
+ uint32_t sensormode;
+ uint32_t fps_divider; /* init to 1 * 0x00000400 */
+ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */
+ uint16_t fps;
+
+ int16_t curr_lens_pos;
+ uint16_t curr_step_pos;
+ uint16_t my_reg_gain;
+ uint32_t my_reg_line_count;
+ uint16_t total_lines_per_frame;
+
+ enum vx6953_resolution_t prev_res;
+ enum vx6953_resolution_t pict_res;
+ enum vx6953_resolution_t curr_res;
+ enum vx6953_test_mode_t set_test;
+ enum sensor_revision_t sensor_type;
+
+ enum edof_mode_t edof_mode;
+
+ unsigned short imgaddr;
+
+ struct v4l2_subdev *sensor_dev;
+ struct vx6953_format *fmt;
+};
+
+
+static uint8_t vx6953_stm5m0edof_delay_msecs_stdby;
+static uint16_t vx6953_stm5m0edof_delay_msecs_stream = 20;
+
+static struct vx6953_ctrl_t *vx6953_ctrl;
+static DECLARE_WAIT_QUEUE_HEAD(vx6953_wait_queue);
+DEFINE_MUTEX(vx6953_mut);
+static struct vx6953_i2c_reg_conf patch_tbl_cut2[] = {
+ {0xFB94, 0}, /*intialise Data Xfer Status reg*/
+ {0xFB95, 0}, /*gain 1 (0x00)*/
+ {0xFB96, 0}, /*gain 1.07 (0x10)*/
+ {0xFB97, 0}, /*gain 1.14 (0x20)*/
+ {0xFB98, 0}, /*gain 1.23 (0x30)*/
+ {0xFB99, 0}, /*gain 1.33 (0x40)*/
+ {0xFB9A, 0}, /*gain 1.45 (0x50)*/
+ {0xFB9B, 0}, /*gain 1.6 (0x60)*/
+ {0xFB9C, 0}, /*gain 1.78 (0x70)*/
+ {0xFB9D, 2}, /*gain 2 (0x80)*/
+ {0xFB9E, 2}, /*gain 2.29 (0x90)*/
+ {0xFB9F, 3}, /*gain 2.67 (0xA0)*/
+ {0xFBA0, 3}, /*gain 3.2 (0xB0)*/
+ {0xFBA1, 4}, /*gain 4 (0xC0)*/
+ {0xFBA2, 7}, /*gain 5.33 (0xD0)*/
+ {0xFBA3, 10}, /*gain 8 (0xE0)*/
+ {0xFBA4, 11}, /*gain 9.14 (0xE4)*/
+ {0xFBA5, 13}, /*gain 10.67 (0xE8)*/
+ {0xFBA6, 15}, /*gain 12.8 (0xEC)*/
+ {0xFBA7, 19}, /*gain 16 (0xF0)*/
+ {0xF800, 0x12},
+ {0xF801, 0x06},
+ {0xF802, 0xf7},
+ {0xF803, 0x90},
+ {0xF804, 0x02},
+ {0xF805, 0x05},
+ {0xF806, 0xe0},
+ {0xF807, 0xff},
+ {0xF808, 0x65},
+ {0xF809, 0x7d},
+ {0xF80A, 0x70},
+ {0xF80B, 0x03},
+ {0xF80C, 0x02},
+ {0xF80D, 0xf9},
+ {0xF80E, 0x1c},
+ {0xF80F, 0x8f},
+ {0xF810, 0x7d},
+ {0xF811, 0xe4},
+ {0xF812, 0xf5},
+ {0xF813, 0x7a},
+ {0xF814, 0x75},
+ {0xF815, 0x78},
+ {0xF816, 0x30},
+ {0xF817, 0x75},
+ {0xF818, 0x79},
+ {0xF819, 0x53},
+ {0xF81A, 0x85},
+ {0xF81B, 0x79},
+ {0xF81C, 0x82},
+ {0xF81D, 0x85},
+ {0xF81E, 0x78},
+ {0xF81F, 0x83},
+ {0xF820, 0xe0},
+ {0xF821, 0xc3},
+ {0xF822, 0x95},
+ {0xF823, 0x7b},
+ {0xF824, 0xf0},
+ {0xF825, 0x74},
+ {0xF826, 0x02},
+ {0xF827, 0x25},
+ {0xF828, 0x79},
+ {0xF829, 0xf5},
+ {0xF82A, 0x79},
+ {0xF82B, 0xe4},
+ {0xF82C, 0x35},
+ {0xF82D, 0x78},
+ {0xF82E, 0xf5},
+ {0xF82F, 0x78},
+ {0xF830, 0x05},
+ {0xF831, 0x7a},
+ {0xF832, 0xe5},
+ {0xF833, 0x7a},
+ {0xF834, 0xb4},
+ {0xF835, 0x08},
+ {0xF836, 0xe3},
+ {0xF837, 0xe5},
+ {0xF838, 0x7d},
+ {0xF839, 0x70},
+ {0xF83A, 0x04},
+ {0xF83B, 0xff},
+ {0xF83C, 0x02},
+ {0xF83D, 0xf8},
+ {0xF83E, 0xe4},
+ {0xF83F, 0xe5},
+ {0xF840, 0x7d},
+ {0xF841, 0xb4},
+ {0xF842, 0x10},
+ {0xF843, 0x05},
+ {0xF844, 0x7f},
+ {0xF845, 0x01},
+ {0xF846, 0x02},
+ {0xF847, 0xf8},
+ {0xF848, 0xe4},
+ {0xF849, 0xe5},
+ {0xF84A, 0x7d},
+ {0xF84B, 0xb4},
+ {0xF84C, 0x20},
+ {0xF84D, 0x05},
+ {0xF84E, 0x7f},
+ {0xF84F, 0x02},
+ {0xF850, 0x02},
+ {0xF851, 0xf8},
+ {0xF852, 0xe4},
+ {0xF853, 0xe5},
+ {0xF854, 0x7d},
+ {0xF855, 0xb4},
+ {0xF856, 0x30},
+ {0xF857, 0x05},
+ {0xF858, 0x7f},
+ {0xF859, 0x03},
+ {0xF85A, 0x02},
+ {0xF85B, 0xf8},
+ {0xF85C, 0xe4},
+ {0xF85D, 0xe5},
+ {0xF85E, 0x7d},
+ {0xF85F, 0xb4},
+ {0xF860, 0x40},
+ {0xF861, 0x04},
+ {0xF862, 0x7f},
+ {0xF863, 0x04},
+ {0xF864, 0x80},
+ {0xF865, 0x7e},
+ {0xF866, 0xe5},
+ {0xF867, 0x7d},
+ {0xF868, 0xb4},
+ {0xF869, 0x50},
+ {0xF86A, 0x04},
+ {0xF86B, 0x7f},
+ {0xF86C, 0x05},
+ {0xF86D, 0x80},
+ {0xF86E, 0x75},
+ {0xF86F, 0xe5},
+ {0xF870, 0x7d},
+ {0xF871, 0xb4},
+ {0xF872, 0x60},
+ {0xF873, 0x04},
+ {0xF874, 0x7f},
+ {0xF875, 0x06},
+ {0xF876, 0x80},
+ {0xF877, 0x6c},
+ {0xF878, 0xe5},
+ {0xF879, 0x7d},
+ {0xF87A, 0xb4},
+ {0xF87B, 0x70},
+ {0xF87C, 0x04},
+ {0xF87D, 0x7f},
+ {0xF87E, 0x07},
+ {0xF87F, 0x80},
+ {0xF880, 0x63},
+ {0xF881, 0xe5},
+ {0xF882, 0x7d},
+ {0xF883, 0xb4},
+ {0xF884, 0x80},
+ {0xF885, 0x04},
+ {0xF886, 0x7f},
+ {0xF887, 0x08},
+ {0xF888, 0x80},
+ {0xF889, 0x5a},
+ {0xF88A, 0xe5},
+ {0xF88B, 0x7d},
+ {0xF88C, 0xb4},
+ {0xF88D, 0x90},
+ {0xF88E, 0x04},
+ {0xF88F, 0x7f},
+ {0xF890, 0x09},
+ {0xF891, 0x80},
+ {0xF892, 0x51},
+ {0xF893, 0xe5},
+ {0xF894, 0x7d},
+ {0xF895, 0xb4},
+ {0xF896, 0xa0},
+ {0xF897, 0x04},
+ {0xF898, 0x7f},
+ {0xF899, 0x0a},
+ {0xF89A, 0x80},
+ {0xF89B, 0x48},
+ {0xF89C, 0xe5},
+ {0xF89D, 0x7d},
+ {0xF89E, 0xb4},
+ {0xF89F, 0xb0},
+ {0xF8A0, 0x04},
+ {0xF8A1, 0x7f},
+ {0xF8A2, 0x0b},
+ {0xF8A3, 0x80},
+ {0xF8A4, 0x3f},
+ {0xF8A5, 0xe5},
+ {0xF8A6, 0x7d},
+ {0xF8A7, 0xb4},
+ {0xF8A8, 0xc0},
+ {0xF8A9, 0x04},
+ {0xF8AA, 0x7f},
+ {0xF8AB, 0x0c},
+ {0xF8AC, 0x80},
+ {0xF8AD, 0x36},
+ {0xF8AE, 0xe5},
+ {0xF8AF, 0x7d},
+ {0xF8B0, 0xb4},
+ {0xF8B1, 0xd0},
+ {0xF8B2, 0x04},
+ {0xF8B3, 0x7f},
+ {0xF8B4, 0x0d},
+ {0xF8B5, 0x80},
+ {0xF8B6, 0x2d},
+ {0xF8B7, 0xe5},
+ {0xF8B8, 0x7d},
+ {0xF8B9, 0xb4},
+ {0xF8BA, 0xe0},
+ {0xF8BB, 0x04},
+ {0xF8BC, 0x7f},
+ {0xF8BD, 0x0e},
+ {0xF8BE, 0x80},
+ {0xF8BF, 0x24},
+ {0xF8C0, 0xe5},
+ {0xF8C1, 0x7d},
+ {0xF8C2, 0xb4},
+ {0xF8C3, 0xe4},
+ {0xF8C4, 0x04},
+ {0xF8C5, 0x7f},
+ {0xF8C6, 0x0f},
+ {0xF8C7, 0x80},
+ {0xF8C8, 0x1b},
+ {0xF8C9, 0xe5},
+ {0xF8CA, 0x7d},
+ {0xF8CB, 0xb4},
+ {0xF8CC, 0xe8},
+ {0xF8CD, 0x04},
+ {0xF8CE, 0x7f},
+ {0xF8CF, 0x10},
+ {0xF8D0, 0x80},
+ {0xF8D1, 0x12},
+ {0xF8D2, 0xe5},
+ {0xF8D3, 0x7d},
+ {0xF8D4, 0xb4},
+ {0xF8D5, 0xec},
+ {0xF8D6, 0x04},
+ {0xF8D7, 0x7f},
+ {0xF8D8, 0x11},
+ {0xF8D9, 0x80},
+ {0xF8DA, 0x09},
+ {0xF8DB, 0xe5},
+ {0xF8DC, 0x7d},
+ {0xF8DD, 0x7f},
+ {0xF8DE, 0x00},
+ {0xF8DF, 0xb4},
+ {0xF8E0, 0xf0},
+ {0xF8E1, 0x02},
+ {0xF8E2, 0x7f},
+ {0xF8E3, 0x12},
+ {0xF8E4, 0x8f},
+ {0xF8E5, 0x7c},
+ {0xF8E6, 0xef},
+ {0xF8E7, 0x24},
+ {0xF8E8, 0x95},
+ {0xF8E9, 0xff},
+ {0xF8EA, 0xe4},
+ {0xF8EB, 0x34},
+ {0xF8EC, 0xfb},
+ {0xF8ED, 0x8f},
+ {0xF8EE, 0x82},
+ {0xF8EF, 0xf5},
+ {0xF8F0, 0x83},
+ {0xF8F1, 0xe4},
+ {0xF8F2, 0x93},
+ {0xF8F3, 0xf5},
+ {0xF8F4, 0x7c},
+ {0xF8F5, 0xf5},
+ {0xF8F6, 0x7b},
+ {0xF8F7, 0xe4},
+ {0xF8F8, 0xf5},
+ {0xF8F9, 0x7a},
+ {0xF8FA, 0x75},
+ {0xF8FB, 0x78},
+ {0xF8FC, 0x30},
+ {0xF8FD, 0x75},
+ {0xF8FE, 0x79},
+ {0xF8FF, 0x53},
+ {0xF900, 0x85},
+ {0xF901, 0x79},
+ {0xF902, 0x82},
+ {0xF903, 0x85},
+ {0xF904, 0x78},
+ {0xF905, 0x83},
+ {0xF906, 0xe0},
+ {0xF907, 0x25},
+ {0xF908, 0x7c},
+ {0xF909, 0xf0},
+ {0xF90A, 0x74},
+ {0xF90B, 0x02},
+ {0xF90C, 0x25},
+ {0xF90D, 0x79},
+ {0xF90E, 0xf5},
+ {0xF90F, 0x79},
+ {0xF910, 0xe4},
+ {0xF911, 0x35},
+ {0xF912, 0x78},
+ {0xF913, 0xf5},
+ {0xF914, 0x78},
+ {0xF915, 0x05},
+ {0xF916, 0x7a},
+ {0xF917, 0xe5},
+ {0xF918, 0x7a},
+ {0xF919, 0xb4},
+ {0xF91A, 0x08},
+ {0xF91B, 0xe4},
+ {0xF91C, 0x02},
+ {0xF91D, 0x18},
+ {0xF91E, 0x32},
+ {0xF91F, 0x22},
+ {0xF920, 0xf0},
+ {0xF921, 0x90},
+ {0xF922, 0xa0},
+ {0xF923, 0xf8},
+ {0xF924, 0xe0},
+ {0xF925, 0x70},
+ {0xF926, 0x02},
+ {0xF927, 0xa3},
+ {0xF928, 0xe0},
+ {0xF929, 0x70},
+ {0xF92A, 0x0a},
+ {0xF92B, 0x90},
+ {0xF92C, 0xa1},
+ {0xF92D, 0x10},
+ {0xF92E, 0xe0},
+ {0xF92F, 0xfe},
+ {0xF930, 0xa3},
+ {0xF931, 0xe0},
+ {0xF932, 0xff},
+ {0xF933, 0x80},
+ {0xF934, 0x04},
+ {0xF935, 0x7e},
+ {0xF936, 0x00},
+ {0xF937, 0x7f},
+ {0xF938, 0x00},
+ {0xF939, 0x8e},
+ {0xF93A, 0x7e},
+ {0xF93B, 0x8f},
+ {0xF93C, 0x7f},
+ {0xF93D, 0x90},
+ {0xF93E, 0x36},
+ {0xF93F, 0x0d},
+ {0xF940, 0xe0},
+ {0xF941, 0x44},
+ {0xF942, 0x02},
+ {0xF943, 0xf0},
+ {0xF944, 0x90},
+ {0xF945, 0x36},
+ {0xF946, 0x0e},
+ {0xF947, 0xe5},
+ {0xF948, 0x7e},
+ {0xF949, 0xf0},
+ {0xF94A, 0xa3},
+ {0xF94B, 0xe5},
+ {0xF94C, 0x7f},
+ {0xF94D, 0xf0},
+ {0xF94E, 0xe5},
+ {0xF94F, 0x3a},
+ {0xF950, 0x60},
+ {0xF951, 0x0c},
+ {0xF952, 0x90},
+ {0xF953, 0x36},
+ {0xF954, 0x09},
+ {0xF955, 0xe0},
+ {0xF956, 0x70},
+ {0xF957, 0x06},
+ {0xF958, 0x90},
+ {0xF959, 0x36},
+ {0xF95A, 0x08},
+ {0xF95B, 0xf0},
+ {0xF95C, 0xf5},
+ {0xF95D, 0x3a},
+ {0xF95E, 0x02},
+ {0xF95F, 0x03},
+ {0xF960, 0x94},
+ {0xF961, 0x22},
+ {0xF962, 0x78},
+ {0xF963, 0x07},
+ {0xF964, 0xe6},
+ {0xF965, 0xd3},
+ {0xF966, 0x94},
+ {0xF967, 0x00},
+ {0xF968, 0x40},
+ {0xF969, 0x16},
+ {0xF96A, 0x16},
+ {0xF96B, 0xe6},
+ {0xF96C, 0x90},
+ {0xF96D, 0x30},
+ {0xF96E, 0xa1},
+ {0xF96F, 0xf0},
+ {0xF970, 0x90},
+ {0xF971, 0x43},
+ {0xF972, 0x83},
+ {0xF973, 0xe0},
+ {0xF974, 0xb4},
+ {0xF975, 0x01},
+ {0xF976, 0x0f},
+ {0xF977, 0x90},
+ {0xF978, 0x43},
+ {0xF979, 0x87},
+ {0xF97A, 0xe0},
+ {0xF97B, 0xb4},
+ {0xF97C, 0x01},
+ {0xF97D, 0x08},
+ {0xF97E, 0x80},
+ {0xF97F, 0x00},
+ {0xF980, 0x90},
+ {0xF981, 0x30},
+ {0xF982, 0xa0},
+ {0xF983, 0x74},
+ {0xF984, 0x01},
+ {0xF985, 0xf0},
+ {0xF986, 0x22},
+ {0xF987, 0xf0},
+ {0xF988, 0x90},
+ {0xF989, 0x35},
+ {0xF98A, 0xba},
+ {0xF98B, 0xe0},
+ {0xF98C, 0xb4},
+ {0xF98D, 0x0a},
+ {0xF98E, 0x0d},
+ {0xF98F, 0xa3},
+ {0xF990, 0xe0},
+ {0xF991, 0xb4},
+ {0xF992, 0x01},
+ {0xF993, 0x08},
+ {0xF994, 0x90},
+ {0xF995, 0xfb},
+ {0xF996, 0x94},
+ {0xF997, 0xe0},
+ {0xF998, 0x90},
+ {0xF999, 0x35},
+ {0xF99A, 0xb8},
+ {0xF99B, 0xf0},
+ {0xF99C, 0xd0},
+ {0xF99D, 0xd0},
+ {0xF99E, 0xd0},
+ {0xF99F, 0x82},
+ {0xF9A0, 0xd0},
+ {0xF9A1, 0x83},
+ {0xF9A2, 0xd0},
+ {0xF9A3, 0xe0},
+ {0xF9A4, 0x32},
+ {0xF9A5, 0x22},
+ {0xF9A6, 0xe5},
+ {0xF9A7, 0x7f},
+ {0xF9A8, 0x45},
+ {0xF9A9, 0x7e},
+ {0xF9AA, 0x60},
+ {0xF9AB, 0x15},
+ {0xF9AC, 0x90},
+ {0xF9AD, 0x01},
+ {0xF9AE, 0x00},
+ {0xF9AF, 0xe0},
+ {0xF9B0, 0x70},
+ {0xF9B1, 0x0f},
+ {0xF9B2, 0x90},
+ {0xF9B3, 0xa0},
+ {0xF9B4, 0xf8},
+ {0xF9B5, 0xe5},
+ {0xF9B6, 0x7e},
+ {0xF9B7, 0xf0},
+ {0xF9B8, 0xa3},
+ {0xF9B9, 0xe5},
+ {0xF9BA, 0x7f},
+ {0xF9BB, 0xf0},
+ {0xF9BC, 0xe4},
+ {0xF9BD, 0xf5},
+ {0xF9BE, 0x7e},
+ {0xF9BF, 0xf5},
+ {0xF9C0, 0x7f},
+ {0xF9C1, 0x22},
+ {0xF9C2, 0x02},
+ {0xF9C3, 0x0e},
+ {0xF9C4, 0x79},
+ {0xF9C5, 0x22},
+ /* Offsets:*/
+ {0x35C6, 0x00},/* FIDDLEDARKCAL*/
+ {0x35C7, 0x00},
+ {0x35C8, 0x01},/*STOREDISTANCEATSTOPSTREAMING*/
+ {0x35C9, 0x20},
+ {0x35CA, 0x01},/*BRUCEFIX*/
+ {0x35CB, 0x62},
+ {0x35CC, 0x01},/*FIXDATAXFERSTATUSREG*/
+ {0x35CD, 0x87},
+ {0x35CE, 0x01},/*FOCUSDISTANCEUPDATE*/
+ {0x35CF, 0xA6},
+ {0x35D0, 0x01},/*SKIPEDOFRESET*/
+ {0x35D1, 0xC2},
+ {0x35D2, 0x00},
+ {0x35D3, 0xFB},
+ {0x35D4, 0x00},
+ {0x35D5, 0x94},
+ {0x35D6, 0x00},
+ {0x35D7, 0xFB},
+ {0x35D8, 0x00},
+ {0x35D9, 0x94},
+ {0x35DA, 0x00},
+ {0x35DB, 0xFB},
+ {0x35DC, 0x00},
+ {0x35DD, 0x94},
+ {0x35DE, 0x00},
+ {0x35DF, 0xFB},
+ {0x35E0, 0x00},
+ {0x35E1, 0x94},
+ {0x35E6, 0x18},/* FIDDLEDARKCAL*/
+ {0x35E7, 0x2F},
+ {0x35E8, 0x03},/* STOREDISTANCEATSTOPSTREAMING*/
+ {0x35E9, 0x93},
+ {0x35EA, 0x18},/* BRUCEFIX*/
+ {0x35EB, 0x99},
+ {0x35EC, 0x00},/* FIXDATAXFERSTATUSREG*/
+ {0x35ED, 0xA3},
+ {0x35EE, 0x21},/* FOCUSDISTANCEUPDATE*/
+ {0x35EF, 0x5B},
+ {0x35F0, 0x0E},/* SKIPEDOFRESET*/
+ {0x35F1, 0x74},
+ {0x35F2, 0x04},
+ {0x35F3, 0x64},
+ {0x35F4, 0x04},
+ {0x35F5, 0x65},
+ {0x35F6, 0x04},
+ {0x35F7, 0x7B},
+ {0x35F8, 0x04},
+ {0x35F9, 0x7C},
+ {0x35FA, 0x04},
+ {0x35FB, 0xDD},
+ {0x35FC, 0x04},
+ {0x35FD, 0xDE},
+ {0x35FE, 0x04},
+ {0x35FF, 0xEF},
+ {0x3600, 0x04},
+ {0x3601, 0xF0},
+ /*Jump/Data:*/
+ {0x35C2, 0x3F},/* Jump Reg*/
+ {0x35C3, 0xFF},/* Jump Reg*/
+ {0x35C4, 0x3F},/* Data Reg*/
+ {0x35C5, 0xC0},/* Data Reg*/
+ {0x35C0, 0x01},/* Enable*/
+
+};
+
+static struct vx6953_i2c_reg_conf edof_tbl[] = {
+ {0xa098, 0x02},
+ {0xa099, 0x87},
+ {0xa09c, 0x00},
+ {0xa09d, 0xc5},
+ {0xa4ec, 0x05},
+ {0xa4ed, 0x05},
+ {0xa4f0, 0x04},
+ {0xa4f1, 0x04},
+ {0xa4f4, 0x04},
+ {0xa4f5, 0x05},
+ {0xa4f8, 0x05},
+ {0xa4f9, 0x07},
+ {0xa4fc, 0x07},
+ {0xa4fd, 0x07},
+ {0xa500, 0x07},
+ {0xa501, 0x07},
+ {0xa504, 0x08},
+ {0xa505, 0x08},
+ {0xa518, 0x01},
+ {0xa519, 0x02},
+ {0xa51c, 0x01},
+ {0xa51d, 0x00},
+ {0xa534, 0x00},
+ {0xa535, 0x04},
+ {0xa538, 0x04},
+ {0xa539, 0x03},
+ {0xa53c, 0x05},
+ {0xa53d, 0x07},
+ {0xa540, 0x07},
+ {0xa541, 0x06},
+ {0xa544, 0x07},
+ {0xa545, 0x06},
+ {0xa548, 0x05},
+ {0xa549, 0x06},
+ {0xa54c, 0x06},
+ {0xa54d, 0x07},
+ {0xa550, 0x07},
+ {0xa551, 0x04},
+ {0xa554, 0x04},
+ {0xa555, 0x04},
+ {0xa558, 0x05},
+ {0xa559, 0x06},
+ {0xa55c, 0x07},
+ {0xa55d, 0x07},
+ {0xa56c, 0x00},
+ {0xa56d, 0x0a},
+ {0xa570, 0x08},
+ {0xa571, 0x05},
+ {0xa574, 0x04},
+ {0xa575, 0x03},
+ {0xa578, 0x04},
+ {0xa579, 0x04},
+ {0xa58c, 0x1f},
+ {0xa58d, 0x1b},
+ {0xa590, 0x17},
+ {0xa591, 0x13},
+ {0xa594, 0x10},
+ {0xa595, 0x0d},
+ {0xa598, 0x0f},
+ {0xa599, 0x11},
+ {0xa59c, 0x03},
+ {0xa59d, 0x03},
+ {0xa5a0, 0x03},
+ {0xa5a1, 0x03},
+ {0xa5a4, 0x03},
+ {0xa5a5, 0x04},
+ {0xa5a8, 0x05},
+ {0xa5a9, 0x00},
+ {0xa5ac, 0x00},
+ {0xa5ad, 0x00},
+ {0xa5b0, 0x00},
+ {0xa5b1, 0x00},
+ {0xa5b4, 0x00},
+ {0xa5b5, 0x00},
+ {0xa5c4, 0x1f},
+ {0xa5c5, 0x13},
+ {0xa5c8, 0x14},
+ {0xa5c9, 0x14},
+ {0xa5cc, 0x14},
+ {0xa5cd, 0x13},
+ {0xa5d0, 0x17},
+ {0xa5d1, 0x1a},
+ {0xa5f4, 0x05},
+ {0xa5f5, 0x05},
+ {0xa5f8, 0x05},
+ {0xa5f9, 0x06},
+ {0xa5fc, 0x06},
+ {0xa5fd, 0x06},
+ {0xa600, 0x06},
+ {0xa601, 0x06},
+ {0xa608, 0x07},
+ {0xa609, 0x08},
+ {0xa60c, 0x08},
+ {0xa60d, 0x07},
+ {0xa63c, 0x00},
+ {0xa63d, 0x02},
+ {0xa640, 0x02},
+ {0xa641, 0x02},
+ {0xa644, 0x02},
+ {0xa645, 0x02},
+ {0xa648, 0x03},
+ {0xa649, 0x04},
+ {0xa64c, 0x0a},
+ {0xa64d, 0x09},
+ {0xa650, 0x08},
+ {0xa651, 0x09},
+ {0xa654, 0x09},
+ {0xa655, 0x0a},
+ {0xa658, 0x0a},
+ {0xa659, 0x0a},
+ {0xa65c, 0x0a},
+ {0xa65d, 0x09},
+ {0xa660, 0x09},
+ {0xa661, 0x09},
+ {0xa664, 0x09},
+ {0xa665, 0x08},
+ {0xa680, 0x01},
+ {0xa681, 0x02},
+ {0xa694, 0x1f},
+ {0xa695, 0x10},
+ {0xa698, 0x0e},
+ {0xa699, 0x0c},
+ {0xa69c, 0x0d},
+ {0xa69d, 0x0d},
+ {0xa6a0, 0x0f},
+ {0xa6a1, 0x11},
+ {0xa6a4, 0x00},
+ {0xa6a5, 0x00},
+ {0xa6a8, 0x00},
+ {0xa6a9, 0x00},
+ {0xa6ac, 0x00},
+ {0xa6ad, 0x00},
+ {0xa6b0, 0x00},
+ {0xa6b1, 0x04},
+ {0xa6b4, 0x04},
+ {0xa6b5, 0x04},
+ {0xa6b8, 0x04},
+ {0xa6b9, 0x04},
+ {0xa6bc, 0x05},
+ {0xa6bd, 0x05},
+ {0xa6c0, 0x1f},
+ {0xa6c1, 0x1f},
+ {0xa6c4, 0x1f},
+ {0xa6c5, 0x1f},
+ {0xa6c8, 0x1f},
+ {0xa6c9, 0x1f},
+ {0xa6cc, 0x1f},
+ {0xa6cd, 0x0b},
+ {0xa6d0, 0x0c},
+ {0xa6d1, 0x0d},
+ {0xa6d4, 0x0d},
+ {0xa6d5, 0x0d},
+ {0xa6d8, 0x11},
+ {0xa6d9, 0x14},
+ {0xa6fc, 0x02},
+ {0xa6fd, 0x03},
+ {0xa700, 0x03},
+ {0xa701, 0x03},
+ {0xa704, 0x03},
+ {0xa705, 0x04},
+ {0xa708, 0x05},
+ {0xa709, 0x02},
+ {0xa70c, 0x02},
+ {0xa70d, 0x02},
+ {0xa710, 0x03},
+ {0xa711, 0x04},
+ {0xa714, 0x04},
+ {0xa715, 0x04},
+ {0xa744, 0x00},
+ {0xa745, 0x03},
+ {0xa748, 0x04},
+ {0xa749, 0x04},
+ {0xa74c, 0x05},
+ {0xa74d, 0x06},
+ {0xa750, 0x07},
+ {0xa751, 0x07},
+ {0xa754, 0x05},
+ {0xa755, 0x05},
+ {0xa758, 0x05},
+ {0xa759, 0x05},
+ {0xa75c, 0x05},
+ {0xa75d, 0x06},
+ {0xa760, 0x07},
+ {0xa761, 0x07},
+ {0xa764, 0x06},
+ {0xa765, 0x05},
+ {0xa768, 0x05},
+ {0xa769, 0x05},
+ {0xa76c, 0x06},
+ {0xa76d, 0x07},
+ {0xa77c, 0x00},
+ {0xa77d, 0x05},
+ {0xa780, 0x05},
+ {0xa781, 0x05},
+ {0xa784, 0x05},
+ {0xa785, 0x04},
+ {0xa788, 0x05},
+ {0xa789, 0x06},
+ {0xa79c, 0x1f},
+ {0xa79d, 0x15},
+ {0xa7a0, 0x13},
+ {0xa7a1, 0x10},
+ {0xa7a4, 0x0f},
+ {0xa7a5, 0x0d},
+ {0xa7a8, 0x11},
+ {0xa7a9, 0x14},
+ {0xa7ac, 0x02},
+ {0xa7ad, 0x02},
+ {0xa7b0, 0x02},
+ {0xa7b1, 0x02},
+ {0xa7b4, 0x02},
+ {0xa7b5, 0x03},
+ {0xa7b8, 0x03},
+ {0xa7b9, 0x00},
+ {0xa7bc, 0x00},
+ {0xa7bd, 0x00},
+ {0xa7c0, 0x00},
+ {0xa7c1, 0x00},
+ {0xa7c4, 0x00},
+ {0xa7c5, 0x00},
+ {0xa7d4, 0x1f},
+ {0xa7d5, 0x0d},
+ {0xa7d8, 0x0f},
+ {0xa7d9, 0x10},
+ {0xa7dc, 0x10},
+ {0xa7dd, 0x10},
+ {0xa7e0, 0x13},
+ {0xa7e1, 0x16},
+ {0xa7f4, 0x00},
+ {0xa7f5, 0x03},
+ {0xa7f8, 0x04},
+ {0xa7f9, 0x04},
+ {0xa7fc, 0x04},
+ {0xa7fd, 0x03},
+ {0xa800, 0x03},
+ {0xa801, 0x03},
+ {0xa804, 0x03},
+ {0xa805, 0x03},
+ {0xa808, 0x03},
+ {0xa809, 0x03},
+ {0xa80c, 0x03},
+ {0xa80d, 0x04},
+ {0xa810, 0x04},
+ {0xa811, 0x0a},
+ {0xa814, 0x0a},
+ {0xa815, 0x0a},
+ {0xa818, 0x0f},
+ {0xa819, 0x14},
+ {0xa81c, 0x14},
+ {0xa81d, 0x14},
+ {0xa82c, 0x00},
+ {0xa82d, 0x04},
+ {0xa830, 0x02},
+ {0xa831, 0x00},
+ {0xa834, 0x00},
+ {0xa835, 0x00},
+ {0xa838, 0x00},
+ {0xa839, 0x00},
+ {0xa840, 0x1f},
+ {0xa841, 0x1f},
+ {0xa848, 0x1f},
+ {0xa849, 0x1f},
+ {0xa84c, 0x1f},
+ {0xa84d, 0x0c},
+ {0xa850, 0x0c},
+ {0xa851, 0x0c},
+ {0xa854, 0x0c},
+ {0xa855, 0x0c},
+ {0xa858, 0x0c},
+ {0xa859, 0x0c},
+ {0xa85c, 0x0c},
+ {0xa85d, 0x0c},
+ {0xa860, 0x0c},
+ {0xa861, 0x0c},
+ {0xa864, 0x0c},
+ {0xa865, 0x0c},
+ {0xa868, 0x0c},
+ {0xa869, 0x0c},
+ {0xa86c, 0x0c},
+ {0xa86d, 0x0c},
+ {0xa870, 0x0c},
+ {0xa871, 0x0c},
+ {0xa874, 0x0c},
+ {0xa875, 0x0c},
+ {0xa878, 0x1f},
+ {0xa879, 0x1f},
+ {0xa87c, 0x1f},
+ {0xa87d, 0x1f},
+ {0xa880, 0x1f},
+ {0xa881, 0x1f},
+ {0xa884, 0x1f},
+ {0xa885, 0x0c},
+ {0xa888, 0x0c},
+ {0xa889, 0x0c},
+ {0xa88c, 0x0c},
+ {0xa88d, 0x0c},
+ {0xa890, 0x0c},
+ {0xa891, 0x0c},
+ {0xa898, 0x1f},
+ {0xa899, 0x1f},
+ {0xa8a0, 0x1f},
+ {0xa8a1, 0x1f},
+ {0xa8a4, 0x1f},
+ {0xa8a5, 0x0c},
+ {0xa8a8, 0x0c},
+ {0xa8a9, 0x0c},
+ {0xa8ac, 0x0c},
+ {0xa8ad, 0x0c},
+ {0xa8b0, 0x0c},
+ {0xa8b1, 0x0c},
+ {0xa8b4, 0x0c},
+ {0xa8b5, 0x0c},
+ {0xa8b8, 0x0c},
+ {0xa8b9, 0x0c},
+ {0xa8bc, 0x0c},
+ {0xa8bd, 0x0c},
+ {0xa8c0, 0x0c},
+ {0xa8c1, 0x0c},
+ {0xa8c4, 0x0c},
+ {0xa8c5, 0x0c},
+ {0xa8c8, 0x0c},
+ {0xa8c9, 0x0c},
+ {0xa8cc, 0x0c},
+ {0xa8cd, 0x0c},
+ {0xa8d0, 0x1f},
+ {0xa8d1, 0x1f},
+ {0xa8d4, 0x1f},
+ {0xa8d5, 0x1f},
+ {0xa8d8, 0x1f},
+ {0xa8d9, 0x1f},
+ {0xa8dc, 0x1f},
+ {0xa8dd, 0x0c},
+ {0xa8e0, 0x0c},
+ {0xa8e1, 0x0c},
+ {0xa8e4, 0x0c},
+ {0xa8e5, 0x0c},
+ {0xa8e8, 0x0c},
+ {0xa8e9, 0x0c},
+ {0xa8f0, 0x1f},
+ {0xa8f1, 0x1f},
+ {0xa8f8, 0x1f},
+ {0xa8f9, 0x1f},
+ {0xa8fc, 0x1f},
+ {0xa8fd, 0x0c},
+ {0xa900, 0x0c},
+ {0xa901, 0x0c},
+ {0xa904, 0x0c},
+ {0xa905, 0x0c},
+ {0xa908, 0x0c},
+ {0xa909, 0x0c},
+ {0xa90c, 0x0c},
+ {0xa90d, 0x0c},
+ {0xa910, 0x0c},
+ {0xa911, 0x0c},
+ {0xa914, 0x0c},
+ {0xa915, 0x0c},
+ {0xa918, 0x0c},
+ {0xa919, 0x0c},
+ {0xa91c, 0x0c},
+ {0xa91d, 0x0c},
+ {0xa920, 0x0c},
+ {0xa921, 0x0c},
+ {0xa924, 0x0c},
+ {0xa925, 0x0c},
+ {0xa928, 0x1f},
+ {0xa929, 0x1f},
+ {0xa92c, 0x1f},
+ {0xa92d, 0x1f},
+ {0xa930, 0x1f},
+ {0xa931, 0x1f},
+ {0xa934, 0x1f},
+ {0xa935, 0x0c},
+ {0xa938, 0x0c},
+ {0xa939, 0x0c},
+ {0xa93c, 0x0c},
+ {0xa93d, 0x0c},
+ {0xa940, 0x0c},
+ {0xa941, 0x0c},
+ {0xa96c, 0x0d},
+ {0xa96d, 0x16},
+ {0xa970, 0x19},
+ {0xa971, 0x0e},
+ {0xa974, 0x16},
+ {0xa975, 0x1a},
+ {0xa978, 0x0d},
+ {0xa979, 0x15},
+ {0xa97c, 0x19},
+ {0xa97d, 0x0d},
+ {0xa980, 0x15},
+ {0xa981, 0x1a},
+ {0xa984, 0x0d},
+ {0xa985, 0x15},
+ {0xa988, 0x1a},
+ {0xa989, 0x0d},
+ {0xa98c, 0x15},
+ {0xa98d, 0x1a},
+ {0xa990, 0x0b},
+ {0xa991, 0x11},
+ {0xa994, 0x02},
+ {0xa995, 0x0e},
+ {0xa998, 0x16},
+ {0xa999, 0x02},
+ {0xa99c, 0x0c},
+ {0xa99d, 0x13},
+ {0xa9a0, 0x02},
+ {0xa9a1, 0x0c},
+ {0xa9a4, 0x12},
+ {0xa9a5, 0x02},
+ {0xa9a8, 0x0c},
+ {0xa9a9, 0x12},
+ {0xa9ac, 0x02},
+ {0xa9ad, 0x0c},
+ {0xa9b0, 0x12},
+ {0xa9b1, 0x02},
+ {0xa9b4, 0x10},
+ {0xa9b5, 0x1e},
+ {0xa9b8, 0x0f},
+ {0xa9b9, 0x13},
+ {0xa9bc, 0x20},
+ {0xa9bd, 0x10},
+ {0xa9c0, 0x11},
+ {0xa9c1, 0x1e},
+ {0xa9c4, 0x10},
+ {0xa9c5, 0x11},
+ {0xa9c8, 0x1e},
+ {0xa9c9, 0x10},
+ {0xa9cc, 0x11},
+ {0xa9cd, 0x20},
+ {0xa9d0, 0x10},
+ {0xa9d1, 0x13},
+ {0xa9d4, 0x24},
+ {0xa9d5, 0x10},
+ {0xa9f0, 0x02},
+ {0xa9f1, 0x01},
+ {0xa9f8, 0x19},
+ {0xa9f9, 0x0b},
+ {0xa9fc, 0x0a},
+ {0xa9fd, 0x07},
+ {0xaa00, 0x0c},
+ {0xaa01, 0x0e},
+ {0xaa08, 0x0c},
+ {0xaa09, 0x06},
+ {0xaa0c, 0x0c},
+ {0xaa0d, 0x0a},
+ {0xaa24, 0x10},
+ {0xaa25, 0x12},
+ {0xaa28, 0x0b},
+ {0xaa29, 0x07},
+ {0xaa2c, 0x10},
+ {0xaa2d, 0x14},
+ {0xaa34, 0x0e},
+ {0xaa35, 0x0e},
+ {0xaa38, 0x07},
+ {0xaa39, 0x07},
+ {0xaa3c, 0x0e},
+ {0xaa3d, 0x0c},
+ {0xaa48, 0x09},
+ {0xaa49, 0x0c},
+ {0xaa4c, 0x0c},
+ {0xaa4d, 0x07},
+ {0xaa54, 0x08},
+ {0xaa55, 0x06},
+ {0xaa58, 0x04},
+ {0xaa59, 0x05},
+ {0xaa5c, 0x06},
+ {0xaa5d, 0x06},
+ {0xaa68, 0x05},
+ {0xaa69, 0x05},
+ {0xaa6c, 0x04},
+ {0xaa6d, 0x05},
+ {0xaa74, 0x06},
+ {0xaa75, 0x04},
+ {0xaa78, 0x05},
+ {0xaa79, 0x05},
+ {0xaa7c, 0x04},
+ {0xaa7d, 0x06},
+ {0xac18, 0x14},
+ {0xac19, 0x00},
+ {0xac1c, 0x14},
+ {0xac1d, 0x00},
+ {0xac20, 0x14},
+ {0xac21, 0x00},
+ {0xac24, 0x14},
+ {0xac25, 0x00},
+ {0xac28, 0x14},
+ {0xac29, 0x00},
+ {0xac2c, 0x14},
+ {0xac2d, 0x00},
+ {0xac34, 0x16},
+ {0xac35, 0x00},
+ {0xac38, 0x16},
+ {0xac39, 0x00},
+ {0xac3c, 0x16},
+ {0xac3d, 0x00},
+ {0xac40, 0x16},
+ {0xac41, 0x00},
+ {0xac44, 0x16},
+ {0xac45, 0x00},
+ {0xac48, 0x16},
+ {0xac49, 0x00},
+ {0xac50, 0x1b},
+ {0xac51, 0x00},
+ {0xac54, 0x1b},
+ {0xac55, 0x00},
+ {0xac58, 0x1b},
+ {0xac59, 0x00},
+ {0xac5c, 0x1b},
+ {0xac5d, 0x00},
+ {0xac60, 0x1b},
+ {0xac61, 0x00},
+ {0xac64, 0x1b},
+ {0xac65, 0x00},
+ {0xac74, 0x09},
+ {0xac75, 0x0c},
+ {0xac78, 0x0f},
+ {0xac79, 0x11},
+ {0xac7c, 0x12},
+ {0xac7d, 0x14},
+ {0xac80, 0x09},
+ {0xac81, 0x0c},
+ {0xac84, 0x0f},
+ {0xac85, 0x11},
+ {0xac88, 0x12},
+ {0xac89, 0x14},
+ {0xac8c, 0x09},
+ {0xac8d, 0x0c},
+ {0xac90, 0x0f},
+ {0xac91, 0x11},
+ {0xac94, 0x12},
+ {0xac95, 0x14},
+ {0xac98, 0x09},
+ {0xac99, 0x0c},
+ {0xac9c, 0x0f},
+ {0xac9d, 0x11},
+ {0xaca0, 0x12},
+ {0xaca1, 0x14},
+ {0xaca4, 0x09},
+ {0xaca5, 0x0c},
+ {0xaca8, 0x0f},
+ {0xaca9, 0x11},
+ {0xacac, 0x12},
+ {0xacad, 0x14},
+ {0xacb0, 0x07},
+ {0xacb1, 0x09},
+ {0xacb4, 0x0c},
+ {0xacb5, 0x0d},
+ {0xacb8, 0x0d},
+ {0xacb9, 0x0e},
+ {0xacbc, 0x05},
+ {0xacbd, 0x07},
+ {0xacc0, 0x0a},
+ {0xacc1, 0x0b},
+ {0xacc4, 0x0b},
+ {0xacc5, 0x0c},
+ {0xacc8, 0x03},
+ {0xacc9, 0x04},
+ {0xaccc, 0x07},
+ {0xaccd, 0x08},
+ {0xacd0, 0x09},
+ {0xacd1, 0x09}
+};
+
+static struct vx6953_i2c_reg_conf patch_tbl_cut3[] = {
+ {0xF800, 0x90},
+ {0xF801, 0x30},
+ {0xF802, 0x31},
+ {0xF803, 0xe0},
+ {0xF804, 0xf5},
+ {0xF805, 0x7d},
+ {0xF806, 0xb4},
+ {0xF807, 0x01},
+ {0xF808, 0x06},
+ {0xF809, 0x75},
+ {0xF80A, 0x7d},
+ {0xF80B, 0x03},
+ {0xF80C, 0x74},
+ {0xF80D, 0x03},
+ {0xF80E, 0xf0},
+ {0xF80F, 0x90},
+ {0xF810, 0x30},
+ {0xF811, 0x04},
+ {0xF812, 0x74},
+ {0xF813, 0x33},
+ {0xF814, 0xf0},
+ {0xF815, 0x90},
+ {0xF816, 0x30},
+ {0xF817, 0x06},
+ {0xF818, 0xe4},
+ {0xF819, 0xf0},
+ {0xF81A, 0xa3},
+ {0xF81B, 0x74},
+ {0xF81C, 0x09},
+ {0xF81D, 0xf0},
+ {0xF81E, 0x90},
+ {0xF81F, 0x30},
+ {0xF820, 0x10},
+ {0xF821, 0xe4},
+ {0xF822, 0xf0},
+ {0xF823, 0xa3},
+ {0xF824, 0xf0},
+ {0xF825, 0x90},
+ {0xF826, 0x30},
+ {0xF827, 0x16},
+ {0xF828, 0x74},
+ {0xF829, 0x1e},
+ {0xF82A, 0xf0},
+ {0xF82B, 0x90},
+ {0xF82C, 0x30},
+ {0xF82D, 0x1a},
+ {0xF82E, 0x74},
+ {0xF82F, 0x6a},
+ {0xF830, 0xf0},
+ {0xF831, 0xa3},
+ {0xF832, 0x74},
+ {0xF833, 0x29},
+ {0xF834, 0xf0},
+ {0xF835, 0x90},
+ {0xF836, 0x30},
+ {0xF837, 0x30},
+ {0xF838, 0x74},
+ {0xF839, 0x08},
+ {0xF83A, 0xf0},
+ {0xF83B, 0x90},
+ {0xF83C, 0x30},
+ {0xF83D, 0x36},
+ {0xF83E, 0x74},
+ {0xF83F, 0x2c},
+ {0xF840, 0xf0},
+ {0xF841, 0x90},
+ {0xF842, 0x30},
+ {0xF843, 0x41},
+ {0xF844, 0xe4},
+ {0xF845, 0xf0},
+ {0xF846, 0xa3},
+ {0xF847, 0x74},
+ {0xF848, 0x24},
+ {0xF849, 0xf0},
+ {0xF84A, 0x90},
+ {0xF84B, 0x30},
+ {0xF84C, 0x45},
+ {0xF84D, 0x74},
+ {0xF84E, 0x81},
+ {0xF84F, 0xf0},
+ {0xF850, 0x90},
+ {0xF851, 0x30},
+ {0xF852, 0x98},
+ {0xF853, 0x74},
+ {0xF854, 0x01},
+ {0xF855, 0xf0},
+ {0xF856, 0x90},
+ {0xF857, 0x30},
+ {0xF858, 0x9d},
+ {0xF859, 0x74},
+ {0xF85A, 0x05},
+ {0xF85B, 0xf0},
+ {0xF85C, 0xe5},
+ {0xF85D, 0x7d},
+ {0xF85E, 0x70},
+ {0xF85F, 0x10},
+ {0xF860, 0x90},
+ {0xF861, 0x30},
+ {0xF862, 0x05},
+ {0xF863, 0x04},
+ {0xF864, 0xf0},
+ {0xF865, 0x90},
+ {0xF866, 0x30},
+ {0xF867, 0x30},
+ {0xF868, 0xe4},
+ {0xF869, 0xf0},
+ {0xF86A, 0x90},
+ {0xF86B, 0x30},
+ {0xF86C, 0x35},
+ {0xF86D, 0x04},
+ {0xF86E, 0xf0},
+ {0xF86F, 0x22},
+ {0xF870, 0xe5},
+ {0xF871, 0x7d},
+ {0xF872, 0x64},
+ {0xF873, 0x02},
+ {0xF874, 0x70},
+ {0xF875, 0x2d},
+ {0xF876, 0x90},
+ {0xF877, 0x30},
+ {0xF878, 0x04},
+ {0xF879, 0x74},
+ {0xF87A, 0x34},
+ {0xF87B, 0xf0},
+ {0xF87C, 0xa3},
+ {0xF87D, 0x74},
+ {0xF87E, 0x07},
+ {0xF87F, 0xf0},
+ {0xF880, 0x90},
+ {0xF881, 0x30},
+ {0xF882, 0x10},
+ {0xF883, 0x74},
+ {0xF884, 0x10},
+ {0xF885, 0xf0},
+ {0xF886, 0x90},
+ {0xF887, 0x30},
+ {0xF888, 0x16},
+ {0xF889, 0x74},
+ {0xF88A, 0x1f},
+ {0xF88B, 0xf0},
+ {0xF88C, 0x90},
+ {0xF88D, 0x30},
+ {0xF88E, 0x1a},
+ {0xF88F, 0x74},
+ {0xF890, 0x62},
+ {0xF891, 0xf0},
+ {0xF892, 0x90},
+ {0xF893, 0x30},
+ {0xF894, 0x35},
+ {0xF895, 0x74},
+ {0xF896, 0x04},
+ {0xF897, 0xf0},
+ {0xF898, 0x90},
+ {0xF899, 0x30},
+ {0xF89A, 0x41},
+ {0xF89B, 0x74},
+ {0xF89C, 0x60},
+ {0xF89D, 0xf0},
+ {0xF89E, 0xa3},
+ {0xF89F, 0x74},
+ {0xF8A0, 0x64},
+ {0xF8A1, 0xf0},
+ {0xF8A2, 0x22},
+ {0xF8A3, 0xe5},
+ {0xF8A4, 0x7d},
+ {0xF8A5, 0xb4},
+ {0xF8A6, 0x03},
+ {0xF8A7, 0x12},
+ {0xF8A8, 0x90},
+ {0xF8A9, 0x30},
+ {0xF8AA, 0x05},
+ {0xF8AB, 0x74},
+ {0xF8AC, 0x03},
+ {0xF8AD, 0xf0},
+ {0xF8AE, 0x90},
+ {0xF8AF, 0x30},
+ {0xF8B0, 0x11},
+ {0xF8B1, 0x74},
+ {0xF8B2, 0x01},
+ {0xF8B3, 0xf0},
+ {0xF8B4, 0x90},
+ {0xF8B5, 0x30},
+ {0xF8B6, 0x35},
+ {0xF8B7, 0x74},
+ {0xF8B8, 0x03},
+ {0xF8B9, 0xf0},
+ {0xF8BA, 0x22},
+ {0xF8BB, 0xc3},
+ {0xF8BC, 0x90},
+ {0xF8BD, 0x0b},
+ {0xF8BE, 0x89},
+ {0xF8BF, 0xe0},
+ {0xF8C0, 0x94},
+ {0xF8C1, 0x1e},
+ {0xF8C2, 0x90},
+ {0xF8C3, 0x0b},
+ {0xF8C4, 0x88},
+ {0xF8C5, 0xe0},
+ {0xF8C6, 0x94},
+ {0xF8C7, 0x00},
+ {0xF8C8, 0x50},
+ {0xF8C9, 0x06},
+ {0xF8CA, 0x7e},
+ {0xF8CB, 0x00},
+ {0xF8CC, 0x7f},
+ {0xF8CD, 0x01},
+ {0xF8CE, 0x80},
+ {0xF8CF, 0x3d},
+ {0xF8D0, 0xc3},
+ {0xF8D1, 0x90},
+ {0xF8D2, 0x0b},
+ {0xF8D3, 0x89},
+ {0xF8D4, 0xe0},
+ {0xF8D5, 0x94},
+ {0xF8D6, 0x3c},
+ {0xF8D7, 0x90},
+ {0xF8D8, 0x0b},
+ {0xF8D9, 0x88},
+ {0xF8DA, 0xe0},
+ {0xF8DB, 0x94},
+ {0xF8DC, 0x00},
+ {0xF8DD, 0x50},
+ {0xF8DE, 0x06},
+ {0xF8DF, 0x7e},
+ {0xF8E0, 0x00},
+ {0xF8E1, 0x7f},
+ {0xF8E2, 0x02},
+ {0xF8E3, 0x80},
+ {0xF8E4, 0x28},
+ {0xF8E5, 0xc3},
+ {0xF8E6, 0x90},
+ {0xF8E7, 0x0b},
+ {0xF8E8, 0x89},
+ {0xF8E9, 0xe0},
+ {0xF8EA, 0x94},
+ {0xF8EB, 0xfa},
+ {0xF8EC, 0x90},
+ {0xF8ED, 0x0b},
+ {0xF8EE, 0x88},
+ {0xF8EF, 0xe0},
+ {0xF8F0, 0x94},
+ {0xF8F1, 0x00},
+ {0xF8F2, 0x50},
+ {0xF8F3, 0x06},
+ {0xF8F4, 0x7e},
+ {0xF8F5, 0x00},
+ {0xF8F6, 0x7f},
+ {0xF8F7, 0x03},
+ {0xF8F8, 0x80},
+ {0xF8F9, 0x13},
+ {0xF8FA, 0xc3},
+ {0xF8FB, 0x90},
+ {0xF8FC, 0x0b},
+ {0xF8FD, 0x88},
+ {0xF8FE, 0xe0},
+ {0xF8FF, 0x94},
+ {0xF900, 0x80},
+ {0xF901, 0x50},
+ {0xF902, 0x06},
+ {0xF903, 0x7e},
+ {0xF904, 0x00},
+ {0xF905, 0x7f},
+ {0xF906, 0x04},
+ {0xF907, 0x80},
+ {0xF908, 0x04},
+ {0xF909, 0xae},
+ {0xF90A, 0x7e},
+ {0xF90B, 0xaf},
+ {0xF90C, 0x7f},
+ {0xF90D, 0x90},
+ {0xF90E, 0xa0},
+ {0xF90F, 0xf8},
+ {0xF910, 0xee},
+ {0xF911, 0xf0},
+ {0xF912, 0xa3},
+ {0xF913, 0xef},
+ {0xF914, 0xf0},
+ {0xF915, 0x22},
+ {0xF916, 0x90},
+ {0xF917, 0x33},
+ {0xF918, 0x82},
+ {0xF919, 0xe0},
+ {0xF91A, 0xff},
+ {0xF91B, 0x64},
+ {0xF91C, 0x01},
+ {0xF91D, 0x70},
+ {0xF91E, 0x30},
+ {0xF91F, 0xe5},
+ {0xF920, 0x7f},
+ {0xF921, 0x64},
+ {0xF922, 0x02},
+ {0xF923, 0x45},
+ {0xF924, 0x7e},
+ {0xF925, 0x70},
+ {0xF926, 0x04},
+ {0xF927, 0x7d},
+ {0xF928, 0x1e},
+ {0xF929, 0x80},
+ {0xF92A, 0x1d},
+ {0xF92B, 0xe5},
+ {0xF92C, 0x7f},
+ {0xF92D, 0x64},
+ {0xF92E, 0x03},
+ {0xF92F, 0x45},
+ {0xF930, 0x7e},
+ {0xF931, 0x70},
+ {0xF932, 0x04},
+ {0xF933, 0x7d},
+ {0xF934, 0x3c},
+ {0xF935, 0x80},
+ {0xF936, 0x11},
+ {0xF937, 0xe5},
+ {0xF938, 0x7f},
+ {0xF939, 0x64},
+ {0xF93A, 0x04},
+ {0xF93B, 0x45},
+ {0xF93C, 0x7e},
+ {0xF93D, 0x70},
+ {0xF93E, 0x04},
+ {0xF93F, 0x7d},
+ {0xF940, 0xfa},
+ {0xF941, 0x80},
+ {0xF942, 0x05},
+ {0xF943, 0x90},
+ {0xF944, 0x33},
+ {0xF945, 0x81},
+ {0xF946, 0xe0},
+ {0xF947, 0xfd},
+ {0xF948, 0xae},
+ {0xF949, 0x05},
+ {0xF94A, 0x90},
+ {0xF94B, 0x33},
+ {0xF94C, 0x81},
+ {0xF94D, 0xed},
+ {0xF94E, 0xf0},
+ {0xF94F, 0xef},
+ {0xF950, 0xb4},
+ {0xF951, 0x01},
+ {0xF952, 0x10},
+ {0xF953, 0x90},
+ {0xF954, 0x01},
+ {0xF955, 0x00},
+ {0xF956, 0xe0},
+ {0xF957, 0x60},
+ {0xF958, 0x0a},
+ {0xF959, 0x90},
+ {0xF95A, 0xa1},
+ {0xF95B, 0x10},
+ {0xF95C, 0xe0},
+ {0xF95D, 0xf5},
+ {0xF95E, 0x7e},
+ {0xF95F, 0xa3},
+ {0xF960, 0xe0},
+ {0xF961, 0xf5},
+ {0xF962, 0x7f},
+ {0xF963, 0x22},
+ {0xF964, 0x12},
+ {0xF965, 0x2f},
+ {0xF966, 0x4d},
+ {0xF967, 0x90},
+ {0xF968, 0x35},
+ {0xF969, 0x38},
+ {0xF96A, 0xe0},
+ {0xF96B, 0x70},
+ {0xF96C, 0x05},
+ {0xF96D, 0x12},
+ {0xF96E, 0x00},
+ {0xF96F, 0x0e},
+ {0xF970, 0x80},
+ {0xF971, 0x03},
+ {0xF972, 0x12},
+ {0xF973, 0x07},
+ {0xF974, 0xc9},
+ {0xF975, 0x90},
+ {0xF976, 0x40},
+ {0xF977, 0x06},
+ {0xF978, 0xe0},
+ {0xF979, 0xf4},
+ {0xF97A, 0x54},
+ {0xF97B, 0x02},
+ {0xF97C, 0xff},
+ {0xF97D, 0xe0},
+ {0xF97E, 0x54},
+ {0xF97F, 0x01},
+ {0xF980, 0x4f},
+ {0xF981, 0x90},
+ {0xF982, 0x31},
+ {0xF983, 0x32},
+ {0xF984, 0xf0},
+ {0xF985, 0x90},
+ {0xF986, 0xfa},
+ {0xF987, 0x9d},
+ {0xF988, 0xe0},
+ {0xF989, 0x70},
+ {0xF98A, 0x03},
+ {0xF98B, 0x12},
+ {0xF98C, 0x27},
+ {0xF98D, 0x27},
+ {0xF98E, 0x02},
+ {0xF98F, 0x05},
+ {0xF990, 0xac},
+ {0xF991, 0x22},
+ {0xF992, 0x78},
+ {0xF993, 0x07},
+ {0xF994, 0xe6},
+ {0xF995, 0xf5},
+ {0xF996, 0x7c},
+ {0xF997, 0xe5},
+ {0xF998, 0x7c},
+ {0xF999, 0x60},
+ {0xF99A, 0x1d},
+ {0xF99B, 0x90},
+ {0xF99C, 0x43},
+ {0xF99D, 0x83},
+ {0xF99E, 0xe0},
+ {0xF99F, 0xb4},
+ {0xF9A0, 0x01},
+ {0xF9A1, 0x16},
+ {0xF9A2, 0x90},
+ {0xF9A3, 0x43},
+ {0xF9A4, 0x87},
+ {0xF9A5, 0xe0},
+ {0xF9A6, 0xb4},
+ {0xF9A7, 0x01},
+ {0xF9A8, 0x0f},
+ {0xF9A9, 0x15},
+ {0xF9AA, 0x7c},
+ {0xF9AB, 0x90},
+ {0xF9AC, 0x30},
+ {0xF9AD, 0xa1},
+ {0xF9AE, 0xe5},
+ {0xF9AF, 0x7c},
+ {0xF9B0, 0xf0},
+ {0xF9B1, 0x90},
+ {0xF9B2, 0x30},
+ {0xF9B3, 0xa0},
+ {0xF9B4, 0x74},
+ {0xF9B5, 0x01},
+ {0xF9B6, 0xf0},
+ {0xF9B7, 0x22},
+ {0xF9B8, 0xe4},
+ {0xF9B9, 0x90},
+ {0xF9BA, 0x30},
+ {0xF9BB, 0xa0},
+ {0xF9BC, 0xf0},
+ {0xF9BD, 0x22},
+ {0xF9BE, 0xf0},
+ {0xF9BF, 0xe5},
+ {0xF9C0, 0x3a},
+ {0xF9C1, 0xb4},
+ {0xF9C2, 0x06},
+ {0xF9C3, 0x06},
+ {0xF9C4, 0x63},
+ {0xF9C5, 0x3e},
+ {0xF9C6, 0x02},
+ {0xF9C7, 0x12},
+ {0xF9C8, 0x03},
+ {0xF9C9, 0xea},
+ {0xF9CA, 0x02},
+ {0xF9CB, 0x17},
+ {0xF9CC, 0x4a},
+ {0xF9CD, 0x22},
+ {0x35C9, 0xBB},
+ {0x35CA, 0x01},
+ {0x35CB, 0x16},
+ {0x35CC, 0x01},
+ {0x35CD, 0x64},
+ {0x35CE, 0x01},
+ {0x35CF, 0x92},
+ {0x35D0, 0x01},
+ {0x35D1, 0xBE},
+ {0x35D3, 0xF6},
+ {0x35D5, 0x07},
+ {0x35D7, 0xA3},
+ {0x35DB, 0x02},
+ {0x35DD, 0x06},
+ {0x35DF, 0x1B},
+ {0x35E6, 0x28},
+ {0x35E7, 0x76},
+ {0x35E8, 0x2D},
+ {0x35E9, 0x07},
+ {0x35EA, 0x04},
+ {0x35EB, 0x43},
+ {0x35EC, 0x05},
+ {0x35ED, 0xA9},
+ {0x35EE, 0x2A},
+ {0x35EF, 0x15},
+ {0x35F0, 0x17},
+ {0x35F1, 0x41},
+ {0x35F2, 0x24},
+ {0x35F3, 0x88},
+ {0x35F4, 0x01},
+ {0x35F5, 0x54},
+ {0x35F6, 0x01},
+ {0x35F7, 0x55},
+ {0x35F8, 0x2E},
+ {0x35F9, 0xF2},
+ {0x35FA, 0x06},
+ {0x35FB, 0x02},
+ {0x35FC, 0x06},
+ {0x35FD, 0x03},
+ {0x35FE, 0x06},
+ {0x35FF, 0x04},
+ {0x35C2, 0x1F},
+ {0x35C3, 0xFF},
+ {0x35C4, 0x1F},
+ {0x35C5, 0xC0},
+ {0x35C0, 0x01},
+};
+
+struct vx6953_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+
+static const struct vx6953_format vx6953_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ }
+ /* more can be supported, to be added later */
+};
+
+
+/*=============================================================*/
+
+static int vx6953_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 = 2,
+ .buf = rxdata,
+ },
+ };
+ if (i2c_transfer(vx6953_client->adapter, msgs, 2) < 0) {
+ CDBG("vx6953_i2c_rxdata failed!\n");
+ return -EIO;
+ }
+ return 0;
+}
+static int32_t vx6953_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(vx6953_client->adapter, msg, 1) < 0) {
+ CDBG("vx6953_i2c_txdata faild 0x%x\n", vx6953_client->addr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int32_t vx6953_i2c_read(unsigned short raddr,
+ unsigned short *rdata, int rlen)
+{
+ int32_t rc = 0;
+ unsigned char buf[2];
+ if (!rdata)
+ return -EIO;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (raddr & 0xFF00) >> 8;
+ buf[1] = (raddr & 0x00FF);
+ rc = vx6953_i2c_rxdata(vx6953_client->addr>>1, buf, rlen);
+ if (rc < 0) {
+ CDBG("vx6953_i2c_read 0x%x failed!\n", raddr);
+ return rc;
+ }
+ *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]);
+ return rc;
+}
+static int32_t vx6953_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[3];
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ buf[2] = bdata;
+ CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata);
+ rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, 3);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata);
+ }
+ return rc;
+}
+static int32_t vx6953_i2c_write_seq_sensor(unsigned short waddr,
+ uint8_t *bdata, uint16_t len)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[len+2];
+ int i;
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (waddr & 0xFF00) >> 8;
+ buf[1] = (waddr & 0x00FF);
+ for (i = 2; i < len+2; i++)
+ buf[i] = *bdata++;
+ rc = vx6953_i2c_txdata(vx6953_client->addr>>1, buf, len+2);
+ if (rc < 0) {
+ CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n",
+ waddr, bdata[0]);
+ }
+ return rc;
+}
+
+static int32_t vx6953_i2c_write_w_table(struct vx6953_i2c_reg_conf const
+ *reg_conf_tbl, int num)
+{
+ int i;
+ int32_t rc = -EIO;
+ for (i = 0; i < num; i++) {
+ rc = vx6953_i2c_write_b_sensor(reg_conf_tbl->waddr,
+ reg_conf_tbl->wdata);
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
+static void vx6953_get_pict_fps(uint16_t fps, uint16_t *pfps)
+{
+ /* input fps is preview fps in Q8 format */
+ uint16_t preview_frame_length_lines, snapshot_frame_length_lines;
+ uint16_t preview_line_length_pck, snapshot_line_length_pck;
+ uint32_t divider, d1, d2;
+ /* Total frame_length_lines and line_length_pck for preview */
+ preview_frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ preview_line_length_pck = VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ /* Total frame_length_lines and line_length_pck for snapshot */
+ snapshot_frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+ snapshot_line_length_pck = VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+ d1 = preview_frame_length_lines * 0x00000400/
+ snapshot_frame_length_lines;
+ d2 = preview_line_length_pck * 0x00000400/
+ snapshot_line_length_pck;
+ divider = d1 * d2 / 0x400;
+ /*Verify PCLK settings and frame sizes.*/
+ *pfps = (uint16_t) (fps * divider / 0x400);
+ /* 2 is the ratio of no.of snapshot channels
+ to number of preview channels */
+
+}
+
+static uint16_t vx6953_get_prev_lines_pf(void)
+{
+ if (vx6953_ctrl->prev_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_HEIGHT + VX6953_VER_QTR_BLK_LINES;
+ else
+ return VX6953_FULL_SIZE_HEIGHT + VX6953_VER_FULL_BLK_LINES;
+
+}
+
+static uint16_t vx6953_get_prev_pixels_pl(void)
+{
+ if (vx6953_ctrl->prev_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_WIDTH + VX6953_HRZ_QTR_BLK_PIXELS;
+ else
+ return VX6953_FULL_SIZE_WIDTH + VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint16_t vx6953_get_pict_lines_pf(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ else
+ return VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+}
+
+static uint16_t vx6953_get_pict_pixels_pl(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ else
+ return VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+}
+
+static uint32_t vx6953_get_pict_max_exp_lc(void)
+{
+ if (vx6953_ctrl->pict_res == QTR_SIZE)
+ return (VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES)*24;
+ else
+ return (VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES)*24;
+}
+
+static int32_t vx6953_set_fps(struct fps_cfg *fps)
+{
+ uint16_t total_lines_per_frame;
+ int32_t rc = 0;
+ total_lines_per_frame = (uint16_t)((VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES) * vx6953_ctrl->fps_divider/0x400);
+ if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI,
+ ((total_lines_per_frame & 0xFF00) >> 8)) < 0)
+ return rc;
+ if (vx6953_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO,
+ (total_lines_per_frame & 0x00FF)) < 0)
+ return rc;
+ return rc;
+}
+
+static int32_t vx6953_write_exp_gain(uint16_t gain, uint32_t line)
+{
+ uint16_t line_length_pck, frame_length_lines;
+ uint8_t gain_hi, gain_lo;
+ uint8_t intg_time_hi, intg_time_lo;
+ uint8_t line_length_pck_hi = 0, line_length_pck_lo = 0;
+ uint16_t line_length_ratio = 1 * Q8;
+ int32_t rc = 0;
+ if (vx6953_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) {
+ frame_length_lines = VX6953_QTR_SIZE_HEIGHT +
+ VX6953_VER_QTR_BLK_LINES;
+ line_length_pck = VX6953_QTR_SIZE_WIDTH +
+ VX6953_HRZ_QTR_BLK_PIXELS;
+ if (line > (frame_length_lines -
+ VX6953_STM5M0EDOF_OFFSET)) {
+ vx6953_ctrl->fps = (uint16_t) (30 * Q8 *
+ (frame_length_lines - VX6953_STM5M0EDOF_OFFSET)/
+ line);
+ } else {
+ vx6953_ctrl->fps = (uint16_t) (30 * Q8);
+ }
+ } else {
+ frame_length_lines = VX6953_FULL_SIZE_HEIGHT +
+ VX6953_VER_FULL_BLK_LINES;
+ line_length_pck = VX6953_FULL_SIZE_WIDTH +
+ VX6953_HRZ_FULL_BLK_PIXELS;
+ }
+ /* calculate line_length_ratio */
+ if ((frame_length_lines - VX6953_STM5M0EDOF_OFFSET) < line) {
+ line_length_ratio = (line*Q8) /
+ (frame_length_lines - VX6953_STM5M0EDOF_OFFSET);
+ line = frame_length_lines - VX6953_STM5M0EDOF_OFFSET;
+ } else {
+ line_length_ratio = 1*Q8;
+ }
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD);
+ line_length_pck = (line_length_pck >
+ MAX_LINE_LENGTH_PCK) ?
+ MAX_LINE_LENGTH_PCK : line_length_pck;
+ line_length_pck = (uint16_t) (line_length_pck *
+ line_length_ratio/Q8);
+ line_length_pck_hi = (uint8_t) ((line_length_pck &
+ 0xFF00) >> 8);
+ line_length_pck_lo = (uint8_t) (line_length_pck &
+ 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_HI,
+ line_length_pck_hi);
+ vx6953_i2c_write_b_sensor(REG_LINE_LENGTH_PCK_LO,
+ line_length_pck_lo);
+ /* update analogue gain registers */
+ gain_hi = (uint8_t) ((gain & 0xFF00) >> 8);
+ gain_lo = (uint8_t) (gain & 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ gain_lo);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_R_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_RED_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_BLUE_LO, gain_hi);
+ vx6953_i2c_write_b_sensor(REG_DIGITAL_GAIN_GREEN_B_LO, gain_hi);
+ CDBG("%s, gain_hi 0x%x, gain_lo 0x%x\n", __func__,
+ gain_hi, gain_lo);
+ /* update line count registers */
+ intg_time_hi = (uint8_t) (((uint16_t)line & 0xFF00) >> 8);
+ intg_time_lo = (uint8_t) ((uint16_t)line & 0x00FF);
+ vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI,
+ intg_time_hi);
+ vx6953_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO,
+ intg_time_lo);
+ vx6953_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD,
+ GROUPED_PARAMETER_HOLD_OFF);
+
+ return rc;
+}
+
+static int32_t vx6953_set_pict_exp_gain(uint16_t gain, uint32_t line)
+{
+ int32_t rc = 0;
+ rc = vx6953_write_exp_gain(gain, line);
+ return rc;
+} /* endof vx6953_set_pict_exp_gain*/
+
+static int32_t vx6953_move_focus(int direction,
+ int32_t num_steps)
+{
+ return 0;
+}
+
+
+static int32_t vx6953_set_default_focus(uint8_t af_step)
+{
+ return 0;
+}
+
+static int32_t vx6953_test(enum vx6953_test_mode_t mo)
+{
+ int32_t rc = 0;
+ if (mo == TEST_OFF)
+ return rc;
+ else {
+ /* REG_0x30D8[4] is TESBYPEN: 0: Normal Operation,
+ 1: Bypass Signal Processing
+ REG_0x30D8[5] is EBDMASK: 0:
+ Output Embedded data, 1: No output embedded data */
+ if (vx6953_i2c_write_b_sensor(REG_TEST_PATTERN_MODE,
+ (uint8_t) mo) < 0) {
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int vx6953_enable_edof(enum edof_mode_t edof_mode)
+{
+ int rc = 0;
+ if (edof_mode == VX6953_EDOF_ESTIMATION) {
+ /* EDof Estimation mode for preview */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x02) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_ESTIMATION");
+ } else if (edof_mode == VX6953_EDOF_APPLICATION) {
+ /* EDof Application mode for Capture */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x01) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_APPLICATION");
+ } else {
+ /* EDOF disabled */
+ if (vx6953_i2c_write_b_sensor(REG_0x0b80, 0x00) < 0)
+ return rc;
+ CDBG("VX6953_EDOF_DISABLE");
+ }
+ return rc;
+}
+
+static int32_t vx6953_patch_for_cut2(void)
+{
+ int32_t rc = 0;
+ rc = vx6953_i2c_write_w_table(patch_tbl_cut2,
+ ARRAY_SIZE(patch_tbl_cut2));
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+static int32_t vx6953_patch_for_cut3(void)
+{
+ int32_t rc = 0;
+ rc = vx6953_i2c_write_w_table(patch_tbl_cut3,
+ ARRAY_SIZE(patch_tbl_cut3));
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+static int32_t vx6953_sensor_setting(int update_type, int rt)
+{
+
+ int32_t rc = 0;
+ unsigned short frame_cnt;
+ struct msm_camera_csi_params vx6953_csi_params;
+ if (vx6953_ctrl->sensor_type != VX6953_STM5M0EDOF_CUT_2) {
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {0x6003, 0x01},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {0x3006, 0x00},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {0x301b, 0x29},
+ /* DEFCOR settings */
+ /*Single Defect Correction Weight DISABLE*/
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {REG_0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {REG_0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {REG_0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {REG_0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {REG_0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ };
+ /* reset fps_divider */
+ vx6953_ctrl->fps = 30 * Q8;
+ /* stop streaming */
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ CDBG("Init vx6953_sensor_setting standby\n");
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+
+
+ vx6953_patch_for_cut3();
+ rc = vx6953_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_i2c_write_b_sensor(0x0b80, 0x00);
+ vx6953_i2c_write_b_sensor(0x3388, 0x03);
+ vx6953_i2c_write_b_sensor(0x3640, 0x00);
+
+ rc = vx6953_i2c_write_w_table(&edof_tbl[0],
+ ARRAY_SIZE(edof_tbl));
+ vx6953_i2c_write_b_sensor(0x3388, 0x00);
+
+ }
+ return rc;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf preview_mode_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {0x6003, 0x01},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+
+ {REG_0x3210, vx6953_regs.reg_pat[rt].reg_0x3210},
+ {REG_0x0111, vx6953_regs.reg_pat[rt].reg_0x111},
+ {REG_0x3410, vx6953_regs.reg_pat[rt].reg_0x3410},
+
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3006, 0x00},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {REG_0x301b, 0x29},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+ {REG_0x3098, vx6953_regs.reg_pat[rt].reg_0x3098},
+ {REG_0x309d, vx6953_regs.reg_pat[rt].reg_0x309D},
+
+ {REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+
+ {REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+ {REG_0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3030, 0x08},
+ {REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+
+ {0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+ {0x201, vx6953_regs.reg_pat[rt].reg_0x0201},
+
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+
+ {REG_0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {REG_0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture
+ mode(standard settings - Not tuned) */
+ {REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {REG_0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {REG_0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {REG_0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ {0x3393, 0x06}, /* man_spec_edof_ctrl_edof*/
+ {0x3394, 0x07}, /* man_spec_edof_ctrl_edof*/
+ };
+
+ struct vx6953_i2c_reg_conf snapshot_mode_tbl[] = {
+ {REG_MODE_SELECT, MODE_SELECT_STANDBY_MODE},
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {0x6003, 0x01},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {0x303, 1}, /* VT_SYS_CLK_DIV */
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {0x30b, 1},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_0x3210, vx6953_regs.reg_pat[rt].reg_0x3210},
+ {REG_0x0111, vx6953_regs.reg_pat[rt].reg_0x111},
+
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {0x3140, 0x01}, /* AV2X2 block enabled */
+ {REG_0x3410, vx6953_regs.reg_pat[rt].reg_0x3410},
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+
+
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3006, 0x00},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {0x301A, 0x6A},
+ {REG_0x301b, 0x29},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+ {REG_0x3098, vx6953_regs.reg_pat[rt].reg_0x3098},
+ {REG_0x309d, vx6953_regs.reg_pat[rt].reg_0x309D},
+
+ {REG_0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {REG_0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+
+ {REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {REG_0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {REG_0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {REG_0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ {0x3393, 0x06}, /* man_spec_edof_ctrl*/
+ {0x3394, 0x07}, /* man_spec_edof_ctrl*/
+ };
+ /* stop streaming */
+ msleep(5);
+
+ /* Reset everything first */
+
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_csi_params.data_format = CSI_8BIT;
+ vx6953_csi_params.lane_cnt = 1;
+ vx6953_csi_params.lane_assign = 0xe4;
+ vx6953_csi_params.dpcm_scheme = 0;
+ vx6953_csi_params.settle_cnt = 7;
+ rc = msm_camio_csi_config(&vx6953_csi_params);
+ if (rc < 0)
+ CDBG(" config csi controller failed\n");
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_patch_for_cut3();
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ if (rt == RES_PREVIEW) {
+ rc = vx6953_i2c_write_w_table(
+ &preview_mode_tbl[0],
+ ARRAY_SIZE(preview_mode_tbl));
+ if (rc < 0)
+ return rc;
+ }
+ if (rt == RES_CAPTURE) {
+ rc = vx6953_i2c_write_w_table(
+ &snapshot_mode_tbl[0],
+ ARRAY_SIZE(snapshot_mode_tbl));
+ if (rc < 0)
+ return rc;
+ }
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ /* Start sensor streaming */
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stream);
+ /* man_spec_edof_ctrl_tune_smooth_lowlight*/
+ vx6953_i2c_write_b_sensor(0x338d, 0x08);
+ /* man_spec_edof_ctrl_tune_smooth_indoor*/
+ vx6953_i2c_write_b_sensor(0x338e, 0x08);
+ /* man_spec_edof_ctrl_tune_smooth_outdoor*/
+ vx6953_i2c_write_b_sensor(0x338f, 0x00);
+ /*Apply Capture FPGA state machine reset*/
+ vx6953_i2c_write_b_sensor(0x16, 0x00);
+ msleep(100);
+ vx6953_i2c_write_b_sensor(0x16, 0x01);
+
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+
+ while (frame_cnt == 0xFF) {
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+ CDBG("frame_cnt=%d", frame_cnt);
+ msleep(10);
+ }
+ }
+ return rc;
+ default:
+ return rc;
+ }
+ } else {
+ switch (update_type) {
+ case REG_INIT:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {REG_0x3016,
+ vx6953_regs.reg_pat_init[0].reg_0x3016},
+ {REG_0x301d,
+ vx6953_regs.reg_pat_init[0].reg_0x301d},
+ {REG_0x317e,
+ vx6953_regs.reg_pat_init[0].reg_0x317e},
+ {REG_0x317f,
+ vx6953_regs.reg_pat_init[0].reg_0x317f},
+ {REG_0x3400,
+ vx6953_regs.reg_pat_init[0].reg_0x3400},
+ /* DEFCOR settings */
+ /*Single Defect Correction Weight DISABLE*/
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ {REG_0x1716,
+ vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717,
+ vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718,
+ vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719,
+ vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ /* reset fps_divider */
+ vx6953_ctrl->fps = 30 * Q8;
+ /* stop streaming */
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ CDBG("Init vx6953_sensor_setting standby\n");
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+ vx6953_patch_for_cut2();
+ rc = vx6953_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+ }
+ return rc;
+ case UPDATE_PERIODIC:
+ if (rt == RES_PREVIEW || rt == RES_CAPTURE) {
+ struct vx6953_i2c_reg_conf init_mode_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {REG_0x3016,
+ vx6953_regs.reg_pat_init[0].reg_0x3016},
+ {REG_0x301d,
+ vx6953_regs.reg_pat_init[0].reg_0x301d},
+ {REG_0x317e,
+ vx6953_regs.reg_pat_init[0].reg_0x317e},
+ {REG_0x317f,
+ vx6953_regs.reg_pat_init[0].reg_0x317f},
+ {REG_0x3400,
+ vx6953_regs.reg_pat_init[0].reg_0x3400},
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ {REG_0x1716,
+ vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717,
+ vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718,
+ vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719,
+ vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ struct vx6953_i2c_reg_conf mode_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].line_length_pck_lo},
+ {REG_0x3005, vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010, vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011, vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a, vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035, vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036, vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041, vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042, vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045, vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture
+ mode(standard settings - Not tuned) */
+ {REG_0x0b80, vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900, vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901, vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902, vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383, vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387, vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c, vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d, vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e, vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f, vx6953_regs.reg_pat[rt].reg_0x034f},
+ /*{0x200, vx6953_regs.reg_pat[rt].reg_0x0200},
+ {0x201, vx6953_regs.reg_pat[rt].reg_0x0201},*/
+ {REG_0x1716, vx6953_regs.reg_pat[rt].reg_0x1716},
+ {REG_0x1717, vx6953_regs.reg_pat[rt].reg_0x1717},
+ {REG_0x1718, vx6953_regs.reg_pat[rt].reg_0x1718},
+ {REG_0x1719, vx6953_regs.reg_pat[rt].reg_0x1719},
+ };
+ /* stop streaming */
+ msleep(5);
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_csi_params.data_format = CSI_8BIT;
+ vx6953_csi_params.lane_cnt = 1;
+ vx6953_csi_params.lane_assign = 0xe4;
+ vx6953_csi_params.dpcm_scheme = 0;
+ vx6953_csi_params.settle_cnt = 7;
+ rc = msm_camio_csi_config(&vx6953_csi_params);
+ if (rc < 0)
+ CDBG(" config csi controller failed\n");
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_patch_for_cut2();
+ rc = vx6953_i2c_write_w_table(&init_mode_tbl[0],
+ ARRAY_SIZE(init_mode_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ rc = vx6953_i2c_write_w_table(&mode_tbl[0],
+ ARRAY_SIZE(mode_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ /* Start sensor streaming */
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+ msleep(vx6953_stm5m0edof_delay_msecs_stream);
+
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+
+ while (frame_cnt == 0xFF) {
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+ CDBG("frame_cnt=%d", frame_cnt);
+ msleep(10);
+ }
+ }
+ return rc;
+ default:
+ return rc;
+ }
+ }
+ return rc;
+}
+
+
+static int32_t vx6953_video_config(int mode)
+{
+
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (vx6953_ctrl->prev_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ if (vx6953_ctrl->set_test) {
+ if (vx6953_test(vx6953_ctrl->set_test) < 0)
+ return rc;
+ }
+ vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+ rc = vx6953_enable_edof(vx6953_ctrl->edof_mode);
+ if (rc < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->prev_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+}
+
+static int32_t vx6953_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /*change sensor resolution if needed */
+ if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+ if (vx6953_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider) /
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+
+ vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+ if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int32_t vx6953_raw_snapshot_config(int mode)
+{
+ int32_t rc = 0;
+ int rt;
+ /* change sensor resolution if needed */
+ if (vx6953_ctrl->curr_res != vx6953_ctrl->pict_res) {
+ if (vx6953_ctrl->pict_res == QTR_SIZE) {
+ rt = RES_PREVIEW;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((2 * 1000 * vx6953_ctrl->fps_divider)/
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ } else {
+ rt = RES_CAPTURE;
+ vx6953_stm5m0edof_delay_msecs_stdby =
+ ((((1000 * vx6953_ctrl->fps_divider)/
+ vx6953_ctrl->fps) * Q8) / Q10) + 1;
+ }
+ if (vx6953_sensor_setting(UPDATE_PERIODIC, rt) < 0)
+ return rc;
+ }
+ vx6953_ctrl->edof_mode = VX6953_EDOF_APPLICATION;
+ if (vx6953_enable_edof(vx6953_ctrl->edof_mode) < 0)
+ return rc;
+ vx6953_ctrl->curr_res = vx6953_ctrl->pict_res;
+ vx6953_ctrl->sensormode = mode;
+ return rc;
+} /*end of vx6953_raw_snapshot_config*/
+static int32_t vx6953_set_sensor_mode(int mode,
+ int res)
+{
+ int32_t rc = 0;
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ rc = vx6953_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ rc = vx6953_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ rc = vx6953_raw_snapshot_config(mode);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+static int32_t vx6953_power_down(void)
+{
+ vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE);
+ return 0;
+}
+
+
+static int vx6953_probe_init_done(const struct msm_camera_sensor_info *data)
+{
+ gpio_free(data->sensor_reset);
+ kfree(vx6953_ctrl);
+ vx6953_ctrl = NULL;
+ return 0;
+}
+static int vx6953_probe_init_sensor(const struct msm_camera_sensor_info *data)
+{
+ unsigned short revision_number;
+ int32_t rc = 0;
+ unsigned short chipidl, chipidh;
+ CDBG("%s: %d\n", __func__, __LINE__);
+ rc = gpio_request(data->sensor_reset, "vx6953");
+ CDBG(" vx6953_probe_init_sensor\n");
+ if (!rc) {
+ CDBG("sensor_reset = %d\n", rc);
+ CDBG(" vx6953_probe_init_sensor 1\n");
+ gpio_direction_output(data->sensor_reset, 0);
+ msleep(50);
+ CDBG(" vx6953_probe_init_sensor 1\n");
+ gpio_direction_output(data->sensor_reset, 1);
+ msleep(13);
+ } else {
+ CDBG(" vx6953_probe_init_sensor 2\n");
+ goto init_probe_done;
+ }
+ msleep(20);
+ CDBG(" vx6953_probe_init_sensor is called\n");
+ /* 3. Read sensor Model ID: */
+ rc = vx6953_i2c_read(0x0000, &chipidh, 1);
+ if (rc < 0) {
+ CDBG(" vx6953_probe_init_sensor 3\n");
+ goto init_probe_fail;
+ }
+ rc = vx6953_i2c_read(0x0001, &chipidl, 1);
+ if (rc < 0) {
+ CDBG(" vx6953_probe_init_sensor4\n");
+ goto init_probe_fail;
+ }
+ CDBG("vx6953 model_id = 0x%x 0x%x\n", chipidh, chipidl);
+ /* 4. Compare sensor ID to VX6953 ID: */
+ if (chipidh != 0x03 || chipidl != 0xB9) {
+ rc = -ENODEV;
+ CDBG("vx6953_probe_init_sensor fail chip id doesnot match\n");
+ goto init_probe_fail;
+ }
+
+ vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+ if (!vx6953_ctrl) {
+ CDBG("vx6953_init failed!\n");
+ rc = -ENOMEM;
+ }
+ vx6953_ctrl->fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->set_test = TEST_OFF;
+ vx6953_ctrl->prev_res = QTR_SIZE;
+ vx6953_ctrl->pict_res = FULL_SIZE;
+ vx6953_ctrl->curr_res = INVALID_SIZE;
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+
+ if (data)
+ vx6953_ctrl->sensordata = data;
+
+ if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number major = 0x%x\n", revision_number);
+ if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number = 0x%x\n", revision_number);
+ if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+ CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+ } else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+ } else {/* Cut1.0 reads 0x00 for register 0x0018*/
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+ CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+ }
+
+ if (vx6953_ctrl->prev_res == QTR_SIZE) {
+ if (vx6953_sensor_setting(REG_INIT, RES_PREVIEW) < 0)
+ goto init_probe_fail;
+ } else {
+ if (vx6953_sensor_setting(REG_INIT, RES_CAPTURE) < 0)
+ goto init_probe_fail;
+ }
+
+ goto init_probe_done;
+init_probe_fail:
+ CDBG(" vx6953_probe_init_sensor fails\n");
+ gpio_direction_output(data->sensor_reset, 0);
+ vx6953_probe_init_done(data);
+init_probe_done:
+ CDBG(" vx6953_probe_init_sensor finishes\n");
+ return rc;
+ }
+/* camsensor_iu060f_vx6953_reset */
+int vx6953_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ unsigned short revision_number;
+ int32_t rc = 0;
+
+ CDBG("%s: %d\n", __func__, __LINE__);
+ CDBG("Calling vx6953_sensor_open_init\n");
+ rc = gpio_request(data->sensor_reset, "vx6953");
+ if (!rc)
+ CDBG("vx6953 gpio_request fail\n");
+
+ vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+ if (!vx6953_ctrl) {
+ CDBG("vx6953_init failed!\n");
+ rc = -ENOMEM;
+ goto init_done;
+ }
+ vx6953_ctrl->fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->pict_fps_divider = 1 * 0x00000400;
+ vx6953_ctrl->set_test = TEST_OFF;
+ vx6953_ctrl->prev_res = QTR_SIZE;
+ vx6953_ctrl->pict_res = FULL_SIZE;
+ vx6953_ctrl->curr_res = INVALID_SIZE;
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ vx6953_ctrl->edof_mode = VX6953_EDOF_ESTIMATION;
+ if (data)
+ vx6953_ctrl->sensordata = data;
+ if (rc < 0) {
+ CDBG("Calling vx6953_sensor_open_init fail1\n");
+ return rc;
+ }
+ CDBG("%s: %d\n", __func__, __LINE__);
+ /* enable mclk first */
+ msm_camio_clk_rate_set(VX6953_STM5M0EDOF_DEFAULT_MASTER_CLK_RATE);
+ CDBG("%s: %d\n", __func__, __LINE__);
+ if (vx6953_i2c_read(0x0002, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number major = 0x%x\n", revision_number);
+ if (vx6953_i2c_read(0x0018, &revision_number, 1) < 0)
+ return rc;
+ CDBG("sensor revision number = 0x%x\n", revision_number);
+ if (revision_number == VX6953_REVISION_NUMBER_CUT3) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_3;
+ CDBG("VX6953 EDof Cut 3.0 sensor\n ");
+ } else if (revision_number == VX6953_REVISION_NUMBER_CUT2) {
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_2;
+ CDBG("VX6953 EDof Cut 2.0 sensor\n ");
+ } else {/* Cut1.0 reads 0x00 for register 0x0018*/
+ vx6953_ctrl->sensor_type = VX6953_STM5M0EDOF_CUT_1;
+ CDBG("VX6953 EDof Cut 1.0 sensor\n ");
+ }
+
+ vx6953_ctrl->fps = 30*Q8;
+ if (rc < 0)
+ goto init_fail;
+ else
+ goto init_done;
+init_fail:
+ CDBG("init_fail\n");
+ gpio_direction_output(data->sensor_reset, 0);
+ vx6953_probe_init_done(data);
+init_done:
+ CDBG("init_done\n");
+ return rc;
+} /*endof vx6953_sensor_open_init*/
+
+static int vx6953_init_client(struct i2c_client *client)
+{
+ /* Initialize the MSM_CAMI2C Chip */
+ init_waitqueue_head(&vx6953_wait_queue);
+ return 0;
+}
+
+static const struct i2c_device_id vx6953_i2c_id[] = {
+ {"vx6953", 0},
+ { }
+};
+
+static int vx6953_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ CDBG("vx6953_probe called!\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ CDBG("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ vx6953_sensorw = kzalloc(sizeof(struct vx6953_work_t), GFP_KERNEL);
+ if (!vx6953_sensorw) {
+ CDBG("kzalloc failed.\n");
+ rc = -ENOMEM;
+ goto probe_failure;
+ }
+
+ i2c_set_clientdata(client, vx6953_sensorw);
+ vx6953_init_client(client);
+ vx6953_client = client;
+
+ msleep(50);
+
+ CDBG("vx6953_probe successed! rc = %d\n", rc);
+ return 0;
+
+probe_failure:
+ CDBG("vx6953_probe failed! rc = %d\n", rc);
+ return rc;
+}
+
+static int vx6953_send_wb_info(struct wb_info_cfg *wb)
+{
+ unsigned short read_data;
+ uint8_t temp[8];
+ int rc = 0;
+ int i = 0;
+
+ /* red_gain */
+ temp[2] = wb->red_gain >> 8;
+ temp[3] = wb->red_gain & 0xFF;
+
+ /* green_gain */
+ temp[0] = wb->green_gain >> 8;
+ temp[1] = wb->green_gain & 0xFF;
+ temp[6] = temp[0];
+ temp[7] = temp[1];
+
+ /* blue_gain */
+ temp[4] = wb->blue_gain >> 8;
+ temp[5] = wb->blue_gain & 0xFF;
+ rc = vx6953_i2c_write_seq_sensor(0x0B8E, &temp[0], 8);
+
+ for (i = 0; i < 6; i++) {
+ rc = vx6953_i2c_read(0x0B8E + i, &read_data, 1);
+ CDBG("%s addr 0x%x val %d\n", __func__, 0x0B8E + i, read_data);
+ }
+ rc = vx6953_i2c_read(0x0B82, &read_data, 1);
+ CDBG("%s addr 0x%x val %d\n", __func__, 0x0B82, read_data);
+ if (rc < 0)
+ return rc;
+ return rc;
+} /*end of vx6953_snapshot_config*/
+
+static int __exit vx6953_remove(struct i2c_client *client)
+{
+ struct vx6953_work_t_t *sensorw = i2c_get_clientdata(client);
+ free_irq(client->irq, sensorw);
+ vx6953_client = NULL;
+ kfree(sensorw);
+ return 0;
+}
+
+static struct i2c_driver vx6953_i2c_driver = {
+ .id_table = vx6953_i2c_id,
+ .probe = vx6953_i2c_probe,
+ .remove = __exit_p(vx6953_i2c_remove),
+ .driver = {
+ .name = "vx6953",
+ },
+};
+
+static int vx6953_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;
+ mutex_lock(&vx6953_mut);
+ CDBG("vx6953_sensor_config: cfgtype = %d\n",
+ cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_GET_PICT_FPS:
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 =
+ vx6953_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 = vx6953_set_fps(&(cdata.cfg.fps));
+ break;
+
+ case CFG_SET_EXP_GAIN:
+ rc =
+ vx6953_write_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_PICT_EXP_GAIN:
+ rc =
+ vx6953_set_pict_exp_gain(
+ cdata.cfg.exp_gain.gain,
+ cdata.cfg.exp_gain.line);
+ break;
+
+ case CFG_SET_MODE:
+ rc = vx6953_set_sensor_mode(cdata.mode,
+ cdata.rs);
+ break;
+
+ case CFG_PWR_DOWN:
+ rc = vx6953_power_down();
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc =
+ vx6953_move_focus(
+ cdata.cfg.focus.dir,
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc =
+ vx6953_set_default_focus(
+ cdata.cfg.focus.steps);
+ break;
+
+ case CFG_SET_EFFECT:
+ rc = vx6953_set_default_focus(
+ cdata.cfg.effect);
+ break;
+
+
+ case CFG_SEND_WB_INFO:
+ rc = vx6953_send_wb_info(
+ &(cdata.cfg.wb_info));
+ break;
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&vx6953_mut);
+
+ return rc;
+}
+
+
+
+
+static int vx6953_sensor_release(void)
+{
+ int rc = -EBADF;
+ mutex_lock(&vx6953_mut);
+ vx6953_power_down();
+ gpio_free(vx6953_ctrl->sensordata->sensor_reset);
+ kfree(vx6953_ctrl);
+ vx6953_ctrl = NULL;
+ CDBG("vx6953_release completed\n");
+ mutex_unlock(&vx6953_mut);
+
+ return rc;
+}
+
+static int vx6953_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ /* TODO: Need to add this ID in v4l2-chip-ident.h */
+ id->ident = V4L2_IDENT_VX6953;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int vx6953_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
+{
+ int ret = 0;
+ /* return current mode value */
+ param->parm.capture.capturemode = vx6953_ctrl->sensormode;
+ return ret;
+}
+
+static int vx6953_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
+{
+ /* set the desired mode */
+ /* right now, the only purpose is to set the desired mode -
+ preview or snapshot */
+ vx6953_ctrl->sensormode = param->parm.capture.capturemode;
+ return 0;
+}
+
+static int vx6953_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ long rc = 0;
+ int mode = vx6953_ctrl->sensormode;
+ int rt = RES_PREVIEW;
+ unsigned short frame_cnt;
+ struct msm_camera_csi_params vx6953_csi_params;
+
+ CDBG("mode = %d, enable = %d\n", mode, enable);
+
+ if (!enable) {
+ /* turn off streaming */
+ /* TODO: Make call to I2C write to turn streaming off */
+ /* rc = vx6953_i2c_write_b_sensor(); */
+
+ struct vx6953_i2c_reg_conf init_tbl[] = {
+ {REG_0x0112,
+ vx6953_regs.reg_pat_init[0].reg_0x0112},
+ {0x6003, 0x01},
+ {REG_0x0113,
+ vx6953_regs.reg_pat_init[0].reg_0x0113},
+ {REG_VT_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ vt_pix_clk_div},
+ {REG_PRE_PLL_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ pre_pll_clk_div},
+ {REG_PLL_MULTIPLIER,
+ vx6953_regs.reg_pat_init[0].
+ pll_multiplier},
+ {REG_OP_PIX_CLK_DIV,
+ vx6953_regs.reg_pat_init[0].
+ op_pix_clk_div},
+ {REG_COARSE_INTEGRATION_TIME_HI,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_hi},
+ {REG_COARSE_INTEGRATION_TIME_LO,
+ vx6953_regs.reg_pat[rt].
+ coarse_integration_time_lo},
+ {REG_ANALOGUE_GAIN_CODE_GLOBAL_LO,
+ vx6953_regs.reg_pat[rt].
+ analogue_gain_code_global},
+ {REG_0x3030,
+ vx6953_regs.reg_pat_init[0].reg_0x3030},
+ /* 953 specific registers */
+ {REG_0x0111,
+ vx6953_regs.reg_pat_init[0].reg_0x0111},
+ {REG_0x0b00,
+ vx6953_regs.reg_pat_init[0].reg_0x0b00},
+ {REG_0x3001,
+ vx6953_regs.reg_pat_init[0].reg_0x3001},
+ {REG_0x3004,
+ vx6953_regs.reg_pat_init[0].reg_0x3004},
+ {0x3006, 0x00},
+ {REG_0x3007,
+ vx6953_regs.reg_pat_init[0].reg_0x3007},
+ {0x301b, 0x29},
+ /* DEFCOR settings */
+ /*Single Defect Correction Weight DISABLE*/
+ {0x0b06,
+ vx6953_regs.reg_pat_init[0].reg_0x0b06},
+ /*Single_defect_correct_weight = auto*/
+ {0x0b07,
+ vx6953_regs.reg_pat_init[0].reg_0x0b07},
+ /*Dynamic couplet correction ENABLED*/
+ {0x0b08,
+ vx6953_regs.reg_pat_init[0].reg_0x0b08},
+ /*Dynamic couplet correction weight*/
+ {0x0b09,
+ vx6953_regs.reg_pat_init[0].reg_0x0b09},
+ /* Clock Setup */
+ /* Tell sensor ext clk is 24MHz*/
+ {REG_0x0136,
+ vx6953_regs.reg_pat_init[0].reg_0x0136},
+ {REG_0x0137,
+ vx6953_regs.reg_pat_init[0].reg_0x0137},
+ /* The white balance gains must be written
+ to the sensor every frame. */
+ /* Edof */
+ {REG_0x0b83,
+ vx6953_regs.reg_pat_init[0].reg_0x0b83},
+ {REG_0x0b84,
+ vx6953_regs.reg_pat_init[0].reg_0x0b84},
+ {REG_0x0b85,
+ vx6953_regs.reg_pat_init[0].reg_0x0b85},
+ {REG_0x0b88,
+ vx6953_regs.reg_pat_init[0].reg_0x0b88},
+ {REG_0x0b89,
+ vx6953_regs.reg_pat_init[0].reg_0x0b89},
+ {REG_0x0b8a,
+ vx6953_regs.reg_pat_init[0].reg_0x0b8a},
+ /* Mode specific regieters */
+ {REG_FRAME_LENGTH_LINES_HI,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_hi},
+ {REG_FRAME_LENGTH_LINES_LO,
+ vx6953_regs.reg_pat[rt].
+ frame_length_lines_lo},
+ {REG_LINE_LENGTH_PCK_HI,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_hi},
+ {REG_LINE_LENGTH_PCK_LO,
+ vx6953_regs.reg_pat[rt].
+ line_length_pck_lo},
+ {REG_0x3005,
+ vx6953_regs.reg_pat[rt].reg_0x3005},
+ {0x3010,
+ vx6953_regs.reg_pat[rt].reg_0x3010},
+ {REG_0x3011,
+ vx6953_regs.reg_pat[rt].reg_0x3011},
+ {REG_0x301a,
+ vx6953_regs.reg_pat[rt].reg_0x301a},
+ {REG_0x3035,
+ vx6953_regs.reg_pat[rt].reg_0x3035},
+ {REG_0x3036,
+ vx6953_regs.reg_pat[rt].reg_0x3036},
+ {REG_0x3041,
+ vx6953_regs.reg_pat[rt].reg_0x3041},
+ {0x3042,
+ vx6953_regs.reg_pat[rt].reg_0x3042},
+ {REG_0x3045,
+ vx6953_regs.reg_pat[rt].reg_0x3045},
+ /*EDOF: Estimation settings for Preview mode
+ Application settings for capture mode
+ (standard settings - Not tuned) */
+ {REG_0x0b80,
+ vx6953_regs.reg_pat[rt].reg_0x0b80},
+ {REG_0x0900,
+ vx6953_regs.reg_pat[rt].reg_0x0900},
+ {REG_0x0901,
+ vx6953_regs.reg_pat[rt].reg_0x0901},
+ {REG_0x0902,
+ vx6953_regs.reg_pat[rt].reg_0x0902},
+ {REG_0x0383,
+ vx6953_regs.reg_pat[rt].reg_0x0383},
+ {REG_0x0387,
+ vx6953_regs.reg_pat[rt].reg_0x0387},
+ /* Change output size / frame rate */
+ {REG_0x034c,
+ vx6953_regs.reg_pat[rt].reg_0x034c},
+ {REG_0x034d,
+ vx6953_regs.reg_pat[rt].reg_0x034d},
+ {REG_0x034e,
+ vx6953_regs.reg_pat[rt].reg_0x034e},
+ {REG_0x034f,
+ vx6953_regs.reg_pat[rt].reg_0x034f},
+ };
+ /* reset fps_divider */
+ vx6953_ctrl->fps = 30 * Q8;
+ /* stop streaming */
+
+ /* Reset everything first */
+ if (vx6953_i2c_write_b_sensor(0x103, 0x01) < 0) {
+ CDBG("S/W reset failed\n");
+ return rc;
+ } else
+ CDBG("S/W reset successful\n");
+
+ msleep(10);
+
+ CDBG("Init vx6953_sensor_setting standby\n");
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STANDBY_MODE) < 0)
+ return rc;
+
+ /*vx6953_stm5m0edof_delay_msecs_stdby*/
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_csi_params.data_format = CSI_8BIT;
+ vx6953_csi_params.lane_cnt = 1;
+ vx6953_csi_params.lane_assign = 0xe4;
+ vx6953_csi_params.dpcm_scheme = 0;
+ vx6953_csi_params.settle_cnt = 7;
+ rc = msm_camio_csi_config(&vx6953_csi_params);
+ if (rc < 0)
+ CDBG(" config csi controller failed\n");
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_patch_for_cut3();
+ rc = vx6953_i2c_write_w_table(&init_tbl[0],
+ ARRAY_SIZE(init_tbl));
+ if (rc < 0)
+ return rc;
+
+ msleep(vx6953_stm5m0edof_delay_msecs_stdby);
+
+ vx6953_i2c_write_b_sensor(0x0b80, 0x00);
+ vx6953_i2c_write_b_sensor(0x3388, 0x03);
+ vx6953_i2c_write_b_sensor(0x3640, 0x00);
+ return rc;
+ } else {
+ /* Start sensor streaming */
+ if (vx6953_i2c_write_b_sensor(REG_MODE_SELECT,
+ MODE_SELECT_STREAM) < 0)
+ return rc;
+ CDBG("Init vx6953_sensor_setting stream\n");
+ msleep(vx6953_stm5m0edof_delay_msecs_stream);
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+
+ rc = vx6953_i2c_write_w_table(&edof_tbl[0],
+ ARRAY_SIZE(edof_tbl));
+ vx6953_i2c_write_b_sensor(0x3388, 0x00);
+
+ while (frame_cnt == 0xFF) {
+ if (vx6953_i2c_read(0x0005, &frame_cnt, 1) < 0)
+ return rc;
+ CDBG("frame_cnt=%d", frame_cnt);
+ msleep(10);
+ }
+
+ /* set desired mode */
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ CDBG("SENSOR_PREVIEW_MODE\n");
+ rc = vx6953_video_config(mode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ CDBG("SENSOR_SNAPSHOT_MODE\n");
+ rc = vx6953_snapshot_config(mode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ CDBG("SENSOR_RAW_SNAPSHOT_MODE\n");
+ rc = vx6953_raw_snapshot_config(mode);
+ break;
+ default:
+ CDBG("default\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void vx6953_frame_check(u32 *width, u32 *height)
+{
+ /* get mode first */
+ int mode = vx6953_ctrl->sensormode;
+
+ switch (mode) {
+ case SENSOR_PREVIEW_MODE:
+ if (*width > VX6953_QTR_SIZE_WIDTH)
+ *width = VX6953_QTR_SIZE_WIDTH;
+
+ if (*height > VX6953_QTR_SIZE_HEIGHT)
+ *height = VX6953_QTR_SIZE_HEIGHT;
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ if (*width > VX6953_HRZ_FULL_BLK_PIXELS)
+ *width = VX6953_HRZ_FULL_BLK_PIXELS;
+
+ if (*height > VX6953_VER_FULL_BLK_LINES)
+ *height = VX6953_VER_FULL_BLK_LINES;
+ break;
+ default:
+ break;
+ }
+}
+
+
+static int vx6953_set_params(struct i2c_client *client, u32 width, u32 height,
+ enum v4l2_mbus_pixelcode code)
+{
+ int i;
+ vx6953_ctrl->fmt = NULL;
+
+ /*
+ * frame size check
+ */
+ vx6953_frame_check(&width, &height);
+
+ /*
+ * get color format
+ */
+ for (i = 0; i < ARRAY_SIZE(vx6953_cfmts); i++)
+ if (vx6953_cfmts[i].code == code)
+ break;
+ if (i == ARRAY_SIZE(vx6953_cfmts))
+ return -EINVAL;
+
+ /* sensor supports one fixed size depending upon the mode */
+ switch (vx6953_ctrl->sensormode) {
+ case SENSOR_PREVIEW_MODE:
+ vx6953_video_config(vx6953_ctrl->sensormode);
+ break;
+ case SENSOR_SNAPSHOT_MODE:
+ vx6953_snapshot_config(vx6953_ctrl->sensormode);
+ break;
+ case SENSOR_RAW_SNAPSHOT_MODE:
+ vx6953_raw_snapshot_config(vx6953_ctrl->sensormode);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* why need this ? vx6953_ctrl->fmt = &(vx6953_cfmts[i]); */
+
+ return 0;
+}
+
+static int vx6953_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ /* right now we are not supporting, probably vfe can take care */
+ return -EINVAL;
+}
+
+static int vx6953_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ return -EINVAL;
+}
+
+static int vx6953_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ return -EINVAL;
+}
+
+static int vx6953_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ /* by this time vx6953_client should already be set */
+ struct i2c_client *client = vx6953_client;
+
+ /* currently sensor supports fixed dimensions only
+ * depending upon the mode*/
+ if (!vx6953_ctrl->fmt) {
+ int ret = vx6953_set_params(client, VX6953_QTR_SIZE_WIDTH,
+ VX6953_QTR_SIZE_HEIGHT,
+ V4L2_MBUS_FMT_YUYV8_2X8);
+ if (ret < 0)
+ return ret;
+ }
+
+ mf->width = vx6953_get_pict_pixels_pl();
+ mf->height = vx6953_get_pict_lines_pf();
+ /* TODO: set colorspace */
+ mf->code = vx6953_ctrl->fmt->code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int vx6953_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ /* by this time vx6953_client should already be set */
+ struct i2c_client *client = vx6953_client;
+
+ /* TODO: We need to define this function */
+ /* TODO: set colorspace */
+ return vx6953_set_params(client, mf->width, mf->height, mf->code);
+}
+
+static int vx6953_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vx6953_cfmts); i++)
+ if (mf->code == vx6953_cfmts[i].code)
+ break;
+
+ if (i == ARRAY_SIZE(vx6953_cfmts))
+ return -EINVAL;
+
+ /* check that frame is within max sensor supported frame size */
+ vx6953_frame_check(&mf->width, &mf->height);
+
+ /* TODO: set colorspace */
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int vx6953_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ printk(KERN_DEBUG "Index is %d\n", index);
+ if ((unsigned int)index >= ARRAY_SIZE(vx6953_cfmts))
+ return -EINVAL;
+
+ *code = vx6953_cfmts[index].code;
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops vx6953_subdev_core_ops = {
+ .g_chip_ident = vx6953_g_chip_ident,
+};
+
+static struct v4l2_subdev_video_ops vx6953_subdev_video_ops = {
+ .g_parm = vx6953_g_parm,
+ .s_parm = vx6953_s_parm,
+ .s_stream = vx6953_s_stream,
+ .g_mbus_fmt = vx6953_g_fmt,
+ .s_mbus_fmt = vx6953_s_fmt,
+ .try_mbus_fmt = vx6953_try_fmt,
+ .cropcap = vx6953_cropcap,
+ .g_crop = vx6953_g_crop,
+ .s_crop = vx6953_s_crop,
+ .enum_mbus_fmt = vx6953_enum_fmt,
+};
+
+static struct v4l2_subdev_ops vx6953_subdev_ops = {
+ .core = &vx6953_subdev_core_ops,
+ .video = &vx6953_subdev_video_ops,
+};
+
+static int vx6953_sensor_probe(const struct msm_camera_sensor_info *info,
+ struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = i2c_add_driver(&vx6953_i2c_driver);
+ if (rc < 0 || vx6953_client == NULL) {
+ rc = -ENOTSUPP;
+ goto probe_fail;
+ }
+ msm_camio_clk_rate_set(24000000);
+ rc = vx6953_probe_init_sensor(info);
+ if (rc < 0)
+ goto probe_fail;
+ s->s_init = vx6953_sensor_open_init;
+ s->s_release = vx6953_sensor_release;
+ s->s_config = vx6953_sensor_config;
+ vx6953_probe_init_done(info);
+ return rc;
+
+probe_fail:
+ CDBG("vx6953_sensor_probe: SENSOR PROBE FAILS!\n");
+ return rc;
+}
+
+
+static int vx6953_sensor_probe_cb(const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+ int rc = 0;
+ rc = vx6953_sensor_probe(info, s);
+ if (rc < 0)
+ return rc;
+
+ vx6953_ctrl = kzalloc(sizeof(struct vx6953_ctrl_t), GFP_KERNEL);
+ if (!vx6953_ctrl) {
+ CDBG("vx6953_sensor_probe failed!\n");
+ return -ENOMEM;
+ }
+
+ /* probe is successful, init a v4l2 subdevice */
+ printk(KERN_DEBUG "going into v4l2_i2c_subdev_init\n");
+ if (sdev) {
+ v4l2_i2c_subdev_init(sdev, vx6953_client,
+ &vx6953_subdev_ops);
+ vx6953_ctrl->sensor_dev = sdev;
+ }
+ return rc;
+}
+
+static int __vx6953_probe(struct platform_device *pdev)
+{
+ return msm_sensor_register(pdev, vx6953_sensor_probe_cb);
+}
+
+static struct platform_driver msm_camera_driver = {
+ .probe = __vx6953_probe,
+ .driver = {
+ .name = "msm_camera_vx6953",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vx6953_init(void)
+{
+ return platform_driver_register(&msm_camera_driver);
+}
+
+module_init(vx6953_init);
+void vx6953_exit(void)
+{
+ i2c_del_driver(&vx6953_i2c_driver);
+}
+
+
diff --git a/drivers/media/video/msm/vx6953_v4l2.h b/drivers/media/video/msm/vx6953_v4l2.h
new file mode 100644
index 0000000..e5428e9
--- /dev/null
+++ b/drivers/media/video/msm/vx6953_v4l2.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef VX6953_V4L2_H
+#define VX6953_V4L2_H
+#include <linux/types.h>
+#include <mach/board.h>
+extern struct vx6953_reg vx6953_regs;
+struct reg_struct_init {
+ uint8_t reg_0x0112; /* 0x0112*/
+ uint8_t reg_0x0113; /* 0x0113*/
+ uint8_t vt_pix_clk_div; /* 0x0301*/
+ uint8_t pre_pll_clk_div; /* 0x0305*/
+ uint8_t pll_multiplier; /* 0x0307*/
+ uint8_t op_pix_clk_div; /* 0x0309*/
+ uint8_t reg_0x3030; /*0x3030*/
+ uint8_t reg_0x0111; /*0x0111*/
+ uint8_t reg_0x0b00; /*0x0b00*/
+ uint8_t reg_0x3001; /*0x3001*/
+ uint8_t reg_0x3004; /*0x3004*/
+ uint8_t reg_0x3007; /*0x3007*/
+ uint8_t reg_0x3016; /*0x3016*/
+ uint8_t reg_0x301d; /*0x301d*/
+ uint8_t reg_0x317e; /*0x317E*/
+ uint8_t reg_0x317f; /*0x317F*/
+ uint8_t reg_0x3400; /*0x3400*/
+ uint8_t reg_0x0b06; /*0x0b06*/
+ uint8_t reg_0x0b07; /*0x0b07*/
+ uint8_t reg_0x0b08; /*0x0b08*/
+ uint8_t reg_0x0b09; /*0x0b09*/
+ uint8_t reg_0x0136;
+ uint8_t reg_0x0137;
+ /* Edof */
+ uint8_t reg_0x0b83; /*0x0b83*/
+ uint8_t reg_0x0b84; /*0x0b84*/
+ uint8_t reg_0x0b85; /*0x0b85*/
+ uint8_t reg_0x0b88; /*0x0b88*/
+ uint8_t reg_0x0b89; /*0x0b89*/
+ uint8_t reg_0x0b8a; /*0x0b8a*/
+ };
+struct reg_struct {
+ uint8_t coarse_integration_time_hi; /*REG_COARSE_INTEGRATION_TIME_HI*/
+ uint8_t coarse_integration_time_lo; /*REG_COARSE_INTEGRATION_TIME_LO*/
+ uint8_t analogue_gain_code_global;
+ uint8_t frame_length_lines_hi; /* 0x0340*/
+ uint8_t frame_length_lines_lo; /* 0x0341*/
+ uint8_t line_length_pck_hi; /* 0x0342*/
+ uint8_t line_length_pck_lo; /* 0x0343*/
+ uint8_t reg_0x3005; /* 0x3005*/
+ uint8_t reg_0x3010; /* 0x3010*/
+ uint8_t reg_0x3011; /* 0x3011*/
+ uint8_t reg_0x301a; /* 0x301a*/
+ uint8_t reg_0x3035; /* 0x3035*/
+ uint8_t reg_0x3036; /* 0x3036*/
+ uint8_t reg_0x3041; /*0x3041*/
+ uint8_t reg_0x3042; /*0x3042*/
+ uint8_t reg_0x3045; /*0x3045*/
+ uint8_t reg_0x0b80; /* 0x0b80*/
+ uint8_t reg_0x0900; /*0x0900*/
+ uint8_t reg_0x0901; /* 0x0901*/
+ uint8_t reg_0x0902; /*0x0902*/
+ uint8_t reg_0x0383; /*0x0383*/
+ uint8_t reg_0x0387; /* 0x0387*/
+ uint8_t reg_0x034c; /* 0x034c*/
+ uint8_t reg_0x034d; /*0x034d*/
+ uint8_t reg_0x034e; /* 0x034e*/
+ uint8_t reg_0x034f; /* 0x034f*/
+ uint8_t reg_0x1716; /*0x1716*/
+ uint8_t reg_0x1717; /*0x1717*/
+ uint8_t reg_0x1718; /*0x1718*/
+ uint8_t reg_0x1719; /*0x1719*/
+ uint8_t reg_0x3210;/*0x3210*/
+ uint8_t reg_0x111; /*0x111*/
+ uint8_t reg_0x3410; /*0x3410*/
+ uint8_t reg_0x3098;
+ uint8_t reg_0x309D;
+ uint8_t reg_0x0200;
+ uint8_t reg_0x0201;
+ };
+struct vx6953_i2c_reg_conf {
+ unsigned short waddr;
+ unsigned short wdata;
+};
+
+enum vx6953_test_mode_t {
+ TEST_OFF,
+ TEST_1,
+ TEST_2,
+ TEST_3
+};
+
+enum vx6953_resolution_t {
+ QTR_SIZE,
+ FULL_SIZE,
+ INVALID_SIZE
+};
+enum vx6953_setting {
+ RES_PREVIEW,
+ RES_CAPTURE
+};
+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 sensor_revision_t {
+ VX6953_STM5M0EDOF_CUT_1,
+ VX6953_STM5M0EDOF_CUT_2,
+ VX6953_STM5M0EDOF_CUT_3
+};
+enum edof_mode_t {
+ VX6953_EDOF_DISABLE, /* 0x00 */
+ VX6953_EDOF_APPLICATION, /* 0x01 */
+ VX6953_EDOF_ESTIMATION /* 0x02 */
+};
+struct vx6953_reg {
+ const struct reg_struct_init *reg_pat_init;
+ const struct reg_struct *reg_pat;
+};
+#endif /* VX6953_H */
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index de4fa4e..ac48afd 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -330,6 +330,7 @@
break;
case V4L2_MEMORY_USERPTR:
b->m.userptr = vb->baddr;
+ b->reserved = vb->boff;
b->length = vb->bsize;
break;
case V4L2_MEMORY_OVERLAY:
@@ -600,6 +601,7 @@
buf->baddr != b->m.userptr)
q->ops->buf_release(q, buf);
buf->baddr = b->m.userptr;
+ buf->boff = b->reserved;
break;
case V4L2_MEMORY_OVERLAY:
buf->boff = b->m.offset;
@@ -1138,8 +1140,6 @@
buf = list_entry(q->stream.next,
struct videobuf_buffer, stream);
} else {
- if (!q->reading)
- __videobuf_read_start(q);
if (!q->reading) {
rc = POLLERR;
} else if (NULL == q->read_buf) {
diff --git a/drivers/media/video/videobuf-msm-mem.c b/drivers/media/video/videobuf-msm-mem.c
new file mode 100644
index 0000000..6f2cf9f
--- /dev/null
+++ b/drivers/media/video/videobuf-msm-mem.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on videobuf-dma-contig.c,
+ * (c) 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ * helper functions for physically contiguous pmem capture buffers
+ * The functions support contiguous memory allocations using pmem
+ * kernel API.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <linux/memory_alloc.h>
+#include <media/videobuf-msm-mem.h>
+#include <media/msm_camera.h>
+#include <mach/memory.h>
+
+#define MAGIC_PMEM 0x0733ac64
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+ BUG(); \
+ }
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) printk(KERN_DEBUG "videobuf-msm-mem: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static int32_t msm_mem_allocate(const size_t size)
+{
+ int32_t phyaddr;
+ phyaddr = allocate_contiguous_ebi_nomap(size, SZ_4K);
+ return phyaddr;
+}
+
+static int32_t msm_mem_free(const int32_t phyaddr)
+{
+ int32_t rc = 0;
+ free_contiguous_memory_by_paddr(phyaddr);
+ return rc;
+}
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ D("vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ int i, rc;
+
+ D("vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ struct videobuf_contig_pmem *mem;
+
+ D("munmap %p q=%p\n", map, q);
+ mutex_lock(&q->vb_lock);
+
+ /* We need first to cancel streams, before unmapping */
+ if (q->streaming)
+ videobuf_queue_cancel(q);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+
+ if (q->bufs[i]->map != map)
+ continue;
+
+ mem = q->bufs[i]->priv;
+ if (mem) {
+ /* This callback is called only if kernel has
+ * allocated memory and this memory is mmapped.
+ * In this case, memory should be freed,
+ * in order to do memory unmap.
+ */
+
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ /* vfree is not atomic - can't be
+ called with IRQ's disabled
+ */
+ D("buf[%d] freeing physical %d\n",
+ i, mem->phyaddr);
+
+ rc = msm_mem_free(mem->phyaddr);
+ if (rc < 0)
+ D("%s: Invalid memory location\n",
+ __func__);
+ else {
+ mem->phyaddr = 0;
+ }
+ }
+
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ }
+
+ kfree(map);
+
+ mutex_unlock(&q->vb_lock);
+
+ /* deallocate the q->bufs[i] structure not a good solution
+ as it will result in unnecessary iterations but right now
+ this looks like the only cleaner way */
+ videobuf_mmap_free(q);
+ }
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+};
+
+/**
+ * videobuf_pmem_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-contig-pmem data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_pmem_contig_user_put(struct videobuf_contig_pmem *mem)
+{
+ put_pmem_file(mem->file);
+ mem->is_userptr = 0;
+ mem->phyaddr = 0;
+ mem->size = 0;
+}
+
+/**
+ * videobuf_pmem_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-contig-pmem data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_pmem_contig_user_get(struct videobuf_contig_pmem *mem,
+ struct videobuf_buffer *vb)
+{
+ unsigned long kvstart;
+ unsigned long len;
+ int rc;
+
+ mem->size = PAGE_ALIGN(vb->size);
+ rc = get_pmem_file(vb->baddr, (unsigned long *)&mem->phyaddr,
+ &kvstart, &len, &mem->file);
+ if (rc < 0) {
+ pr_err("%s: get_pmem_file fd %lu error %d\n",
+ __func__, vb->baddr,
+ rc);
+ return rc;
+ }
+ mem->phyaddr += vb->boff;
+ mem->y_off = 0;
+ mem->cbcr_off = (vb->size)*2/3;
+ mem->is_userptr = 1;
+ return rc;
+}
+
+static struct videobuf_buffer *__videobuf_alloc(size_t size)
+{
+ struct videobuf_contig_pmem *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (vb) {
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_PMEM;
+ }
+
+ return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+ struct videobuf_contig_pmem *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ int rc = 0;
+ struct videobuf_contig_pmem *mem = vb->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ D("%s memory method MMAP\n", __func__);
+
+ /* All handling should be done by __videobuf_mmap_mapper() */
+ break;
+ case V4L2_MEMORY_USERPTR:
+ D("%s memory method USERPTR\n", __func__);
+
+ /* handle pointer from user space */
+ rc = videobuf_pmem_contig_user_get(mem, vb);
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ default:
+ pr_err("%s memory method OVERLAY/unknown\n", __func__);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_contig_pmem *mem;
+ struct videobuf_mapping *map;
+ int retval;
+ unsigned long size;
+
+ D("%s\n", __func__);
+
+ /* create mapping + update buffer list */
+ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (!map) {
+ pr_err("%s: kzalloc failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ buf->map = map;
+ map->q = q;
+
+ buf->baddr = vma->vm_start;
+
+ mem = buf->priv;
+ D("mem = 0x%x\n", (u32)mem);
+ D("buf = 0x%x\n", (u32)buf);
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ mem->size = PAGE_ALIGN(buf->bsize);
+ mem->y_off = 0;
+ mem->cbcr_off = (buf->bsize)*2/3;
+ if (buf->i >= 0 && buf->i <= 3)
+ mem->buffer_type = OUTPUT_TYPE_P;
+ else
+ mem->buffer_type = OUTPUT_TYPE_V;
+
+ buf->bsize = mem->size;
+ mem->phyaddr = msm_mem_allocate(mem->size);
+
+ if (IS_ERR((void *)mem->phyaddr)) {
+ pr_err("%s : pmem memory allocation failed\n", __func__);
+ goto error;
+ }
+
+ /* Try to remap memory */
+ size = vma->vm_end - vma->vm_start;
+ size = (size < mem->size) ? size : mem->size;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ retval = remap_pfn_range(vma, vma->vm_start,
+ mem->phyaddr >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (retval) {
+ pr_err("mmap: remap failed with error %d. ", retval);
+ retval = msm_mem_free(mem->phyaddr);
+ if (retval < 0)
+ printk(KERN_ERR "%s: Invalid memory location\n",
+ __func__);
+ else {
+ mem->phyaddr = 0;
+ }
+ goto error;
+ }
+
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ D("mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+ map, q, vma->vm_start, vma->vm_end,
+ (long int)buf->bsize,
+ vma->vm_pgoff, buf->i);
+
+ videobuf_vm_open(vma);
+
+ return 0;
+
+error:
+ kfree(map);
+ return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc_vb = __videobuf_alloc,
+ .iolock = __videobuf_iolock,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
+};
+
+void videobuf_queue_pmem_contig_init(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_pmem_contig_init);
+
+int videobuf_to_pmem_contig(struct videobuf_buffer *buf)
+{
+ struct videobuf_contig_pmem *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ return mem->phyaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_pmem_contig);
+
+int videobuf_pmem_contig_free(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_contig_pmem *mem = buf->priv;
+
+ /* mmapped memory can't be freed here, otherwise mmapped region
+ would be released, while still needed. In this case, the memory
+ release should happen inside videobuf_vm_close().
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+ if (buf->memory != V4L2_MEMORY_USERPTR)
+ return -EINVAL;
+
+ if (!mem)
+ return -ENOMEM;
+
+ MAGIC_CHECK(mem->magic, MAGIC_PMEM);
+
+ /* handle user space pointer case */
+ if (buf->baddr) {
+ videobuf_pmem_contig_user_put(mem);
+ return 0;
+ } else {
+ /* don't support read() method */
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(videobuf_pmem_contig_free);
+
+MODULE_DESCRIPTION("helper module to manage video4linux PMEM contig buffers");
+MODULE_LICENSE("GPL v2");