[COMMON] fimc-is2: Add LC898217 actuator files
authorEunyoung Lee <ey470.lee@samsung.com>
Mon, 22 Jan 2018 06:41:25 +0000 (15:41 +0900)
committerEunyoung Lee <ey470.lee@samsung.com>
Tue, 19 Jun 2018 08:43:25 +0000 (17:43 +0900)
Change-Id: I9d3df91c7d8e63d4c4761cb6b674739158a7f050
Signed-off-by: Eunyoung Lee <ey470.lee@samsung.com>
drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/Kconfig
drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/Makefile
drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.c [new file with mode: 0644]
drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.h [new file with mode: 0644]

index 8c82485ac0eea633e6221243efe69f7ff5bf903a..4f18d087ecb5cba4d67d05a4ab0894346770d956 100644 (file)
@@ -68,6 +68,14 @@ config CAMERA_ACT_AK7372_OBJ
         help
           Use to build AK7372 driver.
 
+config CAMERA_ACT_LC898217_OBJ
+        bool "Use actuator LC898217"
+       depends on USE_DIRECT_IS_CONTROL
+       depends on CAMERA_ACT_SELECT
+        default n
+        help
+          Use to build LC898217 driver.
+
 config CAMERA_ACT_AK737X_OBJ
         bool "Use actuator AK737X"
        depends on USE_DIRECT_IS_CONTROL
index e141e62c7a77192c731bd02a7fbc553419b0d2a3..f1dfe867ce4a4276852cf0fcac19ffe5aaad29cc 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_CAMERA_ACT_DW9714_OBJ) += fimc-is-actuator-dw9714.o
 obj-$(CONFIG_CAMERA_ACT_DW9780_OBJ) += fimc-is-actuator-dw9780.o
 obj-$(CONFIG_CAMERA_ACT_DW9804_OBJ) += fimc-is-actuator-dw9804.o
 obj-$(CONFIG_CAMERA_ACT_DW9807_OBJ) += fimc-is-actuator-dw9807.o
+obj-$(CONFIG_CAMERA_ACT_LC898217_OBJ) += fimc-is-actuator-lc898217.o
 obj-$(CONFIG_CAMERA_ACT_ZC533_OBJ) += fimc-is-actuator-zc533.o
 
 EXTRA_CFLAGS += -Idrivers/media/platform/exynos/fimc-is2
