aboutsummaryrefslogtreecommitdiff
path: root/camera/cci_intf.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/cci_intf.c')
-rw-r--r--camera/cci_intf.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/camera/cci_intf.c b/camera/cci_intf.c
new file mode 100644
index 00000000..d3365458
--- /dev/null
+++ b/camera/cci_intf.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 Motorola Mobility LLC.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include "msm_sd.h"
+#include "sensor/cci/msm_cci.h"
+#include <lenovo_media/cci_intf.h>
+#include <media/v4l2-ioctl.h>
+
+#ifdef CONFIG_COMPAT
+static struct v4l2_file_operations msm_cci_intf_v4l2_subdev_fops;
+#endif
+
+struct msm_cci_intf_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ uint32_t subdev_id;
+};
+
+static struct msm_cci_intf_ctrl_t fctrl;
+
+static int32_t cci_intf_xfer(struct v4l2_subdev *sd,
+ struct msm_cci_intf_xfer *xfer,
+ int cmd)
+{
+ int32_t rc, rc2;
+ struct msm_camera_cci_client cci_info = {
+ .cci_subdev = msm_cci_get_subdev(),
+ .cci_i2c_master = xfer->cci_bus,
+ .sid = xfer->slave_addr,
+ };
+ struct msm_camera_cci_ctrl cci_ctrl = {
+ .cci_info = &cci_info,
+ };
+ int i;
+ struct msm_camera_i2c_reg_array *reg_conf_tbl;
+
+ pr_debug("%s cmd:%d bus:%d devaddr:%02x regw:%d rega:%04x count:%d\n",
+ __func__, cmd, xfer->cci_bus, xfer->slave_addr,
+ xfer->reg.width, xfer->reg.addr, xfer->data.count);
+
+ if (xfer->cci_bus > 1 || xfer->slave_addr > 0x7F ||
+ xfer->reg.width < 1 || xfer->reg.width > 2 ||
+ xfer->reg.addr > ((1<<(8*xfer->reg.width))-1) ||
+ xfer->data.count < 1 ||
+ xfer->data.count > MSM_CCI_INTF_MAX_XFER)
+ return -EINVAL;
+
+ /* init */
+ cci_ctrl.cmd = MSM_CCI_INIT;
+ rc = v4l2_subdev_call(msm_cci_get_subdev(),
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cci init fail (%d)\n", __func__, rc);
+ return rc;
+ }
+
+ switch (cmd) {
+ case MSM_CCI_INTF_READ:
+ /* read */
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = xfer->reg.addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type =
+ (xfer->reg.width == 1 ?
+ MSM_CAMERA_I2C_BYTE_ADDR :
+ MSM_CAMERA_I2C_WORD_ADDR);
+ cci_ctrl.cfg.cci_i2c_read_cfg.data = xfer->data.buf;
+ cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = xfer->data.count;
+ rc = v4l2_subdev_call(msm_cci_get_subdev(),
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cci read fail (%d)\n", __func__, rc);
+ goto release;
+ }
+ rc = cci_ctrl.status;
+ break;
+ case MSM_CCI_INTF_WRITE:
+ /* write */
+ reg_conf_tbl = kzalloc(xfer->data.count *
+ sizeof(struct msm_camera_i2c_reg_array),
+ GFP_KERNEL);
+ if (!reg_conf_tbl) {
+ rc = -ENOMEM;
+ goto release;
+ }
+ reg_conf_tbl[0].reg_addr = xfer->reg.addr;
+ for (i = 0; i < xfer->data.count; i++) {
+ reg_conf_tbl[i].reg_data = xfer->data.buf[i];
+ reg_conf_tbl[i].delay = 0;
+ }
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = reg_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type =
+ (xfer->reg.width == 1 ?
+ MSM_CAMERA_I2C_BYTE_ADDR :
+ MSM_CAMERA_I2C_WORD_ADDR);
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type =
+ MSM_CAMERA_I2C_BYTE_DATA;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = xfer->data.count;
+ rc = v4l2_subdev_call(msm_cci_get_subdev(),
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ kfree(reg_conf_tbl);
+ if (rc < 0) {
+ pr_err("%s: cci write fail (%d)\n", __func__, rc);
+ goto release;
+ }
+ rc = cci_ctrl.status;
+ break;
+ default:
+ pr_err("%s: Unknown command (%d)\n", __func__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+
+release:
+ /* release */
+ cci_ctrl.cmd = MSM_CCI_RELEASE;
+ rc2 = v4l2_subdev_call(msm_cci_get_subdev(),
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc2 < 0) {
+ pr_err("%s: cci release fail (%d)\n", __func__, rc2);
+ return rc2;
+ }
+
+ return rc;
+}
+
+static long msm_cci_intf_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ pr_debug("%s cmd=%x\n", __func__, cmd);
+
+ switch (cmd) {
+ case MSM_CCI_INTF_READ:
+ case MSM_CCI_INTF_WRITE:
+ return cci_intf_xfer(sd, (struct msm_cci_intf_xfer *)arg, cmd);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static struct v4l2_subdev_core_ops msm_cci_intf_subdev_core_ops = {
+ .ioctl = msm_cci_intf_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_cci_intf_subdev_ops = {
+ .core = &msm_cci_intf_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_cci_intf_internal_ops;
+
+#ifdef CONFIG_COMPAT
+static long msm_cci_intf_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ switch (cmd) {
+ case MSM_CCI_INTF_READ32:
+ cmd = MSM_CCI_INTF_READ;
+ return cci_intf_xfer(sd, (struct msm_cci_intf_xfer *)arg, cmd);
+ case MSM_CCI_INTF_WRITE32:
+ cmd = MSM_CCI_INTF_WRITE;
+ return cci_intf_xfer(sd, (struct msm_cci_intf_xfer *)arg, cmd);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static long msm_cci_intf_subdev_fops_ioctl(
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_cci_intf_subdev_do_ioctl);
+}
+#endif
+
+static int __init cci_intf_init(void)
+{
+ int rc;
+
+ pr_debug("%s\n", __func__);
+
+ v4l2_subdev_init(&fctrl.msm_sd.sd, &msm_cci_intf_subdev_ops);
+ v4l2_set_subdevdata(&fctrl.msm_sd.sd, &fctrl);
+
+ fctrl.msm_sd.sd.internal_ops = &msm_cci_intf_internal_ops;
+ fctrl.msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(fctrl.msm_sd.sd.name, ARRAY_SIZE(fctrl.msm_sd.sd.name),
+ "msm_cci_intf");
+ rc = media_entity_init(&fctrl.msm_sd.sd.entity, 0, NULL, 0);
+ if (rc < 0) {
+ pr_err("%s: failed media_entity_init (%d)\n", __func__, rc);
+ return rc;
+ }
+ fctrl.msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ fctrl.msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CCI_INTF;
+ rc = msm_sd_register(&fctrl.msm_sd);
+ if (rc < 0) {
+ pr_err("%s: failed msm_sd_register (%d)\n", __func__, rc);
+ media_entity_cleanup(&fctrl.msm_sd.sd.entity);
+ return rc;
+ }
+
+#ifdef CONFIG_COMPAT
+ msm_cci_intf_v4l2_subdev_fops = v4l2_subdev_fops;
+ msm_cci_intf_v4l2_subdev_fops.compat_ioctl32 =
+ msm_cci_intf_subdev_fops_ioctl;
+ fctrl.msm_sd.sd.devnode->fops =
+ &msm_cci_intf_v4l2_subdev_fops;
+#endif
+ return 0;
+}
+
+static void __exit cci_intf_exit(void)
+{
+ pr_debug("%s\n", __func__);
+
+ msm_sd_unregister(&fctrl.msm_sd);
+ media_entity_cleanup(&fctrl.msm_sd.sd.entity);
+}
+
+module_init(cci_intf_init);
+module_exit(cci_intf_exit);
+MODULE_DESCRIPTION("CCI DEBUG INTF");
+MODULE_LICENSE("GPL v2");