isp10: rockchip: v0.1.6
[firefly-linux-kernel-4.4.55.git] / drivers / media / platform / rk-isp10 / cif_isp10_pltfrm.c
diff --git a/drivers/media/platform/rk-isp10/cif_isp10_pltfrm.c b/drivers/media/platform/rk-isp10/cif_isp10_pltfrm.c
new file mode 100644 (file)
index 0000000..126a037
--- /dev/null
@@ -0,0 +1,1443 @@
+/*
+ *************************************************************************
+ * Rockchip driver for CIF ISP 1.0
+ * (Based on Intel driver for sofiaxxx)
+ *
+ * Copyright (C) 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016 Fuzhou Rockchip 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *************************************************************************
+ */
+
+#ifndef CONFIG_OF
+#error "this driver requires a kernel with device tree support"
+#endif
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include "cif_isp10.h"
+#include <linux/platform_data/rk_isp10_platform.h>
+#include "cif_isp10_regs.h"
+#ifndef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <media/v4l2-controls_rockchip.h>
+#ifdef CONFIG_CIF_ISP10_REG_TRACE
+#include <stdarg.h>
+
+static struct {
+       char *reg_trace;
+       loff_t reg_trace_read_pos;
+       loff_t reg_trace_write_pos;
+       size_t reg_trace_max_size;
+       void __iomem *base_addr;
+       bool rtrace;
+       bool ftrace;
+       bool internal;
+       spinlock_t lock;/* spin lock */
+} cif_isp10_reg_trace;
+#endif
+#endif
+
+struct cif_isp10_pltfrm_csi_config {
+       struct list_head list;
+       u32 pps;
+       struct cif_isp10_csi_config csi_config;
+};
+
+struct cif_isp10_pltfrm_data {
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pins_default;
+       struct pinctrl_state *pins_sleep;
+       struct pinctrl_state *pins_inactive;
+       void __iomem *base_addr;
+       int irq;
+       struct {
+               int mis;
+               int (*isr)(unsigned int mis, void *cntxt);
+       } irq_handlers[4];
+       struct list_head csi0_configs;
+       struct list_head csi1_configs;
+       s32 exp_time;
+       u16 gain;
+
+#ifndef CONFIG_DEBUG_FS
+       struct {
+               struct dentry *dir;
+               struct dentry *cif_isp10_file;
+               struct dentry *csi0_file;
+               struct dentry *csi1_file;
+#ifdef CONFIG_CIF_ISP10_REG_TRACE
+               struct dentry *reg_trace_file;
+#endif
+               void (*print_func)(void *cntxt, const char *block_name);
+               void *print_cntxt;
+       } dbgfs;
+#endif
+};
+
+void cif_isp10_pltfrm_debug_register_print_cb(
+       struct device *dev,
+       void (*print)(void *cntxt, const char *block),
+       void *cntxt) {
+#ifndef CONFIG_DEBUG_FS
+       struct cif_isp10_pltfrm_data *pdata = dev->platform_data;
+
+       pdata->dbgfs.print_cntxt = cntxt;
+       pdata->dbgfs.print_func = print;
+#endif
+}
+
+#ifndef CONFIG_DEBUG_FS
+#define CIF_ISP10_DBGFS_BUF_SIZE 1024
+static char cif_isp10_dbgfs_buf[CIF_ISP10_DBGFS_BUF_SIZE];
+
+static int cif_isp10_dbgfs_fill_csi_config_from_string(
+       struct device *dev,
+       struct cif_isp10_csi_config *csi_config,
+       char *strp)
+{
+       char *token;
+
+       token = strsep(&strp, " ");
+       if (IS_ERR_OR_NULL(token))
+               goto missing_token;
+       if (IS_ERR_VALUE(kstrtou32(token, 10,
+                       &csi_config->vc)))
+               goto wrong_token_format;
+       token = strsep(&strp, " ");
+       if (IS_ERR_OR_NULL(token))
+               goto missing_token;
+       if (IS_ERR_VALUE(kstrtou32(token, 10,
+                       &csi_config->nb_lanes)))
+               goto wrong_token_format;
+       token = strsep(&strp, " ");
+       if (IS_ERR_OR_NULL(token))
+               goto missing_token;
+       if (IS_ERR_VALUE(kstrtou32(token, 16,
+                       &csi_config->dphy1)))
+               goto wrong_token_format;
+       token = strsep(&strp, " ");
+       if (IS_ERR_OR_NULL(token))
+               goto missing_token;
+       if (IS_ERR_VALUE(kstrtou32(token, 16,
+                       &csi_config->dphy2)))
+               goto wrong_token_format;
+       token = strsep(&strp, " ");
+       if (!IS_ERR_OR_NULL(token)) {
+               if (IS_ERR_VALUE(kstrtou32(token, 10,
+                               &csi_config->ana_bandgab_bias)))
+                       goto wrong_token_format;
+       } else {
+               csi_config->ana_bandgab_bias = (u32)-1;
+       }
+
+       return 0;
+missing_token:
+       cif_isp10_pltfrm_pr_err(dev,
+               "missing token, command format is 'push <pps> <vc> <#lanes> <HEX dphy1> <HEX dphy2> <analog bandgap bias>'\n");
+       return -EINVAL;
+wrong_token_format:
+       cif_isp10_pltfrm_pr_err(dev,
+               "wrong token format, command format is 'push <pps> <vc> <#lanes> <HEX dphy1> <HEX dphy2> <analog bandgap bias>'\n");
+       return -EINVAL;
+}
+
+static int cif_isp10_dbgfs_csi_configs_init(
+       struct device *dev,
+       enum cif_isp10_inp inp,
+       struct list_head *csi_configs)
+{
+       int ret = 0;
+       struct device *img_src_dev = NULL;
+       struct device_node *parent_node = NULL;
+       struct device_node *child_node = NULL, *prev_node = NULL;
+       struct cif_isp10_pltfrm_csi_config *cfg = NULL;
+       u32 pps;
+
+       img_src_dev = cif_isp10_pltfrm_get_img_src_device(dev, inp);
+       if (IS_ERR_OR_NULL(img_src_dev)) {
+               ret = -EFAULT;
+               goto err;
+       }
+       parent_node = of_node_get(img_src_dev->of_node);
+       put_device(img_src_dev);
+       img_src_dev = NULL;
+
+       while (!IS_ERR_OR_NULL(child_node =
+               of_get_next_child(parent_node, prev_node))) {
+               if (!strncasecmp(child_node->name,
+                       "intel,camera-module-csi-config",
+                       strlen("intel,camera-module-csi-config"))) {
+                       ret = of_property_read_u32(child_node,
+                               "intel,csi-pixels-per-second", &pps);
+                       if (IS_ERR_VALUE(ret)) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "reading property 'intel,csi-pixels-per-second'\n");
+                               goto err;
+                       }
+                       cfg = kmalloc(
+                               sizeof(struct cif_isp10_pltfrm_csi_config),
+                       GFP_KERNEL);
+                       if (!cfg) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "memory allocation failed\n");
+                               ret = -ENOMEM;
+                               goto err;
+                       }
+                       cfg->pps = pps;
+                       ret = cif_isp10_pltfrm_fill_csi_config_from_node(
+                                       dev, &cfg->csi_config, child_node);
+                       if (IS_ERR_VALUE(ret))
+                               goto err;
+                       list_add_tail(&cfg->list, csi_configs);
+                       cfg = NULL;
+               }
+               of_node_put(prev_node);
+               prev_node = child_node;
+       }
+       of_node_put(prev_node);
+       of_node_put(parent_node);
+
+       return 0;
+err:
+       of_node_put(prev_node);
+       of_node_put(child_node);
+       of_node_put(parent_node);
+       kfree(cfg);
+       if (!IS_ERR_OR_NULL(img_src_dev))
+               put_device(img_src_dev);
+       return ret;
+}
+
+static ssize_t cif_isp10_dbgfs_csi_read(
+       struct file *f,
+       char __user *out,
+       size_t count,
+       loff_t *pos)
+{
+       u32 out_size = 0;
+       u32 str_len;
+       struct cif_isp10_pltfrm_csi_config *cfg;
+       u32 index = 0;
+       struct list_head *list_pos;
+       struct device *dev = f->f_inode->i_private;
+       struct cif_isp10_pltfrm_data *pdata = dev->platform_data;
+       struct list_head *csi_configs;
+       enum cif_isp10_inp inp;
+
+       if (f->f_inode == pdata->dbgfs.csi0_file->d_inode) {
+               csi_configs = &pdata->csi0_configs;
+               inp = CIF_ISP10_INP_CSI_0;
+       } else if (f->f_inode == pdata->dbgfs.csi1_file->d_inode) {
+               csi_configs = &pdata->csi1_configs;
+               inp = CIF_ISP10_INP_CSI_1;
+       } else {
+               cif_isp10_pltfrm_pr_err(dev, "wrong file handle\n");
+               return -EINVAL;
+       }
+
+       if (list_empty(csi_configs))
+               if (IS_ERR_VALUE(cif_isp10_dbgfs_csi_configs_init(
+               dev, inp, csi_configs)))
+                       return -EFAULT;
+
+       if (*pos)
+               return 0;
+
+       list_for_each(list_pos, csi_configs) {
+               cfg = list_entry(list_pos,
+                       struct cif_isp10_pltfrm_csi_config, list);
+               sprintf(cif_isp10_dbgfs_buf,
+                       "csi-config-%d:\n"
+                       "   pps = %d\n"
+                       "   vc = %d\n"
+                       "   nb_lanes = %d\n"
+                       "   dphy1 = 0x%08x\n"
+                       "   dphy2 = 0x%08x\n"
+                       "   ana_bandgap_bias = %d\n",
+                       index,
+                       cfg->pps, cfg->csi_config.vc, cfg->csi_config.nb_lanes,
+                       cfg->csi_config.dphy1, cfg->csi_config.dphy2,
+                       cfg->csi_config.ana_bandgab_bias);
+               index++;
+               str_len = strnlen(cif_isp10_dbgfs_buf,
+                       CIF_ISP10_DBGFS_BUF_SIZE);
+               if (str_len > count) {
+                       *pos += out_size;
+                       return 0;
+               }
+               *pos = 0;
+               if (IS_ERR_VALUE(simple_read_from_buffer(
+                       out + out_size, str_len, pos,
+                       cif_isp10_dbgfs_buf, str_len)))
+                       break;
+               out_size += strnlen(cif_isp10_dbgfs_buf,
+                       CIF_ISP10_DBGFS_BUF_SIZE);
+               count -= str_len;
+       }
+
+       *pos += out_size;
+       return out_size;
+}
+
+static ssize_t cif_isp10_dbgfs_csi_write(
+       struct file *f,
+       const char __user *in,
+       size_t count,
+       loff_t *pos)
+{
+       ssize_t ret;
+       char *strp = cif_isp10_dbgfs_buf;
+       char *token;
+       struct device *dev = f->f_inode->i_private;
+       struct cif_isp10_pltfrm_data *pdata = dev->platform_data;
+       struct list_head *csi_configs;
+       enum cif_isp10_inp inp;
+
+       if (count > CIF_ISP10_DBGFS_BUF_SIZE) {
+               cif_isp10_pltfrm_pr_err(dev, "command line too large\n");
+               return -EINVAL;
+       }
+
+       if (f->f_inode == pdata->dbgfs.csi0_file->d_inode) {
+               csi_configs = &pdata->csi0_configs;
+               inp = CIF_ISP10_INP_CSI_0;
+       } else if (f->f_inode == pdata->dbgfs.csi1_file->d_inode) {
+               csi_configs = &pdata->csi1_configs;
+               inp = CIF_ISP10_INP_CSI_1;
+       } else {
+               cif_isp10_pltfrm_pr_err(dev, "wrong file handle\n");
+               return -EINVAL;
+       }
+
+       if (list_empty(csi_configs))
+               if (IS_ERR_VALUE(cif_isp10_dbgfs_csi_configs_init(
+               dev, inp, csi_configs)))
+                       return -EFAULT;
+
+       memset(cif_isp10_dbgfs_buf, 0, CIF_ISP10_DBGFS_BUF_SIZE);
+       ret = simple_write_to_buffer(strp,
+               CIF_ISP10_DBGFS_BUF_SIZE, pos, in, count);
+       if (IS_ERR_VALUE(ret))
+               return ret;
+
+       token = strsep(&strp, " ");
+       if (!strcmp(token, "push")) {
+               struct cif_isp10_pltfrm_csi_config cfg;
+
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'push <pps> <vc> <#lanes> <HEX dphy1> <HEX dphy2> <analog bandgap bias>'\n");
+                       return -EINVAL;
+               }
+               if (IS_ERR_VALUE(kstrtou32(token, 10,
+                               &cfg.pps))) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'push <pps> <vc> <#lanes> <HEX dphy1> <HEX dphy2> <analog bandgap bias>'\n");
+                       return -EINVAL;
+               }
+               ret = cif_isp10_dbgfs_fill_csi_config_from_string(
+                       dev, &cfg.csi_config, strp);
+               if (IS_ERR_VALUE(ret))
+                       return ret;
+               ret = cif_isp10_pltfrm_l_s_csi_config(
+                       dev, inp, cfg.pps, &cfg.csi_config);
+               if (IS_ERR_VALUE(ret))
+                       return ret;
+       } else if (!strncmp(token, "reset", 5)) {
+       } else {
+               cif_isp10_pltfrm_pr_err(dev, "unknown command %s\n", token);
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+void cif_isp10_dbgfs_fill_sensor_aec_para(
+       struct cif_isp10_device *cif_isp10_dev,
+       s32 exp_time,
+       u16 gain)
+{
+       struct cif_isp10_pltfrm_data *pdata;
+
+       pdata = (struct cif_isp10_pltfrm_data *)
+                       cif_isp10_dev->dev->platform_data;
+       pdata->exp_time = exp_time;
+       pdata->gain = gain;
+}
+
+static ssize_t cif_isp10_dbgfs_sensor_read(
+       struct file *f,
+       char __user *out,
+       size_t count,
+       loff_t *pos)
+{
+       u32 out_size = 0;
+       u32 str_len;
+       struct device *dev = f->f_inode->i_private;
+       struct cif_isp10_pltfrm_data *pdata = dev->platform_data;
+
+       if (*pos)
+               return 0;
+
+       sprintf(cif_isp10_dbgfs_buf,
+               "sensor current exp_time: %d\n"
+               "               gain = %d\n",
+               pdata->exp_time,
+               pdata->gain);
+       str_len = strnlen(cif_isp10_dbgfs_buf,
+                         CIF_ISP10_DBGFS_BUF_SIZE);
+       if (str_len > count) {
+               *pos += out_size;
+               return 0;
+       }
+       *pos = 0;
+       if (IS_ERR_VALUE(simple_read_from_buffer(
+               out + out_size, str_len, pos,
+               cif_isp10_dbgfs_buf, str_len)))
+               goto ERR;
+       out_size += strnlen(cif_isp10_dbgfs_buf,
+               CIF_ISP10_DBGFS_BUF_SIZE);
+       count -= str_len;
+
+       *pos += out_size;
+ERR:
+       return out_size;
+}
+
+static ssize_t cif_isp10_dbgfs_sensor_write(
+       struct file *f,
+       const char __user *in,
+       size_t count,
+       loff_t *pos)
+{
+       return 0;
+}
+
+static ssize_t cif_isp10_dbgfs_write(
+       struct file *f,
+       const char __user *in,
+       size_t count,
+       loff_t *pos)
+{
+       ssize_t ret;
+       char *strp = cif_isp10_dbgfs_buf;
+       char *token;
+       struct device *dev = f->f_inode->i_private;
+       struct cif_isp10_pltfrm_data *pdata = dev->platform_data;
+
+       if (count > CIF_ISP10_DBGFS_BUF_SIZE) {
+               cif_isp10_pltfrm_pr_err(dev, "command line too large\n");
+               return -EINVAL;
+       }
+
+       memset(cif_isp10_dbgfs_buf, 0, CIF_ISP10_DBGFS_BUF_SIZE);
+       ret = simple_write_to_buffer(strp,
+               CIF_ISP10_DBGFS_BUF_SIZE, pos, in, count);
+       if (IS_ERR_VALUE(ret))
+               return ret;
+
+       token = strsep(&strp, " ");
+       if (!strncmp(token, "print", 5)) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'print all|<list of block name>'\n");
+                       return -EINVAL;
+               }
+               if (!strncmp(token, "register", 8)) {
+                       u32 addr;
+                       struct cif_isp10_pltfrm_data *pdata =
+                               dev->platform_data;
+                       token = strsep(&strp, " ");
+                       while (token) {
+                               if (IS_ERR_VALUE(kstrtou32(token,
+                                       16, &addr))) {
+                                       cif_isp10_pltfrm_pr_err(dev,
+                                               "malformed token, must be a hexadecimal register address\n");
+                                       return -EINVAL;
+                               }
+                               pr_info("0x%04x: 0x%08x\n",
+                                       addr,
+                                       ioread32(pdata->base_addr +
+                                               addr));
+                               token = strsep(&strp, " ");
+                       }
+               } else if (pdata->dbgfs.print_func) {
+                       unsigned long flags;
+
+                       local_irq_save(flags);
+                       while (token) {
+                               pdata->dbgfs.print_func(
+                                       pdata->dbgfs.print_cntxt,
+                                       token);
+                               token = strsep(&strp, " ");
+                       }
+                       local_irq_restore(flags);
+               }
+       } else if (!strncmp(token, "power", 5)) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'power [off|on]'\n");
+                       return -EINVAL;
+               }
+               if (!strncmp(token, "on", 2)) {
+                       if (IS_ERR_VALUE(cif_isp10_pltfrm_pm_set_state(dev,
+                               CIF_ISP10_PM_STATE_SW_STNDBY, NULL)))
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "power on failed\n");
+                       else
+                               cif_isp10_pltfrm_pr_info(dev,
+                                       "switched on\n");
+               } else if (!strncmp(token, "off", 3)) {
+                       if (IS_ERR_VALUE(cif_isp10_pltfrm_pm_set_state(dev,
+                               CIF_ISP10_PM_STATE_OFF, NULL)))
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "power off failed\n");
+                       else
+                               cif_isp10_pltfrm_pr_info(dev,
+                                       "switched off\n");
+               } else {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'power [off|on]'\n");
+                       return -EINVAL;
+               }
+       } else if (!strncmp(token, "set", 3)) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'set register <hex addr>=<hex val>'\n");
+                       return -EINVAL;
+               }
+               if (!strncmp(token, "register", 8)) {
+                       u32 addr;
+                       u32 val;
+                       struct cif_isp10_pltfrm_data *pdata =
+                               dev->platform_data;
+                       token = strsep(&strp, "=");
+                       if (IS_ERR_VALUE(kstrtou32(token,
+                               16, &addr))) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "malformed token, address must be a hexadecimal register address\n");
+                               return -EINVAL;
+                       }
+                       token = strp;
+                       if (IS_ERR_VALUE(kstrtou32(token,
+                               16, &val))) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "malformed token, value must be a hexadecimal value\n");
+                               return -EINVAL;
+                       }
+                       iowrite32(val, pdata->base_addr + addr);
+               } else {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "unknown command %s\n", token);
+                       return -EINVAL;
+               }
+       } else {
+               cif_isp10_pltfrm_pr_err(dev,
+                       "unknown command %s\n", token);
+               return -EINVAL;
+       }
+       return count;
+}
+
+static const struct file_operations cif_isp10_dbgfs_csi_fops = {
+       .read = cif_isp10_dbgfs_csi_read,
+       .write = cif_isp10_dbgfs_csi_write
+};
+
+static const struct file_operations cif_isp10_dbgfs_fops = {
+       .write = cif_isp10_dbgfs_write
+};
+
+static const struct file_operations cif_isp10_dbgfs_sensor_fops = {
+       .read = cif_isp10_dbgfs_sensor_read,
+       .write = cif_isp10_dbgfs_sensor_write
+};
+
+#ifdef CONFIG_CIF_ISP10_REG_TRACE
+
+static inline int cif_isp10_pltfrm_trace_printf(
+       struct device *dev,
+       const char *fmt,
+       va_list args)
+{
+       int i;
+       u32 rem_size;
+       unsigned long flags = 0;
+
+       if (!in_irq())
+               spin_lock_irqsave(&cif_isp10_reg_trace.lock, flags);
+       cif_isp10_reg_trace.internal = true;
+
+       rem_size = cif_isp10_reg_trace.reg_trace_max_size -
+               cif_isp10_reg_trace.reg_trace_write_pos;
+
+       if (rem_size <= 0) {
+               if (!in_irq())
+                       spin_unlock_irqrestore(
+                               &cif_isp10_reg_trace.lock, flags);
+               cif_isp10_reg_trace.internal = false;
+               return 0;
+       }
+
+       i = vsnprintf(cif_isp10_reg_trace.reg_trace +
+               cif_isp10_reg_trace.reg_trace_write_pos,
+               rem_size,
+               fmt, args);
+       if (i == rem_size) /* buffer full */
+               i = 0;
+       else if (i < 0)
+               cif_isp10_pltfrm_pr_err(dev,
+                       "error writing trace buffer, error %d\n", i);
+       else
+               cif_isp10_reg_trace.reg_trace_write_pos += i;
+       cif_isp10_reg_trace.internal = false;
+       if (!in_irq())
+               spin_unlock_irqrestore(&cif_isp10_reg_trace.lock, flags);
+
+       return i;
+}
+
+inline int cif_isp10_pltfrm_rtrace_printf(
+       struct device *dev,
+       const char *fmt,
+       ...)
+{
+       va_list args;
+       int i;
+
+       va_start(args, fmt);
+       i = cif_isp10_pltfrm_trace_printf(dev, fmt, args);
+       va_end(args);
+
+       return i;
+}
+
+inline int cif_isp10_pltfrm_ftrace_printf(
+       struct device *dev,
+       const char *fmt,
+       ...)
+{
+       va_list args;
+       int i;
+
+       if (!cif_isp10_reg_trace.ftrace ||
+               cif_isp10_reg_trace.internal)
+               return 0;
+
+       va_start(args, fmt);
+       i = cif_isp10_pltfrm_trace_printf(dev, fmt, args);
+       va_end(args);
+
+       return i;
+}
+
+static void cif_isp10_dbgfs_reg_trace_clear(
+       struct device *dev)
+{
+       cif_isp10_reg_trace.reg_trace_write_pos = 0;
+       cif_isp10_reg_trace.reg_trace_read_pos = 0;
+}
+
+static ssize_t cif_isp10_dbgfs_reg_trace_read(
+       struct file *f,
+       char __user *out,
+       size_t count,
+       loff_t *pos)
+{
+       ssize_t bytes;
+       size_t available = cif_isp10_reg_trace.reg_trace_write_pos -
+               cif_isp10_reg_trace.reg_trace_read_pos;
+       size_t rem = count;
+
+       cif_isp10_reg_trace.internal = true;
+
+       if (!available)
+               cif_isp10_reg_trace.reg_trace_read_pos = 0;
+
+       while (rem && available) {
+               bytes = simple_read_from_buffer(
+                       out + (count - rem), count,
+                       &cif_isp10_reg_trace.reg_trace_read_pos,
+                       cif_isp10_reg_trace.reg_trace,
+                       cif_isp10_reg_trace.reg_trace_write_pos);
+               if (bytes < 0) {
+                       cif_isp10_pltfrm_pr_err(NULL,
+                               "buffer read failed with error %d\n",
+                               bytes);
+                       cif_isp10_reg_trace.internal = false;
+                       return bytes;
+               }
+               rem -= bytes;
+               available = cif_isp10_reg_trace.reg_trace_write_pos -
+                       cif_isp10_reg_trace.reg_trace_read_pos;
+       }
+
+       cif_isp10_reg_trace.internal = false;
+       return count - rem;
+}
+
+static ssize_t cif_isp10_dbgfs_reg_trace_write(
+       struct file *f,
+       const char __user *in,
+       size_t count,
+       loff_t *pos)
+{
+       ssize_t ret;
+       char *strp = cif_isp10_dbgfs_buf;
+       char *token;
+       struct device *dev = f->f_inode->i_private;
+       u32 max_size;
+       unsigned long flags = 0;
+
+       if (!in_irq())
+               spin_lock_irqsave(&cif_isp10_reg_trace.lock, flags);
+
+       cif_isp10_reg_trace.internal = true;
+
+       if (count > CIF_ISP10_DBGFS_BUF_SIZE) {
+               cif_isp10_pltfrm_pr_err(dev, "command line too long\n");
+               return -EINVAL;
+       }
+
+       memset(cif_isp10_dbgfs_buf, 0, CIF_ISP10_DBGFS_BUF_SIZE);
+       ret = simple_write_to_buffer(strp,
+               CIF_ISP10_DBGFS_BUF_SIZE, pos, in, count);
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       token = strsep(&strp, " ");
+       if (!strncmp(token, "clear", 5)) {
+               cif_isp10_dbgfs_reg_trace_clear(dev);
+               cif_isp10_pltfrm_pr_info(dev,
+                       "register trace buffer cleared\n");
+       } else if (!strcmp(token, "size")) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'size <num entries>'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (IS_ERR_VALUE(kstrtou32(token, 10,
+                               &max_size))) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "wrong token format, <num entries> must be positive integer>'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (cif_isp10_reg_trace.reg_trace) {
+                       devm_kfree(dev, cif_isp10_reg_trace.reg_trace);
+                       cif_isp10_reg_trace.reg_trace = NULL;
+               }
+               cif_isp10_dbgfs_reg_trace_clear(dev);
+               if (max_size > 0) {
+                       cif_isp10_reg_trace.reg_trace = devm_kzalloc(dev,
+                               max_size, GFP_KERNEL);
+                       if (!cif_isp10_reg_trace.reg_trace) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "memory allocation failed\n");
+                               ret = -ENOMEM;
+                               goto err;
+                       }
+                       cif_isp10_reg_trace.reg_trace_max_size = max_size;
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "register trace buffer size set to %d Byte\n",
+                               max_size);
+               }
+       } else if (!strncmp(token, "rtrace", 6)) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'rtrace [off|on]'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (!strncmp(token, "on", 2)) {
+                       cif_isp10_reg_trace.rtrace = true;
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "register trace enabled\n");
+               } else if (!strncmp(token, "off", 3)) {
+                       cif_isp10_reg_trace.rtrace = false;
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "register trace disabled\n");
+               } else {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'rtrace [off|on]'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+       } else if (!strncmp(token, "ftrace", 6)) {
+               token = strsep(&strp, " ");
+               if (IS_ERR_OR_NULL(token)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'ftrace [off|on]'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (!strncmp(token, "on", 2)) {
+                       cif_isp10_reg_trace.ftrace = true;
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "function trace enabled\n");
+               } else if (!strncmp(token, "off", 3)) {
+                       cif_isp10_reg_trace.ftrace = false;
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "function trace disabled\n");
+               } else {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "missing token, command format is 'ftrace [off|on]'\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+       } else {
+               cif_isp10_pltfrm_pr_err(dev, "unknown command %s\n", token);
+               ret = -EINVAL;
+               goto err;
+       }
+       cif_isp10_reg_trace.internal = false;
+       if (!in_irq())
+               spin_unlock_irqrestore(&cif_isp10_reg_trace.lock, flags);
+       return count;
+err:
+       cif_isp10_reg_trace.internal = false;
+       if (!in_irq())
+               spin_unlock_irqrestore(&cif_isp10_reg_trace.lock, flags);
+       return ret;
+}
+
+static const struct file_operations
+cif_isp10_dbgfs_reg_trace_fops = {
+       .read = cif_isp10_dbgfs_reg_trace_read,
+       .write = cif_isp10_dbgfs_reg_trace_write
+};
+
+#endif
+#endif
+
+static irqreturn_t cif_isp10_pltfrm_irq_handler(int irq, void *cntxt)
+{
+       unsigned int i, mis_val;
+       int ret;
+       struct device *dev = cntxt;
+       struct cif_isp10_pltfrm_data *pdata =
+               dev_get_platdata(dev);
+       void *cif_isp10_dev = dev_get_drvdata(dev);
+
+       if (irq != pdata->irq)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(pdata->irq_handlers); i++) {
+               if (IS_ERR_VALUE(pdata->irq_handlers[i].mis))
+                       break;
+
+               if (IS_ERR_OR_NULL(pdata->irq_handlers[i].isr)) {
+                       cif_isp10_pltfrm_pr_err(NULL,
+                               "ISR for IRQ #%d not set\n", irq);
+                       break;
+               }
+
+               mis_val = cif_ioread32(pdata->base_addr +
+                       pdata->irq_handlers[i].mis);
+               if (mis_val == 0)
+                       continue;
+
+               ret = pdata->irq_handlers[i].isr(mis_val, cif_isp10_dev);
+               if (IS_ERR_VALUE(ret)) {
+                       cif_isp10_pltfrm_pr_err(NULL,
+                               "ISR for IRQ #%d failed with error %d\n",
+                               irq, ret);
+               }
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+const char *cif_isp10_pltfrm_pm_state_string(
+       enum cif_isp10_pm_state pm_state)
+{
+       switch (pm_state) {
+       case CIF_ISP10_PM_STATE_OFF:
+               return "CIF_ISP10_PM_STATE_OFF";
+       case CIF_ISP10_PM_STATE_SUSPENDED:
+               return "CIF_ISP10_PM_STATE_SUSPENDED";
+       case CIF_ISP10_PM_STATE_SW_STNDBY:
+               return "CIF_ISP10_PM_STATE_SW_STNDBY";
+       case CIF_ISP10_PM_STATE_STREAMING:
+               return "CIF_ISP10_PM_STATE_STREAMING";
+       default:
+               return "PM_STATE_UNKNOWN";
+       }
+}
+
+inline void cif_isp10_pltfrm_write_reg(
+       struct device *dev,
+       u32 data,
+       CIF_ISP10_PLTFRM_MEM_IO_ADDR addr)
+{
+       iowrite32(data, addr);
+#ifdef CONFIG_CIF_ISP10_REG_TRACE
+       {
+               unsigned long flags = 0;
+
+               if (!in_irq())
+                       spin_lock_irqsave(&cif_isp10_reg_trace.lock, flags);
+               cif_isp10_reg_trace.internal = true;
+               if (((cif_isp10_reg_trace.reg_trace_write_pos +
+                       (20 * sizeof(char))) <
+                       cif_isp10_reg_trace.reg_trace_max_size) &&
+                       cif_isp10_reg_trace.rtrace) {
+                       int bytes =
+                               sprintf(cif_isp10_reg_trace.reg_trace +
+                                       cif_isp10_reg_trace.reg_trace_write_pos,
+                                       "%04x %08x\n",
+                                       addr - cif_isp10_reg_trace.base_addr,
+                                       data);
+                       if (bytes > 0)
+                               cif_isp10_reg_trace.reg_trace_write_pos +=
+                                       bytes;
+                       else
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "error writing trace buffer, error %d\n",
+                                       bytes);
+               }
+               cif_isp10_reg_trace.internal = false;
+               if (!in_irq())
+                       spin_unlock_irqrestore(
+                               &cif_isp10_reg_trace.lock, flags);
+       }
+#endif
+}
+
+inline void cif_isp10_pltfrm_write_reg_OR(
+       struct device *dev,
+       u32 data,
+       CIF_ISP10_PLTFRM_MEM_IO_ADDR addr)
+{
+       cif_isp10_pltfrm_write_reg(dev,
+               (ioread32(addr) | data), addr);
+}
+
+inline void cif_isp10_pltfrm_write_reg_AND(
+       struct device *dev,
+       u32 data,
+       CIF_ISP10_PLTFRM_MEM_IO_ADDR addr)
+{
+       cif_isp10_pltfrm_write_reg(dev,
+               (ioread32(addr) & data), addr);
+}
+
+inline u32 cif_isp10_pltfrm_read_reg(
+       struct device *dev,
+       CIF_ISP10_PLTFRM_MEM_IO_ADDR addr)
+{
+       return ioread32(addr);
+}
+
+int cif_isp10_pltfrm_dev_init(
+       struct cif_isp10_device *cif_isp10_dev,
+       struct device **_dev,
+       void __iomem **reg_base_addr)
+{
+       int ret;
+       struct cif_isp10_pltfrm_data *pdata;
+       struct device *dev = *_dev;
+       struct platform_device *pdev =
+               container_of(dev, struct platform_device, dev);
+       struct resource *res;
+       void __iomem *base_addr;
+       unsigned int i, irq;
+
+       dev_set_drvdata(dev, cif_isp10_dev);
+       cif_isp10_dev->dev = dev;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               cif_isp10_pltfrm_pr_err(dev,
+                       "could not allocate memory for platform data\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "register");
+       if (!res) {
+               cif_isp10_pltfrm_pr_err(NULL,
+                       "platform_get_resource_byname failed\n");
+               ret = -ENODEV;
+               goto err;
+       }
+       base_addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR_OR_NULL(base_addr)) {
+               cif_isp10_pltfrm_pr_err(NULL, "devm_ioremap_resource failed\n");
+               if (IS_ERR(base_addr))
+                       ret = PTR_ERR(base_addr);
+               else
+                       ret = -ENODEV;
+       }
+       *reg_base_addr = base_addr;
+       pdata->base_addr = base_addr;
+
+       irq = platform_get_irq_byname(pdev, "cif_isp10_irq");
+       if (IS_ERR_VALUE(irq)) {
+               ret = irq;
+               cif_isp10_pltfrm_pr_err(NULL,
+               "platform_get_irq_byname failed\n");
+               goto err;
+       }
+
+       ret = devm_request_threaded_irq(dev,
+                       irq,
+                       cif_isp10_pltfrm_irq_handler,
+                       NULL,
+                       0,
+                       dev_driver_string(dev),
+                       dev);
+       if (IS_ERR_VALUE(ret)) {
+               cif_isp10_pltfrm_pr_err(NULL,
+               "devm_request_threaded_irq failed\n");
+               goto err;
+       }
+       pdata->irq = irq;
+
+       pdata->pinctrl = devm_pinctrl_get(dev);
+       if (!IS_ERR_OR_NULL(pdata->pinctrl)) {
+               pdata->pins_default = pinctrl_lookup_state(pdata->pinctrl,
+                       PINCTRL_STATE_DEFAULT);
+               if (IS_ERR(pdata->pins_default))
+                       cif_isp10_pltfrm_pr_err(dev,
+                                               "could not get default pinstate\n");
+
+               pdata->pins_sleep = pinctrl_lookup_state(pdata->pinctrl,
+                       PINCTRL_STATE_SLEEP);
+               if (IS_ERR(pdata->pins_sleep))
+                       cif_isp10_pltfrm_pr_warn(dev,
+                                               "could not get pins_sleep pinstate\n");
+
+               pdata->pins_inactive = pinctrl_lookup_state(pdata->pinctrl,
+                       "inactive");
+               if (IS_ERR(pdata->pins_inactive))
+                       cif_isp10_pltfrm_pr_warn(dev,
+                                               "could not get pins_inactive pinstate\n");
+
+               if (!IS_ERR_OR_NULL(pdata->pins_default))
+                       pinctrl_select_state(pdata->pinctrl,
+                               pdata->pins_default);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(pdata->irq_handlers); i++)
+               pdata->irq_handlers[i].mis = -EINVAL;
+
+       dev->platform_data = pdata;
+
+       INIT_LIST_HEAD(&pdata->csi0_configs);
+       INIT_LIST_HEAD(&pdata->csi1_configs);
+
+#ifndef CONFIG_DEBUG_FS
+       pdata->dbgfs.dir = debugfs_create_dir("cif_isp10", NULL);
+       pdata->dbgfs.csi0_file = debugfs_create_file(
+               "csi-0",
+               0644,
+               pdata->dbgfs.dir,
+               dev,
+               &cif_isp10_dbgfs_csi_fops);
+       pdata->dbgfs.csi1_file = debugfs_create_file(
+               "csi-1",
+               0644,
+               pdata->dbgfs.dir,
+               dev,
+               &cif_isp10_dbgfs_csi_fops);
+       pdata->dbgfs.cif_isp10_file = debugfs_create_file(
+               "cif_isp20",
+               0200,
+               pdata->dbgfs.dir,
+               dev,
+               &cif_isp10_dbgfs_fops);
+       pdata->dbgfs.cif_isp10_file = debugfs_create_file(
+               "sensor",
+               0644,
+               pdata->dbgfs.dir,
+               dev,
+               &cif_isp10_dbgfs_sensor_fops);
+#ifdef CONFIG_CIF_ISP10_REG_TRACE
+       pdata->dbgfs.reg_trace_file = debugfs_create_file(
+               "reg_trace",
+               0644,
+               pdata->dbgfs.dir,
+               dev,
+               &cif_isp10_dbgfs_reg_trace_fops);
+       spin_lock_init(&cif_isp10_reg_trace.lock);
+       cif_isp10_reg_trace.reg_trace = NULL;
+       cif_isp10_dbgfs_reg_trace_clear(dev);
+       cif_isp10_reg_trace.reg_trace_max_size = 0;
+       cif_isp10_reg_trace.base_addr = base_addr;
+       cif_isp10_reg_trace.rtrace = true;
+       cif_isp10_reg_trace.ftrace = false;
+       cif_isp10_reg_trace.internal = false;
+#endif
+#endif
+
+       return 0;
+err:
+       cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", ret);
+       if (!IS_ERR_OR_NULL(pdata))
+               devm_kfree(dev, pdata);
+       return ret;
+}
+
+int cif_isp10_pltfrm_soc_init(
+       struct cif_isp10_device *cif_isp10_dev,
+       struct pltfrm_soc_cfg *soc_cfg)
+{
+       struct pltfrm_soc_cfg_para cfg_para;
+       struct device *dev = cif_isp10_dev->dev;
+       struct platform_device *pdev =
+               container_of(dev, struct platform_device, dev);
+       struct pltfrm_soc_init_para init_para;
+       int ret = 0;
+
+       if (!IS_ERR_OR_NULL(soc_cfg) && !IS_ERR_OR_NULL(soc_cfg->soc_cfg)) {
+               cfg_para.cmd = PLTFRM_SOC_INIT;
+               cfg_para.cfg_para = &init_para;
+               init_para.pdev = pdev;
+               init_para.isp_base = cif_isp10_dev->config.base_addr;
+               ret = soc_cfg->soc_cfg(&cfg_para);
+               if (ret == 0)
+                       cif_isp10_dev->soc_cfg = soc_cfg;
+       }
+
+       return ret;
+}
+
+int cif_isp10_pltfrm_mipi_dphy_config(
+       struct cif_isp10_device *cif_isp10_dev)
+{
+       struct pltfrm_soc_cfg_para cfg_para;
+       struct pltfrm_soc_cfg *soc_cfg;
+       int ret = 0;
+
+       soc_cfg = cif_isp10_dev->soc_cfg;
+       if (!IS_ERR_OR_NULL(soc_cfg) &&
+               !IS_ERR_OR_NULL(soc_cfg->soc_cfg)) {
+               cfg_para.cmd =
+                       PLTFRM_MIPI_DPHY_CFG;
+               cfg_para.cfg_para =
+                       (void *)(&cif_isp10_dev->config.cam_itf.cfg.mipi);
+               ret = soc_cfg->soc_cfg(&cfg_para);
+       }
+
+       return ret;
+}
+
+int cif_isp10_pltfrm_pm_set_state(
+       struct device *dev,
+       enum cif_isp10_pm_state pm_state)
+{
+       int ret;
+       struct cif_isp10_device *cif_isp10_dev = dev_get_drvdata(dev);
+       struct pltfrm_soc_cfg *soc_cfg = cif_isp10_dev->soc_cfg;
+       struct pltfrm_soc_cfg_para cfg_para;
+
+       switch (pm_state) {
+       case CIF_ISP10_PM_STATE_OFF:
+       case CIF_ISP10_PM_STATE_SUSPENDED:
+               cfg_para.cmd = PLTFRM_CLKDIS;
+               cfg_para.cfg_para = NULL;
+               ret = soc_cfg->soc_cfg(&cfg_para);
+               break;
+       case CIF_ISP10_PM_STATE_SW_STNDBY:
+       case CIF_ISP10_PM_STATE_STREAMING:
+               cfg_para.cmd = PLTFRM_CLKEN;
+               cfg_para.cfg_para = NULL;
+               ret = soc_cfg->soc_cfg(&cfg_para);
+               break;
+       default:
+               cif_isp10_pltfrm_pr_err(dev,
+                       "unknown or unsupported PM state %d\n", pm_state);
+               return -EINVAL;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               cif_isp10_pltfrm_pr_err(dev,
+                       "setting pm state to %s failed with error %d\n",
+                       cif_isp10_pltfrm_pm_state_string(pm_state), ret);
+       else
+               cif_isp10_pltfrm_pr_dbg(dev,
+                       "successfully changed pm state to %s\n",
+                       cif_isp10_pltfrm_pm_state_string(pm_state));
+       return ret;
+}
+
+int cif_isp10_pltfrm_g_interface_config(
+       struct cif_isp10_img_src *img_src,
+       struct pltfrm_cam_itf *cam_itf)
+{
+       int ret = 0;
+
+       ret = cif_isp10_img_src_ioctl(img_src,
+                       PLTFRM_CIFCAM_G_ITF_CFG, (void *)cam_itf);
+       if (IS_ERR_VALUE(ret)) {
+               cif_isp10_pltfrm_pr_err(
+                       dev,
+                       "cif_isp10_img_src_ioctl PLTFRM_CIFCAM_G_ITF_CFG failed!\n");
+               return ret;
+       }
+       return 0;
+}
+
+int cif_isp10_pltfrm_pinctrl_set_state(
+       struct device *dev,
+       enum cif_isp10_pinctrl_state pinctrl_state)
+{
+       int ret = 0;
+       struct cif_isp10_pltfrm_data *pdata = dev_get_platdata(dev);
+
+       cif_isp10_pltfrm_pr_dbg(dev,
+               "set pinctrl state to %d\n", pinctrl_state);
+
+       if (!pdata) {
+               cif_isp10_pltfrm_pr_err(dev,
+                       "unable to retrieve CIF platform data\n");
+               ret = -EINVAL;
+               goto err;
+       }
+       if (IS_ERR_OR_NULL(pdata->pinctrl))
+               return 0;
+
+       switch (pinctrl_state) {
+       case CIF_ISP10_PINCTRL_STATE_SLEEP:
+               if (!IS_ERR_OR_NULL(pdata->pins_sleep))
+                       ret = pinctrl_select_state(pdata->pinctrl,
+                               pdata->pins_sleep);
+               break;
+       case CIF_ISP10_PINCTRL_STATE_ACTIVE:
+       case CIF_ISP10_PINCTRL_STATE_DEFAULT:
+               if (!IS_ERR_OR_NULL(pdata->pins_default))
+                       ret = pinctrl_select_state(pdata->pinctrl,
+                               pdata->pins_default);
+               break;
+       case CIF_ISP10_PINCTRL_STATE_INACTIVE:
+               if (!IS_ERR_OR_NULL(pdata->pins_inactive))
+                       ret = pinctrl_select_state(pdata->pinctrl,
+                               pdata->pins_inactive);
+               break;
+       default:
+               cif_isp10_pltfrm_pr_err(dev,
+                       "unknown or unsupported pinctrl state %d\n",
+                       pinctrl_state);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (IS_ERR_VALUE(ret))
+               goto err;
+
+       return 0;
+err:
+       cif_isp10_pltfrm_pr_err(dev, "failed with error %d\n", ret);
+       return ret;
+}
+
+int cif_isp10_pltfrm_irq_register_isr(
+       struct device *dev,
+       unsigned int mis,
+       int (*isr)(unsigned int mis, void *cntxt),
+       void *cntxt)
+{
+       int ret = 0;
+       unsigned int i;
+       int slot = -EINVAL;
+       struct platform_device *pdev =
+               container_of(dev, struct platform_device, dev);
+       struct cif_isp10_pltfrm_data *pdata =
+               dev_get_platdata(&pdev->dev);
+       bool skip_request_irq = false;
+       const char *irq_name;
+
+       switch (mis) {
+       case CIF_MIPI_MIS:
+               irq_name = "CIF_ISP10_MIPI_IRQ";
+               break;
+       case CIF_ISP_MIS:
+               irq_name = "CIF_ISP10_ISP_IRQ";
+               break;
+       case CIF_MI_MIS:
+               irq_name = "CIF_ISP10_MI_IRQ";
+               break;
+       default:
+               cif_isp10_pltfrm_pr_err(dev,
+                       "unknown or unsupported IRQ %d\n", mis);
+               ret = -EINVAL;
+               goto err;
+       }
+       cif_isp10_pltfrm_pr_dbg(dev,
+               "registering ISR for IRQ %s\n", irq_name);
+
+       for (i = 0; i < ARRAY_SIZE(pdata->irq_handlers); i++) {
+               if (IS_ERR_VALUE(slot) &&
+                       IS_ERR_VALUE(pdata->irq_handlers[i].mis))
+                       slot = (int)i;
+               if (pdata->irq_handlers[i].mis == mis) {
+                       cif_isp10_pltfrm_pr_dbg(dev,
+                               "overwriting ISR for IRQ %s\n", irq_name);
+                       slot = (int)i;
+                       skip_request_irq = true;
+                       break;
+               }
+       }
+       if (IS_ERR_VALUE(slot)) {
+               if (!isr)
+                       return 0;
+               cif_isp10_pltfrm_pr_err(dev,
+                       "cannot register ISR for IRQ %s, too many ISRs already registered\n",
+                       irq_name);
+               ret = -EFAULT;
+               goto err;
+       }
+       pdata->irq_handlers[slot].isr = isr;
+       if (!isr) {
+               pdata->irq_handlers[slot].mis = -EINVAL;
+               skip_request_irq = true;
+       } else {
+               pdata->irq_handlers[slot].mis = mis;
+       }
+
+       return 0;
+err:
+       cif_isp10_pltfrm_pr_err(dev, "failed with error %d\n", ret);
+       return ret;
+}
+
+const char *cif_isp10_pltfrm_get_device_type(
+       struct device *dev)
+{
+       return dev->of_node->type;
+}
+
+const char *cif_isp10_pltfrm_dev_string(
+       struct device *dev)
+{
+       return dev_driver_string(dev);
+}
+
+int cif_isp10_pltfrm_get_img_src_device(
+       struct device *dev,
+       struct cif_isp10_img_src **img_src_array,
+       unsigned int array_len)
+{
+       struct device_node *node = NULL;
+       struct device_node *camera_list_node = NULL;
+       struct i2c_client *client = NULL;
+       int ret = 0;
+       int index, size = 0;
+       const __be32 *phandle;
+       int num_cameras = 0;
+       struct cif_isp10_device *cif_isp10_dev = dev_get_drvdata(dev);
+
+       node = of_node_get(dev->of_node);
+       if (IS_ERR_OR_NULL(node)) {
+               dev_err(dev, "Unable to obtain CIF device node\n");
+               ret = -EEXIST;
+               goto err;
+       }
+
+       phandle = of_get_property(node,
+               "rockchip,camera-modules-attached", &size);
+       if (IS_ERR_OR_NULL(phandle)) {
+               cif_isp10_pltfrm_pr_err(dev,
+                       "no camera-modules-attached'\n");
+                       ret = -EINVAL;
+                       goto err;
+       }
+
+       for (index = 0; index < size / sizeof(*phandle); index++) {
+               camera_list_node = of_parse_phandle(node,
+                       "rockchip,camera-modules-attached", index);
+               of_node_put(node);
+               if (IS_ERR_OR_NULL(camera_list_node)) {
+                       cif_isp10_pltfrm_pr_err(dev,
+                               "invalid index %d for property 'rockchip,camera-modules-attached'\n",
+                               index);
+                               ret = -EINVAL;
+                               goto err;
+               }
+
+               if (!strcmp(camera_list_node->type,
+                                       "v4l2-i2c-subdev")) {
+                       client = of_find_i2c_device_by_node(
+                               camera_list_node);
+                       of_node_put(camera_list_node);
+                       if (IS_ERR_OR_NULL(client)) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "could not get camera i2c client, maybe not yet created, deferring device probing...\n");
+                               continue;
+                       }
+               } else {
+                       cif_isp10_pltfrm_pr_dbg(dev,
+                               "device of type %s not supported\n",
+                               camera_list_node->type);
+                       of_node_put(camera_list_node);
+                       continue;
+               }
+
+               img_src_array[num_cameras] =
+                       cif_isp10_img_src_to_img_src(
+                               &client->dev,
+                               cif_isp10_dev->soc_cfg);
+               if (!IS_ERR_OR_NULL(img_src_array[num_cameras])) {
+                       cif_isp10_pltfrm_pr_info(dev,
+                               "%s attach to cif isp10 img_src_array[%d]\n",
+                               cif_isp10_img_src_g_name(
+                                       img_src_array[num_cameras]),
+                               num_cameras);
+                       num_cameras++;
+                       if (num_cameras >= array_len) {
+                               cif_isp10_pltfrm_pr_err(dev,
+                                       "cif isp10 isn't support > %d 'camera modules attached'\n",
+                                       array_len);
+                               break;
+                       }
+               } else {
+                       continue;
+               }
+       }
+
+       return num_cameras;
+err:
+       dev_err(dev, "failed with error %d\n", ret);
+       if (!IS_ERR_OR_NULL(client))
+               put_device(&client->dev);
+       if (!IS_ERR_OR_NULL(camera_list_node))
+               of_node_put(camera_list_node);
+       return ret;
+}
+
+void cif_isp10_pltfrm_dev_release(
+       struct device *dev)
+{
+#ifndef CONFIG_DEBUG_FS
+       {
+               struct cif_isp10_pltfrm_data *pdata =
+                       dev->platform_data;
+               debugfs_remove(pdata->dbgfs.csi0_file);
+               debugfs_remove(pdata->dbgfs.csi1_file);
+               debugfs_remove_recursive(pdata->dbgfs.dir);
+       }
+#endif
+}
+