diff --git a/drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.c b/drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.c
new file mode 100644 (file)
index 0000000..190094d
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Samsung Exynos5 SoC series Actuator driver
+ *
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_exynos_camera.h>
+
+#include "fimc-is-actuator-lc898217.h"
+#include "fimc-is-device-sensor.h"
+#include "fimc-is-device-sensor-peri.h"
+#include "fimc-is-core.h"
+
+#include "fimc-is-helper-i2c.h"
+
+#include "interface/fimc-is-interface-library.h"
+
+#define ACTUATOR_NAME          "LC898217"
+
+#define DEF_LC898217_FIRST_POSITION            120
+#define DEF_LC898217_FIRST_DELAY                       30
+
+/* register definition */
+#define REG_TARGET_HIGH                0x84
+#define REG_TARGET_LOW         0x85
+#define REG_SRV_STATE1         0xB0
+#define REG_TGT_CNVTIM         0xB2
+
+extern struct fimc_is_lib_support gPtr_lib_support;
+extern struct fimc_is_sysfs_actuator sysfs_actuator;
+
+static int sensor_lc898217_write_position(struct i2c_client *client, u32 val)
+{
+       int ret = 0;
+       u8 val_high = 0, val_low = 0;
+
+       BUG_ON(!client);
+
+       if (!client->adapter) {
+               err("Could not find adapter!\n");
+               ret = -ENODEV;
+               goto p_err;
+       }
+
+       if (val > LC898217_POS_MAX_SIZE) {
+               err("Invalid af position(position : %d, Max : %d).\n",
+                                       val, LC898217_POS_MAX_SIZE);
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       val_high = (val >> 8) & 0xFF;
+       val_low = val & 0xFF;
+
+       ret = fimc_is_sensor_addr8_write8(client, REG_TARGET_HIGH, val_high);
+       if (ret < 0)
+               goto p_err;
+       ret = fimc_is_sensor_addr8_write8(client, REG_TARGET_LOW, val_low);
+
+p_err:
+       return ret;
+}
+
+int sensor_lc898217_actuator_init(struct v4l2_subdev *subdev, u32 val)
+{
+       int ret = 0;
+       u8 product_id = 0;
+       struct fimc_is_actuator *actuator;
+       struct i2c_client *client = NULL;
+#ifdef DEBUG_ACTUATOR_TIME
+       struct timeval st, end;
+       do_gettimeofday(&st);
+#endif
+
+       long cal_addr;
+       u32 cal_data;
+
+       int first_position = DEF_LC898217_FIRST_POSITION;
+
+       BUG_ON(!subdev);
+
+       dbg_actuator("%s\n", __func__);
+
+       actuator = (struct fimc_is_actuator *)v4l2_get_subdevdata(subdev);
+       if (!actuator) {
+               err("actuator is not detect!\n");
+               goto p_err;
+       }
+
+       client = actuator->client;
+       if (unlikely(!client)) {
+               err("client is NULL");
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       I2C_MUTEX_LOCK(actuator->i2c_lock);
+       ret = fimc_is_sensor_addr8_read8(client, 0x03, &product_id);
+       if (ret < 0)
+               goto p_err_mutex;
+
+#if 0
+       if (product_id != LC898217_PRODUCT_ID) {
+               err("LC898217 is not detected(%d), Slave: %d\n", product_id, client->addr);
+               goto p_err_mutex;
+       }
+#endif
+
+       /* EEPROM AF calData address */
+       if (gPtr_lib_support.binary_load_flg) {
+               /* get pan_focus */
+               cal_addr = gPtr_lib_support.minfo->kvaddr_rear_cal + EEPROM_OEM_BASE;
+               memcpy((void *)&cal_data, (void *)cal_addr, sizeof(cal_data));
+
+               if (cal_data > 0)
+                       first_position = cal_data;
+       } else {
+               warn("SDK library is not loaded");
+       }
+
+       ret = sensor_lc898217_write_position(client, first_position);
+       if (ret < 0)
+               goto p_err_mutex;
+       actuator->position = first_position;
+
+       /* Go active mode */
+       ret = fimc_is_sensor_addr8_write8(client, 0x02, 0);
+       if (ret < 0)
+               goto p_err_mutex;
+
+       dbg_actuator("initial position: %d\n", first_position);
+
+       mdelay(DEF_LC898217_FIRST_DELAY);
+
+#ifdef DEBUG_ACTUATOR_TIME
+       do_gettimeofday(&end);
+       pr_info("[%s] time %lu us", __func__, (end.tv_sec - st.tv_sec) * 1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err_mutex:
+       I2C_MUTEX_UNLOCK(actuator->i2c_lock);
+
+p_err:
+       return ret;
+}
+
+int sensor_lc898217_actuator_get_status(struct v4l2_subdev *subdev, u32 *info)
+{
+       int ret = 0;
+       struct fimc_is_actuator *actuator = NULL;
+       struct i2c_client *client = NULL;
+       enum fimc_is_actuator_status status = ACTUATOR_STATUS_NO_BUSY;
+#ifdef DEBUG_ACTUATOR_TIME
+       struct timeval st, end;
+       do_gettimeofday(&st);
+#endif
+
+       dbg_actuator("%s\n", __func__);
+
+       BUG_ON(!subdev);
+       BUG_ON(!info);
+
+       actuator = (struct fimc_is_actuator *)v4l2_get_subdevdata(subdev);
+       BUG_ON(!actuator);
+
+       client = actuator->client;
+       if (unlikely(!client)) {
+               err("client is NULL");
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       /*
+        * The info is busy flag.
+        * But, this module can't get busy flag.
+        */
+       status = ACTUATOR_STATUS_NO_BUSY;
+       *info = status;
+
+#ifdef DEBUG_ACTUATOR_TIME
+       do_gettimeofday(&end);
+       pr_info("[%s] time %lu us", __func__, (end.tv_sec - st.tv_sec) * 1000000 + (end.tv_usec - st.tv_usec));
+#endif
+
+p_err:
+       return ret;
+}
+
+int sensor_lc898217_actuator_set_position(struct v4l2_subdev *subdev, u32 *info)
+{
+       int ret = 0;
+       struct fimc_is_actuator *actuator;
+       struct i2c_client *client;
+       u32 position = 0;
+#ifdef DEBUG_ACTUATOR_TIME
+       struct timeval st, end;
+       do_gettimeofday(&st);
+#endif
+
+       BUG_ON(!subdev);
+       BUG_ON(!info);
+
+       actuator = (struct fimc_is_actuator *)v4l2_get_subdevdata(subdev);
+       BUG_ON(!actuator);
+
+       client = actuator->client;
+       if (unlikely(!client)) {
+               err("client is NULL");
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       I2C_MUTEX_LOCK(actuator->i2c_lock);
+       position = *info;
+       if (position > LC898217_POS_MAX_SIZE) {
+               err("Invalid af position(position : %d, Max : %d).\n",
+                                       position, LC898217_POS_MAX_SIZE);
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       /* debug option : fixed position testing */
+       if (sysfs_actuator.enable_fixed)
+               position = sysfs_actuator.fixed_position;
+
+       /* position Set */
+       ret = sensor_lc898217_write_position(client, position);
+       if (ret < 0)
+               goto p_err;
+       actuator->position = position;
+
+       dbg_actuator("%s: position(%d)\n", __func__, position);
+
+#ifdef DEBUG_ACTUATOR_TIME
+       do_gettimeofday(&end);
+       pr_info("[%s] time %lu us", __func__, (end.tv_sec - st.tv_sec) * 1000000 + (end.tv_usec - st.tv_usec));
+#endif
+p_err:
+       I2C_MUTEX_UNLOCK(actuator->i2c_lock);
+       return ret;
+}
+
+static int sensor_lc898217_actuator_g_ctrl(struct v4l2_subdev *subdev, struct v4l2_control *ctrl)
+{
+       int ret = 0;
+       u32 val = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_ACTUATOR_GET_STATUS:
+               ret = sensor_lc898217_actuator_get_status(subdev, &val);
+               if (ret < 0) {
+                       err("err!!! ret(%d), actuator status(%d)", ret, val);
+                       ret = -EINVAL;
+                       goto p_err;
+               }
+               break;
+       default:
+               err("err!!! Unknown CID(%#x)", ctrl->id);
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+       ctrl->value = val;
+
+p_err:
+       return ret;
+}
+
+static int sensor_lc898217_actuator_s_ctrl(struct v4l2_subdev *subdev, struct v4l2_control *ctrl)
+{
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_ACTUATOR_SET_POSITION:
+               ret = sensor_lc898217_actuator_set_position(subdev, &ctrl->value);
+               if (ret) {
+                       err("failed to actuator set position: %d, (%d)\n", ctrl->value, ret);
+                       ret = -EINVAL;
+                       goto p_err;
+               }
+               break;
+       default:
+               err("err!!! Unknown CID(%#x)", ctrl->id);
+               ret = -EINVAL;
+               goto p_err;
+       }
+
+p_err:
+       return ret;
+}
+
+static const struct v4l2_subdev_core_ops core_ops = {
+       .init = sensor_lc898217_actuator_init,
+       .g_ctrl = sensor_lc898217_actuator_g_ctrl,
+       .s_ctrl = sensor_lc898217_actuator_s_ctrl,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+       .core = &core_ops,
+};
+
+static int sensor_lc898217_actuator_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct fimc_is_core *core = NULL;
+       struct v4l2_subdev *subdev_actuator = NULL;
+       struct fimc_is_actuator *actuator = NULL;
+       struct fimc_is_device_sensor *device = NULL;
+       u32 sensor_id = 0;
+       u32 place = 0;
+       struct device *dev;
+       struct device_node *dnode;
+
+       BUG_ON(!fimc_is_dev);
+       BUG_ON(!client);
+
+       core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
+       if (!core) {
+               err("core device is not yet probed");
+               ret = -EPROBE_DEFER;
+               goto p_err;
+       }
+
+       dev = &client->dev;
+       dnode = dev->of_node;
+
+       ret = of_property_read_u32(dnode, "id", &sensor_id);
+       if (ret) {
+               err("id read is fail(%d)", ret);
+               goto p_err;
+       }
+
+       ret = of_property_read_u32(dnode, "place", &place);
+       if (ret) {
+               pr_info("place read is fail(%d)", ret);
+               place = 0;
+       }
+       probe_info("%s sensor_id(%d) actuator_place(%d)\n", __func__, sensor_id, place);
+
+       device = &core->sensor[sensor_id];
+       if (!test_bit(FIMC_IS_SENSOR_PROBE, &device->state)) {
+               err("sensor device is not yet probed");
+               ret = -EPROBE_DEFER;
+               goto p_err;
+       }
+
+       actuator = kzalloc(sizeof(struct fimc_is_actuator), GFP_KERNEL);
+       if (!actuator) {
+               err("actuator is NULL");
+               ret = -ENOMEM;
+               goto p_err;
+       }
+
+       subdev_actuator = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+       if (!subdev_actuator) {
+               err("subdev_actuator is NULL");
+               ret = -ENOMEM;
+               goto p_err;
+       }
+
+       /* This name must is match to sensor_open_extended actuator name */
+       actuator->id = ACTUATOR_NAME_LC898217;
+       actuator->subdev = subdev_actuator;
+       actuator->device = sensor_id;
+       actuator->client = client;
+       actuator->position = 0;
+       actuator->max_position = LC898217_POS_MAX_SIZE;
+       actuator->pos_size_bit = LC898217_POS_SIZE_BIT;
+       actuator->pos_direction = LC898217_POS_DIRECTION;
+       actuator->i2c_lock = NULL;
+       actuator->need_softlanding = 0;
+       actuator->actuator_ops = NULL;
+
+       device->subdev_actuator[place] = subdev_actuator;
+       device->actuator[place] = actuator;
+
+       v4l2_i2c_subdev_init(subdev_actuator, client, &subdev_ops);
+       v4l2_set_subdevdata(subdev_actuator, actuator);
+       v4l2_set_subdev_hostdata(subdev_actuator, device);
+
+       snprintf(subdev_actuator->name, V4L2_SUBDEV_NAME_SIZE, "actuator-subdev.%d", actuator->id);
+
+       probe_info("%s done\n", __func__);
+       return ret;
+
+p_err:
+       if (!actuator)
+               kzfree(actuator);
+
+       if (!subdev_actuator)
+               kzfree(subdev_actuator);
+
+       return ret;
+}
+
+static const struct of_device_id sensor_actuator_lc898217_match[] = {
+       {
+               .compatible = "samsung,exynos5-fimc-is-actuator-lc898217",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sensor_actuator_lc898217_match);
+
+static const struct i2c_device_id sensor_actuator_lc898217_idt[] = {
+       { ACTUATOR_NAME, 0 },
+       {},
+};
+
+static struct i2c_driver sensor_actuator_lc898217_driver = {
+       .probe  = sensor_lc898217_actuator_probe,
+       .driver = {
+               .name   = ACTUATOR_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = sensor_actuator_lc898217_match,
+               .suppress_bind_attrs = true,
+       },
+       .id_table = sensor_actuator_lc898217_idt,
+};
+
+static int __init sensor_actuator_lc898217_init(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&sensor_actuator_lc898217_driver);
+       if (ret)
+               err("failed to add %s driver: %d\n",
+                       sensor_actuator_lc898217_driver.driver.name, ret);
+
+       return ret;
+}
+late_initcall_sync(sensor_actuator_lc898217_init);
diff --git a/drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.h b/drivers/media/platform/exynos/fimc-is2/sensor/module_framework/actuator/fimc-is-actuator-lc898217.h
new file mode 100644 (file)
index 0000000..25fa4a7
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Samsung Exynos5 SoC series Actuator driver
+ *
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * 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.
+ */
+
+#ifndef FIMC_IS_DEVICE_LC898217_H
+#define FIMC_IS_DEVICE_LC898217_H
+
+#define LC898217_POS_SIZE_BIT          ACTUATOR_POS_SIZE_10BIT
+#define LC898217_POS_MAX_SIZE          ((1 << LC898217_POS_SIZE_BIT) - 1)
+#define LC898217_POS_DIRECTION         ACTUATOR_RANGE_INF_TO_MAC
+#define LC898217_REG_POS_HIGH          0x00
+#define LC898217_REG_POS_LOW           0x01
+
+#endif