aboutsummaryrefslogtreecommitdiff
path: root/camera/sensor/eeprom/msm_eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/sensor/eeprom/msm_eeprom.c')
-rw-r--r--camera/sensor/eeprom/msm_eeprom.c1902
1 files changed, 1902 insertions, 0 deletions
diff --git a/camera/sensor/eeprom/msm_eeprom.c b/camera/sensor/eeprom/msm_eeprom.c
new file mode 100644
index 00000000..d416e4bf
--- /dev/null
+++ b/camera/sensor/eeprom/msm_eeprom.c
@@ -0,0 +1,1902 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. 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/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_eeprom.h"
+#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED
+#include <linux/hardware_info.h>
+#endif
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_eeprom_mutex);
+#ifdef CONFIG_COMPAT
+static struct v4l2_file_operations msm_eeprom_v4l2_subdev_fops;
+#endif
+
+/**
+ * msm_get_read_mem_size - Get the total size for allocation
+ * @eeprom_map_array: mem map
+ *
+ * Returns size after computation size, returns error in case of error
+ */
+static int msm_get_read_mem_size
+ (struct msm_eeprom_memory_map_array *eeprom_map_array) {
+ int size = 0, i, j;
+ struct msm_eeprom_mem_map_t *eeprom_map;
+
+ if (eeprom_map_array->msm_size_of_max_mappings >
+ MSM_EEPROM_MAX_MEM_MAP_CNT) {
+ pr_err("%s:%d Memory map cnt greter then expected: %d",
+ __func__, __LINE__,
+ eeprom_map_array->msm_size_of_max_mappings);
+ return -EINVAL;
+ }
+ for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) {
+ eeprom_map = &(eeprom_map_array->memory_map[j]);
+ if (eeprom_map->memory_map_size >
+ MSM_EEPROM_MEMORY_MAP_MAX_SIZE) {
+ pr_err("%s:%d Memory map size greter then expected: %d",
+ __func__, __LINE__,
+ eeprom_map->memory_map_size);
+ return -EINVAL;
+ }
+ for (i = 0; i < eeprom_map->memory_map_size; i++) {
+ if (eeprom_map->mem_settings[i].i2c_operation ==
+ MSM_CAM_READ) {
+ size += eeprom_map->mem_settings[i].reg_data;
+ }
+ }
+ }
+ CDBG("Total Data Size: %d\n", size);
+ return size;
+}
+
+/**
+ * msm_eeprom_verify_sum - verify crc32 checksum
+ * @mem: data buffer
+ * @size: size of data buffer
+ * @sum: expected checksum
+ *
+ * Returns 0 if checksum match, -EINVAL otherwise.
+ */
+static int msm_eeprom_verify_sum(const char *mem, uint32_t size, uint32_t sum)
+{
+ uint32_t crc = ~0;
+
+ /* check overflow */
+ if (size > crc - sizeof(uint32_t))
+ return -EINVAL;
+
+ crc = crc32_le(crc, mem, size);
+ if (~crc != sum) {
+ CDBG("%s: expect 0x%x, result 0x%x\n", __func__, sum, ~crc);
+ return -EINVAL;
+ }
+ CDBG("%s: checksum pass 0x%x\n", __func__, sum);
+ return 0;
+}
+
+/**
+ * msm_eeprom_match_crc - verify multiple regions using crc
+ * @data: data block to be verified
+ *
+ * Iterates through all regions stored in @data. Regions with odd index
+ * are treated as data, and its next region is treated as checksum. Thus
+ * regions of even index must have valid_size of 4 or 0 (skip verification).
+ * Returns a bitmask of verified regions, starting from LSB. 1 indicates
+ * a checksum match, while 0 indicates checksum mismatch or not verified.
+ */
+static uint32_t msm_eeprom_match_crc(struct msm_eeprom_memory_block_t *data)
+{
+ int j, rc;
+ uint32_t *sum;
+ uint32_t ret = 0;
+ uint8_t *memptr;
+ struct msm_eeprom_memory_map_t *map;
+
+ if (!data) {
+ pr_err("%s data is NULL", __func__);
+ return -EINVAL;
+ }
+ map = data->map;
+ memptr = data->mapdata;
+
+ for (j = 0; j + 1 < data->num_map; j += 2) {
+ /* empty table or no checksum */
+ if (!map[j].mem.valid_size || !map[j+1].mem.valid_size) {
+ memptr += map[j].mem.valid_size
+ + map[j+1].mem.valid_size;
+ continue;
+ }
+ if (map[j+1].mem.valid_size != sizeof(uint32_t)) {
+ CDBG("%s: malformatted data mapping\n", __func__);
+ return -EINVAL;
+ }
+ sum = (uint32_t *) (memptr + map[j].mem.valid_size);
+ rc = msm_eeprom_verify_sum(memptr, map[j].mem.valid_size,
+ *sum);
+ if (!rc)
+ ret |= 1 << (j/2);
+ memptr += map[j].mem.valid_size + map[j+1].mem.valid_size;
+ }
+ return ret;
+}
+
+/**
+ * read_eeprom_memory() - read map data into buffer
+ * @e_ctrl: eeprom control struct
+ * @block: block to be read
+ *
+ * This function iterates through blocks stored in block->map, reads each
+ * region and concatenate them into the pre-allocated block->mapdata
+ */
+static int read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_memory_block_t *block)
+{
+ int rc = 0;
+ int j;
+ struct msm_eeprom_memory_map_t *emap = block->map;
+ struct msm_eeprom_board_info *eb_info;
+ uint8_t *memptr = block->mapdata;
+
+ if (!e_ctrl) {
+ pr_err("%s e_ctrl is NULL", __func__);
+ return -EINVAL;
+ }
+
+ eb_info = e_ctrl->eboard_info;
+
+ for (j = 0; j < block->num_map; j++) {
+ if (emap[j].saddr.addr) {
+ eb_info->i2c_slaveaddr = emap[j].saddr.addr;
+ e_ctrl->i2c_client.cci_client->sid =
+ eb_info->i2c_slaveaddr >> 1;
+ pr_err("qcom,slave-addr = 0x%X\n",
+ eb_info->i2c_slaveaddr);
+ }
+
+ if (emap[j].page.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].page.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].page.addr,
+ emap[j].page.data, emap[j].page.data_t);
+ msleep(emap[j].page.delay);
+ if (rc < 0) {
+ pr_err("%s: page write failed\n", __func__);
+ return rc;
+ }
+ }
+ if (emap[j].pageen.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].pageen.addr,
+ emap[j].pageen.data, emap[j].pageen.data_t);
+ msleep(emap[j].pageen.delay);
+ if (rc < 0) {
+ pr_err("%s: page enable failed\n", __func__);
+ return rc;
+ }
+ }
+ if (emap[j].poll.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].poll.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &(e_ctrl->i2c_client), emap[j].poll.addr,
+ emap[j].poll.data, emap[j].poll.data_t,
+ emap[j].poll.delay);
+ if (rc < 0) {
+ pr_err("%s: poll failed\n", __func__);
+ return rc;
+ }
+ }
+
+ if (emap[j].mem.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].mem.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_read_seq(
+ &(e_ctrl->i2c_client), emap[j].mem.addr,
+ memptr, emap[j].mem.valid_size);
+ if (rc < 0) {
+ pr_err("%s: read failed\n", __func__);
+ return rc;
+ }
+ memptr += emap[j].mem.valid_size;
+ }
+ if (emap[j].pageen.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].pageen.addr,
+ 0, emap[j].pageen.data_t);
+ if (rc < 0) {
+ pr_err("%s: page disable failed\n", __func__);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+/**
+ * msm_eeprom_parse_memory_map() - parse memory map in device node
+ * @of: device node
+ * @data: memory block for output
+ *
+ * This functions parses @of to fill @data. It allocates map itself, parses
+ * the @of node, calculate total data length, and allocates required buffer.
+ * It only fills the map, but does not perform actual reading.
+ */
+static int msm_eeprom_parse_memory_map(struct device_node *of,
+ struct msm_eeprom_memory_block_t *data)
+{
+ int i, rc = 0;
+ char property[PROPERTY_MAXSIZE];
+ uint32_t count = 6;
+ struct msm_eeprom_memory_map_t *map;
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,num-blocks");
+ rc = of_property_read_u32(of, property, &data->num_map);
+ CDBG("%s: %s %d\n", __func__, property, data->num_map);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ return rc;
+ }
+
+ map = kzalloc((sizeof(*map) * data->num_map), GFP_KERNEL);
+ if (!map) {
+ rc = -ENOMEM;
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ data->map = map;
+
+ for (i = 0; i < data->num_map; i++) {
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,page%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].page, count);
+ if (rc < 0) {
+ pr_err("%s: failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ snprintf(property, PROPERTY_MAXSIZE,
+ "qcom,pageen%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].pageen, count);
+ if (rc < 0)
+ CDBG("%s: pageen not needed\n", __func__);
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,saddr%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].saddr.addr, 1);
+ if (rc < 0)
+ CDBG("%s: saddr not needed - block %d\n", __func__, i);
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,poll%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].poll, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,mem%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].mem, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ data->num_data += map[i].mem.valid_size;
+ }
+
+ CDBG("%s num_bytes %d\n", __func__, data->num_data);
+
+ data->mapdata = kzalloc(data->num_data, GFP_KERNEL);
+ if (!data->mapdata) {
+ rc = -ENOMEM;
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ return rc;
+
+ERROR:
+ kfree(data->map);
+ memset(data, 0, sizeof(*data));
+ return rc;
+}
+
+/**
+ * eeprom_parse_memory_map - Parse mem map
+ * @e_ctrl: ctrl structure
+ * @eeprom_map_array: eeprom map
+ *
+ * Returns success or failure
+ */
+static int eeprom_parse_memory_map(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_memory_map_array *eeprom_map_array)
+{
+ int rc = 0, i, j;
+ uint8_t *memptr;
+ struct msm_eeprom_mem_map_t *eeprom_map;
+#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED
+ static char module_id[20] = {0};
+ int32_t module_id_index = 0x000C;
+#endif
+
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.num_data = msm_get_read_mem_size(eeprom_map_array);
+ if (e_ctrl->cal_data.num_data <= 0) {
+ pr_err("%s:%d Error in reading mem size\n",
+ __func__, __LINE__);
+ e_ctrl->cal_data.num_data = 0;
+ return -EINVAL;
+ }
+ e_ctrl->cal_data.mapdata =
+ kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL);
+ if (!e_ctrl->cal_data.mapdata)
+ return -ENOMEM;
+
+ memptr = e_ctrl->cal_data.mapdata;
+ for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) {
+ eeprom_map = &(eeprom_map_array->memory_map[j]);
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->sid =
+ eeprom_map->slave_addr >> 1;
+ } else if (e_ctrl->i2c_client.client) {
+ e_ctrl->i2c_client.client->addr =
+ eeprom_map->slave_addr >> 1;
+ }
+ CDBG("Slave Addr: 0x%X\n", eeprom_map->slave_addr);
+ CDBG("Memory map Size: %d",
+ eeprom_map->memory_map_size);
+ for (i = 0; i < eeprom_map->memory_map_size; i++) {
+ switch (eeprom_map->mem_settings[i].i2c_operation) {
+ case MSM_CAM_WRITE: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ eeprom_map->mem_settings[i].reg_data,
+ eeprom_map->mem_settings[i].data_type);
+ msleep(eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: page write failed\n",
+ __func__);
+ goto clean_up;
+ }
+ }
+ break;
+ case MSM_CAM_POLL: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ eeprom_map->mem_settings[i].reg_data,
+ eeprom_map->mem_settings[i].data_type,
+ eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: poll failed\n",
+ __func__);
+ goto clean_up;
+ }
+ }
+ break;
+ case MSM_CAM_READ: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->
+ i2c_read_seq(&(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ memptr,
+ eeprom_map->mem_settings[i].reg_data);
+ msleep(eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: read failed\n",
+ __func__);
+ goto clean_up;
+ }
+ memptr += eeprom_map->mem_settings[i].reg_data;
+ }
+ break;
+ default:
+ pr_err("%s: %d Invalid i2c operation LC:%d\n",
+ __func__, __LINE__, i);
+ return -EINVAL;
+ }
+ }
+ }
+ memptr = e_ctrl->cal_data.mapdata;
+ for (i = 0; i < e_ctrl->cal_data.num_data; i++)
+ CDBG("memory_data[%d] = 0x%X\n", i, memptr[i]);
+#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED
+ if (e_ctrl->pdev->id == 1) {
+ if (((e_ctrl->cal_data.mapdata[0] >> 6) & 0x3) == 1)
+ module_id_index = 0x3205 - 0x3204;
+ else if (((e_ctrl->cal_data.mapdata[0] >> 4) & 0x3) == 1)
+ module_id_index = 0x3211 - 0x3204;
+ sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]);
+ hardwareinfo_set_prop(HARDWARE_FRONT_CAM_MOUDULE_ID, module_id);
+ } else if (e_ctrl->pdev->id == 0) {
+ module_id_index = 0x000C;
+ sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]);
+ hardwareinfo_set_prop(HARDWARE_BACK_CAM_MOUDULE_ID, module_id);
+ }
+ CDBG("module_id: %s index: 0x%x\n", module_id, module_id_index);
+#endif
+ return rc;
+
+clean_up:
+ kfree(e_ctrl->cal_data.mapdata);
+ e_ctrl->cal_data.num_data = 0;
+ e_ctrl->cal_data.mapdata = NULL;
+ return rc;
+}
+
+/**
+ * msm_eeprom_power_up - Do eeprom power up here
+ * @e_ctrl: ctrl structure
+ * @power_info: power up info for eeprom
+ *
+ * Returns success or failure
+ */
+static int msm_eeprom_power_up(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_camera_power_ctrl_t *power_info) {
+ int32_t rc = 0;
+
+ rc = msm_camera_fill_vreg_params(
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info->power_setting, power_info->power_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d failed in camera_fill_vreg_params rc %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ /* Parse and fill vreg params for powerdown settings*/
+ rc = msm_camera_fill_vreg_params(
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info->power_down_setting,
+ power_info->power_down_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d failed msm_camera_fill_vreg_params for PDOWN rc %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("%s:%d failed in eeprom Power up rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+/**
+ * msm_eeprom_power_up - Do power up, parse and power down
+ * @e_ctrl: ctrl structure
+ * Returns success or failure
+ */
+static int eeprom_init_config(struct msm_eeprom_ctrl_t *e_ctrl,
+ void __user *argp)
+{
+ int rc = 0;
+ struct msm_eeprom_cfg_data *cdata = argp;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_camera_power_ctrl_t *power_info;
+ struct msm_eeprom_memory_map_array *memory_map_arr = NULL;
+
+ power_setting_array =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array),
+ GFP_KERNEL);
+ if (!power_setting_array) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ memory_map_arr = kzalloc(sizeof(struct msm_eeprom_memory_map_array),
+ GFP_KERNEL);
+ if (!memory_map_arr) {
+ rc = -ENOMEM;
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ goto free_mem;
+ }
+
+ if (copy_from_user(power_setting_array,
+ cdata->cfg.eeprom_info.power_setting_array,
+ sizeof(struct msm_sensor_power_setting_array))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+ CDBG("%s:%d Size of power setting array: %d\n",
+ __func__, __LINE__, power_setting_array->size);
+ if (copy_from_user(memory_map_arr,
+ cdata->cfg.eeprom_info.mem_map_array,
+ sizeof(struct msm_eeprom_memory_map_array))) {
+ rc = -EINVAL;
+ pr_err("%s copy_from_user failed for memory map%d\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ power_info = &(e_ctrl->eboard_info->power_info);
+
+ power_info->power_setting =
+ power_setting_array->power_setting_a;
+ power_info->power_down_setting =
+ power_setting_array->power_down_setting_a;
+
+ power_info->power_setting_size =
+ power_setting_array->size;
+ power_info->power_down_setting_size =
+ power_setting_array->size_down;
+
+ if ((power_info->power_setting_size >
+ MAX_POWER_CONFIG) ||
+ (power_info->power_down_setting_size >
+ MAX_POWER_CONFIG) ||
+ (!power_info->power_down_setting_size) ||
+ (!power_info->power_setting_size)) {
+ rc = -EINVAL;
+ pr_err("%s:%d Invalid power setting size :%d, %d\n",
+ __func__, __LINE__,
+ power_info->power_setting_size,
+ power_info->power_down_setting_size);
+ goto free_mem;
+ }
+
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ cdata->cfg.eeprom_info.i2c_freq_mode;
+ if (e_ctrl->i2c_client.cci_client->i2c_freq_mode >
+ I2C_MAX_MODES) {
+ pr_err("%s::%d Improper I2C freq mode\n",
+ __func__, __LINE__);
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ I2C_STANDARD_MODE;
+ }
+ }
+
+ /* Fill vreg power info and power up here */
+ rc = msm_eeprom_power_up(e_ctrl, power_info);
+ if (rc < 0) {
+ pr_err("Power Up failed for eeprom\n");
+ goto free_mem;
+ }
+
+ rc = eeprom_parse_memory_map(e_ctrl, memory_map_arr);
+ if (rc < 0) {
+ pr_err("%s::%d memory map parse failed\n", __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("%s:%d Power down failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_mem;
+ }
+
+free_mem:
+ kfree(power_setting_array);
+ kfree(memory_map_arr);
+ power_setting_array = NULL;
+ memory_map_arr = NULL;
+ return rc;
+}
+
+static int msm_eeprom_get_cmm_data(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_cfg_data *cdata)
+{
+ int rc = 0;
+ struct msm_eeprom_cmm_t *cmm_data = &e_ctrl->eboard_info->cmm_data;
+ cdata->cfg.get_cmm_data.cmm_support = cmm_data->cmm_support;
+ cdata->cfg.get_cmm_data.cmm_compression = cmm_data->cmm_compression;
+ cdata->cfg.get_cmm_data.cmm_size = cmm_data->cmm_size;
+ return rc;
+}
+
+static int eeprom_config_read_cal_data(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_cfg_data *cdata)
+{
+ int rc;
+
+ /* check range */
+ if (cdata->cfg.read_data.num_bytes >
+ e_ctrl->cal_data.num_data) {
+ CDBG("%s: Invalid size. exp %u, req %u\n", __func__,
+ e_ctrl->cal_data.num_data,
+ cdata->cfg.read_data.num_bytes);
+ return -EINVAL;
+ }
+
+ rc = copy_to_user(cdata->cfg.read_data.dbuffer,
+ e_ctrl->cal_data.mapdata,
+ cdata->cfg.read_data.num_bytes);
+
+ return rc;
+}
+
+static int msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl,
+ void __user *argp)
+{
+ struct msm_eeprom_cfg_data *cdata =
+ (struct msm_eeprom_cfg_data *)argp;
+ int rc = 0;
+
+ CDBG("%s E\n", __func__);
+ switch (cdata->cfgtype) {
+ case CFG_EEPROM_GET_INFO:
+ if (e_ctrl->userspace_probe == 1) {
+ pr_err("%s:%d Eeprom name should be module driver",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__);
+ cdata->is_supported = e_ctrl->is_supported;
+ memcpy(cdata->cfg.eeprom_name,
+ e_ctrl->eboard_info->eeprom_name,
+ sizeof(cdata->cfg.eeprom_name));
+ break;
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
+ cdata->cfg.get_data.num_bytes =
+ e_ctrl->cal_data.num_data;
+ break;
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
+ rc = eeprom_config_read_cal_data(e_ctrl, cdata);
+ break;
+ case CFG_EEPROM_GET_MM_INFO:
+ CDBG("%s E CFG_EEPROM_GET_MM_INFO\n", __func__);
+ rc = msm_eeprom_get_cmm_data(e_ctrl, cdata);
+ break;
+ case CFG_EEPROM_INIT:
+ if (e_ctrl->userspace_probe == 0) {
+ pr_err("%s:%d Eeprom already probed at kernel boot",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ if (e_ctrl->cal_data.num_data == 0) {
+ rc = eeprom_init_config(e_ctrl, argp);
+ if (rc < 0) {
+ pr_err("%s:%d Eeprom init failed\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ } else {
+ CDBG("%s:%d Already read eeprom\n",
+ __func__, __LINE__);
+ }
+ break;
+ default:
+ break;
+ }
+
+ CDBG("%s X rc: %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_eeprom_get_subdev_id(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+ CDBG("%s E\n", __func__);
+ if (!subdev_id) {
+ pr_err("%s failed\n", __func__);
+ return -EINVAL;
+ }
+ *subdev_id = e_ctrl->subdev_id;
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("%s X\n", __func__);
+ return 0;
+}
+
+static long msm_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ void __user *argp = (void __user *)arg;
+ CDBG("%s E\n", __func__);
+ CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, e_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_eeprom_get_subdev_id(e_ctrl, argp);
+ case VIDIOC_MSM_EEPROM_CFG:
+ return msm_eeprom_config(e_ctrl, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ CDBG("%s X\n", __func__);
+}
+
+static struct msm_camera_i2c_fn_t msm_eeprom_cci_func_tbl = {
+ .i2c_read = msm_camera_cci_i2c_read,
+ .i2c_read_seq = msm_camera_cci_i2c_read_seq,
+ .i2c_write = msm_camera_cci_i2c_write,
+ .i2c_write_seq = msm_camera_cci_i2c_write_seq,
+ .i2c_write_table = msm_camera_cci_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_cci_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_cci_i2c_util,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+static struct msm_camera_i2c_fn_t msm_eeprom_qup_func_tbl = {
+ .i2c_read = msm_camera_qup_i2c_read,
+ .i2c_read_seq = msm_camera_qup_i2c_read_seq,
+ .i2c_write = msm_camera_qup_i2c_write,
+ .i2c_write_table = msm_camera_qup_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_qup_i2c_write_table_w_microdelay,
+};
+
+static struct msm_camera_i2c_fn_t msm_eeprom_spi_func_tbl = {
+ .i2c_read = msm_camera_spi_read,
+ .i2c_read_seq = msm_camera_spi_read_seq,
+};
+
+static int msm_eeprom_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ CDBG("%s E\n", __func__);
+ if (!e_ctrl) {
+ pr_err("%s failed e_ctrl is NULL\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s X\n", __func__);
+ return rc;
+}
+
+static int msm_eeprom_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ CDBG("%s E\n", __func__);
+ if (!e_ctrl) {
+ pr_err("%s failed e_ctrl is NULL\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s X\n", __func__);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_eeprom_internal_ops = {
+ .open = msm_eeprom_open,
+ .close = msm_eeprom_close,
+};
+
+static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = {
+ .ioctl = msm_eeprom_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_eeprom_subdev_ops = {
+ .core = &msm_eeprom_subdev_core_ops,
+};
+
+static int msm_eeprom_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+ CDBG("%s E\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s i2c_check_functionality failed\n", __func__);
+ goto probe_failure;
+ }
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl) {
+ pr_err("%s:%d kzalloc failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+ CDBG("%s client = 0x%p\n", __func__, client);
+ e_ctrl->eboard_info = (struct msm_eeprom_board_info *)(id->driver_data);
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s:%d board info NULL\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto ectrl_free;
+ }
+ e_ctrl->i2c_client.client = client;
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->is_supported = 1;
+
+ /* Set device type as I2C */
+ e_ctrl->eeprom_device_type = MSM_CAMERA_I2C_DEVICE;
+ e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_qup_func_tbl;
+
+ if (e_ctrl->eboard_info->i2c_slaveaddr != 0)
+ e_ctrl->i2c_client.client->addr =
+ e_ctrl->eboard_info->i2c_slaveaddr;
+
+ /*Get clocks information*/
+ rc = msm_camera_get_clk_info(e_ctrl->pdev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ &e_ctrl->eboard_info->power_info.clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto ectrl_free;
+ }
+
+ /*IMPLEMENT READING PART*/
+ /* Initialize sub device */
+ v4l2_i2c_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->i2c_client.client,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM;
+ msm_sd_register(&e_ctrl->msm_sd);
+ CDBG("%s success result=%d X\n", __func__, rc);
+ return rc;
+
+ectrl_free:
+ kfree(e_ctrl);
+probe_failure:
+ pr_err("%s failed! rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_eeprom_i2c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: eboard_info is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_camera_put_clk_info(e_ctrl->pdev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ e_ctrl->cal_data.mapdata = NULL;
+ kfree(e_ctrl);
+ e_ctrl = NULL;
+
+ return 0;
+}
+
+#define msm_eeprom_spi_parse_cmd(spic, str, name, out, size) \
+ { \
+ if (of_property_read_u32_array( \
+ spic->spi_master->dev.of_node, \
+ str, out, size)) { \
+ return -EFAULT; \
+ } else { \
+ spic->cmd_tbl.name.opcode = out[0]; \
+ spic->cmd_tbl.name.addr_len = out[1]; \
+ spic->cmd_tbl.name.dummy_len = out[2]; \
+ } \
+ }
+
+static int msm_eeprom_spi_parse_of(struct msm_camera_spi_client *spic)
+{
+ int rc = -EFAULT;
+ uint32_t tmp[3];
+ msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,read", read, tmp, 3);
+ msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,readseq", read_seq, tmp, 3);
+ msm_eeprom_spi_parse_cmd(spic, "qcom,spiop,queryid", query_id, tmp, 3);
+
+ rc = of_property_read_u32_array(spic->spi_master->dev.of_node,
+ "qcom,eeprom-id", tmp, 2);
+ if (rc) {
+ pr_err("%s: Failed to get eeprom id\n", __func__);
+ return rc;
+ }
+ spic->mfr_id0 = tmp[0];
+ spic->device_id0 = tmp[1];
+
+ return 0;
+}
+
+static int msm_eeprom_match_id(struct msm_eeprom_ctrl_t *e_ctrl)
+{
+ int rc;
+ struct msm_camera_i2c_client *client = &e_ctrl->i2c_client;
+ uint8_t id[2];
+
+ rc = msm_camera_spi_query_id(client, 0, &id[0], 2);
+ if (rc < 0)
+ return rc;
+ CDBG("%s: read 0x%x 0x%x, check 0x%x 0x%x\n", __func__, id[0],
+ id[1], client->spi_client->mfr_id0,
+ client->spi_client->device_id0);
+ if (id[0] != client->spi_client->mfr_id0
+ || id[1] != client->spi_client->device_id0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int msm_eeprom_get_dt_data(struct msm_eeprom_ctrl_t *e_ctrl)
+{
+ int rc = 0, i = 0;
+ struct msm_eeprom_board_info *eb_info;
+ struct msm_camera_power_ctrl_t *power_info =
+ &e_ctrl->eboard_info->power_info;
+ struct device_node *of_node = NULL;
+ struct msm_camera_gpio_conf *gconf = NULL;
+ int8_t gpio_array_size = 0;
+ uint16_t *gpio_array = NULL;
+
+ eb_info = e_ctrl->eboard_info;
+ if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE)
+ of_node = e_ctrl->i2c_client.
+ spi_client->spi_master->dev.of_node;
+ else if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ of_node = e_ctrl->pdev->dev.of_node;
+
+ if (!of_node) {
+ pr_err("%s: %d of_node is NULL\n", __func__ , __LINE__);
+ return -ENOMEM;
+ }
+ rc = msm_camera_get_dt_vreg_data(of_node, &power_info->cam_vreg,
+ &power_info->num_vreg);
+ if (rc < 0)
+ return rc;
+
+ if (e_ctrl->userspace_probe == 0) {
+ rc = msm_camera_get_dt_power_setting_data(of_node,
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info);
+ if (rc < 0)
+ goto ERROR1;
+ }
+
+ power_info->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!power_info->gpio_conf) {
+ rc = -ENOMEM;
+ goto ERROR2;
+ }
+ gconf = power_info->gpio_conf;
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size > 0) {
+ gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size,
+ GFP_KERNEL);
+ if (!gpio_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR3;
+ }
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ CDBG("%s gpio_array[%d] = %d\n", __func__, i,
+ gpio_array[i]);
+ }
+
+ rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+
+ rc = msm_camera_init_gpio_pin_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+ kfree(gpio_array);
+ }
+
+ return rc;
+ERROR4:
+ kfree(gpio_array);
+ERROR3:
+ kfree(power_info->gpio_conf);
+ERROR2:
+ kfree(power_info->cam_vreg);
+ERROR1:
+ kfree(power_info->power_setting);
+ return rc;
+}
+
+
+static int msm_eeprom_cmm_dts(struct msm_eeprom_board_info *eb_info,
+ struct device_node *of_node)
+{
+ int rc = 0;
+ struct msm_eeprom_cmm_t *cmm_data = &eb_info->cmm_data;
+
+ cmm_data->cmm_support =
+ of_property_read_bool(of_node, "qcom,cmm-data-support");
+ if (!cmm_data->cmm_support)
+ return -EINVAL;
+ cmm_data->cmm_compression =
+ of_property_read_bool(of_node, "qcom,cmm-data-compressed");
+ if (!cmm_data->cmm_compression)
+ CDBG("No MM compression data\n");
+
+ rc = of_property_read_u32(of_node, "qcom,cmm-data-offset",
+ &cmm_data->cmm_offset);
+ if (rc < 0)
+ CDBG("No MM offset data\n");
+
+ rc = of_property_read_u32(of_node, "qcom,cmm-data-size",
+ &cmm_data->cmm_size);
+ if (rc < 0)
+ CDBG("No MM size data\n");
+
+ CDBG("cmm_support: cmm_compr %d, cmm_offset %d, cmm_size %d\n",
+ cmm_data->cmm_compression,
+ cmm_data->cmm_offset,
+ cmm_data->cmm_size);
+ return 0;
+}
+
+static int msm_eeprom_spi_setup(struct spi_device *spi)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+ struct msm_camera_i2c_client *client = NULL;
+ struct msm_camera_spi_client *spi_client;
+ struct msm_eeprom_board_info *eb_info;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+ int rc = 0;
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl) {
+ pr_err("%s:%d kzalloc failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+ client = &e_ctrl->i2c_client;
+ e_ctrl->is_supported = 0;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+
+ spi_client = kzalloc(sizeof(*spi_client), GFP_KERNEL);
+ if (!spi_client) {
+ pr_err("%s:%d kzalloc failed\n", __func__, __LINE__);
+ kfree(e_ctrl);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32(spi->dev.of_node, "cell-index",
+ &e_ctrl->subdev_id);
+ CDBG("cell-index %d, rc %d\n", e_ctrl->subdev_id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ eb_info = kzalloc(sizeof(*eb_info), GFP_KERNEL);
+ if (!eb_info)
+ goto spi_free;
+ e_ctrl->eboard_info = eb_info;
+
+ rc = of_property_read_string(spi->dev.of_node, "qcom,eeprom-name",
+ &eb_info->eeprom_name);
+ CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__,
+ eb_info->eeprom_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ e_ctrl->userspace_probe = 1;
+ goto board_free;
+ }
+
+ e_ctrl->eeprom_device_type = MSM_CAMERA_SPI_DEVICE;
+ client->spi_client = spi_client;
+ spi_client->spi_master = spi;
+ client->i2c_func_tbl = &msm_eeprom_spi_func_tbl;
+ client->addr_type = MSM_CAMERA_I2C_3B_ADDR;
+
+ rc = msm_eeprom_cmm_dts(e_ctrl->eboard_info, spi->dev.of_node);
+ if (rc < 0)
+ CDBG("%s MM data miss:%d\n", __func__, __LINE__);
+
+ power_info = &eb_info->power_info;
+ power_info->dev = &spi->dev;
+
+ /*Get clocks information*/
+ rc = msm_camera_get_clk_info(e_ctrl->pdev,
+ &power_info->clk_info,
+ &power_info->clk_ptr,
+ &power_info->clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto board_free;
+ }
+
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc < 0)
+ goto board_free;
+
+ /* set spi instruction info */
+ spi_client->retry_delay = 1;
+ spi_client->retries = 0;
+
+ rc = msm_eeprom_spi_parse_of(spi_client);
+ if (rc < 0) {
+ dev_err(&spi->dev,
+ "%s: Error parsing device properties\n", __func__);
+ goto board_free;
+ }
+
+ if (e_ctrl->userspace_probe == 0) {
+ /* prepare memory buffer */
+ rc = msm_eeprom_parse_memory_map(spi->dev.of_node,
+ &e_ctrl->cal_data);
+ if (rc < 0)
+ CDBG("%s: no cal memory map\n", __func__);
+
+ /* power up eeprom for reading */
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto caldata_free;
+ }
+
+ /* check eeprom id */
+ rc = msm_eeprom_match_id(e_ctrl);
+ if (rc < 0) {
+ CDBG("%s: eeprom not matching %d\n", __func__, rc);
+ goto power_down;
+ }
+ /* read eeprom */
+ if (e_ctrl->cal_data.map) {
+ rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data);
+ if (rc < 0) {
+ pr_err("%s: read cal data failed\n", __func__);
+ goto power_down;
+ }
+ e_ctrl->is_supported |= msm_eeprom_match_crc(
+ &e_ctrl->cal_data);
+ }
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto caldata_free;
+ }
+ } else
+ e_ctrl->is_supported = 1;
+
+ /* initiazlie subdev */
+ v4l2_spi_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->i2c_client.spi_client->spi_master,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM;
+ msm_sd_register(&e_ctrl->msm_sd);
+ e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1;
+ CDBG("%s success result=%d supported=%x X\n", __func__, rc,
+ e_ctrl->is_supported);
+
+ return 0;
+
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+caldata_free:
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+board_free:
+ kfree(e_ctrl->eboard_info);
+spi_free:
+ kfree(spi_client);
+ kfree(e_ctrl);
+ return rc;
+}
+
+static int msm_eeprom_spi_probe(struct spi_device *spi)
+{
+ int irq, cs, cpha, cpol, cs_high;
+
+ CDBG("%s\n", __func__);
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ spi_setup(spi);
+
+ irq = spi->irq;
+ cs = spi->chip_select;
+ cpha = (spi->mode & SPI_CPHA) ? 1 : 0;
+ cpol = (spi->mode & SPI_CPOL) ? 1 : 0;
+ cs_high = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
+ CDBG("%s: irq[%d] cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x]\n",
+ __func__, irq, cs, cpha, cpol, cs_high);
+ CDBG("%s: max_speed[%u]\n", __func__, spi->max_speed_hz);
+
+ return msm_eeprom_spi_setup(spi);
+}
+
+static int msm_eeprom_spi_remove(struct spi_device *sdev)
+{
+ struct v4l2_subdev *sd = spi_get_drvdata(sdev);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: eboard_info is NULL\n", __func__);
+ return 0;
+ }
+ msm_camera_put_clk_info(e_ctrl->pdev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->i2c_client.spi_client);
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ e_ctrl->cal_data.mapdata = NULL;
+ kfree(e_ctrl);
+ e_ctrl = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static void msm_eeprom_copy_power_settings_compat(
+ struct msm_sensor_power_setting_array *ps,
+ struct msm_sensor_power_setting_array32 *ps32)
+{
+ uint16_t i = 0;
+
+ ps->size = ps32->size;
+ for (i = 0; i < ps32->size; i++) {
+ ps->power_setting_a[i].config_val =
+ ps32->power_setting_a[i].config_val;
+ ps->power_setting_a[i].delay =
+ ps32->power_setting_a[i].delay;
+ ps->power_setting_a[i].seq_type =
+ ps32->power_setting_a[i].seq_type;
+ ps->power_setting_a[i].seq_val =
+ ps32->power_setting_a[i].seq_val;
+ }
+
+ ps->size_down = ps32->size_down;
+ for (i = 0; i < ps32->size_down; i++) {
+ ps->power_down_setting_a[i].config_val =
+ ps32->power_down_setting_a[i].config_val;
+ ps->power_down_setting_a[i].delay =
+ ps32->power_down_setting_a[i].delay;
+ ps->power_down_setting_a[i].seq_type =
+ ps32->power_down_setting_a[i].seq_type;
+ ps->power_down_setting_a[i].seq_val =
+ ps32->power_down_setting_a[i].seq_val;
+ }
+}
+
+static int eeprom_config_read_cal_data32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void __user *arg)
+{
+ int rc;
+ uint8_t *ptr_dest = NULL;
+ struct msm_eeprom_cfg_data32 *cdata32 =
+ (struct msm_eeprom_cfg_data32 *) arg;
+ struct msm_eeprom_cfg_data cdata;
+
+ cdata.cfgtype = cdata32->cfgtype;
+ cdata.is_supported = cdata32->is_supported;
+ cdata.cfg.read_data.num_bytes = cdata32->cfg.read_data.num_bytes;
+ /* check range */
+ if (cdata.cfg.read_data.num_bytes >
+ e_ctrl->cal_data.num_data) {
+ CDBG("%s: Invalid size. exp %u, req %u\n", __func__,
+ e_ctrl->cal_data.num_data,
+ cdata.cfg.read_data.num_bytes);
+ return -EINVAL;
+ }
+ if (!e_ctrl->cal_data.mapdata)
+ return -EFAULT;
+
+ ptr_dest = (uint8_t *) compat_ptr(cdata32->cfg.read_data.dbuffer);
+
+ rc = copy_to_user(ptr_dest, e_ctrl->cal_data.mapdata,
+ cdata.cfg.read_data.num_bytes);
+
+ return rc;
+}
+
+static int eeprom_init_config32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void __user *argp)
+{
+ int rc = 0;
+ struct msm_eeprom_cfg_data32 *cdata32 = argp;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting_array32 *power_setting_array32 = NULL;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+ struct msm_eeprom_memory_map_array *mem_map_array = NULL;
+
+ power_setting_array32 =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array32),
+ GFP_KERNEL);
+ if (!power_setting_array32) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ power_setting_array =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array),
+ GFP_KERNEL);
+ if (power_setting_array == NULL) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto free_mem;
+ }
+ mem_map_array =
+ kzalloc(sizeof(struct msm_eeprom_memory_map_array),
+ GFP_KERNEL);
+ if (mem_map_array == NULL) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (copy_from_user(power_setting_array32,
+ (void *)compat_ptr(cdata32->cfg.eeprom_info.
+ power_setting_array),
+ sizeof(struct msm_sensor_power_setting_array32))) {
+ pr_err("%s:%d copy_from_user failed\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+ CDBG("%s:%d Size of power setting array: %d",
+ __func__, __LINE__, power_setting_array32->size);
+ if (copy_from_user(mem_map_array,
+ (void *)compat_ptr(cdata32->cfg.eeprom_info.mem_map_array),
+ sizeof(struct msm_eeprom_memory_map_array))) {
+ pr_err("%s:%d copy_from_user failed for memory map\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ power_info = &(e_ctrl->eboard_info->power_info);
+
+ msm_eeprom_copy_power_settings_compat(
+ power_setting_array,
+ power_setting_array32);
+
+ power_info->power_setting =
+ power_setting_array->power_setting_a;
+ power_info->power_down_setting =
+ power_setting_array->power_down_setting_a;
+
+ power_info->power_setting_size =
+ power_setting_array->size;
+ power_info->power_down_setting_size =
+ power_setting_array->size_down;
+
+ if ((power_info->power_setting_size >
+ MAX_POWER_CONFIG) ||
+ (power_info->power_down_setting_size >
+ MAX_POWER_CONFIG) ||
+ (!power_info->power_down_setting_size) ||
+ (!power_info->power_setting_size)) {
+ rc = -EINVAL;
+ pr_err("%s:%d Invalid power setting size :%d, %d\n",
+ __func__, __LINE__,
+ power_info->power_setting_size,
+ power_info->power_down_setting_size);
+ goto free_mem;
+ }
+
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ cdata32->cfg.eeprom_info.i2c_freq_mode;
+ if (e_ctrl->i2c_client.cci_client->i2c_freq_mode >
+ I2C_MAX_MODES) {
+ pr_err("%s::%d Improper I2C Freq Mode\n",
+ __func__, __LINE__);
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ I2C_STANDARD_MODE;
+ }
+ CDBG("%s:%d Not CCI probe", __func__, __LINE__);
+ }
+ /* Fill vreg power info and power up here */
+ rc = msm_eeprom_power_up(e_ctrl, power_info);
+ if (rc < 0) {
+ pr_err("%s:%d Power Up failed for eeprom\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = eeprom_parse_memory_map(e_ctrl, mem_map_array);
+ if (rc < 0) {
+ pr_err("%s:%d memory map parse failed\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc < 0)
+ pr_err("%s:%d Power down failed rc %d\n",
+ __func__, __LINE__, rc);
+
+free_mem:
+ kfree(power_setting_array32);
+ kfree(power_setting_array);
+ kfree(mem_map_array);
+ power_setting_array32 = NULL;
+ power_setting_array = NULL;
+ mem_map_array = NULL;
+ return rc;
+}
+
+static int msm_eeprom_config32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void __user *argp)
+{
+ struct msm_eeprom_cfg_data32 *cdata =
+ (struct msm_eeprom_cfg_data32 *)argp;
+ int rc = 0;
+
+ CDBG("%s E\n", __func__);
+ switch (cdata->cfgtype) {
+ case CFG_EEPROM_GET_INFO:
+ if (e_ctrl->userspace_probe == 1) {
+ pr_err("%s:%d Eeprom name should be module driver",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__);
+ cdata->is_supported = e_ctrl->is_supported;
+ memcpy(cdata->cfg.eeprom_name,
+ e_ctrl->eboard_info->eeprom_name,
+ sizeof(cdata->cfg.eeprom_name));
+ break;
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
+ cdata->cfg.get_data.num_bytes =
+ e_ctrl->cal_data.num_data;
+ break;
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
+ rc = eeprom_config_read_cal_data32(e_ctrl, argp);
+ break;
+ case CFG_EEPROM_INIT:
+ if (e_ctrl->userspace_probe == 0) {
+ pr_err("%s:%d Eeprom already probed at kernel boot",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ if (e_ctrl->cal_data.num_data == 0) {
+ rc = eeprom_init_config32(e_ctrl, argp);
+ if (rc < 0)
+ pr_err("%s:%d Eeprom init failed\n",
+ __func__, __LINE__);
+ } else {
+ CDBG("%s:%d Already read eeprom\n",
+ __func__, __LINE__);
+ }
+ break;
+ default:
+ break;
+ }
+
+ CDBG("%s X rc: %d\n", __func__, rc);
+ return rc;
+}
+
+static long msm_eeprom_subdev_ioctl32(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ void __user *argp = (void __user *)arg;
+
+ CDBG("%s E\n", __func__);
+ CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, e_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_eeprom_get_subdev_id(e_ctrl, argp);
+ case VIDIOC_MSM_EEPROM_CFG32:
+ return msm_eeprom_config32(e_ctrl, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ CDBG("%s X\n", __func__);
+}
+
+static long msm_eeprom_subdev_do_ioctl32(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return msm_eeprom_subdev_ioctl32(sd, cmd, arg);
+}
+
+static long msm_eeprom_subdev_fops_ioctl32(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_eeprom_subdev_do_ioctl32);
+}
+
+#endif
+
+static int msm_eeprom_platform_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ int j = 0;
+ uint32_t temp;
+#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED
+ static char module_id[20] = {0};
+ int32_t module_id_index = 0x000C;
+#endif
+
+ struct msm_camera_cci_client *cci_client = NULL;
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+ struct msm_eeprom_board_info *eb_info = NULL;
+ struct device_node *of_node = pdev->dev.of_node;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+
+ CDBG("%s E\n", __func__);
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl) {
+ pr_err("%s:%d kzalloc failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->is_supported = 0;
+ if (!of_node) {
+ pr_err("%s dev.of_node NULL\n", __func__);
+ rc = -EINVAL;
+ goto ectrl_free;
+ }
+
+ /* Set platform device handle */
+ e_ctrl->pdev = pdev;
+ /* Set device type as platform device */
+ e_ctrl->eeprom_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_cci_func_tbl;
+ e_ctrl->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!e_ctrl->i2c_client.cci_client) {
+ pr_err("%s failed no memory\n", __func__);
+ rc = -ENOMEM;
+ goto ectrl_free;
+ }
+
+ e_ctrl->eboard_info = kzalloc(sizeof(
+ struct msm_eeprom_board_info), GFP_KERNEL);
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto cciclient_free;
+ }
+
+ eb_info = e_ctrl->eboard_info;
+ power_info = &eb_info->power_info;
+ cci_client = e_ctrl->i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ power_info->dev = &pdev->dev;
+
+ /*Get clocks information*/
+ rc = msm_camera_get_clk_info(e_ctrl->pdev,
+ &power_info->clk_info,
+ &power_info->clk_ptr,
+ &power_info->clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto board_free;
+ }
+
+ rc = of_property_read_u32(of_node, "cell-index",
+ &pdev->id);
+ CDBG("cell-index %d, rc %d\n", pdev->id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto board_free;
+ }
+ e_ctrl->subdev_id = pdev->id;
+
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &e_ctrl->cci_master);
+ CDBG("qcom,cci-master %d, rc %d\n", e_ctrl->cci_master, rc);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ goto board_free;
+ }
+ cci_client->cci_i2c_master = e_ctrl->cci_master;
+
+ rc = of_property_read_string(of_node, "qcom,eeprom-name",
+ &eb_info->eeprom_name);
+ CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__,
+ eb_info->eeprom_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ e_ctrl->userspace_probe = 1;
+ }
+
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc < 0)
+ goto board_free;
+
+ if (e_ctrl->userspace_probe == 0) {
+ rc = of_property_read_u32(of_node, "qcom,slave-addr",
+ &temp);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ goto board_free;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,i2c-freq-mode",
+ &e_ctrl->i2c_freq_mode);
+ CDBG("qcom,i2c_freq_mode %d, rc %d\n",
+ e_ctrl->i2c_freq_mode, rc);
+ if (rc < 0) {
+ pr_err("%s qcom,i2c-freq-mode read fail. Setting to 0 %d\n",
+ __func__, rc);
+ e_ctrl->i2c_freq_mode = 0;
+ }
+ if (e_ctrl->i2c_freq_mode >= I2C_MAX_MODES) {
+ pr_err("%s:%d invalid i2c_freq_mode = %d\n",
+ __func__, __LINE__, e_ctrl->i2c_freq_mode);
+ e_ctrl->i2c_freq_mode = 0;
+ }
+ eb_info->i2c_slaveaddr = temp;
+ CDBG("qcom,slave-addr = 0x%X\n", eb_info->i2c_slaveaddr);
+ eb_info->i2c_freq_mode = e_ctrl->i2c_freq_mode;
+ cci_client->i2c_freq_mode = e_ctrl->i2c_freq_mode;
+ cci_client->sid = eb_info->i2c_slaveaddr >> 1;
+
+ rc = msm_eeprom_parse_memory_map(of_node, &e_ctrl->cal_data);
+ if (rc < 0)
+ goto board_free;
+
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
+ rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data);
+ if (rc < 0) {
+ pr_err("%s read_eeprom_memory failed\n", __func__);
+ goto power_down;
+ }
+#ifdef CONFIG_CAMERA_WT_FACTORY_SUPPORTED
+ else {
+ if (pdev->id == 1) {
+ if (((e_ctrl->cal_data.mapdata[0] >> 6) & 0x3) == 1)
+ module_id_index = 0x3205 - 0x3204;
+ else if (((e_ctrl->cal_data.mapdata[0] >> 4) & 0x3) == 1)
+ module_id_index = 0x3211 - 0x3204;
+ sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]);
+ hardwareinfo_set_prop(HARDWARE_FRONT_CAM_MOUDULE_ID, module_id);
+ } else if (pdev->id == 0) {
+ module_id_index = 0x000C;
+ sprintf(module_id, "%x", e_ctrl->cal_data.mapdata[module_id_index]);
+ hardwareinfo_set_prop(HARDWARE_BACK_CAM_MOUDULE_ID, module_id);
+ }
+
+ CDBG("module_id: %s index: 0x%x\n", module_id, module_id_index);
+ }
+#endif
+ for (j = 0; j < e_ctrl->cal_data.num_data; j++)
+ CDBG("memory_data[%d] = 0x%X\n", j,
+ e_ctrl->cal_data.mapdata[j]);
+
+ e_ctrl->is_supported |= msm_eeprom_match_crc(&e_ctrl->cal_data);
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
+ } else
+ e_ctrl->is_supported = 1;
+
+ v4l2_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ platform_set_drvdata(pdev, &e_ctrl->msm_sd.sd);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(e_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(e_ctrl->msm_sd.sd.name), "msm_eeprom");
+ media_entity_init(&e_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ e_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ e_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_EEPROM;
+ msm_sd_register(&e_ctrl->msm_sd);
+
+#ifdef CONFIG_COMPAT
+ msm_cam_copy_v4l2_subdev_fops(&msm_eeprom_v4l2_subdev_fops);
+ msm_eeprom_v4l2_subdev_fops.compat_ioctl32 =
+ msm_eeprom_subdev_fops_ioctl32;
+ e_ctrl->msm_sd.sd.devnode->fops = &msm_eeprom_v4l2_subdev_fops;
+#endif
+
+ e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1;
+ CDBG("%s X\n", __func__);
+ return rc;
+
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+memdata_free:
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+board_free:
+ kfree(e_ctrl->eboard_info);
+cciclient_free:
+ kfree(e_ctrl->i2c_client.cci_client);
+ectrl_free:
+ kfree(e_ctrl);
+ return rc;
+}
+
+static int msm_eeprom_platform_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: eboard_info is NULL\n", __func__);
+ return 0;
+ }
+ msm_camera_put_clk_info(e_ctrl->pdev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->i2c_client.cci_client);
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ kfree(e_ctrl);
+ return 0;
+}
+
+static const struct of_device_id msm_eeprom_dt_match[] = {
+ { .compatible = "qcom,eeprom" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_eeprom_dt_match);
+
+static struct platform_driver msm_eeprom_platform_driver = {
+ .driver = {
+ .name = "qcom,eeprom",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_eeprom_dt_match,
+ },
+ .probe = msm_eeprom_platform_probe,
+ .remove = msm_eeprom_platform_remove,
+};
+
+static const struct i2c_device_id msm_eeprom_i2c_id[] = {
+ { "msm_eeprom", (kernel_ulong_t)NULL},
+ { }
+};
+
+static struct i2c_driver msm_eeprom_i2c_driver = {
+ .id_table = msm_eeprom_i2c_id,
+ .probe = msm_eeprom_i2c_probe,
+ .remove = msm_eeprom_i2c_remove,
+ .driver = {
+ .name = "msm_eeprom",
+ },
+};
+
+static struct spi_driver msm_eeprom_spi_driver = {
+ .driver = {
+ .name = "qcom_eeprom",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_eeprom_dt_match,
+ },
+ .probe = msm_eeprom_spi_probe,
+ .remove = msm_eeprom_spi_remove,
+};
+
+static int __init msm_eeprom_init_module(void)
+{
+ int rc = 0;
+ CDBG("%s E\n", __func__);
+ rc = platform_driver_register(&msm_eeprom_platform_driver);
+ CDBG("%s:%d platform rc %d\n", __func__, __LINE__, rc);
+ rc = spi_register_driver(&msm_eeprom_spi_driver);
+ CDBG("%s:%d spi rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&msm_eeprom_i2c_driver);
+}
+
+static void __exit msm_eeprom_exit_module(void)
+{
+ platform_driver_unregister(&msm_eeprom_platform_driver);
+ spi_unregister_driver(&msm_eeprom_spi_driver);
+ i2c_del_driver(&msm_eeprom_i2c_driver);
+}
+
+module_init(msm_eeprom_init_module);
+module_exit(msm_eeprom_exit_module);
+MODULE_DESCRIPTION("MSM EEPROM driver");
+MODULE_LICENSE("GPL v2");