summaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
authorCharlie Huang <chahuang@nvidia.com>2013-06-05 18:40:07 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:33:29 -0700
commite9f563e5ce40d6e9bac26e004aae8d24d725924c (patch)
tree7380dfe538fab1aa1facedb1b17b089db9b7641c /drivers/media/video
parent014008e1fd73edcb0dfdc5e69cd01bfee9f5e11a (diff)
media: video: tegra: implement PCL kerner driver
Implement unified PCL (physical camera layer) kernel driver. It will virtualize all camera device driver to minimize kernel development. Instead, camera devices can be configured and controlled solely from the user space. bug 1272149 Change-Id: I94614206e94895221e1697f65185e356887b1de3 Signed-off-by: Charlie Huang <chahuang@nvidia.com> Reviewed-on: http://git-master/r/243508 (cherry picked from commit a9be39befa080a7eefeb469e6a73d4e409388b0f) Reviewed-on: http://git-master/r/247526 Reviewed-by: Mandar Padmawar <mpadmawar@nvidia.com> Tested-by: Mandar Padmawar <mpadmawar@nvidia.com>
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/tegra/Kconfig15
-rw-r--r--drivers/media/video/tegra/Makefile2
-rw-r--r--drivers/media/video/tegra/cam_dev/Kconfig19
-rw-r--r--drivers/media/video/tegra/cam_dev/Makefile12
-rw-r--r--drivers/media/video/tegra/cam_dev/as364x.c180
-rw-r--r--drivers/media/video/tegra/cam_dev/debugfs.c249
-rw-r--r--drivers/media/video/tegra/cam_dev/dev_access.c334
-rw-r--r--drivers/media/video/tegra/cam_dev/imx135.c254
-rw-r--r--drivers/media/video/tegra/cam_dev/virtual.c415
-rw-r--r--drivers/media/video/tegra/camera.c879
10 files changed, 2359 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig
index ecc76f1068ff..1e8944b5e434 100644
--- a/drivers/media/video/tegra/Kconfig
+++ b/drivers/media/video/tegra/Kconfig
@@ -1,7 +1,15 @@
+#
+# camera/video device drivers supported.
+#
+
source "drivers/media/video/tegra/avp/Kconfig"
source "drivers/media/video/tegra/mediaserver/Kconfig"
source "drivers/media/video/tegra/nvavp/Kconfig"
+# only involve device specific PCL drivers while virtual driver cannot
+# handle it full functionally.
+#source "drivers/media/video/tegra/cam_dev/Kconfig"
+
config TEGRA_DTV
bool "Enable support for tegra dtv interface"
depends on ARCH_TEGRA
@@ -189,3 +197,10 @@ config VIDEO_AR0833
---help---
This is a driver for the AR0833 camera sensor
for use with the tegra isp.
+
+config VIDEO_CAMERA
+ tristate "generic camera device support"
+ depends on I2C && ARCH_TEGRA
+ ---help---
+ This is a driver for generic camera devices
+ for use with the tegra isp.
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 6d9706c4f0eb..f804079965ac 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -5,6 +5,7 @@ subdir-ccflags-y := -Werror
# Makefile for the video capture/playback device drivers.
#
obj-y += avp/
+obj-y += cam_dev/
obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/
obj-$(CONFIG_TEGRA_NVAVP) += nvavp/
#obj-$(CONFIG_TEGRA_DTV) += tegra_dtv.o
@@ -36,3 +37,4 @@ obj-$(CONFIG_DEBUG_FS) += nvc_debugfs.o
obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
obj-$(CONFIG_VIDEO_AD5823) += ad5823.o
obj-$(CONFIG_VIDEO_OV7695) += ov7695.o
+obj-$(CONFIG_VIDEO_CAMERA) += camera.o
diff --git a/drivers/media/video/tegra/cam_dev/Kconfig b/drivers/media/video/tegra/cam_dev/Kconfig
new file mode 100644
index 000000000000..5700a1e37b50
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/Kconfig
@@ -0,0 +1,19 @@
+#
+# devices supported by generic pcl kernel driver
+#
+
+config CAMERA_DEV_AS364X
+ bool "Enable support for AS364X flash device"
+ depends on VIDEO_CAMERA
+ default n
+ help
+
+ If unsure, say N
+
+config CAMERA_DEV_IMX135
+ bool "Enable support for IMX135 camera device"
+ depends on VIDEO_CAMERA
+ default n
+ help
+
+ If unsure, say N
diff --git a/drivers/media/video/tegra/cam_dev/Makefile b/drivers/media/video/tegra/cam_dev/Makefile
new file mode 100644
index 000000000000..994f9648094e
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/Makefile
@@ -0,0 +1,12 @@
+GCOV_PROFILE := y
+
+subdir-ccflags-y := -Werror
+#
+# Makefile for the camera devices spec.
+#
+
+obj-$(CONFIG_VIDEO_CAMERA) += dev_access.o
+obj-$(CONFIG_VIDEO_CAMERA) += debugfs.o
+obj-$(CONFIG_VIDEO_CAMERA) += virtual.o
+obj-$(CONFIG_CAMERA_DEV_AS364X) += as364x.o
+obj-$(CONFIG_CAMERA_DEV_IMX135) += imx135.o
diff --git a/drivers/media/video/tegra/cam_dev/as364x.c b/drivers/media/video/tegra/cam_dev/as364x.c
new file mode 100644
index 000000000000..aca9afd0275e
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/as364x.c
@@ -0,0 +1,180 @@
+/*
+ * AS364X.c - AS364X flash/torch kernel PCL driver.
+ * As an example, some devices can be implmented specifically instead of using
+ * the virtual PCL driver to handle some special features (hardware resources,
+ * sequences, etc.).
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <media/nvc.h>
+#include <media/camera.h>
+
+struct as364x_info {
+ struct nvc_regulator v_in;
+ struct nvc_regulator v_i2c;
+};
+
+static int as364x_power_on(struct camera_device *cdev)
+{
+ struct as364x_info *info = dev_get_drvdata(cdev->dev);
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s %x\n", __func__, cdev->is_power_on);
+ if (cdev->is_power_on)
+ return 0;
+
+ mutex_lock(&cdev->mutex);
+ if (info->v_in.vreg) {
+ err = regulator_enable(info->v_in.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s v_in err\n", __func__);
+ goto power_on_end;
+ }
+ }
+
+ if (info->v_i2c.vreg) {
+ err = regulator_enable(info->v_i2c.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s v_i2c err\n", __func__);
+ if (info->v_in.vreg)
+ regulator_disable(info->v_in.vreg);
+ goto power_on_end;
+ }
+ }
+
+ cdev->is_power_on = 1;
+
+power_on_end:
+ mutex_unlock(&cdev->mutex);
+
+ if (!err)
+ usleep_range(100, 120);
+
+ return err;
+}
+
+static int as364x_power_off(struct camera_device *cdev)
+{
+ struct as364x_info *info = dev_get_drvdata(cdev->dev);
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s %x\n", __func__, cdev->is_power_on);
+ if (!cdev->is_power_on)
+ return 0;
+
+ mutex_lock(&cdev->mutex);
+ if (info->v_in.vreg) {
+ err = regulator_disable(info->v_in.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vi_in err\n", __func__);
+ goto power_off_end;
+ }
+ }
+
+ if (info->v_i2c.vreg) {
+ err = regulator_disable(info->v_i2c.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vi_i2c err\n", __func__);
+ goto power_off_end;
+ }
+ }
+
+ cdev->is_power_on = 0;
+
+power_off_end:
+ mutex_unlock(&cdev->mutex);
+ return err;
+}
+
+static int as364x_instance_destroy(struct camera_device *cdev)
+{
+ struct as364x_info *info = dev_get_drvdata(cdev->dev);
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ if (!info)
+ return 0;
+
+ dev_set_drvdata(cdev->dev, NULL);
+
+ if (likely(info->v_in.vreg))
+ regulator_put(info->v_in.vreg);
+
+ if (likely(info->v_i2c.vreg))
+ regulator_put(info->v_i2c.vreg);
+
+ kfree(info);
+ return 0;
+}
+
+static int as364x_instance_create(struct camera_device *cdev, void *pdata)
+{
+ struct as364x_info *info;
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ dev_err(cdev->dev, "%s memory low!\n", __func__);
+ return -ENOMEM;
+ }
+
+ camera_regulator_get(cdev->dev, &info->v_in, "vin"); /* 3.7v */
+ camera_regulator_get(cdev->dev, &info->v_i2c, "vi2c"); /* 1.8v */
+ dev_set_drvdata(cdev->dev, info);
+ return 0;
+}
+
+static struct camera_chip as364x_chip = {
+ .name = "pcl_as364x_demo",
+ .type = CAMERA_DEVICE_TYPE_I2C,
+ .regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ },
+ .init = as364x_instance_create,
+ .release = as364x_instance_destroy,
+ .power_on = as364x_power_on,
+ .power_off = as364x_power_off,
+};
+
+static int __init as364x_init(void)
+{
+ pr_info("%s\n", __func__);
+ INIT_LIST_HEAD(&as364x_chip.list);
+ camera_chip_add(&as364x_chip);
+ return 0;
+}
+device_initcall(as364x_init);
+
+static void __exit as364x_exit(void)
+{
+}
+module_exit(as364x_exit);
+
+MODULE_DESCRIPTION("AS364x flash/torch device");
+MODULE_AUTHOR("Charlie Huang <chahuang@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/tegra/cam_dev/debugfs.c b/drivers/media/video/tegra/cam_dev/debugfs.c
new file mode 100644
index 000000000000..8e95036ff37c
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/debugfs.c
@@ -0,0 +1,249 @@
+/*
+ * debugfs.c
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <media/nvc.h>
+#include <media/camera.h>
+
+static struct camera_platform_info *cam_desc;
+
+static int camera_debugfs_layout(struct seq_file *s)
+{
+ struct cam_device_layout *layout = cam_desc->layout;
+ int num = cam_desc->size_layout / sizeof(*layout);
+
+ if (!layout)
+ return -EEXIST;
+
+ if (unlikely(num * sizeof(*layout) != cam_desc->size_layout)) {
+ seq_printf(s, "WHAT? layout size is incorrect!\n");
+ return -EFAULT;
+ }
+
+ while (num--) {
+ seq_printf(s, "%.20s %016llx %1x %1x %02x %1x %.20s\n",
+ layout->name, layout->guid, layout->is_front,
+ layout->bus, layout->addr, layout->addr_byte,
+ layout->alt_name);
+ layout++;
+ }
+
+ return 0;
+}
+
+static int camera_debugfs_platform_data(struct seq_file *s)
+{
+ struct camera_platform_data *pd = cam_desc->pdata;
+
+ seq_printf(s, "platform data: cfg = %x\n", pd->cfg);
+ return 0;
+}
+
+static int camera_debugfs_chips(struct seq_file *s)
+{
+ struct camera_chip *ccp;
+ const struct regmap_config *rcfg;
+
+ seq_printf(s, "\n%s: camera devices supported:\n", cam_desc->dname);
+ mutex_lock(cam_desc->c_mutex);
+ list_for_each_entry(ccp, cam_desc->chip_list, list) {
+ rcfg = &ccp->regmap_cfg;
+ seq_printf(s, " %.16s: type %d, ref_cnt %d. ",
+ ccp->name,
+ ccp->type,
+ atomic_read(&ccp->ref_cnt)
+ );
+ seq_printf(s, "%02d bit addr, %02d bit data, stride %d, pad %d",
+ rcfg->reg_bits,
+ rcfg->val_bits,
+ rcfg->reg_stride,
+ rcfg->pad_bits
+ );
+ seq_printf(s, " bits. cache type: %d, flags r %02x / w %02x\n",
+ rcfg->cache_type,
+ rcfg->read_flag_mask,
+ rcfg->write_flag_mask
+ );
+ }
+ mutex_unlock(cam_desc->c_mutex);
+
+ return 0;
+}
+
+static int camera_debugfs_devices(struct seq_file *s)
+{
+ struct camera_device *cdev;
+
+ if (list_empty(cam_desc->dev_list)) {
+ seq_printf(s, "\n%s: No device installed.\n", cam_desc->dname);
+ return 0;
+ }
+
+ seq_printf(s, "\n%s: activated devices:\n", cam_desc->dname);
+ mutex_lock(cam_desc->d_mutex);
+ list_for_each_entry(cdev, cam_desc->dev_list, list) {
+ seq_printf(s, " %s: on %s, %s power %s\n",
+ cdev->name,
+ cdev->chip->name,
+ atomic_read(&cdev->in_use) ? "occupied" : "free",
+ cdev->is_power_on ? "on" : "off"
+ );
+ }
+ mutex_unlock(cam_desc->d_mutex);
+
+ return 0;
+}
+
+static int camera_debugfs_apps(struct seq_file *s)
+{
+ struct camera_info *user;
+ int num = 0;
+
+ if (list_empty(cam_desc->app_list)) {
+ seq_printf(s, "\n%s: No App running.\n", cam_desc->dname);
+ return 0;
+ }
+
+ mutex_lock(cam_desc->u_mutex);
+ list_for_each_entry(user, cam_desc->app_list, list) {
+ struct camera_device *cdev = user->cdev;
+ struct camera_chip *ccp = NULL;
+
+ if (cdev)
+ ccp = cdev->chip;
+ seq_printf(s, "app #%02d: on %s chip type: %s\n", num,
+ cdev ? (char *)cdev->name : "NULL",
+ ccp ? (char *)ccp->name : "NULL");
+ num++;
+ }
+ mutex_unlock(cam_desc->u_mutex);
+ seq_printf(s, "\n%s: There are %d apps running.\n",
+ cam_desc->dname, num);
+
+ return 0;
+}
+
+static int camera_status_show(struct seq_file *s, void *data)
+{
+ pr_info("%s %s\n", __func__, cam_desc->dname);
+
+ if (list_empty(cam_desc->chip_list)) {
+ seq_printf(s, "%s: No devices supported.\n", cam_desc->dname);
+ return 0;
+ }
+
+ camera_debugfs_layout(s);
+ camera_debugfs_platform_data(s);
+ camera_debugfs_chips(s);
+ camera_debugfs_devices(s);
+ camera_debugfs_apps(s);
+
+ return 0;
+}
+
+static ssize_t camera_attr_set(struct file *s,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char buf[24];
+ int buf_size;
+ u32 val = 0;
+
+ pr_info("%s\n", __func__);
+
+ if (!user_buf || count <= 1)
+ return -EFAULT;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (sscanf(buf + 1, "0x%x", &val) == 1)
+ goto set_attr;
+ if (sscanf(buf + 1, "0X%x", &val) == 1)
+ goto set_attr;
+ if (sscanf(buf + 1, "%d", &val) == 1)
+ goto set_attr;
+
+ pr_info("SYNTAX ERROR: %s\n", buf);
+ return -EFAULT;
+
+set_attr:
+ pr_info("new data = %x\n", val);
+ switch (buf[0]) {
+ case 'd':
+ cam_desc->pdata->cfg = val;
+ break;
+ }
+
+ return count;
+}
+
+static int camera_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, camera_status_show, inode->i_private);
+}
+
+static const struct file_operations camera_debugfs_fops = {
+ .open = camera_debugfs_open,
+ .read = seq_read,
+ .write = camera_attr_set,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int camera_debugfs_init(struct camera_platform_info *info)
+{
+ struct dentry *d;
+
+ dev_dbg(info->dev, "%s %s\n", __func__, info->dname);
+ info->d_entry = debugfs_create_dir(
+ info->miscdev.this_device->kobj.name, NULL);
+ if (info->d_entry == NULL) {
+ dev_err(info->dev, "%s: create dir failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ d = debugfs_create_file("d", S_IRUGO|S_IWUSR, info->d_entry,
+ (void *)info, &camera_debugfs_fops);
+ if (!d) {
+ dev_err(info->dev, "%s: create file failed\n", __func__);
+ debugfs_remove_recursive(info->d_entry);
+ info->d_entry = NULL;
+ }
+
+ cam_desc = info;
+ return -EFAULT;
+}
+
+int camera_debugfs_remove(void)
+{
+ if (cam_desc->d_entry)
+ debugfs_remove_recursive(cam_desc->d_entry);
+ cam_desc->d_entry = NULL;
+ return 0;
+}
diff --git a/drivers/media/video/tegra/cam_dev/dev_access.c b/drivers/media/video/tegra/cam_dev/dev_access.c
new file mode 100644
index 000000000000..cdb723733a73
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/dev_access.c
@@ -0,0 +1,334 @@
+/*
+ * dev_access.c
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
+
+#include <media/nvc.h>
+#include <media/camera.h>
+
+/*#define DEBUG_I2C_TRAFFIC*/
+#ifdef DEBUG_I2C_TRAFFIC
+static unsigned char dump_buf[32 + 3 * 16];
+static DEFINE_MUTEX(dump_mutex);
+
+static void camera_dev_dump(
+ struct camera_device *cdev, u32 reg, u8 *buf, u8 num)
+{
+ int len;
+ int i;
+
+ mutex_lock(&dump_mutex);
+ len = sprintf(dump_buf, "%s %04x =", __func__, reg);
+ for (i = 0; i < num; i++)
+ len += sprintf(dump_buf + len, " %02x", buf[i]);
+ dump_buf[len] = 0;
+ dev_info(cdev->dev, "%s\n", dump_buf);
+ mutex_unlock(&dump_mutex);
+}
+#else
+static void camera_dev_dump(
+ struct camera_device *cdev, u32 reg, u8 *buf, u8 num)
+{
+ if (num == 1) {
+ dev_dbg(cdev->dev, "%s %04x = %02x\n", __func__, reg, *buf);
+ return;
+ }
+ dev_dbg(cdev->dev, "%s %04x = %02x %02x ...\n",
+ __func__, reg, buf[0], buf[1]);
+}
+#endif
+
+static int camera_dev_rd(struct camera_device *cdev, u32 reg, u32 *val)
+{
+ int ret = -ENODEV;
+
+ mutex_lock(&cdev->mutex);
+ if (cdev->is_power_on)
+ ret = regmap_read(cdev->regmap, reg, val);
+ else
+ dev_err(cdev->dev, "%s: power is off.\n", __func__);
+ mutex_unlock(&cdev->mutex);
+ camera_dev_dump(cdev, reg, (u8 *)val, sizeof(*val));
+ return ret;
+}
+
+int camera_dev_rd_table(struct camera_device *cdev, struct camera_reg *table)
+{
+ struct camera_reg *p_table = table;
+ u32 val;
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s", __func__);
+ while (p_table->addr != CAMERA_TABLE_END) {
+ err = camera_dev_rd(cdev, p_table->addr, &val);
+ if (err)
+ goto dev_rd_tbl_done;
+
+ p_table->val = (u16)val;
+ p_table++;
+ }
+
+dev_rd_tbl_done:
+ return err;
+}
+
+static int camera_dev_wr_blk(
+ struct camera_device *cdev, u32 reg, u8 *buf, int len)
+{
+ int ret = -ENODEV;
+
+ dev_dbg(cdev->dev, "%s %d\n", __func__, len);
+ camera_dev_dump(cdev, reg, buf, len);
+ mutex_lock(&cdev->mutex);
+ if (cdev->is_power_on)
+ ret = regmap_raw_write(cdev->regmap, reg, buf, len);
+ else
+ dev_err(cdev->dev, "%s: power is off.\n", __func__);
+ mutex_unlock(&cdev->mutex);
+ return ret;
+}
+
+int camera_dev_parser(
+ struct camera_device *cdev, u32 addr, u32 val)
+{
+ u8 flag = 0;
+
+ switch (addr) {
+ case CAMERA_TABLE_PWR:
+ {
+ struct nvc_regulator *preg;
+ flag = val & CAMERA_TABLE_PWR_FLAG_ON ? 0xff : 0;
+ val &= ~CAMERA_TABLE_PWR_FLAG_MASK;
+ if (val >= cdev->num_reg) {
+ dev_err(cdev->dev,
+ "reg index %d out of range.\n", val);
+ return -ENODEV;
+ }
+
+ preg = &cdev->regs[val];
+ if (preg->vreg) {
+ int err;
+ if (flag) {
+ err = regulator_enable(preg->vreg);
+ dev_dbg(cdev->dev, "enable %s\n",
+ preg->vreg_name);
+ } else {
+ err = regulator_disable(preg->vreg);
+ dev_dbg(cdev->dev, "disable %s\n",
+ preg->vreg_name);
+ }
+ if (err) {
+ dev_err(cdev->dev, "%s %s err\n",
+ __func__, preg->vreg_name);
+ return -EFAULT;
+ }
+ } else
+ dev_dbg(cdev->dev, "%s not available\n",
+ preg->vreg_name);
+ break;
+ }
+ case CAMERA_TABLE_GPIO_INX_ACT:
+ case CAMERA_TABLE_GPIO_INX_DEACT:
+ {
+ struct nvc_gpio *gpio;
+ if (val >= cdev->num_gpio) {
+ dev_err(cdev->dev,
+ "gpio index %d out of range.\n", val);
+ return -ENODEV;
+ }
+
+ gpio = &cdev->gpios[val];
+ if (gpio->valid) {
+ flag = gpio->active_high ? 0xff : 0;
+ if (addr != CAMERA_TABLE_GPIO_INX_ACT)
+ flag = !flag;
+ gpio_set_value(gpio->gpio, flag & 0x01);
+ dev_dbg(cdev->dev, "IDX %d(%d) %d\n", val,
+ gpio->gpio, flag & 0x01);
+ }
+ break;
+ }
+ case CAMERA_TABLE_GPIO_ACT:
+ case CAMERA_TABLE_GPIO_DEACT:
+ if (val >= ARCH_NR_GPIOS) {
+ dev_err(cdev->dev,
+ "gpio index %d out of range.\n", val);
+ return -ENODEV;
+ }
+
+ flag = 0xff;
+ if (addr != CAMERA_TABLE_GPIO_ACT)
+ flag = !flag;
+ gpio_set_value(val, flag & 0x01);
+ dev_dbg(cdev->dev,
+ "GPIO %d %d\n", val, flag & 0x01);
+ break;
+ case CAMERA_TABLE_PINMUX:
+ if (!cdev->pinmux_num) {
+ dev_dbg(cdev->dev,
+ "%s PINMUX TABLE\n", "no");
+ break;
+ }
+ flag = val & CAMERA_TABLE_PINMUX_FLAG_ON ? 0xff : 0;
+ if (flag) {
+ if (cdev->mclk_enable_idx == CAMDEV_INVALID) {
+ dev_dbg(cdev->dev,
+ "%s enable PINMUX\n", "no");
+ break;
+ }
+ val = cdev->mclk_enable_idx;
+ } else {
+ if (cdev->mclk_disable_idx == CAMDEV_INVALID) {
+ dev_dbg(cdev->dev,
+ "%s disable PINMUX\n", "no");
+ break;
+ }
+ val = cdev->mclk_disable_idx;
+ }
+ tegra_pinmux_config_table(
+ cdev->pinmux_tbl[val], 1);
+ dev_dbg(cdev->dev, "PINMUX %d\n", flag & 0x01);
+ break;
+ case CAMERA_TABLE_INX_PINMUX:
+ if (val >= cdev->pinmux_num) {
+ dev_err(cdev->dev,
+ "pinmux idx %d out of range.\n", val);
+ break;
+ }
+ tegra_pinmux_config_table(cdev->pinmux_tbl[val], 1);
+ dev_dbg(cdev->dev, "PINMUX %d done.\n", val);
+ break;
+ case CAMERA_TABLE_REG_NEW_POWER:
+ break;
+ case CAMERA_TABLE_INX_POWER:
+ break;
+ case CAMERA_TABLE_WAIT_MS:
+ val *= 1000;
+ case CAMERA_TABLE_WAIT_US:
+ dev_dbg(cdev->dev, "Sleep %d uS\n", val);
+ usleep_range(val, val + 20);
+ break;
+ default:
+ dev_err(cdev->dev, "unrecognized cmd %x.\n", addr);
+ return -ENODEV;
+ break;
+ }
+
+ return 1;
+}
+
+int camera_dev_wr_table(struct camera_device *cdev, struct camera_reg *table)
+{
+ const struct camera_reg *next;
+ u8 *b_ptr = cdev->i2c_buf;
+ u8 byte_num;
+ u16 buf_count = 0;
+ u32 addr = 0;
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ if (!cdev->chip) {
+ dev_err(cdev->dev, "%s chip?\n", "EMPTY");
+ return -EFAULT;
+ }
+
+ byte_num = cdev->chip->regmap_cfg.val_bits / 8;
+ if (byte_num != 1 && byte_num != 2) {
+ dev_err(cdev->dev,
+ "unsupported byte length %d.\n", byte_num);
+ return -EFAULT;
+ }
+
+ for (next = table; next->addr != CAMERA_TABLE_END; next++) {
+ dev_dbg(cdev->dev, "%x - %x\n", next->addr, next->val);
+ if (next->addr & CAMERA_INT_MASK) {
+ err = camera_dev_parser(cdev, next->addr, next->val);
+ if (err > 0) { /* special cmd executed */
+ err = 0;
+ continue;
+ }
+ if (err < 0) /* this is a real error */
+ break;
+ }
+
+ if (!buf_count) {
+ b_ptr = cdev->i2c_buf;
+ addr = next->addr;
+ }
+ switch (byte_num) {
+ case 2:
+ *b_ptr++ = (u8)(next->val >> 8);
+ buf_count++;
+ case 1:
+ *b_ptr++ = (u8)next->val;
+ buf_count++;
+ break;
+ }
+ {
+ const struct camera_reg *n_next = next + 1;
+ if (n_next->addr == next->addr + 1 &&
+ (buf_count + byte_num <= SIZEOF_I2C_BUF) &&
+ !(n_next->addr & CAMERA_INT_MASK))
+ continue;
+ }
+
+ err = camera_dev_wr_blk(cdev, addr, cdev->i2c_buf, buf_count);
+ if (err)
+ break;
+
+ buf_count = 0;
+ }
+
+ return err;
+}
+
+int camera_regulator_get(struct device *dev,
+ struct nvc_regulator *nvc_reg, char *vreg_name)
+{
+ struct regulator *reg = NULL;
+ int err = 0;
+
+ dev_dbg(dev, "%s %s", __func__, vreg_name);
+ if (vreg_name == NULL) {
+ dev_err(dev, "%s NULL regulator name.\n", __func__);
+ return -EFAULT;
+ }
+ reg = regulator_get(dev, vreg_name);
+ if (unlikely(IS_ERR_OR_NULL(reg))) {
+ dev_err(dev, "%s %s ERR: %d\n", __func__, vreg_name, (int)reg);
+ err = PTR_ERR(reg);
+ nvc_reg->vreg = NULL;
+ } else {
+ dev_dbg(dev, "%s: %s\n", __func__, vreg_name);
+ nvc_reg->vreg = reg;
+ nvc_reg->vreg_name = vreg_name;
+ nvc_reg->vreg_flag = false;
+ }
+
+ return err;
+}
diff --git a/drivers/media/video/tegra/cam_dev/imx135.c b/drivers/media/video/tegra/cam_dev/imx135.c
new file mode 100644
index 000000000000..eaa085637aa4
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/imx135.c
@@ -0,0 +1,254 @@
+/*
+ * imx135.c - camera sensor IMX135 kernel PCL driver.
+ * As an example, some devices can be implmented specifically instead of using
+ * the virtual PCL driver to handle some special features (hardware resources,
+ * sequences, etc.).
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <media/nvc.h>
+#include <media/camera.h>
+
+struct imx135_info {
+ struct nvc_pinmux mclk_enable;
+ struct nvc_pinmux mclk_disable;
+ struct nvc_gpio xclr;
+ struct nvc_regulator vana;
+ struct nvc_regulator vdig;
+ struct nvc_regulator vif;
+};
+
+static int imx135_update(
+ struct camera_device *cdev, struct cam_update *upd, int num)
+{
+ /* struct imx135_info *info = dev_get_drvdata(cdev->dev); */
+ int err = 0;
+ int idx;
+
+ dev_dbg(cdev->dev, "%s %d\n", __func__, num);
+ mutex_lock(&cdev->mutex);
+ for (idx = 0; idx < num; idx++) {
+ switch (upd[idx].type) {
+ case UPDATE_PINMUX:
+ break;
+ case UPDATE_GPIO:
+ break;
+ default:
+ dev_err(cdev->dev,
+ "unsupported upd type %d\n", upd[idx].type);
+ break;
+ }
+ }
+ mutex_unlock(&cdev->mutex);
+ return err;
+}
+
+static int imx135_power_on(struct camera_device *cdev)
+{
+ struct imx135_info *info = dev_get_drvdata(cdev->dev);
+ int err = 0;
+
+ if (cdev->is_power_on)
+ return 0;
+
+ dev_dbg(cdev->dev, "%s %x\n", __func__, cdev->is_power_on);
+ mutex_lock(&cdev->mutex);
+ if (info->xclr.valid)
+ gpio_set_value(info->xclr.gpio,
+ info->xclr.active_high ? 1 : 0);
+
+ if (info->vana.vreg) {
+ err = regulator_enable(info->vana.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vana err\n", __func__);
+ goto power_on_end;
+ }
+ }
+
+ if (info->vdig.vreg) {
+ err = regulator_enable(info->vdig.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vdig err\n", __func__);
+ if (info->vana.vreg)
+ regulator_disable(info->vana.vreg);
+ goto power_on_end;
+ }
+ }
+
+ if (info->vif.vreg) {
+ err = regulator_enable(info->vif.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vif err\n", __func__);
+ if (info->vdig.vreg)
+ regulator_disable(info->vdig.vreg);
+ if (info->vana.vreg)
+ regulator_disable(info->vana.vreg);
+ goto power_on_end;
+ }
+ }
+
+ if (info->mclk_enable.valid)
+ tegra_pinmux_config_table(&info->mclk_enable.pcfg, 1);
+
+ usleep_range(2000, 2020);
+
+ if (info->xclr.valid)
+ gpio_set_value(info->xclr.gpio,
+ info->xclr.active_high ? 0 : 1);
+
+ cdev->is_power_on = 1;
+
+power_on_end:
+ mutex_unlock(&cdev->mutex);
+
+ if (!err)
+ usleep_range(250, 270);
+
+ return err;
+}
+
+static int imx135_power_off(struct camera_device *cdev)
+{
+ struct imx135_info *info = dev_get_drvdata(cdev->dev);
+ int err = 0;
+
+ if (!cdev->is_power_on)
+ return 0;
+
+ dev_dbg(cdev->dev, "%s %x\n", __func__, cdev->is_power_on);
+ mutex_lock(&cdev->mutex);
+ if (info->mclk_disable.valid)
+ tegra_pinmux_config_table(&info->mclk_disable.pcfg, 1);
+
+ if (info->xclr.valid)
+ gpio_set_value(info->xclr.gpio,
+ info->xclr.active_high ? 1 : 0);
+
+ if (info->vana.vreg) {
+ err = regulator_disable(info->vana.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vana err\n", __func__);
+ goto power_off_end;
+ }
+ }
+
+ if (info->vdig.vreg) {
+ err = regulator_disable(info->vdig.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vdig err\n", __func__);
+ goto power_off_end;
+ }
+ }
+
+ if (info->vif.vreg) {
+ err = regulator_disable(info->vif.vreg);
+ if (err) {
+ dev_err(cdev->dev, "%s vif err\n", __func__);
+ goto power_off_end;
+ }
+ }
+
+ cdev->is_power_on = 0;
+
+power_off_end:
+ mutex_unlock(&cdev->mutex);
+
+ return err;
+}
+
+static int imx135_instance_destroy(struct camera_device *cdev)
+{
+ struct imx135_info *info = dev_get_drvdata(cdev->dev);
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ if (!info)
+ return 0;
+
+ dev_set_drvdata(cdev->dev, NULL);
+
+ if (likely(info->vana.vreg))
+ regulator_put(info->vana.vreg);
+
+ if (likely(info->vdig.vreg))
+ regulator_put(info->vdig.vreg);
+
+ if (likely(info->vif.vreg))
+ regulator_put(info->vif.vreg);
+
+ kfree(info);
+ return 0;
+}
+
+static int imx135_instance_create(struct camera_device *cdev, void *pdata)
+{
+ struct imx135_info *info;
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ dev_err(cdev->dev, "%s memory low!\n", __func__);
+ return -ENOMEM;
+ }
+
+ camera_regulator_get(cdev->dev, &info->vana, "vana"); /* 2.7v */
+ camera_regulator_get(cdev->dev, &info->vdig, "vdig"); /* 1.2v */
+ camera_regulator_get(cdev->dev, &info->vif, "vif"); /* 1.8v */
+ dev_set_drvdata(cdev->dev, info);
+ return 0;
+}
+
+static struct camera_chip imx135_chip = {
+ .name = "pcl_imx135_demo",
+ .type = CAMERA_DEVICE_TYPE_I2C,
+ .regmap_cfg = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ },
+ .init = imx135_instance_create,
+ .release = imx135_instance_destroy,
+ .power_on = imx135_power_on,
+ .power_off = imx135_power_off,
+ .update = imx135_update,
+};
+
+static int __init imx135_init(void)
+{
+ pr_info("%s\n", __func__);
+ INIT_LIST_HEAD(&imx135_chip.list);
+ camera_chip_add(&imx135_chip);
+ return 0;
+}
+device_initcall(imx135_init);
+
+static void __exit imx135_exit(void)
+{
+}
+module_exit(imx135_exit);
+
+MODULE_DESCRIPTION("IMX135 sensor device");
+MODULE_AUTHOR("Charlie Huang <chahuang@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/tegra/cam_dev/virtual.c b/drivers/media/video/tegra/cam_dev/virtual.c
new file mode 100644
index 000000000000..0610b4870574
--- /dev/null
+++ b/drivers/media/video/tegra/cam_dev/virtual.c
@@ -0,0 +1,415 @@
+/*
+ * virtual.c - Virtual kernel driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <media/nvc.h>
+#include <media/camera.h>
+
+struct chip_config {
+ int gpio_num;
+ int reg_num;
+ struct camera_reg *seq_power_on;
+ struct camera_reg *seq_power_off;
+ char *reg_names[];
+};
+
+static int virtual_update(
+ struct camera_device *cdev, struct cam_update *upd, int num)
+{
+ struct nvc_gpio *gpio;
+ u32 *pinmux;
+ int err = 0;
+ int idx;
+
+ dev_dbg(cdev->dev, "%s %d\n", __func__, num);
+ mutex_lock(&cdev->mutex);
+ for (idx = 0; idx < num; idx++) {
+ switch (upd[idx].type) {
+ case UPDATE_PINMUX:
+ if (!cdev->pinmux_num) {
+ dev_err(cdev->dev, "NO pinmux available.\n");
+ err = -ENODEV;
+ break;
+ }
+ if (upd[idx].arg >= cdev->pinmux_num) {
+ dev_err(cdev->dev,
+ "pinmux index %d out of range.\n",
+ upd[idx].arg);
+ err = -ENODEV;
+ break;
+ }
+
+ dev_dbg(cdev->dev, "UPDATE_PINMUX: %d %d\n",
+ upd[idx].index, upd[idx].arg);
+ if (!upd[idx].index)
+ pinmux = &cdev->mclk_enable_idx;
+ else
+ pinmux = &cdev->mclk_disable_idx;
+ *pinmux = upd[idx].arg;
+ break;
+ case UPDATE_GPIO:
+ if (upd[idx].index >= cdev->num_gpio) {
+ dev_err(cdev->dev,
+ "gpio index %d out of range.\n",
+ upd[idx].index);
+ err = -ENODEV;
+ break;
+ }
+ gpio = (void *)upd[idx].arg;
+ if (gpio->gpio >= ARCH_NR_GPIOS) {
+ dev_err(cdev->dev,
+ "gpio index %d out of range.\n",
+ gpio->gpio);
+ err = -ENODEV;
+ break;
+ }
+
+ dev_dbg(cdev->dev, "UPDATE_GPIO: %d %d\n",
+ upd[idx].index, upd[idx].arg);
+ gpio->valid = true;
+ cdev->gpios[upd[idx].index] = *gpio;
+ break;
+ default:
+ dev_err(cdev->dev,
+ "unsupported upd type %d\n", upd[idx].type);
+ break;
+ }
+
+ if (err)
+ break;
+ }
+ mutex_unlock(&cdev->mutex);
+ return err;
+}
+
+static int virtual_power_on(struct camera_device *cdev)
+{
+ struct chip_config *c_info = cdev->chip->private;
+ struct camera_reg *pwr_seq = c_info->seq_power_on;
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s %x %p\n",
+ __func__, cdev->is_power_on, pwr_seq);
+ if (cdev->is_power_on || !pwr_seq)
+ return 0;
+
+ mutex_lock(&cdev->mutex);
+ err = camera_dev_wr_table(cdev, pwr_seq);
+ if (!err)
+ cdev->is_power_on = 1;
+ mutex_unlock(&cdev->mutex);
+ return err;
+}
+
+static int virtual_power_off(struct camera_device *cdev)
+{
+ struct chip_config *c_info = cdev->chip->private;
+ struct camera_reg *pwr_seq = c_info->seq_power_off;
+ int err = 0;
+
+ dev_dbg(cdev->dev, "%s %x %p\n",
+ __func__, cdev->is_power_on, pwr_seq);
+ if (!cdev->is_power_on || !pwr_seq)
+ return 0;
+
+ mutex_lock(&cdev->mutex);
+ err = camera_dev_wr_table(cdev, pwr_seq);
+ if (!err)
+ cdev->is_power_on = 0;
+ mutex_unlock(&cdev->mutex);
+
+ return err;
+}
+
+static int virtual_instance_destroy(struct camera_device *cdev)
+{
+ void *buf;
+ u32 idx;
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+
+ if (!cdev->dev)
+ return 0;
+
+ buf = dev_get_drvdata(cdev->dev);
+ dev_set_drvdata(cdev->dev, NULL);
+
+ for (idx = 0; idx < cdev->num_reg; idx++)
+ if (likely(cdev->regs[idx].vreg))
+ regulator_put(cdev->regs[idx].vreg);
+
+ cdev->num_gpio = 0;
+ cdev->num_reg = 0;
+ kfree(buf);
+ return 0;
+}
+
+static int virtual_instance_create(struct camera_device *cdev, void *pdata)
+{
+ struct chip_config *c_info = cdev->chip->private;
+ u32 idx;
+
+ dev_dbg(cdev->dev, "%s\n", __func__);
+ cdev->gpios = kzalloc(c_info->gpio_num * sizeof(struct nvc_gpio) +
+ c_info->reg_num * sizeof(struct nvc_regulator),
+ GFP_KERNEL);
+ if (cdev->gpios == NULL) {
+ dev_err(cdev->dev, "%s memory low!\n", __func__);
+ return -ENOMEM;
+ }
+
+ cdev->pinmux_num = ((struct camera_platform_data *)pdata)->pinmux_num;
+ cdev->pinmux_tbl = ((struct camera_platform_data *)pdata)->pinmux;
+ cdev->num_gpio = c_info->gpio_num;
+ cdev->regs = (void *)cdev->gpios +
+ c_info->gpio_num * sizeof(struct nvc_gpio);
+ cdev->num_reg = c_info->reg_num;
+ cdev->mclk_enable_idx = CAMDEV_INVALID;
+ cdev->mclk_disable_idx = CAMDEV_INVALID;
+
+ for (idx = 0; idx < cdev->num_gpio; idx++)
+ cdev->gpios[idx].valid = false;
+
+ for (idx = 0; idx < cdev->num_reg; idx++) {
+ cdev->regs[idx].vreg_name =
+ (const char *)c_info->reg_names[idx];
+ camera_regulator_get(cdev->dev, &cdev->regs[idx],
+ (char *)cdev->regs[idx].vreg_name);
+ }
+
+ dev_set_drvdata(cdev->dev, cdev->gpios);
+ return 0;
+}
+
+static struct regmap_config regmap_cfg_default = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int virtual_device_sanity_check(
+ struct device *dev, struct virtual_device *dev_info, int *len)
+{
+ u32 num;
+ u8 *nptr;
+ int n;
+
+ dev_dbg(dev, "%s: %s, bus type %d, addr bits %d, val bits %d\n",
+ __func__, dev_info->name, dev_info->bus_type,
+ dev_info->regmap_cfg.addr_bits, dev_info->regmap_cfg.val_bits);
+ dev_dbg(dev, "gpios %d, regs %d\n",
+ dev_info->gpio_num, dev_info->reg_num);
+ if (dev_info->name[0] == '\0') {
+ dev_err(dev, "%s need a device name!\n", __func__);
+ return -ENODEV;
+ }
+
+ if (dev_info->bus_type != CAMERA_DEVICE_TYPE_I2C) {
+ dev_err(dev, "%s unsupported device type %d!\n",
+ __func__, dev_info->bus_type);
+ return -ENODEV;
+ }
+
+ if (dev_info->regmap_cfg.addr_bits != 0 &&
+ dev_info->regmap_cfg.addr_bits != 8 &&
+ dev_info->regmap_cfg.addr_bits != 16) {
+ dev_err(dev, "%s unsupported address bits %d!\n",
+ __func__, dev_info->regmap_cfg.addr_bits);
+ return -ENODEV;
+ }
+
+ if (dev_info->regmap_cfg.val_bits != 8 &&
+ dev_info->regmap_cfg.val_bits != 16) {
+ dev_err(dev, "%s unsupported data bits %d!\n",
+ __func__, dev_info->regmap_cfg.val_bits);
+ return -ENODEV;
+ }
+
+ if (dev_info->regmap_cfg.cache_type != REGCACHE_NONE &&
+ dev_info->regmap_cfg.cache_type != REGCACHE_RBTREE &&
+ dev_info->regmap_cfg.cache_type != REGCACHE_COMPRESSED) {
+ dev_err(dev, "%s unsupported cache type %d!\n",
+ __func__, dev_info->regmap_cfg.cache_type);
+ return -ENODEV;
+ }
+
+ if (dev_info->gpio_num >= ARCH_NR_GPIOS) {
+ dev_err(dev, "%s too many gpios %d!\n",
+ __func__, dev_info->gpio_num);
+ return -ENODEV;
+ }
+
+ if (dev_info->gpio_num >= 6) {
+ dev_notice(dev, "%s WHAT?! Are you sure you need %d gpios?\n",
+ __func__, dev_info->gpio_num);
+ }
+
+ *len = 0;
+ num = dev_info->reg_num;
+ nptr = &dev_info->reg_names[0];
+ while (num) {
+ n = strlen(nptr);
+ if (!n) {
+ dev_err(dev, "%s NULL reg name @ %d\n",
+ __func__, dev_info->reg_num - num);
+ return -ENODEV;
+ }
+ *len += n + 1;
+ nptr += CAMERA_MAX_NAME_LENGTH;
+ num--;
+ }
+ dev_dbg(dev, "regulator name size: %d\n", *len);
+
+ return 0;
+}
+
+static int virtual_chip_config(
+ struct device *dev,
+ struct virtual_device *dev_info,
+ struct chip_config *c_info)
+{
+ char *rptr = (void *)c_info;
+ char *nptr = dev_info->reg_names;
+ int len;
+ u32 idx;
+
+ dev_dbg(dev, "%s regulators:\n", __func__);
+ c_info->gpio_num = dev_info->gpio_num;
+ c_info->reg_num = dev_info->reg_num;
+ rptr += sizeof(*c_info) + sizeof(char *) * c_info->reg_num;
+ for (idx = 0; idx < c_info->reg_num; idx++) {
+ c_info->reg_names[idx] = rptr;
+ len = strlen(nptr);
+ dev_dbg(dev, "#%d %s len %d\n", idx, nptr, len);
+ strcpy(rptr, nptr);
+ rptr[len] = '\0';
+ rptr += len + 1;
+ nptr += CAMERA_MAX_NAME_LENGTH;
+ dev_dbg(dev, "#%d - %s\n", idx, c_info->reg_names[idx]);
+ }
+
+ c_info->seq_power_on = (void *)rptr;
+ if (copy_from_user(
+ c_info->seq_power_on, (const void __user *)dev_info->power_on,
+ sizeof(struct camera_reg) * dev_info->pwr_on_size)) {
+ dev_err(dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ c_info->seq_power_off = (void *)c_info->seq_power_on +
+ sizeof(struct camera_reg) * dev_info->pwr_on_size;
+ if (copy_from_user(
+ c_info->seq_power_off, (const void __user *)dev_info->power_off,
+ sizeof(struct camera_reg) * dev_info->pwr_off_size)) {
+ dev_err(dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int virtual_device_add(struct device *dev, unsigned long arg)
+{
+ struct virtual_device dev_info;
+ struct camera_chip *v_chip;
+ struct chip_config *c_info;
+ struct regmap_config *p_regmap;
+ int buf_len;
+ int err = 0;
+
+ dev_info(dev, "%s\n", __func__);
+
+ if (copy_from_user(
+ &dev_info, (const void __user *)arg, sizeof(dev_info))) {
+ dev_err(dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ err = virtual_device_sanity_check(dev, &dev_info, &buf_len);
+ if (err)
+ return err;
+
+ buf_len += sizeof(char *) * dev_info.reg_num +
+ sizeof(struct camera_reg) * dev_info.pwr_on_size +
+ sizeof(struct camera_reg) * dev_info.pwr_off_size;
+ v_chip = kzalloc(
+ sizeof(*v_chip) + sizeof(*c_info) + buf_len, GFP_KERNEL);
+ if (!v_chip) {
+ dev_err(dev, "%s unable to allocate memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ c_info = (void *)v_chip + sizeof(*v_chip);
+ err = virtual_chip_config(dev, &dev_info, c_info);
+ if (err) {
+ kfree(v_chip);
+ return err;
+ }
+
+ strncpy((u8 *)v_chip->name, (u8 const *)dev_info.name,
+ sizeof(v_chip->name));
+
+ p_regmap = (struct regmap_config *)&v_chip->regmap_cfg;
+ memcpy(p_regmap, &regmap_cfg_default, sizeof(*p_regmap));
+ v_chip->type = dev_info.bus_type;
+ if (dev_info.regmap_cfg.addr_bits)
+ p_regmap->reg_bits = dev_info.regmap_cfg.addr_bits;
+ if (dev_info.regmap_cfg.val_bits)
+ p_regmap->val_bits = dev_info.regmap_cfg.val_bits;
+ p_regmap->cache_type = dev_info.regmap_cfg.cache_type;
+
+ INIT_LIST_HEAD(&v_chip->list);
+ v_chip->private = c_info;
+ v_chip->init = virtual_instance_create;
+ v_chip->release = virtual_instance_destroy,
+ v_chip->power_on = virtual_power_on,
+ v_chip->power_off = virtual_power_off,
+ v_chip->update = virtual_update,
+
+ camera_chip_add(v_chip);
+ return 0;
+}
+
+static int __init virtual_init(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+device_initcall(virtual_init);
+
+static void __exit virtual_exit(void)
+{
+}
+module_exit(virtual_exit);
+
+MODULE_DESCRIPTION("virtual sensor device");
+MODULE_AUTHOR("Charlie Huang <chahuang@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/tegra/camera.c b/drivers/media/video/tegra/camera.c
new file mode 100644
index 000000000000..b63e398ce7f2
--- /dev/null
+++ b/drivers/media/video/tegra/camera.c
@@ -0,0 +1,879 @@
+/*
+ * camera.c - generic camera device driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Contributors:
+ * Charlie Huang <chahuang@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation
+ * --------------
+ * The board level details about the device are to be provided in the board
+ * file with the <device>_platform_data structure.
+ * Standard among NVC kernel drivers in this structure is:
+ * .dev_name = The MISC driver name the device registers as. If not used,
+ * then the part number of the device is used for the driver name.
+ * If using the NVC user driver then use the name found in this
+ * driver under _default_pdata.
+ */
+
+#define CAMERA_DEVICE_INTERNAL
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <media/camera.h>
+
+static struct camera_platform_data camera_dflt_pdata = {
+ .cfg = 0,
+};
+
+static DEFINE_MUTEX(app_mutex);
+static DEFINE_MUTEX(dev_mutex);
+static DEFINE_MUTEX(chip_mutex);
+static LIST_HEAD(app_list);
+static LIST_HEAD(dev_list);
+static LIST_HEAD(chip_list);
+
+static struct camera_platform_info cam_desc = {
+ .in_use = ATOMIC_INIT(0),
+ .u_mutex = &app_mutex,
+ .d_mutex = &dev_mutex,
+ .c_mutex = &chip_mutex,
+ .app_list = &app_list,
+ .dev_list = &dev_list,
+ .chip_list = &chip_list,
+};
+
+static int camera_seq_rd(struct camera_info *cam, unsigned long arg)
+{
+ struct nvc_param params;
+ struct camera_reg *p_i2c_table;
+ int err;
+
+ dev_dbg(cam->dev, "%s %lx\n", __func__, arg);
+ if (copy_from_user(&params, (const void __user *)arg,
+ sizeof(struct nvc_param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ p_i2c_table = kzalloc(sizeof(params.sizeofvalue), GFP_KERNEL);
+ if (p_i2c_table == NULL) {
+ dev_err(cam->dev, "%s: kzalloc error\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(p_i2c_table, (const void __user *)params.p_value,
+ params.sizeofvalue)) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ kfree(p_i2c_table);
+ return -EINVAL;
+ }
+
+ err = camera_dev_rd_table(cam->cdev, p_i2c_table);
+ if (!err && copy_to_user((void __user *)params.p_value,
+ p_i2c_table, params.sizeofvalue)) {
+ dev_err(cam->dev, "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ err = -EINVAL;
+ }
+
+ kfree(p_i2c_table);
+ return err;
+}
+
+static int camera_seq_wr(struct camera_info *cam, unsigned long arg)
+{
+ struct nvc_param params;
+ struct camera_device *cdev = cam->cdev;
+ struct camera_reg *p_i2c_table;
+ int err = 0;
+ int idx;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+
+ if (copy_from_user(&params, (const void __user *)arg,
+ sizeof(struct nvc_param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ dev_dbg(cam->dev, "param: %x, size %d\n", params.param,
+ params.sizeofvalue);
+ if (params.param == CAMERA_SEQ_EXIST) {
+ if (params.variant >= NUM_OF_SEQSTACK) {
+ dev_err(cam->dev, "%s seq index out of range %d\n",
+ __func__, params.variant);
+ return -EFAULT;
+ }
+ p_i2c_table = cdev->seq_stack[params.variant];
+ if (p_i2c_table == NULL) {
+ dev_err(cam->dev, "%s seq index empty! %d\n",
+ __func__, params.variant);
+ return -EEXIST;
+ }
+ err = camera_dev_wr_table(cdev, p_i2c_table);
+ return err;
+ }
+
+ p_i2c_table = devm_kzalloc(cdev->dev, params.sizeofvalue, GFP_KERNEL);
+ if (p_i2c_table == NULL) {
+ dev_err(cam->dev, "%s devm_kzalloc err line %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(p_i2c_table,
+ (const void __user *)params.p_value, params.sizeofvalue)) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ devm_kfree(cdev->dev, p_i2c_table);
+ return -EFAULT;
+ }
+
+ switch (params.param) {
+ case CAMERA_SEQ_REGISTER_EXEC:
+ case CAMERA_SEQ_REGISTER_ONLY:
+ for (idx = 0; idx < NUM_OF_SEQSTACK; idx++) {
+ if (!cdev->seq_stack[idx]) {
+ cdev->seq_stack[idx] = p_i2c_table;
+ break;
+ }
+ }
+ if (idx >= NUM_OF_SEQSTACK) {
+ dev_err(cam->dev, "%s seq index full!\n", __func__);
+ return -EINVAL;
+ } else {
+ params.variant = idx;
+ if (copy_to_user((void __user *)arg,
+ (const void *)&params, sizeof(params))) {
+ dev_err(cam->dev,
+ "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ devm_kfree(cdev->dev, p_i2c_table);
+ return -EFAULT;
+ }
+ }
+ if (params.param == CAMERA_SEQ_REGISTER_EXEC)
+ err |= camera_dev_wr_table(cdev, p_i2c_table);
+ break;
+ case CAMERA_SEQ_EXEC:
+ err |= camera_dev_wr_table(cdev, p_i2c_table);
+ devm_kfree(cdev->dev, p_i2c_table);
+ break;
+ }
+
+ return err;
+}
+
+static int camera_dev_power(struct camera_info *cam, unsigned long pwr)
+{
+ struct camera_device *cdev = cam->cdev;
+ struct camera_chip *chip = cdev->chip;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lu %d\n", __func__, pwr, cdev->pwr_state);
+ if (pwr == cdev->pwr_state) /* power state no change */
+ goto dev_power_end;
+
+ switch (pwr) {
+ case NVC_PWR_OFF:
+ case NVC_PWR_STDBY_OFF:
+ if (chip->power_off)
+ err |= chip->power_off(cdev);
+ break;
+ case NVC_PWR_STDBY:
+ case NVC_PWR_COMM:
+ case NVC_PWR_ON:
+ if (chip->power_on)
+ err = chip->power_on(cdev);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ dev_err(cam->dev, "%s error\n", __func__);
+ pwr = NVC_PWR_ERR;
+ }
+
+ cdev->pwr_state = pwr;
+ if (err > 0)
+ err = 0;
+
+dev_power_end:
+ return err;
+}
+
+static int camera_dev_pwrd(struct camera_info *cam, unsigned long arg)
+{
+ int pwr;
+ int err = 0;
+
+ pwr = cam->cdev->pwr_state;
+ dev_dbg(cam->dev, "%s PWR_RD: %d\n", __func__, pwr);
+ if (copy_to_user((void __user *)arg, (const void *)&pwr,
+ sizeof(pwr))) {
+ dev_err(cam->dev, "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ }
+
+ return err;
+}
+
+static struct camera_chip *camera_chip_chk(char *name)
+{
+ struct camera_chip *ccp;
+
+ mutex_lock(cam_desc.c_mutex);
+ list_for_each_entry(ccp, cam_desc.chip_list, list)
+ if (!strcmp(ccp->name, name)) {
+ dev_dbg(cam_desc.dev, "%s device %s found.\n",
+ __func__, name);
+ mutex_unlock(cam_desc.c_mutex);
+ return ccp;
+ }
+ mutex_unlock(cam_desc.c_mutex);
+ dev_err(cam_desc.dev, "%s device %s not found\n", __func__, name);
+ return NULL;
+}
+
+int camera_chip_add(struct camera_chip *chip)
+{
+ struct camera_chip *ccp;
+ int err = 0;
+
+ dev_dbg(cam_desc.dev, "%s add chip: %s\n", __func__, chip->name);
+ mutex_lock(cam_desc.c_mutex);
+ list_for_each_entry(ccp, cam_desc.chip_list, list)
+ if (!strcmp(ccp->name, chip->name)) {
+ dev_err(cam_desc.dev, "%s device %s already added.\n",
+ __func__, chip->name);
+ mutex_unlock(cam_desc.c_mutex);
+ return -EEXIST;
+ }
+
+ list_add_tail(&chip->list, cam_desc.chip_list);
+ mutex_unlock(cam_desc.c_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(camera_chip_add);
+
+static int camera_remove_device(struct camera_device *cdev, bool ref_dec)
+{
+ dev_dbg(cdev->dev, "%s %s\n", __func__, cdev->name);
+ if (!cdev) {
+ dev_err(cam_desc.dev, "%s cdev is NULL!\n", __func__);
+ return -EFAULT;
+ }
+
+ WARN_ON(atomic_xchg(&cdev->in_use, 0));
+ if (cdev->cam) {
+ struct camera_info *cam = cdev->cam;
+ cam->cdev = NULL;
+ cam->dev = cam_desc.dev;
+ atomic_set(&cam->in_use, 0);
+ }
+ if (cdev->chip)
+ (cdev->chip->release)(cdev);
+ /*if (cdev->regmap)
+ regmap_exit(cdev->regmap);*/
+ if (cdev->dev)
+ i2c_unregister_device(to_i2c_client(cdev->dev));
+ /*for (idx = 0; idx < NUM_OF_SEQSTACK; idx++)
+ kfree(cdev->seq_stack[idx]);*/
+ if (ref_dec)
+ atomic_dec(&cdev->chip->ref_cnt);
+ kfree(cdev);
+ return 0;
+}
+
+static int camera_free_device(struct camera_info *cam, unsigned long arg)
+{
+ struct camera_device *cdev = cam->cdev;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ mutex_lock(cam_desc.d_mutex);
+ atomic_set(&cdev->in_use, 0);
+ mutex_unlock(cam_desc.d_mutex);
+
+ return 0;
+}
+
+static int camera_new_device(struct camera_info *cam, unsigned long arg)
+{
+ struct camera_device_info dev_info;
+ struct camera_device *new_dev;
+ struct camera_device *next_dev;
+ struct camera_chip *c_chip;
+ struct i2c_adapter *adap;
+ struct i2c_board_info brd;
+ struct i2c_client *client;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ if (copy_from_user(
+ &dev_info, (const void __user *)arg, sizeof(dev_info))) {
+ dev_err(cam_desc.dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto new_device_end;
+ }
+ dev_dbg(cam->dev, "%s - %d %d %x\n",
+ dev_info.name, dev_info.type, dev_info.bus, dev_info.addr);
+
+ c_chip = camera_chip_chk(dev_info.name);
+ if (c_chip == NULL) {
+ err = -ENODEV;
+ goto new_device_end;
+ }
+
+ adap = i2c_get_adapter(dev_info.bus);
+ if (!adap) {
+ dev_err(cam_desc.dev, "%s no such i2c bus %d\n",
+ __func__, dev_info.bus);
+ err = -ENODEV;
+ goto new_device_end;
+ }
+
+ new_dev = kzalloc(sizeof(*new_dev), GFP_KERNEL);
+ if (!new_dev) {
+ dev_err(cam_desc.dev, "%s memory low!\n", __func__);
+ err = -ENOMEM;
+ goto new_device_end;
+ }
+
+ new_dev->chip = c_chip;
+ memset(&brd, 0, sizeof(brd));
+ strncpy(brd.type, dev_info.name, sizeof(brd.type));
+ brd.addr = dev_info.addr;
+
+ mutex_lock(cam_desc.d_mutex);
+
+ /* check if device exists, if yes and not occupied pick it and exit */
+ list_for_each_entry(next_dev, cam_desc.dev_list, list) {
+ if (next_dev->client &&
+ next_dev->client->adapter == adap &&
+ next_dev->client->addr == dev_info.addr) {
+ dev_dbg(cam_desc.dev,
+ "%s: device already exists.\n", __func__);
+ camera_remove_device(new_dev, false);
+ if (atomic_xchg(&next_dev->in_use, 1)) {
+ dev_err(cam_desc.dev, "%s device %s BUSY\n",
+ __func__, next_dev->name);
+ err = -EBUSY;
+ goto new_device_err;
+ }
+ new_dev = next_dev;
+ goto new_device_done;
+ }
+ }
+
+ /* device is not present in the dev_list, add it */
+ client = i2c_new_device(adap, &brd);
+ if (!client) {
+ dev_err(cam_desc.dev,
+ "%s cannot allocate client: %s bus %d, %x\n",
+ __func__, dev_info.name, dev_info.bus, dev_info.addr);
+ err = -EINVAL;
+ goto new_device_err;
+ }
+ new_dev->dev = &client->dev;
+
+ new_dev->regmap = devm_regmap_init_i2c(client, &c_chip->regmap_cfg);
+ if (IS_ERR(new_dev->regmap)) {
+ dev_err(new_dev->dev, "%s regmap init failed: %ld\n",
+ __func__, PTR_ERR(new_dev->regmap));
+ err = -ENODEV;
+ goto new_device_err;
+ }
+ strncpy(new_dev->name, dev_info.name, sizeof(new_dev->name));
+ INIT_LIST_HEAD(&new_dev->list);
+ mutex_init(&new_dev->mutex);
+ new_dev->client = client;
+
+ err = (new_dev->chip->init)(new_dev, cam_desc.pdata);
+ if (err)
+ goto new_device_err;
+
+ atomic_inc(&c_chip->ref_cnt);
+ atomic_set(&new_dev->in_use, 1);
+
+ list_add(&new_dev->list, cam_desc.dev_list);
+
+new_device_done:
+ mutex_unlock(cam_desc.d_mutex);
+
+ cam->cdev = new_dev;
+ cam->dev = new_dev->dev;
+ new_dev->cam = cam;
+ goto new_device_end;
+
+new_device_err:
+ mutex_unlock(cam_desc.d_mutex);
+ camera_remove_device(new_dev, false);
+
+new_device_end:
+ return err;
+}
+
+static void camera_app_remove(struct camera_info *cam)
+{
+ dev_dbg(cam->dev, "%s\n", __func__);
+
+ WARN_ON(atomic_xchg(&cam->in_use, 0));
+ if (cam->cdev) {
+ WARN_ON(atomic_xchg(&cam->cdev->in_use, 0));
+ cam->cdev->cam = NULL;
+ }
+ kfree(cam);
+}
+
+static int camera_update(struct camera_info *cam, unsigned long arg)
+{
+ struct camera_device *cdev = cam->cdev;
+ struct camera_chip *chip = cdev->chip;
+ struct nvc_param param;
+ struct cam_update *upd = NULL;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ if (!chip->update) {
+ dev_dbg(cam->dev, "no update pointer.\n");
+ goto update_end;
+ }
+
+ if (copy_from_user(&param, (const void __user *)arg, sizeof(param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto update_end;
+ }
+ upd = kzalloc(param.sizeofvalue * sizeof(*upd), GFP_KERNEL);
+ if (!upd) {
+ dev_err(cam->dev, "%s allocate memory failed!\n", __func__);
+ err = -ENOMEM;
+ goto update_end;
+ }
+ if (copy_from_user(upd, (const void __user *)param.p_value,
+ param.sizeofvalue * sizeof(*upd))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto update_end;
+ }
+
+ err = chip->update(cdev, upd, param.sizeofvalue);
+
+update_end:
+ kfree(upd);
+ return err;
+}
+
+static int camera_layout_update(struct camera_info *cam, unsigned long arg)
+{
+ struct nvc_param param;
+ void *upd = NULL;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ mutex_lock(cam_desc.u_mutex);
+ if (cam_desc.layout) {
+ dev_err(cam->dev, "layout already there.\n");
+ err = -EEXIST;
+ goto layout_end;
+ }
+
+ if (copy_from_user(&param, (const void __user *)arg, sizeof(param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto layout_end;
+ }
+
+ upd = kzalloc(param.sizeofvalue, GFP_KERNEL);
+ if (!upd) {
+ dev_err(cam->dev, "%s allocate memory failed!\n", __func__);
+ err = -ENOMEM;
+ goto layout_end;
+ }
+ if (copy_from_user(upd, (const void __user *)param.p_value,
+ param.sizeofvalue)) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ kfree(upd);
+ err = -EFAULT;
+ goto layout_end;
+ }
+
+ cam_desc.layout = upd;
+ cam_desc.size_layout = param.sizeofvalue;
+
+layout_end:
+ mutex_unlock(cam_desc.u_mutex);
+ return err;
+}
+
+static int camera_layout_get(struct camera_info *cam, unsigned long arg)
+{
+ struct nvc_param param;
+ int len;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ if (!cam_desc.layout) {
+ dev_err(cam->dev, "layout empty.\n");
+ err = -EEXIST;
+ goto getlayout_end;
+ }
+
+ if (copy_from_user(&param, (const void __user *)arg, sizeof(param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto getlayout_end;
+ }
+
+ len = (int)cam_desc.size_layout - param.variant;
+ if (len > param.sizeofvalue) {
+ len = param.sizeofvalue;
+ err = -EAGAIN;
+ }
+ if (copy_to_user((void __user *)param.p_value,
+ cam_desc.layout + param.variant, len)) {
+ dev_err(cam->dev, "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto getlayout_end;
+ }
+
+ param.sizeofvalue = len;
+ if (copy_to_user((void __user *)arg, &param, sizeof(param))) {
+ dev_err(cam->dev, "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ }
+
+getlayout_end:
+ return err;
+}
+
+static int camera_add_drivers(struct camera_info *cam, unsigned long arg)
+{
+ struct nvc_param param;
+ struct camera_module *cm = cam_desc.pdata->modules;
+ struct i2c_adapter *adap = NULL;
+ char ref_name[CAMERA_MAX_NAME_LENGTH];
+ struct i2c_client *client = NULL;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %lx", __func__, arg);
+ if (copy_from_user(&param, (const void __user *)arg, sizeof(param))) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto add_driver_end;
+ }
+
+ if (param.sizeofvalue > sizeof(ref_name)) {
+ dev_err(cam->dev, "%s driver name too long %d\n",
+ __func__, param.sizeofvalue);
+ err = -EFAULT;
+ goto add_driver_end;
+ }
+
+ memset(ref_name, 0, sizeof(ref_name));
+ if (copy_from_user(ref_name, (const void __user *)param.p_value,
+ param.sizeofvalue)) {
+ dev_err(cam->dev, "%s copy_from_user err line %d\n",
+ __func__, __LINE__);
+ err = -EFAULT;
+ goto add_driver_end;
+ }
+
+ dev_dbg(cam->dev, "%s looking for %s on %d\n",
+ __func__, ref_name, param.variant);
+ adap = i2c_get_adapter(param.variant);
+ while (cm && cm->sensor && strlen(cm->sensor->type)) {
+ dev_dbg(cam->dev, "%s\n", cm->sensor->type);
+ if (!strcmp(cm->sensor->type, ref_name)) {
+ client = i2c_new_device(adap, cm->sensor);
+ if (!client) {
+ dev_err(cam->dev, "%s add driver %s fail\n",
+ __func__, cm->sensor->type);
+ err = -EFAULT;
+ break;
+ }
+ if (cm->sensor &&
+ strlen(cm->sensor->type)) {
+ i2c_new_device(adap, cm->sensor);
+ if (!client) {
+ dev_err(cam->dev,
+ "%s add driver %s fail\n",
+ __func__, cm->sensor->type);
+ err = -EFAULT;
+ break;
+ }
+ }
+ if (cm->focuser && strlen(cm->focuser->type)) {
+ i2c_new_device(adap, cm->focuser);
+ if (!client) {
+ dev_err(cam->dev,
+ "%s add driver %s fail\n",
+ __func__, cm->focuser->type);
+ err = -EFAULT;
+ break;
+ }
+ }
+ if (cm->flash && strlen(cm->flash->type)) {
+ i2c_new_device(adap, cm->flash);
+ if (!client) {
+ dev_err(cam->dev,
+ "%s add driver %s fail\n",
+ __func__, cm->flash->type);
+ err = -EFAULT;
+ break;
+ }
+ }
+ }
+ cm++;
+ }
+
+add_driver_end:
+ return err;
+}
+
+static long camera_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct camera_info *cam = file->private_data;
+ int err = 0;
+
+ dev_dbg(cam->dev, "%s %x %lx\n", __func__, cmd, arg);
+ if (!cam->cdev && ((cmd == PCLLK_IOCTL_SEQ_WR) ||
+ (cmd == PCLLK_IOCTL_PWR_WR) ||
+ (cmd == PCLLK_IOCTL_PWR_RD))) {
+ dev_err(cam_desc.dev, "%s %x - no device activated.\n",
+ __func__, cmd);
+ err = -ENODEV;
+ goto ioctl_end;
+ }
+
+ /* command distributor */
+ switch (cmd) {
+ case PCLLK_IOCTL_CHIP_REG:
+ err = virtual_device_add(cam_desc.dev, arg);
+ break;
+ case PCLLK_IOCTL_DEV_REG:
+ err = camera_new_device(cam, arg);
+ break;
+ case PCLLK_IOCTL_DEV_DEL:
+ mutex_lock(cam_desc.d_mutex);
+ list_del(&cam->cdev->list);
+ mutex_unlock(cam_desc.d_mutex);
+ camera_remove_device(cam->cdev, true);
+ break;
+ case PCLLK_IOCTL_DEV_FREE:
+ err = camera_free_device(cam, arg);
+ break;
+ case PCLLK_IOCTL_SEQ_WR:
+ err = camera_seq_wr(cam, arg);
+ break;
+ case PCLLK_IOCTL_SEQ_RD:
+ err = camera_seq_rd(cam, arg);
+ break;
+ case PCLLK_IOCTL_PARAM_RD:
+ /* err = camera_param_rd(cam, arg); */
+ break;
+ case PCLLK_IOCTL_PWR_WR:
+ /* This is a Guaranteed Level of Service (GLOS) call */
+ err = camera_dev_power(cam, arg);
+ break;
+ case PCLLK_IOCTL_PWR_RD:
+ err = camera_dev_pwrd(cam, arg);
+ break;
+ case PCLLK_IOCTL_UPDATE:
+ err = camera_update(cam, arg);
+ break;
+ case PCLLK_IOCTL_LAYOUT_WR:
+ err = camera_layout_update(cam, arg);
+ break;
+ case PCLLK_IOCTL_LAYOUT_RD:
+ err = camera_layout_get(cam, arg);
+ break;
+ case PCLLK_IOCTL_DRV_ADD:
+ err = camera_add_drivers(cam, arg);
+ break;
+ default:
+ dev_err(cam->dev, "%s unsupported ioctl: %x\n",
+ __func__, cmd);
+ err = -EINVAL;
+ }
+
+ioctl_end:
+ if (err)
+ dev_err(cam->dev, "err = %d\n", err);
+
+ return err;
+}
+
+static int camera_open(struct inode *inode, struct file *file)
+{
+ struct camera_info *cam;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(cam_desc.dev,
+ "%s unable to allocate memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&cam->k_mutex);
+ atomic_set(&cam->in_use, 0);
+ INIT_LIST_HEAD(&cam->list);
+ cam->dev = cam_desc.dev;
+ file->private_data = cam;
+
+ mutex_lock(cam_desc.u_mutex);
+ list_add(&cam->list, cam_desc.app_list);
+ mutex_unlock(cam_desc.u_mutex);
+
+ dev_dbg(cam_desc.dev, "%s\n", __func__);
+ return 0;
+}
+
+static int camera_release(struct inode *inode, struct file *file)
+{
+ struct camera_info *cam = file->private_data;
+
+ dev_dbg(cam_desc.dev, "%s\n", __func__);
+
+ mutex_lock(cam_desc.u_mutex);
+ list_del(&cam->list);
+ mutex_unlock(cam_desc.u_mutex);
+
+ camera_app_remove(cam);
+
+ file->private_data = NULL;
+ return 0;
+}
+
+static const struct file_operations camera_fileops = {
+ .owner = THIS_MODULE,
+ .open = camera_open,
+ .unlocked_ioctl = camera_ioctl,
+ .release = camera_release,
+};
+
+static int camera_remove(struct platform_device *dev)
+{
+ struct camera_info *cam;
+ struct camera_device *cdev;
+
+ dev_dbg(cam_desc.dev, "%s\n", __func__);
+ misc_deregister(&cam_desc.miscdev);
+
+ list_for_each_entry(cam, cam_desc.app_list, list) {
+ mutex_lock(cam_desc.u_mutex);
+ list_del(&cam->list);
+ mutex_unlock(cam_desc.u_mutex);
+ camera_app_remove(cam);
+ }
+
+ list_for_each_entry(cdev, cam_desc.dev_list, list) {
+ mutex_lock(cam_desc.d_mutex);
+ list_del(&cdev->list);
+ mutex_unlock(cam_desc.d_mutex);
+ camera_remove_device(cdev, true);
+ }
+
+ kfree(cam_desc.layout);
+
+ camera_debugfs_remove();
+ return 0;
+}
+
+static int camera_probe(struct platform_device *dev)
+{
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ if (atomic_xchg(&cam_desc.in_use, 1)) {
+ dev_err(&dev->dev, "%s OCCUPIED!\n", __func__);
+ return -EBUSY;
+ }
+
+ cam_desc.dev = &dev->dev;
+ if (dev->dev.platform_data) {
+ cam_desc.pdata = dev->dev.platform_data;
+ } else {
+ cam_desc.pdata = &camera_dflt_pdata;
+ dev_dbg(cam_desc.dev, "%s No platform data. Using defaults.\n",
+ __func__);
+ }
+ dev_dbg(&dev->dev, "%x\n", cam_desc.pdata->cfg);
+
+ strcpy(cam_desc.dname, "camera.pcl");
+ dev_set_drvdata(&dev->dev, &cam_desc);
+
+ cam_desc.miscdev.name = cam_desc.dname;
+ cam_desc.miscdev.fops = &camera_fileops;
+ cam_desc.miscdev.minor = MISC_DYNAMIC_MINOR;
+ if (misc_register(&cam_desc.miscdev)) {
+ dev_err(cam_desc.dev, "%s unable to register misc device %s\n",
+ __func__, cam_desc.dname);
+ return -ENODEV;
+ }
+
+ camera_debugfs_init(&cam_desc);
+ return 0;
+}
+
+static const struct platform_device_id camera_id[] = {
+ { "pcl-generic", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, camera_id);
+
+static struct platform_driver camera_driver = {
+ .driver = {
+ .name = "pcl-generic",
+ .owner = THIS_MODULE,
+ },
+ .id_table = camera_id,
+ .probe = camera_probe,
+ .remove = camera_remove,
+};
+
+module_platform_driver(camera_driver);
+
+MODULE_DESCRIPTION("Generic Camera Device Driver");
+MODULE_AUTHOR("Charlie Huang <chahuang@nvidia.com>");
+MODULE_LICENSE("GPL v2");