new file mode 100644
@@ -0,0 +1,421 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bug.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <plat/gpio-cfg.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-of.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-is.h"
+
+int fimc_is_debug;
+module_param(fimc_is_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fimc_is_debug, "Debug level for exynos-fimc-is driver");
+
+static char *fimc_is_clock_name[] = {
+ [IS_CLK_GATE0] = "fimc-is0",
+ [IS_CLK_GATE1] = "fimc-is1",
+ [IS_CLK_266] = "aclk_266_isp",
+ [IS_CLK_266_DIV0] = "aclk_266_isp_div0",
+ [IS_CLK_266_DIV1] = "aclk_266_isp_div1",
+ [IS_CLK_266_DIV_MPWM] = "aclk_266_isp_divmpwm",
+ [IS_CLK_400] = "aclk_400_isp",
+ [IS_CLK_400_DIV0] = "aclk_400_isp_div0",
+ [IS_CLK_400_DIV1] = "aclk_400_isp_div1",
+};
+
+static int fimc_is_create_sensor_subdevs(struct fimc_is *is)
+{
+ struct fimc_is_platdata *pdata = is->pdata;
+ int ret;
+
+ if (pdata->sensor_data[SENSOR_CAM0].enabled) {
+ /* Sensor0 */
+ ret = fimc_is_sensor_subdev_create(&is->sensor[SENSOR_CAM0],
+ &pdata->sensor_data[SENSOR_CAM0],
+ &is->pipeline);
+ if (ret < 0)
+ is_err("Error creating sensor0 subdev");
+ }
+
+ if (pdata->sensor_data[SENSOR_CAM1].enabled) {
+ /* Sensor1 */
+ ret = fimc_is_sensor_subdev_create(&is->sensor[SENSOR_CAM1],
+ &pdata->sensor_data[SENSOR_CAM1],
+ &is->pipeline);
+ if (ret < 0)
+ is_err("Error creating sensor1 subdev");
+ }
+
+ return 0;
+}
+
+static int fimc_is_unregister_sensor_subdevs(struct fimc_is *is)
+{
+ struct fimc_is_platdata *pdata = is->pdata;
+
+ if (pdata->sensor_data[SENSOR_CAM0].enabled)
+ fimc_is_sensor_subdev_destroy(&is->sensor[SENSOR_CAM0]);
+ if (pdata->sensor_data[SENSOR_CAM1].enabled)
+ fimc_is_sensor_subdev_destroy(&is->sensor[SENSOR_CAM1]);
+
+ return 0;
+}
+
+static struct fimc_is_platdata *fimc_is_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *child, *ep, *port;
+ struct fimc_is_platdata *pdata;
+ struct fimc_is_sensor_data *sensor_data;
+ struct v4l2_of_endpoint endpoint;
+ unsigned int i, snum;
+ int num_gpios;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ is_err("Memory allocation for pdata failed\n");
+ return NULL;
+ }
+
+ /* Parse sensor properties */
+ for (snum = 0; snum < FIMC_IS_NUM_SENSORS; snum++) {
+
+ child = of_get_next_child(np, child);
+ if (!child) {
+ is_err("Child node not found\n");
+ continue;
+ }
+
+ sensor_data = &pdata->sensor_data[snum];
+ num_gpios = of_gpio_named_count(child, "gpios");
+ if (num_gpios < 0) {
+ is_err("Sensor gpio property not found\n");
+ of_node_put(child);
+ goto exit;
+ }
+ sensor_data->num_gpios = num_gpios;
+ of_property_read_string(child, "compatible",
+ (const char **)&sensor_data->name);
+ is_dbg(1, "Rear sensor name : %s\n", sensor_data->name);
+ for (i = 0; i < num_gpios; i++) {
+ sensor_data->gpios[i] =
+ of_get_named_gpio(child, "gpios", i);
+ }
+ sensor_data->enabled = 1;
+
+ /* Parse ports */
+ ep = child;
+ while ((ep = of_get_next_child(ep, NULL))) {
+ if (!of_node_cmp(ep->name, "endpoint"))
+ break;
+ of_node_put(ep);
+ };
+ if (!ep) {
+ is_err("Sensor end point not defined\n");
+ continue;
+ }
+ port = of_parse_phandle(ep, "remote-endpoint", 0);
+ if (port) {
+ v4l2_of_parse_endpoint(port, &endpoint);
+ sensor_data->csi_id = endpoint.port;
+ }
+
+ of_node_put(port);
+ of_node_put(ep);
+ of_node_put(child);
+ }
+exit:
+ return pdata;
+}
+
+static void fimc_is_clk_put(struct fimc_is *is)
+{
+ int i;
+
+ for (i = 0; i < IS_CLK_MAX_NUM; i++) {
+ if (IS_ERR_OR_NULL(is->clock[i]))
+ continue;
+ clk_unprepare(is->clock[i]);
+ clk_put(is->clock[i]);
+ is->clock[i] = NULL;
+ }
+}
+
+static int fimc_is_clk_get(struct fimc_is *is)
+{
+ struct device *dev = &is->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < IS_CLK_MAX_NUM; i++) {
+ is->clock[i] = clk_get(dev, fimc_is_clock_name[i]);
+ if (IS_ERR(is->clock[i]))
+ goto err;
+ ret = clk_prepare(is->clock[i]);
+ if (ret < 0) {
+ clk_put(is->clock[i]);
+ is->clock[i] = NULL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ fimc_is_clk_put(is);
+ is_err("Failed to get clock: %s\n", fimc_is_clock_name[i]);
+ return -ENXIO;
+}
+
+static int fimc_is_clk_cfg(struct fimc_is *is)
+{
+ int ret;
+
+ ret = fimc_is_clk_get(is);
+ if (ret)
+ return ret;
+
+ /* Set rates */
+ ret = clk_set_rate(is->clock[IS_CLK_400_DIV0], 200 * 1000000);
+ ret |= clk_set_rate(is->clock[IS_CLK_400_DIV1], 100 * 1000000);
+ ret |= clk_set_rate(is->clock[IS_CLK_266_DIV0], 134 * 1000000);
+ ret |= clk_set_rate(is->clock[IS_CLK_266_DIV1], 68 * 1000000);
+ ret |= clk_set_rate(is->clock[IS_CLK_266_DIV_MPWM], 34 * 1000000);
+
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __devinit fimc_is_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimc_is_platdata *pdata;
+ struct resource *res;
+ struct fimc_is *is;
+ struct pinctrl *pctrl;
+ void __iomem *regs;
+ int irq, ret;
+
+ pr_debug("FIMC-IS Probe Enter\n");
+
+ pctrl = devm_pinctrl_get_select_default(dev);
+ if (IS_ERR(pctrl)) {
+ dev_err(dev, "Pinctrl configuration failed\n");
+ return -EINVAL;
+ }
+
+ if (!pdev->dev.of_node) {
+ dev_err(dev, "Null platform data\n");
+ return -EINVAL;
+ }
+
+ pdata = fimc_is_parse_dt(dev);
+ if (!pdata) {
+ dev_err(dev, "Parse DT failed\n");
+ return -EINVAL;
+ }
+
+ is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL);
+ if (!is)
+ return -ENOMEM;
+
+ is->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_request_and_ioremap(dev, res);
+ if (regs == NULL) {
+ dev_err(dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "Failed to get IRQ\n");
+ return irq;
+ }
+
+ ret = fimc_is_clk_cfg(is);
+ if (ret < 0) {
+ dev_err(dev, "Clock config failed\n");
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, is);
+ pm_runtime_enable(dev);
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err_clk;
+
+ is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(is->alloc_ctx)) {
+ ret = PTR_ERR(is->alloc_ctx);
+ goto err_pm;
+ }
+
+ /* Create sensor subdevs */
+ is->pdata = pdata;
+ ret = fimc_is_create_sensor_subdevs(is);
+ if (ret < 0)
+ goto err_sensor_sd;
+
+ /* Init FIMC Pipeline */
+ ret = fimc_is_pipeline_init(&is->pipeline, 0, is);
+ if (ret < 0)
+ goto err_sd;
+
+ /* Init FIMC Interface */
+ ret = fimc_is_interface_init(&is->interface, regs, irq);
+ if (ret < 0)
+ goto err_sd;
+
+ dev_dbg(dev, "FIMC-IS registered successfully\n");
+
+ return 0;
+
+err_sd:
+ fimc_is_pipeline_destroy(&is->pipeline);
+err_sensor_sd:
+ fimc_is_unregister_sensor_subdevs(is);
+err_vb:
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+err_pm:
+ pm_runtime_put(dev);
+err_clk:
+ fimc_is_clk_put(is);
+
+ return ret;
+}
+
+int fimc_is_clk_enable(struct fimc_is *is)
+{
+ clk_enable(is->clock[IS_CLK_GATE0]);
+ clk_enable(is->clock[IS_CLK_GATE1]);
+ return 0;
+}
+
+void fimc_is_clk_disable(struct fimc_is *is)
+{
+ clk_disable(is->clock[IS_CLK_GATE0]);
+ clk_disable(is->clock[IS_CLK_GATE1]);
+}
+
+static int fimc_is_pm_resume(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+ int ret;
+
+ ret = fimc_is_clk_enable(is);
+ if (ret < 0)
+ dev_err(dev, "Could not enable clocks\n");
+
+ return 0;
+}
+
+static int fimc_is_pm_suspend(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+
+ fimc_is_clk_disable(is);
+ return 0;
+}
+
+static int fimc_is_runtime_resume(struct device *dev)
+{
+ return fimc_is_pm_resume(dev);
+}
+
+static int fimc_is_runtime_suspend(struct device *dev)
+{
+ return fimc_is_pm_suspend(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_resume(struct device *dev)
+{
+ return fimc_is_pm_resume(dev);
+}
+
+static int fimc_is_suspend(struct device *dev)
+{
+ return fimc_is_pm_suspend(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int fimc_is_remove(struct platform_device *pdev)
+{
+ struct fimc_is *is = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ fimc_is_pipeline_destroy(&is->pipeline);
+ fimc_is_unregister_sensor_subdevs(is);
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+ fimc_is_clk_put(is);
+
+ return 0;
+}
+
+static struct platform_device_id fimc_is_driver_ids[] = {
+ {
+ .name = "exynos5-fimc-is",
+ .driver_data = 0,
+ },
+};
+MODULE_DEVICE_TABLE(platform, fimc_is_driver_ids);
+
+static const struct dev_pm_ops fimc_is_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume)
+ SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver fimc_is_driver = {
+ .probe = fimc_is_probe,
+ .remove = fimc_is_remove,
+ .id_table = fimc_is_driver_ids,
+ .driver = {
+ .name = FIMC_IS_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_is_pm_ops,
+ }
+};
+module_platform_driver(fimc_is_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arun Kumar K <arun.kk@samsung.com>");
+MODULE_DESCRIPTION("Samsung Exynos5 (FIMC-IS) Imaging Subsystem driver");
new file mode 100644
@@ -0,0 +1,140 @@
+/*
+ * Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_IS_CORE_H_
+#define FIMC_IS_CORE_H_
+
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <asm/barrier.h>
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5p_fimc.h>
+
+#define FIMC_IS_DRV_NAME "exynos5-fimc-is"
+#define FIMC_IS_CLK_NAME "fimc_is"
+
+#define FIMC_IS_DEBUG_MSG 0x3f
+#define FIMC_IS_DEBUG_LEVEL 3
+
+#define FIMC_IS_COMMAND_TIMEOUT (3*HZ)
+#define FIMC_IS_STARTUP_TIMEOUT (3*HZ)
+#define FIMC_IS_SHUTDOWN_TIMEOUT (10*HZ)
+
+#define FW_SHARED_OFFSET (0x8C0000)
+#define DEBUG_CNT (500*1024)
+#define DEBUG_OFFSET (0x840000)
+#define DEBUGCTL_OFFSET (0x8BD000)
+#define DEBUG_FCOUNT (0x8C64C0)
+
+#define FIMC_IS_MAX_INSTANCES 1
+
+#define FIMC_IS_MAX_GPIOS 32
+#define FIMC_IS_NUM_SENSORS 2
+
+#define FIMC_IS_MAX_PLANES 3
+#define FIMC_IS_NUM_SCALERS 2
+
+enum fimc_is_clks {
+ IS_CLK_GATE0,
+ IS_CLK_GATE1,
+ IS_CLK_266,
+ IS_CLK_266_DIV0,
+ IS_CLK_266_DIV1,
+ IS_CLK_266_DIV_MPWM,
+ IS_CLK_400,
+ IS_CLK_400_DIV0,
+ IS_CLK_400_DIV1,
+ IS_CLK_MAX_NUM,
+};
+
+/* Video capture states */
+enum fimc_is_video_state {
+ STATE_INIT,
+ STATE_BUFS_ALLOCATED,
+ STATE_RUNNING,
+};
+
+enum fimc_is_scaler_id {
+ SCALER_SCC,
+ SCALER_SCP
+};
+
+enum fimc_is_sensor_pos {
+ SENSOR_CAM0,
+ SENSOR_CAM1
+};
+
+struct fimc_is_buf {
+ struct list_head list;
+ struct vb2_buffer *vb;
+ unsigned int paddr[FIMC_IS_MAX_PLANES];
+};
+
+struct fimc_is_meminfo {
+ unsigned int fw_paddr;
+ unsigned int fw_vaddr;
+ unsigned int region_paddr;
+ unsigned int region_vaddr;
+ unsigned int shared_paddr;
+ unsigned int shared_vaddr;
+};
+
+/**
+ * struct fimc_is_fmt - the driver's internal color format data
+ * @name: format description
+ * @fourcc: the fourcc code for this format
+ * @depth: number of bytes per pixel
+ * @num_planes: number of planes for this color format
+ */
+struct fimc_is_fmt {
+ char *name;
+ unsigned int fourcc;
+ unsigned int depth[FIMC_IS_MAX_PLANES];
+ unsigned int num_planes;
+};
+
+struct fimc_is_sensor_data {
+ int enabled;
+ char *name;
+ unsigned int num_gpios;
+ int gpios[FIMC_IS_MAX_GPIOS];
+ unsigned int csi_id;
+};
+
+struct fimc_is_platdata {
+ struct fimc_is_sensor_data sensor_data[FIMC_IS_NUM_SENSORS];
+};
+
+#endif