aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/samsung/fimc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/samsung/fimc')
-rw-r--r--drivers/media/video/samsung/fimc/Kconfig32
-rw-r--r--drivers/media/video/samsung/fimc/Makefile12
-rw-r--r--drivers/media/video/samsung/fimc/csis.c431
-rw-r--r--drivers/media/video/samsung/fimc/csis.h42
-rw-r--r--drivers/media/video/samsung/fimc/fimc.h707
-rw-r--r--drivers/media/video/samsung/fimc/fimc_capture.c1733
-rw-r--r--drivers/media/video/samsung/fimc/fimc_dev.c1688
-rw-r--r--drivers/media/video/samsung/fimc/fimc_output.c2563
-rw-r--r--drivers/media/video/samsung/fimc/fimc_overlay.c312
-rw-r--r--drivers/media/video/samsung/fimc/fimc_regs.c1801
-rw-r--r--drivers/media/video/samsung/fimc/fimc_v4l2.c296
11 files changed, 9617 insertions, 0 deletions
diff --git a/drivers/media/video/samsung/fimc/Kconfig b/drivers/media/video/samsung/fimc/Kconfig
new file mode 100644
index 0000000..c9eae69
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/Kconfig
@@ -0,0 +1,32 @@
+config VIDEO_FIMC
+ bool "Samsung Camera Interface (FIMC) driver"
+ depends on VIDEO_SAMSUNG && ARCH_S5PV210
+ default n
+ help
+ This is a video4linux driver for Samsung FIMC device.
+
+choice
+depends on VIDEO_FIMC
+prompt "Select CSC Range config"
+default VIDEO_FIMC_RANGE_NARROW
+config VIDEO_FIMC_RANGE_NARROW
+ bool "Narrow"
+ depends on VIDEO_FIMC && ARCH_S5PV210
+ ---help---
+ RGB <-> YUV Color Conversion Narrow Range Equation
+
+config VIDEO_FIMC_RANGE_WIDE
+ bool "Wide"
+ depends on VIDEO_FIMC && ARCH_S5PV210
+ ---help---
+ RGB <-> YUV Color Conversion Wide Range Equation
+endchoice
+
+config VIDEO_FIMC_DEBUG
+ bool "FIMC driver debug messages"
+ depends on VIDEO_FIMC
+
+config VIDEO_FIMC_MIPI
+ bool "MIPI-CSI2 Slave Interface support"
+ depends on VIDEO_FIMC && ARCH_S5PV210
+ default y
diff --git a/drivers/media/video/samsung/fimc/Makefile b/drivers/media/video/samsung/fimc/Makefile
new file mode 100644
index 0000000..4f6e5c9
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_VIDEO_FIMC) += fimc_dev.o fimc_v4l2.o fimc_capture.o fimc_output.o fimc_overlay.o fimc_regs.o
+obj-$(CONFIG_VIDEO_FIMC_MIPI) += csis.o
+
+ifeq ($(CONFIG_CPU_S5PV210),y)
+EXTRA_CFLAGS += -DCONFIG_MIPI_CSI_ADV_FEATURE
+endif
+
+EXTRA_CFLAGS += -Idrivers/media/video
+
+ifeq ($(CONFIG_VIDEO_FIMC_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/media/video/samsung/fimc/csis.c b/drivers/media/video/samsung/fimc/csis.c
new file mode 100644
index 0000000..f512d10
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/csis.c
@@ -0,0 +1,431 @@
+/* linux/drivers/media/video/samsung/csis.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co,. Ltd.
+ * http://www.samsung.com/
+ *
+ * MIPI-CSI2 Support file for FIMC driver
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <linux/memory.h>
+#include <plat/clock.h>
+#include <plat/regs-csis.h>
+#include <plat/csis.h>
+#include "csis.h"
+
+static struct s3c_csis_info *s3c_csis;
+
+static struct s3c_platform_csis *to_csis_plat(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return (struct s3c_platform_csis *) pdev->dev.platform_data;
+}
+
+static int s3c_csis_set_info(void)
+{
+ s3c_csis = (struct s3c_csis_info *) \
+ kmalloc(sizeof(struct s3c_csis_info), GFP_KERNEL);
+ if (!s3c_csis) {
+ err("no memory for configuration\n");
+ return -ENOMEM;
+ }
+
+ strcpy(s3c_csis->name, S3C_CSIS_NAME);
+ s3c_csis->nr_lanes = S3C_CSIS_NR_LANES;
+
+ return 0;
+}
+
+static void s3c_csis_reset(void)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg |= S3C_CSIS_CONTROL_RESET;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_set_nr_lanes(int lanes)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONFIG);
+ cfg &= ~S3C_CSIS_CONFIG_NR_LANE_MASK;
+
+ if (lanes == 1)
+ cfg |= S3C_CSIS_CONFIG_NR_LANE_1;
+ else
+ cfg |= S3C_CSIS_CONFIG_NR_LANE_2;
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONFIG);
+}
+
+static void s3c_csis_enable_interrupt(void)
+{
+ u32 cfg = 0;
+
+ /* enable all interrupts */
+ cfg |= S3C_CSIS_INTMSK_EVEN_BEFORE_ENABLE | \
+ S3C_CSIS_INTMSK_EVEN_AFTER_ENABLE | \
+ S3C_CSIS_INTMSK_ODD_BEFORE_ENABLE | \
+ S3C_CSIS_INTMSK_ODD_AFTER_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_SOT_HS_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_ESC_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_CTRL_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_ECC_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_CRC_ENABLE | \
+ S3C_CSIS_INTMSK_ERR_ID_ENABLE;
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_INTMSK);
+}
+
+static void s3c_csis_disable_interrupt(void)
+{
+ /* disable all interrupts */
+ writel(0, s3c_csis->regs + S3C_CSIS_INTMSK);
+}
+
+static void s3c_csis_system_on(void)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg |= S3C_CSIS_CONTROL_ENABLE;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_system_off(void)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg &= ~S3C_CSIS_CONTROL_ENABLE;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_phy_on(void)
+{
+ u32 cfg;
+ cfg = readl(s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+ cfg |= S3C_CSIS_DPHYCTRL_ENABLE;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+}
+
+static void s3c_csis_phy_off(void)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+ cfg &= ~S3C_CSIS_DPHYCTRL_ENABLE;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+}
+
+#ifdef CONFIG_MIPI_CSI_ADV_FEATURE
+static void s3c_csis_update_shadow(void)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg |= S3C_CSIS_CONTROL_UPDATE_SHADOW;
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_set_data_align(int align)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg &= ~S3C_CSIS_CONTROL_ALIGN_MASK;
+
+ if (align == 24)
+ cfg |= S3C_CSIS_CONTROL_ALIGN_24BIT;
+ else
+ cfg |= S3C_CSIS_CONTROL_ALIGN_32BIT;
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_set_wclk(int extclk)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONTROL);
+ cfg &= ~S3C_CSIS_CONTROL_WCLK_MASK;
+
+ if (extclk)
+ cfg |= S3C_CSIS_CONTROL_WCLK_EXTCLK;
+ else
+ cfg |= S3C_CSIS_CONTROL_WCLK_PCLK;
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONTROL);
+}
+
+static void s3c_csis_set_format(enum mipi_format fmt)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_CONFIG);
+ cfg &= ~S3C_CSIS_CONFIG_FORMAT_MASK;
+ cfg |= (fmt << S3C_CSIS_CONFIG_FORMAT_SHIFT);
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_CONFIG);
+}
+
+static void s3c_csis_set_resol(int width, int height)
+{
+ u32 cfg = 0;
+
+ cfg |= width << S3C_CSIS_RESOL_HOR_SHIFT;
+ cfg |= height << S3C_CSIS_RESOL_VER_SHIFT;
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_RESOL);
+}
+
+static void s3c_csis_set_hs_settle(int settle)
+{
+ u32 cfg;
+
+ cfg = readl(s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+ cfg &= ~S3C_CSIS_DPHYCTRL_HS_SETTLE_MASK;
+ cfg |= (settle << S3C_CSIS_DPHYCTRL_HS_SETTLE_SHIFT);
+
+ writel(cfg, s3c_csis->regs + S3C_CSIS_DPHYCTRL);
+}
+#endif
+
+void s3c_csis_start(int lanes, int settle, int align, int width,
+ int height, int pixel_format)
+{
+ struct platform_device *pdev = to_platform_device(s3c_csis->dev);
+ struct s3c_platform_csis *pdata;
+
+ pdata = to_csis_plat(s3c_csis->dev);
+ if (pdata->cfg_phy_global)
+ pdata->cfg_phy_global(pdev, 1);
+
+ s3c_csis_reset();
+ s3c_csis_set_nr_lanes(lanes);
+
+#ifdef CONFIG_MIPI_CSI_ADV_FEATURE
+ /* FIXME: how configure the followings with FIMC dynamically? */
+ s3c_csis_set_hs_settle(settle); /* s5k6aa */
+ s3c_csis_set_data_align(align);
+ s3c_csis_set_wclk(0);
+ if (pixel_format == V4L2_PIX_FMT_JPEG)
+ s3c_csis_set_format(MIPI_USER_DEF_PACKET_1);
+ else
+ s3c_csis_set_format(MIPI_CSI_YCBCR422_8BIT);
+ s3c_csis_set_resol(width, height);
+ s3c_csis_update_shadow();
+#endif
+
+ s3c_csis_enable_interrupt();
+ s3c_csis_system_on();
+ s3c_csis_phy_on();
+
+ info("Samsung MIPI-CSI2 operation started\n");
+}
+
+static void s3c_csis_stop(struct platform_device *pdev)
+{
+ struct s3c_platform_csis *plat;
+
+ s3c_csis_disable_interrupt();
+ s3c_csis_system_off();
+ s3c_csis_phy_off();
+
+ plat = to_csis_plat(&pdev->dev);
+ if (plat->cfg_phy_global)
+ plat->cfg_phy_global(pdev, 0);
+}
+
+static irqreturn_t s3c_csis_irq(int irq, void *dev_id)
+{
+ u32 cfg;
+
+ /* just clearing the pends */
+ cfg = readl(s3c_csis->regs + S3C_CSIS_INTSRC);
+ writel(cfg, s3c_csis->regs + S3C_CSIS_INTSRC);
+
+ return IRQ_HANDLED;
+}
+
+static int s3c_csis_clk_on(struct platform_device *pdev)
+{
+ struct s3c_platform_csis *pdata;
+ struct clk *parent, *mout_csis;
+
+ pdata = to_csis_plat(&pdev->dev);
+
+ /* mout_mpll */
+ parent = clk_get(&pdev->dev, pdata->srclk_name);
+ if (IS_ERR(parent)) {
+ err("failed to get parent clock for csis\n");
+ return -EINVAL;
+ }
+
+ /* mout_csis */
+ mout_csis = clk_get(&pdev->dev, "mout_csis");
+
+ /* sclk_csis */
+ s3c_csis->clock = clk_get(&pdev->dev, pdata->clk_name);
+ if (IS_ERR(s3c_csis->clock)) {
+ err("failed to get csis clock source\n");
+ return -EINVAL;
+ }
+
+ clk_set_parent(mout_csis, parent);
+ clk_set_parent(s3c_csis->clock, mout_csis);
+
+ /* Turn on csis power domain regulator */
+ regulator_enable(s3c_csis->regulator);
+ /* clock enable for csis */
+ clk_enable(s3c_csis->clock);
+
+ return 0;
+}
+
+static int s3c_csis_clk_off(struct platform_device *pdev)
+{
+ struct s3c_platform_csis *plat;
+
+ plat = to_csis_plat(&pdev->dev);
+
+ /* sclk_csis */
+ s3c_csis->clock = clk_get(&pdev->dev, plat->clk_name);
+ if (IS_ERR(s3c_csis->clock)) {
+ err("failed to get csis clock source\n");
+ return -EINVAL;
+ }
+
+ /* clock disable for csis */
+ clk_disable(s3c_csis->clock);
+ /* Turn off csis power domain regulator */
+ regulator_disable(s3c_csis->regulator);
+
+ return 0;
+}
+
+static int s3c_csis_probe(struct platform_device *pdev)
+{
+ struct s3c_platform_csis *pdata;
+ struct resource *res;
+
+ s3c_csis_set_info();
+
+ s3c_csis->dev = &pdev->dev;
+
+ pdata = to_csis_plat(&pdev->dev);
+ if (pdata->cfg_gpio)
+ pdata->cfg_gpio();
+
+ /* Get csis power domain regulator */
+ s3c_csis->regulator = regulator_get(&pdev->dev, "pd");
+ if (IS_ERR(s3c_csis->regulator)) {
+ err("%s: failed to get resource %s\n",
+ __func__, "s3c-csis");
+ return PTR_ERR(s3c_csis->regulator);
+ }
+ /* clock & power on */
+ s3c_csis_clk_on(pdev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err("failed to get io memory region\n");
+ return -EINVAL;
+ }
+
+ res = request_mem_region(res->start,
+ res->end - res->start + 1, pdev->name);
+ if (!res) {
+ err("failed to request io memory region\n");
+ return -EINVAL;
+ }
+
+ /* ioremap for register block */
+ s3c_csis->regs = ioremap(res->start, res->end - res->start + 1);
+ if (!s3c_csis->regs) {
+ err("failed to remap io region\n");
+ return -EINVAL;
+ }
+
+ /* irq */
+ s3c_csis->irq = platform_get_irq(pdev, 0);
+ if (request_irq(s3c_csis->irq, s3c_csis_irq, IRQF_DISABLED, \
+ s3c_csis->name, s3c_csis))
+ err("request_irq failed\n");
+
+ info("Samsung MIPI-CSI2 driver probed successfully\n");
+
+ return 0;
+}
+
+static int s3c_csis_remove(struct platform_device *pdev)
+{
+ s3c_csis_stop(pdev);
+ kfree(s3c_csis);
+
+ return 0;
+}
+
+/* sleep */
+int s3c_csis_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ s3c_csis_clk_off(pdev);
+ return 0;
+}
+
+/* wakeup */
+int s3c_csis_resume(struct platform_device *pdev)
+{
+ s3c_csis_clk_on(pdev);
+ return 0;
+}
+
+static struct platform_driver s3c_csis_driver = {
+ .probe = s3c_csis_probe,
+ .remove = s3c_csis_remove,
+ .suspend = s3c_csis_suspend,
+ .resume = s3c_csis_resume,
+ .driver = {
+ .name = "s5p-mipi-csis",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int s3c_csis_register(void)
+{
+ platform_driver_register(&s3c_csis_driver);
+
+ return 0;
+}
+
+static void s3c_csis_unregister(void)
+{
+ platform_driver_unregister(&s3c_csis_driver);
+}
+
+module_init(s3c_csis_register);
+module_exit(s3c_csis_unregister);
+
+MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@samsung.com>");
+MODULE_AUTHOR("Sewoon, Park <seuni.park@samsung.com>");
+MODULE_DESCRIPTION("MIPI-CSI2 support for FIMC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/samsung/fimc/csis.h b/drivers/media/video/samsung/fimc/csis.h
new file mode 100644
index 0000000..4943c8f
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/csis.h
@@ -0,0 +1,42 @@
+/* linux/drivers/media/video/samsung/csis.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co,. Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for Samsung MIPI-CSI2 driver
+ *
+ * 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 __CSIS_H
+#define __CSIS_H __FILE__
+
+#define S3C_CSIS_NAME "s5p-mipi-csis"
+#define S3C_CSIS_NR_LANES 1
+
+#define info(args...) \
+ do { printk(KERN_INFO S3C_CSIS_NAME ": " args); } while (0)
+#define err(args...) \
+ do { printk(KERN_ERR S3C_CSIS_NAME ": " args); } while (0)
+
+enum mipi_format {
+ MIPI_CSI_YCBCR422_8BIT = 0x1e,
+ MIPI_CSI_RAW8 = 0x2a,
+ MIPI_CSI_RAW10 = 0x2b,
+ MIPI_CSI_RAW12 = 0x2c,
+ MIPI_USER_DEF_PACKET_1 = 0x30, /* User defined Byte-based packet 1 */
+};
+
+struct s3c_csis_info {
+ char name[16];
+ struct device *dev;
+ struct clk *clock;
+ struct regulator *regulator;
+ void __iomem *regs;
+ int irq;
+ int nr_lanes;
+};
+
+#endif /* __CSIS_H */
diff --git a/drivers/media/video/samsung/fimc/fimc.h b/drivers/media/video/samsung/fimc/fimc.h
new file mode 100644
index 0000000..de137c2
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc.h
@@ -0,0 +1,707 @@
+/* linux/drivers/media/video/samsung/fimc/fimc.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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_H
+#define __FIMC_H __FILE__
+
+#ifdef __KERNEL__
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-core.h>
+#include <plat/media.h>
+#include <plat/fimc.h>
+#endif
+
+#define FIMC_NAME "s3c-fimc"
+
+#define FIMC_DEVICES 3
+#define FIMC_SUBDEVS 3
+#define FIMC_MAXCAMS 5 /* added 1 because of WriteBack */
+#define FIMC_PHYBUFS 4
+#define FIMC_OUTBUFS 3
+#define FIMC_INQUEUES 10
+#define FIMC_MAX_CTXS 1
+#define FIMC_TPID 3
+#define FIMC_CAPBUFS 16
+#define FIMC_ONESHOT_TIMEOUT 200
+#define FIMC_DQUEUE_TIMEOUT 200
+#define FIMC_FIFOOFF_CNT 1000000 /* Sufficiently big value for stop */
+
+#define FORMAT_FLAGS_PACKED 0x1
+#define FORMAT_FLAGS_PLANAR 0x2
+#define FORMAT_FLAGS_ENCODED 0x3
+
+#define FIMC_ADDR_Y 0
+#define FIMC_ADDR_CB 1
+#define FIMC_ADDR_CR 2
+
+#define FIMC_HD_WIDTH 1280
+#define FIMC_HD_HEIGHT 720
+
+#define FIMC_FHD_WIDTH 1920
+#define FIMC_FHD_HEIGHT 1080
+
+#define FIMC_MMAP_IDX -1
+#define FIMC_USERPTR_IDX -2
+
+#define FIMC_HCLK 0
+#define FIMC_SCLK 1
+#define FIMC_OVLY_MODE FIMC_OVLY_DMA_AUTO
+
+#define FIMC_PINGPONG 2
+
+/*
+ * ENUMERATIONS
+*/
+enum fimc_status {
+ FIMC_READY_OFF = 0x00,
+ FIMC_STREAMOFF = 0x01,
+ FIMC_READY_ON = 0x02,
+ FIMC_STREAMON = 0x03,
+ FIMC_STREAMON_IDLE = 0x04, /* oneshot mode */
+ FIMC_OFF_SLEEP = 0x05,
+ FIMC_ON_SLEEP = 0x06,
+ FIMC_ON_IDLE_SLEEP = 0x07, /* oneshot mode */
+ FIMC_READY_RESUME = 0x08,
+};
+
+enum fimc_fifo_state {
+ FIFO_CLOSE,
+ FIFO_SLEEP,
+};
+
+enum fimc_fimd_state {
+ FIMD_OFF,
+ FIMD_ON,
+};
+
+enum fimc_rot_flip {
+ FIMC_XFLIP = 0x01,
+ FIMC_YFLIP = 0x02,
+ FIMC_ROT = 0x10,
+};
+
+enum fimc_input {
+ FIMC_SRC_CAM,
+ FIMC_SRC_MSDMA,
+};
+
+enum fimc_overlay_mode {
+ /* Overlay mode isn't fixed. */
+ FIMC_OVLY_NOT_FIXED = 0x0,
+ /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_DMA_AUTO = 0x1,
+ /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_DMA_MANUAL = 0x2,
+ /* Destructive Overlay with DMA single destination buffer */
+ FIMC_OVLY_NONE_SINGLE_BUF = 0x3,
+ /* Destructive Overlay with DMA multiple dstination buffer */
+ FIMC_OVLY_NONE_MULTI_BUF = 0x4,
+};
+
+enum fimc_autoload {
+ FIMC_AUTO_LOAD,
+ FIMC_ONE_SHOT,
+};
+
+enum fimc_log {
+ FIMC_LOG_DEBUG = 0x1000,
+ FIMC_LOG_INFO_L2 = 0x0200,
+ FIMC_LOG_INFO_L1 = 0x0100,
+ FIMC_LOG_WARN = 0x0010,
+ FIMC_LOG_ERR = 0x0001,
+};
+
+enum fimc_pixel_format_type{
+ FIMC_RGB,
+ FIMC_YUV420,
+ FIMC_YUV422,
+ FIMC_YUV444,
+};
+
+/*
+ * STRUCTURES
+*/
+
+/* for reserved memory */
+struct fimc_meminfo {
+ dma_addr_t base; /* buffer base */
+ size_t size; /* total length */
+ dma_addr_t curr; /* current addr */
+};
+
+struct fimc_buf {
+ dma_addr_t base[3];
+ size_t length[3];
+};
+
+struct fimc_overlay_buf {
+ u32 vir_addr[3];
+ size_t size[3];
+ u32 phy_addr[3];
+};
+
+struct fimc_overlay {
+ enum fimc_overlay_mode mode;
+ struct fimc_overlay_buf buf;
+ s32 req_idx;
+ int fb_id;
+};
+
+/* general buffer */
+struct fimc_buf_set {
+ int id;
+ /* Plane 0/1/2 for raw data, Plane 3 for padding buffer (if required) */
+ dma_addr_t base[4];
+ size_t length[4];
+ size_t garbage[4];
+ enum videobuf_state state;
+ u32 flags;
+ atomic_t mapped_cnt;
+ struct list_head list;
+};
+
+/* for capture device */
+struct fimc_capinfo {
+ struct v4l2_cropcap cropcap;
+ struct v4l2_rect crop;
+ struct v4l2_pix_format fmt;
+ struct fimc_buf_set bufs[FIMC_CAPBUFS];
+ struct list_head inq;
+ int outq[FIMC_PHYBUFS];
+ int nr_bufs;
+ int irq;
+ int lastirq;
+
+ /* flip: V4L2_CID_xFLIP, rotate: 90, 180, 270 */
+ u32 flip;
+ u32 rotate;
+};
+
+/* for output overlay device */
+struct fimc_idx {
+ int ctx;
+ int idx;
+};
+
+struct fimc_ctx_idx {
+ struct fimc_idx prev;
+ struct fimc_idx active;
+ struct fimc_idx next;
+};
+
+/* scaler abstraction: local use recommended */
+struct fimc_scaler {
+ u32 bypass;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+ u32 shfactor;
+ u32 skipline;
+};
+
+struct fimc_ctx {
+ u32 ctx_num;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_rect crop;
+ struct v4l2_pix_format pix;
+ struct v4l2_window win;
+ struct v4l2_framebuffer fbuf;
+ struct fimc_scaler sc;
+ struct fimc_overlay overlay;
+
+ u32 buf_num;
+ u32 is_requested;
+ struct fimc_buf_set src[FIMC_OUTBUFS];
+ struct fimc_buf_set dst[FIMC_OUTBUFS];
+ s32 inq[FIMC_OUTBUFS];
+ s32 outq[FIMC_OUTBUFS];
+
+ u32 flip;
+ u32 rotate;
+ enum fimc_status status;
+};
+
+struct fimc_outinfo {
+ int last_ctx;
+ spinlock_t lock_in;
+ spinlock_t lock_out;
+ struct fimc_idx inq[FIMC_INQUEUES];
+ struct fimc_ctx ctx[FIMC_MAX_CTXS];
+ struct fimc_ctx_idx idxs;
+};
+
+struct s3cfb_user_window {
+ int x;
+ int y;
+};
+
+enum s3cfb_data_path_t {
+ DATA_PATH_FIFO = 0,
+ DATA_PATH_DMA = 1,
+ DATA_PATH_IPC = 2,
+};
+
+enum s3cfb_mem_owner_t {
+ DMA_MEM_NONE = 0,
+ DMA_MEM_FIMD = 1,
+ DMA_MEM_OTHER = 2,
+};
+
+enum s3cfb_alpha_t {
+ PLANE_BLENDING,
+ PIXEL_BLENDING,
+};
+
+enum s3cfb_chroma_dir_t {
+ CHROMA_FG,
+ CHROMA_BG,
+};
+
+struct s3cfb_alpha {
+ enum s3cfb_alpha_t mode;
+ int channel;
+ unsigned int value;
+};
+
+
+struct s3cfb_chroma {
+ int enabled;
+ int blended;
+ unsigned int key;
+ unsigned int comp_key;
+ unsigned int alpha;
+ enum s3cfb_chroma_dir_t dir;
+};
+
+struct s3cfb_window {
+ int id;
+ int enabled;
+ atomic_t in_use;
+ int x;
+ int y;
+ enum s3cfb_data_path_t path;
+ enum s3cfb_mem_owner_t owner;
+ unsigned int other_mem_addr;
+ unsigned int other_mem_size;
+ int local_channel;
+ int dma_burst;
+ unsigned int pseudo_pal[16];
+ struct s3cfb_alpha alpha;
+ struct s3cfb_chroma chroma;
+};
+
+#define S3CFB_WIN_OFF_ALL _IO('F', 202)
+#define S3CFB_WIN_POSITION _IOW('F', 203, struct s3cfb_user_window)
+#define S3CFB_GET_LCD_WIDTH _IOR('F', 302, int)
+#define S3CFB_GET_LCD_HEIGHT _IOR('F', 303, int)
+#define S3CFB_SET_WRITEBACK _IOW('F', 304, u32)
+#define S3CFB_SET_WIN_ON _IOW('F', 306, u32)
+#define S3CFB_SET_WIN_OFF _IOW('F', 307, u32)
+#define S3CFB_SET_WIN_PATH _IOW('F', 308, enum s3cfb_data_path_t)
+#define S3CFB_SET_WIN_ADDR _IOW('F', 309, unsigned long)
+#define S3CFB_SET_WIN_MEM _IOW('F', 310, enum s3cfb_mem_owner_t)
+/* ------------------------------------------------------------------------ */
+
+struct fimc_fbinfo {
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ int lcd_hres;
+ int lcd_vres;
+ u32 is_enable;
+};
+
+struct fimc_limit {
+ u32 pre_dst_w;
+ u32 bypass_w;
+ u32 trg_h_no_rot;
+ u32 trg_h_rot;
+ u32 real_w_no_rot;
+ u32 real_h_rot;
+};
+
+enum FIMC_EFFECT_FIN {
+ FIMC_EFFECT_FIN_BYPASS = 0,
+ FIMC_EFFECT_FIN_ARBITRARY_CBCR,
+ FIMC_EFFECT_FIN_NEGATIVE,
+ FIMC_EFFECT_FIN_ART_FREEZE,
+ FIMC_EFFECT_FIN_EMBOSSING,
+ FIMC_EFFECT_FIN_SILHOUETTE,
+};
+
+
+struct fimc_effect {
+ int ie_on;
+ int ie_after_sc;
+ enum FIMC_EFFECT_FIN fin;
+ int pat_cb;
+ int pat_cr;
+};
+
+/* fimc controller abstration */
+struct fimc_control {
+ int id; /* controller id */
+ char name[16];
+ atomic_t in_use;
+ void __iomem *regs; /* register i/o */
+ struct clk *clk; /* interface clock */
+ struct regulator *regulator; /* pd regulator */
+ struct fimc_meminfo mem; /* for reserved mem */
+
+ /* kernel helpers */
+ struct mutex lock; /* controller lock */
+ struct mutex alloc_lock;
+ struct mutex v4l2_lock;
+ wait_queue_head_t wq;
+ struct device *dev;
+ int irq;
+
+ /* v4l2 related */
+ struct video_device *vd;
+ struct v4l2_device v4l2_dev;
+
+ /* fimc specific */
+ struct fimc_limit *limit; /* H/W limitation */
+ struct s3c_platform_camera *cam; /* activated camera */
+ struct fimc_capinfo *cap; /* capture dev info */
+ struct fimc_outinfo *out; /* output dev info */
+ struct fimc_fbinfo fb; /* fimd info */
+ struct fimc_scaler sc; /* scaler info */
+ struct fimc_effect fe; /* fimc effect info */
+
+ enum fimc_status status;
+ enum fimc_log log;
+
+ u32 ctx_busy[FIMC_MAX_CTXS];
+};
+
+/* global */
+struct fimc_global {
+ struct fimc_control ctrl[FIMC_DEVICES];
+ struct s3c_platform_camera camera[FIMC_MAXCAMS];
+ int camera_isvalid[FIMC_MAXCAMS];
+ int active_camera;
+ int initialized;
+};
+
+struct fimc_prv_data {
+ struct fimc_control *ctrl;
+ int ctx_id;
+};
+
+/* debug macro */
+#define FIMC_LOG_DEFAULT (FIMC_LOG_WARN | FIMC_LOG_ERR)
+
+#define FIMC_DEBUG(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_DEBUG) \
+ printk(KERN_DEBUG FIMC_NAME "-%d : " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L2(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_INFO_L2) \
+ printk(KERN_INFO FIMC_NAME "-%d : " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L1(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_INFO_L1) \
+ printk(KERN_INFO FIMC_NAME "-%d : " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_WARN(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_WARN) \
+ printk(KERN_WARNING FIMC_NAME "-%d : " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define FIMC_ERROR(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_ERR) \
+ printk(KERN_ERR FIMC_NAME "-%d : " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define fimc_dbg(fmt, ...) FIMC_DEBUG(fmt, ##__VA_ARGS__)
+#define fimc_info2(fmt, ...) FIMC_INFO_L2(fmt, ##__VA_ARGS__)
+#define fimc_info1(fmt, ...) FIMC_INFO_L1(fmt, ##__VA_ARGS__)
+#define fimc_warn(fmt, ...) FIMC_WARN(fmt, ##__VA_ARGS__)
+#define fimc_err(fmt, ...) FIMC_ERROR(fmt, ##__VA_ARGS__)
+
+/*
+ * EXTERNS
+*/
+extern struct fimc_global *fimc_dev;
+extern struct video_device fimc_video_device[FIMC_DEVICES];
+extern const struct v4l2_ioctl_ops fimc_v4l2_ops;
+extern struct fimc_limit fimc40_limits[FIMC_DEVICES];
+extern struct fimc_limit fimc43_limits[FIMC_DEVICES];
+extern struct fimc_limit fimc50_limits[FIMC_DEVICES];
+
+/* general */
+extern void s3c_csis_start(int lanes, int settle, int align,
+ int width, int height,
+ int pixel_format);
+extern int fimc_dma_alloc(struct fimc_control *ctrl,
+ struct fimc_buf_set *bs,
+ int i, int align);
+extern void fimc_dma_free(struct fimc_control *ctrl,
+ struct fimc_buf_set *bs, int i);
+extern u32 fimc_mapping_rot_flip(u32 rot, u32 flip);
+extern int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift);
+extern void fimc_get_nv12t_size(int img_hres, int img_vres,
+ int *y_size, int *cb_size);
+extern void fimc_clk_en(struct fimc_control *ctrl, bool on);
+
+/* camera */
+extern int fimc_select_camera(struct fimc_control *ctrl);
+
+/* capture device */
+extern int fimc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp);
+extern int fimc_g_input(struct file *file, void *fh, unsigned int *i);
+extern int fimc_s_input(struct file *file, void *fh, unsigned int i);
+extern int fimc_enum_fmt_vid_capture(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f);
+extern int fimc_g_fmt_vid_capture(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int fimc_s_fmt_vid_capture(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int fimc_try_fmt_vid_capture(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int fimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b);
+extern int fimc_querybuf_capture(void *fh, struct v4l2_buffer *b);
+extern int fimc_g_ctrl_capture(void *fh, struct v4l2_control *c);
+extern int fimc_s_ctrl_capture(void *fh, struct v4l2_control *c);
+extern int fimc_s_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c);
+extern int fimc_cropcap_capture(void *fh, struct v4l2_cropcap *a);
+extern int fimc_g_crop_capture(void *fh, struct v4l2_crop *a);
+extern int fimc_s_crop_capture(void *fh, struct v4l2_crop *a);
+extern int fimc_streamon_capture(void *fh);
+extern int fimc_streamoff_capture(void *fh);
+extern int fimc_qbuf_capture(void *fh, struct v4l2_buffer *b);
+extern int fimc_dqbuf_capture(void *fh, struct v4l2_buffer *b);
+extern int fimc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a);
+extern int fimc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a);
+extern int fimc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *qc);
+extern int fimc_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *qm);
+
+#if defined(CONFIG_CPU_S5PV210)
+extern int fimc_change_clksrc(struct fimc_control *ctrl, int fimc_clk);
+#endif
+extern int fimc_release_subdev(struct fimc_control *ctrl);
+
+/* output device */
+extern void fimc_outdev_set_src_addr(struct fimc_control *ctrl,
+ dma_addr_t *base);
+extern int fimc_outdev_set_ctx_param(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx);
+extern int fimc_start_fifo(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx);
+extern int fimc_fimd_rect(const struct fimc_control *ctrl,
+ const struct fimc_ctx *ctx,
+ struct v4l2_rect *fimd_rect);
+extern int fimc_outdev_stop_streaming(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx);
+extern int fimc_outdev_resume_dma(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx);
+extern int fimc_outdev_start_camif(void *param);
+extern int fimc_reqbufs_output(void *fh, struct v4l2_requestbuffers *b);
+extern int fimc_querybuf_output(void *fh, struct v4l2_buffer *b);
+extern int fimc_g_ctrl_output(void *fh, struct v4l2_control *c);
+extern int fimc_s_ctrl_output(struct file *filp, void *fh,
+ struct v4l2_control *c);
+extern int fimc_cropcap_output(void *fh, struct v4l2_cropcap *a);
+extern int fimc_g_crop_output(void *fh, struct v4l2_crop *a);
+extern int fimc_s_crop_output(void *fh, struct v4l2_crop *a);
+extern int fimc_streamon_output(void *fh);
+extern int fimc_streamoff_output(void *fh);
+extern int fimc_qbuf_output(void *fh, struct v4l2_buffer *b);
+extern int fimc_dqbuf_output(void *fh, struct v4l2_buffer *b);
+extern int fimc_g_fmt_vid_out(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_s_fmt_vid_out(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_try_fmt_vid_out(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_output_set_dst_addr(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx, int idx);
+extern int fimc_init_in_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx);
+extern int fimc_push_inq(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx, int idx);
+extern int fimc_pop_inq(struct fimc_control *ctrl, int *ctx_num, int *idx);
+extern int fimc_push_outq(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx, int idx);
+extern int fimc_pop_outq(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx, int *idx);
+extern int fimc_init_out_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx);
+extern void fimc_outdev_init_idxs(struct fimc_control *ctrl);
+
+extern void fimc_dump_context(struct fimc_control *ctrl, struct fimc_ctx *ctx);
+extern void fimc_print_signal(struct fimc_control *ctrl);
+
+/* overlay device */
+extern int fimc_try_fmt_overlay(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_g_fmt_vid_overlay(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_s_fmt_vid_overlay(struct file *filp, void *fh,
+ struct v4l2_format *f);
+extern int fimc_g_fbuf(struct file *filp, void *fh,
+ struct v4l2_framebuffer *fb);
+extern int fimc_s_fbuf(struct file *filp, void *fh,
+ struct v4l2_framebuffer *fb);
+
+/* Register access file */
+extern void fimc_reset(struct fimc_control *ctrl);
+extern int fimc_hwset_camera_source(struct fimc_control *ctrl);
+extern int fimc_hwset_enable_irq(struct fimc_control *ctrl,
+ int overflow, int level);
+extern int fimc_hwset_disable_irq(struct fimc_control *ctrl);
+extern int fimc_hwset_clear_irq(struct fimc_control *ctrl);
+extern int fimc_hwset_reset(struct fimc_control *ctrl);
+extern int fimc_hwset_sw_reset(struct fimc_control *ctrl);
+extern int fimc_hwset_clksrc(struct fimc_control *ctrl, int src_clk);
+extern int fimc_hwget_overflow_state(struct fimc_control *ctrl);
+extern int fimc_hwset_camera_offset(struct fimc_control *ctrl);
+extern int fimc_hwset_camera_polarity(struct fimc_control *ctrl);
+extern int fimc_hwset_camera_type(struct fimc_control *ctrl);
+extern int fimc_hwset_output_size(struct fimc_control *ctrl,
+ int width, int height);
+extern int fimc_hwset_output_colorspace(struct fimc_control *ctrl,
+ u32 pixelformat);
+extern int fimc_hwset_output_rot_flip(struct fimc_control *ctrl,
+ u32 rot, u32 flip);
+extern int fimc_hwset_output_area(struct fimc_control *ctrl,
+ u32 width, u32 height);
+extern int fimc_hwset_output_area_size(struct fimc_control *ctrl, u32 size);
+extern int fimc_hwset_output_scan(struct fimc_control *ctrl,
+ struct v4l2_pix_format *fmt);
+extern int fimc_hwset_enable_lastirq(struct fimc_control *ctrl);
+extern int fimc_hwset_disable_lastirq(struct fimc_control *ctrl);
+extern int fimc_hwset_prescaler(struct fimc_control *ctrl,
+ struct fimc_scaler *sc);
+extern int fimc_hwset_output_yuv(struct fimc_control *ctrl, u32 pixelformat);
+extern int fimc_hwset_output_address(struct fimc_control *ctrl,
+ struct fimc_buf_set *bs,
+ int id);
+extern int fimc_hwset_input_rot(struct fimc_control *ctrl, u32 rot, u32 flip);
+extern int fimc_hwset_scaler(struct fimc_control *ctrl, struct fimc_scaler *sc);
+extern int fimc_hwset_scaler_bypass(struct fimc_control *ctrl);
+extern int fimc_hwset_enable_lcdfifo(struct fimc_control *ctrl);
+extern int fimc_hwset_disable_lcdfifo(struct fimc_control *ctrl);
+extern int fimc_hwset_start_scaler(struct fimc_control *ctrl);
+extern int fimc_hwset_stop_scaler(struct fimc_control *ctrl);
+extern int fimc_hwset_input_rgb(struct fimc_control *ctrl, u32 pixelformat);
+extern int fimc_hwset_intput_field(struct fimc_control *ctrl,
+ enum v4l2_field field);
+extern int fimc_hwset_output_rgb(struct fimc_control *ctrl, u32 pixelformat);
+extern int fimc_hwset_ext_rgb(struct fimc_control *ctrl, int enable);
+extern int fimc_hwset_enable_capture(struct fimc_control *ctrl,
+ u32 bypass);
+extern int fimc_hwset_disable_capture(struct fimc_control *ctrl);
+extern void fimc_wait_disable_capture(struct fimc_control *ctrl);
+extern int fimc_hwset_input_address(struct fimc_control *ctrl,
+ dma_addr_t *base);
+extern int fimc_hwset_enable_autoload(struct fimc_control *ctrl);
+extern int fimc_hwset_disable_autoload(struct fimc_control *ctrl);
+extern int fimc_hwset_real_input_size(struct fimc_control *ctrl,
+ u32 width, u32 height);
+extern int fimc_hwset_addr_change_enable(struct fimc_control *ctrl);
+extern int fimc_hwset_addr_change_disable(struct fimc_control *ctrl);
+extern int fimc_hwset_input_burst_cnt(struct fimc_control *ctrl, u32 cnt);
+extern int fimc_hwset_input_colorspace(struct fimc_control *ctrl,
+ u32 pixelformat);
+extern int fimc_hwset_input_yuv(struct fimc_control *ctrl, u32 pixelformat);
+extern int fimc_hwset_input_flip(struct fimc_control *ctrl, u32 rot, u32 flip);
+extern int fimc_hwset_input_source(struct fimc_control *ctrl,
+ enum fimc_input path);
+extern int fimc_hwset_start_input_dma(struct fimc_control *ctrl);
+extern int fimc_hwset_stop_input_dma(struct fimc_control *ctrl);
+extern int fimc_hwset_output_offset(struct fimc_control *ctrl,
+ u32 pixelformat,
+ struct v4l2_rect *bound,
+ struct v4l2_rect *crop);
+extern int fimc_hwset_input_offset(struct fimc_control *ctrl,
+ u32 pixelformat,
+ struct v4l2_rect *bound,
+ struct v4l2_rect *crop);
+extern int fimc_hwset_org_input_size(struct fimc_control *ctrl,
+ u32 width, u32 height);
+extern int fimc_hwset_org_output_size(struct fimc_control *ctrl,
+ u32 width, u32 height);
+extern int fimc_hwset_ext_output_size(struct fimc_control *ctrl,
+ u32 width, u32 height);
+extern int fimc_hwset_input_addr_style(struct fimc_control *ctrl,
+ u32 pixelformat);
+extern int fimc_hwset_output_addr_style(struct fimc_control *ctrl,
+ u32 pixelformat);
+extern int fimc_hwset_jpeg_mode(struct fimc_control *ctrl, bool enable);
+extern int fimc_hwget_frame_count(struct fimc_control *ctrl);
+extern int fimc_hw_wait_winoff(struct fimc_control *ctrl);
+extern int fimc_hw_wait_stop_input_dma(struct fimc_control *ctrl);
+extern int fimc_hwset_input_lineskip(struct fimc_control *ctrl);
+extern int fimc_hw_reset_camera(struct fimc_control *ctrl);
+extern void fimc_hwset_stop_processing(struct fimc_control *ctrl);
+extern int fimc_hwset_image_effect(struct fimc_control *ctrl);
+extern int fimc_hwset_shadow_enable(struct fimc_control *ctrl);
+extern int fimc_hwset_shadow_disable(struct fimc_control *ctrl);
+extern void fimc_save_regs(struct fimc_control *ctrl);
+extern void fimc_load_regs(struct fimc_control *ctrl);
+extern void fimc_dump_regs(struct fimc_control *ctrl);
+
+/*
+ * D R I V E R H E L P E R S
+ *
+*/
+#define to_fimc_plat(d) (to_platform_device(d)->dev.platform_data)
+
+static inline struct fimc_global *get_fimc_dev(void)
+{
+ return fimc_dev;
+}
+
+static inline struct fimc_control *get_fimc_ctrl(int id)
+{
+ return &fimc_dev->ctrl[id];
+}
+
+#endif /* _FIMC_H */
+
diff --git a/drivers/media/video/samsung/fimc/fimc_capture.c b/drivers/media/video/samsung/fimc/fimc_capture.c
new file mode 100644
index 0000000..2028bf9
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_capture.c
@@ -0,0 +1,1733 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_capture.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * V4L2 Capture device support file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/slab.h>
+#include <linux/bootmem.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_samsung.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <plat/media.h>
+#include <plat/clock.h>
+#include <plat/fimc.h>
+#include <linux/delay.h>
+
+#include "fimc.h"
+
+/* subdev handling macro */
+#define subdev_call(ctrl, o, f, args...) \
+ v4l2_subdev_call(ctrl->cam->sd, o, f, ##args)
+
+/* #define FIMC_CAP_DEBUG */
+
+#ifdef FIMC_CAP_DEBUG
+#ifdef fimc_dbg
+#undef fimc_dbg
+#endif
+#define fimc_dbg fimc_err
+#endif
+
+static const struct v4l2_fmtdesc capture_fmts[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "RGB-5-6-5",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ }, {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "RGB-8-8-8, unpacked 24 bpp",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ }, {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "YUV 4:2:2 packed, YCbYCr",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ }, {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "YUV 4:2:2 packed, CbYCrY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ }, {
+ .index = 4,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "YUV 4:2:2 packed, CrYCbY",
+ .pixelformat = V4L2_PIX_FMT_VYUY,
+ }, {
+ .index = 5,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "YUV 4:2:2 packed, YCrYCb",
+ .pixelformat = V4L2_PIX_FMT_YVYU,
+ }, {
+ .index = 6,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ }, {
+ .index = 7,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:0 planar, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ }, {
+ .index = 8,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:0 planar, Y/CbCr, Tiled",
+ .pixelformat = V4L2_PIX_FMT_NV12T,
+ }, {
+ .index = 9,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:0 planar, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ }, {
+ .index = 10,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:2 planar, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV16,
+ }, {
+ .index = 11,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:2 planar, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV61,
+ }, {
+ .index = 12,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "YUV 4:2:0 planar, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ }, {
+ .index = 13,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_ENCODED,
+ .description = "Encoded JPEG bitstream",
+ .pixelformat = V4L2_PIX_FMT_JPEG,
+ },
+};
+
+static const struct v4l2_queryctrl fimc_controls[] = {
+ {
+ .id = V4L2_CID_ROTATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Roataion",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_PADDR_Y,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Physical address Y",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ }, {
+ .id = V4L2_CID_PADDR_CB,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Physical address Cb",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ }, {
+ .id = V4L2_CID_PADDR_CR,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Physical address Cr",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ }, {
+ .id = V4L2_CID_PADDR_CBCR,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Physical address CbCr",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ },
+};
+
+#ifndef CONFIG_VIDEO_FIMC_MIPI
+void s3c_csis_start(int lanes, int settle, int align, int width, int height,
+ int pixel_format) {}
+#endif
+
+static int fimc_camera_init(struct fimc_control *ctrl)
+{
+ int ret;
+
+ fimc_dbg("%s\n", __func__);
+
+ /* do nothing if already initialized */
+ if (ctrl->cam->initialized)
+ return 0;
+
+ /* enable camera power if needed */
+ if (ctrl->cam->cam_power)
+ ctrl->cam->cam_power(1);
+
+ /* subdev call for init */
+ ret = subdev_call(ctrl, core, init, 0);
+ if (ret == -ENOIOCTLCMD) {
+ fimc_err("%s: init subdev api not supported\n",
+ __func__);
+ return ret;
+ }
+
+ if (ctrl->cam->type == CAM_TYPE_MIPI) {
+ /* subdev call for sleep/wakeup:
+ * no error although no s_stream api support
+ */
+ u32 pixelformat;
+ if (ctrl->cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG)
+ pixelformat = V4L2_PIX_FMT_JPEG;
+ else
+ pixelformat = ctrl->cam->pixelformat;
+
+ subdev_call(ctrl, video, s_stream, 0);
+ s3c_csis_start(ctrl->cam->mipi_lanes, ctrl->cam->mipi_settle, \
+ ctrl->cam->mipi_align, ctrl->cam->width, \
+ ctrl->cam->height, pixelformat);
+ subdev_call(ctrl, video, s_stream, 1);
+ }
+
+ ctrl->cam->initialized = 1;
+
+ return 0;
+}
+
+
+/* This function must be called after s_fmt and s_parm call to the subdev
+ * has already been made.
+ *
+ * - obtains the camera output (input to FIMC) resolution.
+ * - sets the preview size (aka camera output resolution) and framerate.
+ * - starts the preview operation.
+ *
+ * On success, returns 0.
+ * On failure, returns the error code of the call that failed.
+ */
+static int fimc_camera_start(struct fimc_control *ctrl)
+{
+ struct v4l2_frmsizeenum cam_frmsize;
+ struct v4l2_control cam_ctrl;
+ int ret;
+
+ ret = subdev_call(ctrl, video, enum_framesizes, &cam_frmsize);
+ if (ret < 0) {
+ fimc_err("%s: enum_framesizes failed\n", __func__);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+ } else {
+ ctrl->cam->width = cam_frmsize.discrete.width;
+ ctrl->cam->height = cam_frmsize.discrete.height;
+
+ ctrl->cam->window.left = 0;
+ ctrl->cam->window.top = 0;
+ ctrl->cam->window.width = ctrl->cam->width;
+ ctrl->cam->window.height = ctrl->cam->height;
+ }
+
+ cam_ctrl.id = V4L2_CID_CAM_PREVIEW_ONOFF;
+ cam_ctrl.value = 1;
+ ret = subdev_call(ctrl, core, s_ctrl, &cam_ctrl);
+
+ /* When the device is waking up from sleep, this call may fail. In
+ * that case, it is better to reset the camera sensor and start again.
+ * If the preview fails again, the reason might be something else and
+ * we should return the error.
+ */
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ ctrl->cam->initialized = 0;
+ fimc_camera_init(ctrl);
+ ret = subdev_call(ctrl, core, s_ctrl, &cam_ctrl);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ fimc_err("%s: Error in V4L2_CID_CAM_PREVIEW_ONOFF"
+ " - start\n", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int fimc_camera_get_jpeg_memsize(struct fimc_control *ctrl)
+{
+ int ret;
+ struct v4l2_control cam_ctrl;
+ cam_ctrl.id = V4L2_CID_CAM_JPEG_MEMSIZE;
+
+ ret = subdev_call(ctrl, core, g_ctrl, &cam_ctrl);
+ if (ret < 0) {
+ fimc_err("%s: Subdev doesn't support JEPG encoding.\n", \
+ __func__);
+ return 0;
+ }
+
+ return cam_ctrl.value;
+}
+
+
+static int fimc_capture_scaler_info(struct fimc_control *ctrl)
+{
+ struct fimc_scaler *sc = &ctrl->sc;
+ struct v4l2_rect *window = &ctrl->cam->window;
+ int tx, ty, sx, sy;
+
+ sx = window->width;
+ sy = window->height;
+ tx = ctrl->cap->fmt.width;
+ ty = ctrl->cap->fmt.height;
+
+ sc->real_width = sx;
+ sc->real_height = sy;
+
+ fimc_dbg("%s: CamOut (%d, %d), TargetOut (%d, %d)\n", \
+ __func__, sx, sy, tx, ty);
+
+ if (sx <= 0 || sy <= 0) {
+ fimc_err("%s: invalid source size\n", __func__);
+ return -EINVAL;
+ }
+
+ if (tx <= 0 || ty <= 0) {
+ fimc_err("%s: invalid target size\n", __func__);
+ return -EINVAL;
+ }
+
+ fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);
+ fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor);
+
+ /* Tushar - sx and sy should be multiple of pre_hratio and pre_vratio */
+ sc->pre_dst_width = sx / sc->pre_hratio;
+ sc->pre_dst_height = sy / sc->pre_vratio;
+
+ sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+ sc->scaleup_h = (tx >= sx) ? 1 : 0;
+ sc->scaleup_v = (ty >= sy) ? 1 : 0;
+
+ return 0;
+}
+
+/**
+ * fimc_add_inqueue: used to add the buffer at given index to inqueue
+ *
+ * Called from qbuf().
+ *
+ * Returns error if buffer is already in queue or buffer index is out of range.
+ */
+static int fimc_add_inqueue(struct fimc_control *ctrl, int i)
+{
+ struct fimc_capinfo *cap = ctrl->cap;
+
+ struct fimc_buf_set *buf;
+
+ if (i >= cap->nr_bufs)
+ return -EINVAL;
+
+ list_for_each_entry(buf, &cap->inq, list) {
+ if (buf->id == i) {
+ fimc_dbg("%s: buffer %d already in inqueue.\n", \
+ __func__, i);
+ return -EINVAL;
+ }
+ }
+ list_add_tail(&cap->bufs[i].list, &cap->inq);
+
+ return 0;
+}
+
+static int fimc_add_outqueue(struct fimc_control *ctrl, int i)
+{
+ struct fimc_capinfo *cap = ctrl->cap;
+ struct fimc_buf_set *buf;
+
+ unsigned int mask = 0x2;
+
+ /* PINGPONG_2ADDR_MODE Only */
+ /* pair_buf_index stands for pair index of i. (0<->2) (1<->3) */
+
+ int pair_buf_index = (i^mask);
+
+ /* FIMC have 4 h/w registers */
+ if (i < 0 || i >= FIMC_PHYBUFS) {
+ fimc_err("%s: invalid queue index : %d\n", __func__, i);
+ return -ENOENT;
+ }
+
+ if (list_empty(&cap->inq))
+ return -ENOENT;
+
+ buf = list_first_entry(&cap->inq, struct fimc_buf_set, list);
+
+ /* pair index buffer should be allocated first */
+ cap->outq[pair_buf_index] = buf->id;
+ fimc_hwset_output_address(ctrl, buf, pair_buf_index);
+
+ cap->outq[i] = buf->id;
+ fimc_hwset_output_address(ctrl, buf, i);
+
+ if (cap->nr_bufs != 1)
+ list_del(&buf->list);
+
+ return 0;
+}
+
+static int fimc_update_hwaddr(struct fimc_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < FIMC_PINGPONG; i++)
+ fimc_add_outqueue(ctrl, i);
+
+ return 0;
+}
+
+int fimc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ ret = subdev_call(ctrl, video, g_parm, a);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+int fimc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ if (ctrl->id != 2)
+ ret = subdev_call(ctrl, video, s_parm, a);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+/* Enumerate controls */
+int fimc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int i, ret;
+
+ fimc_dbg("%s\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(fimc_controls); i++) {
+ if (fimc_controls[i].id == qc->id) {
+ memcpy(qc, &fimc_controls[i], \
+ sizeof(struct v4l2_queryctrl));
+ return 0;
+ }
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ ret = subdev_call(ctrl, core, queryctrl, qc);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+/* Menu control items */
+int fimc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qm)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret;
+
+ fimc_dbg("%s\n", __func__);
+
+ mutex_lock(&ctrl->v4l2_lock);
+ ret = subdev_call(ctrl, core, querymenu, qm);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+
+/* Given the index, we will return the camera name if there is any camera
+ * present at the given id.
+ */
+int fimc_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
+{
+ struct fimc_global *fimc = get_fimc_dev();
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ fimc_dbg("%s: index %d\n", __func__, inp->index);
+
+ if (inp->index < 0 || inp->index >= FIMC_MAXCAMS) {
+ fimc_err("%s: invalid input index, received = %d\n", \
+ __func__, inp->index);
+ return -EINVAL;
+ }
+
+ if (!fimc->camera_isvalid[inp->index])
+ return -EINVAL;
+
+ strcpy(inp->name, fimc->camera[inp->index].info->type);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+int fimc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_global *fimc = get_fimc_dev();
+
+ /* In case of isueing g_input before s_input */
+ if (!ctrl->cam) {
+ fimc_err("no camera device selected yet!" \
+ "do VIDIOC_S_INPUT first\n");
+ return -ENODEV;
+ }
+
+ *i = (unsigned int) fimc->active_camera;
+
+ fimc_dbg("%s: index %d\n", __func__, *i);
+
+ return 0;
+}
+
+int fimc_release_subdev(struct fimc_control *ctrl)
+{
+ struct fimc_global *fimc = get_fimc_dev();
+ struct i2c_client *client;
+
+ if (ctrl && ctrl->cam && ctrl->cam->sd) {
+ fimc_dbg("%s called\n", __func__);
+ client = v4l2_get_subdevdata(ctrl->cam->sd);
+ i2c_unregister_device(client);
+ ctrl->cam->sd = NULL;
+ if (ctrl->cam->cam_power)
+ ctrl->cam->cam_power(0);
+ ctrl->cam->initialized = 0;
+ ctrl->cam = NULL;
+ fimc->active_camera = -1;
+ }
+ return 0;
+}
+
+static int fimc_configure_subdev(struct fimc_control *ctrl)
+{
+ struct i2c_adapter *i2c_adap;
+ struct i2c_board_info *i2c_info;
+ struct v4l2_subdev *sd;
+ unsigned short addr;
+ char *name;
+
+ /* set parent for mclk */
+ if (clk_get_parent(ctrl->cam->clk->parent))
+ clk_set_parent(ctrl->cam->clk->parent, ctrl->cam->srclk);
+
+ /* set rate for mclk */
+ if (clk_get_rate(ctrl->cam->clk))
+ clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate);
+
+ i2c_adap = i2c_get_adapter(ctrl->cam->i2c_busnum);
+ if (!i2c_adap)
+ fimc_err("subdev i2c_adapter missing-skip registration\n");
+
+ i2c_info = ctrl->cam->info;
+ if (!i2c_info) {
+ fimc_err("%s: subdev i2c board info missing\n", __func__);
+ return -ENODEV;
+ }
+
+ name = i2c_info->type;
+ if (!name) {
+ fimc_err("subdev i2c driver name missing-skip registration\n");
+ return -ENODEV;
+ }
+
+ addr = i2c_info->addr;
+ if (!addr) {
+ fimc_err("subdev i2c address missing-skip registration\n");
+ return -ENODEV;
+ }
+ /*
+ * NOTE: first time subdev being registered,
+ * s_config is called and try to initialize subdev device
+ * but in this point, we are not giving MCLK and power to subdev
+ * so nothing happens but pass platform data through
+ */
+ sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap,
+ i2c_info, &addr);
+ if (!sd) {
+ fimc_err("%s: v4l2 subdev board registering failed\n",
+ __func__);
+ }
+
+ /* Assign subdev to proper camera device pointer */
+ ctrl->cam->sd = sd;
+
+ return 0;
+}
+
+int fimc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct fimc_global *fimc = get_fimc_dev();
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ fimc_dbg("%s: index %d\n", __func__, i);
+
+ if (i < 0 || i >= FIMC_MAXCAMS) {
+ fimc_err("%s: invalid input index\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!fimc->camera_isvalid[i])
+ return -EINVAL;
+
+ if (fimc->camera[i].sd && ctrl->id != 2) {
+ fimc_err("%s: Camera already in use.\n", __func__);
+ return -EBUSY;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ /* If ctrl->cam is not NULL, there is one subdev already registered.
+ * We need to unregister that subdev first.
+ */
+ if (i != fimc->active_camera) {
+ fimc_release_subdev(ctrl);
+ ctrl->cam = &fimc->camera[i];
+ ret = fimc_configure_subdev(ctrl);
+ if (ret < 0) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_err("%s: Could not register camera sensor "
+ "with V4L2.\n", __func__);
+ return -ENODEV;
+ }
+ fimc->active_camera = i;
+ }
+
+ if (ctrl->id == 2) {
+ if (i == fimc->active_camera) {
+ ctrl->cam = &fimc->camera[i];
+ } else {
+ mutex_unlock(&ctrl->v4l2_lock);
+ return -EINVAL;
+ }
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_enum_fmt_vid_capture(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int i = f->index;
+ int num_entries = 0;
+ int ret = 0;
+ enum v4l2_mbus_pixelcode code;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (ctrl->out) {
+ fimc_err("%s: fimc is already used for output mode\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ num_entries = sizeof(capture_fmts)/sizeof(struct v4l2_fmtdesc);
+
+ if (i >= num_entries) {
+ mutex_lock(&ctrl->v4l2_lock);
+ ret = subdev_call(ctrl, video, enum_mbus_fmt,
+ f->index - num_entries, &code);
+ mutex_unlock(&ctrl->v4l2_lock);
+ return ret;
+ }
+
+ memcpy(f, &capture_fmts[i], sizeof(*f));
+
+ return 0;
+}
+
+int fimc_g_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cap) {
+ fimc_err("%s: no capture device info\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ memcpy(&f->fmt.pix, &ctrl->cap->fmt, sizeof(f->fmt.pix));
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+/*
+ * Check for whether the requested format
+ * can be streamed out from FIMC
+ * depends on FIMC node
+ */
+static int fimc_fmt_avail(struct fimc_control *ctrl,
+ struct v4l2_format *f)
+{
+ int i;
+
+ /*
+ * TODO: check for which FIMC is used.
+ * Available fmt should be varied for each FIMC
+ */
+
+ for (i = 0; i < sizeof(capture_fmts); i++) {
+ if (capture_fmts[i].pixelformat == f->fmt.pix.pixelformat)
+ return 0;
+ }
+
+ fimc_err("Not supported pixelformat requested\n");
+
+ return -1;
+}
+
+/*
+ * figures out the depth of requested format
+ */
+static int fimc_fmt_depth(struct fimc_control *ctrl, struct v4l2_format *f)
+{
+ int err, depth = 0;
+
+ /* First check for available format or not */
+ err = fimc_fmt_avail(ctrl, f);
+ if (err < 0)
+ return -EINVAL;
+
+ /* handles only supported pixelformats */
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB32:
+ depth = 32;
+ fimc_dbg("32bpp\n");
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ depth = 16;
+ fimc_dbg("16bpp\n");
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12T:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ depth = 12;
+ fimc_dbg("12bpp\n");
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ depth = -1;
+ fimc_dbg("Compressed format.\n");
+ break;
+ default:
+ fimc_dbg("why am I here? - received %x\n",
+ f->fmt.pix.pixelformat);
+ break;
+ }
+
+ return depth;
+}
+
+int fimc_s_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_capinfo *cap;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret = 0;
+ int depth;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+ /*
+ * The first time alloc for struct cap_info, and will be
+ * released at the file close.
+ * Anyone has better idea to do this?
+ */
+ mutex_lock(&ctrl->v4l2_lock);
+
+ if (!ctrl->cap) {
+ ctrl->cap = kmalloc(sizeof(*cap), GFP_KERNEL);
+ if (!ctrl->cap) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_err("%s: no memory for "
+ "capture device info\n", __func__);
+ return -ENOMEM;
+ }
+
+ }
+ cap = ctrl->cap;
+ memset(cap, 0, sizeof(*cap));
+ memcpy(&cap->fmt, &f->fmt.pix, sizeof(cap->fmt));
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, 0);
+
+ /*
+ * Note that expecting format only can be with
+ * available output format from FIMC
+ * Following items should be handled in driver
+ * bytesperline = width * depth / 8
+ * sizeimage = bytesperline * height
+ */
+ /* This function may return 0 or -1 in case of error, hence need to
+ * check here.
+ */
+ depth = fimc_fmt_depth(ctrl, f);
+ if (depth == 0) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_err("%s: Invalid pixel format\n", __func__);
+ return -EINVAL;
+ } else if (depth < 0) {
+ /*
+ * When the pixelformat is JPEG, the application is requesting
+ * for data in JPEG compressed format.
+ */
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ ret = subdev_call(ctrl, video, try_mbus_fmt, &mbus_fmt);
+ if (ret < 0) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ return -EINVAL;
+ }
+ cap->fmt.colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ cap->fmt.bytesperline = (cap->fmt.width * depth) >> 3;
+ cap->fmt.sizeimage = (cap->fmt.bytesperline * cap->fmt.height);
+ }
+
+ if (cap->fmt.colorspace == V4L2_COLORSPACE_JPEG) {
+ ctrl->sc.bypass = 1;
+ cap->lastirq = 1;
+ }
+
+ if (ctrl->id != 2) {
+ ret = subdev_call(ctrl, video, s_mbus_fmt, &mbus_fmt);
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+int fimc_try_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
+{
+ return 0;
+}
+
+static int fimc_alloc_buffers(struct fimc_control *ctrl, int size[], int align)
+{
+ struct fimc_capinfo *cap = ctrl->cap;
+ int i, plane;
+
+ for (i = 0; i < cap->nr_bufs; i++) {
+ for (plane = 0; plane < 4; plane++) {
+ cap->bufs[i].length[plane] = size[plane];
+ if (!cap->bufs[i].length[plane])
+ continue;
+
+ fimc_dma_alloc(ctrl, &cap->bufs[i], plane, align);
+
+ if (!cap->bufs[i].base[plane])
+ goto err_alloc;
+ }
+
+ cap->bufs[i].state = VIDEOBUF_PREPARED;
+ cap->bufs[i].id = i;
+ }
+
+ return 0;
+
+err_alloc:
+ for (i = 0; i < cap->nr_bufs; i++) {
+ if (cap->bufs[i].base[plane])
+ fimc_dma_free(ctrl, &cap->bufs[i], plane);
+
+ memset(&cap->bufs[i], 0, sizeof(cap->bufs[i]));
+ }
+
+ return -ENOMEM;
+}
+
+static void fimc_free_buffers(struct fimc_control *ctrl)
+{
+ struct fimc_capinfo *cap;
+ int i;
+
+ if (ctrl && ctrl->cap)
+ cap = ctrl->cap;
+ else
+ return;
+
+
+ for (i = 0; i < cap->nr_bufs; i++) {
+ memset(&cap->bufs[i], 0, sizeof(cap->bufs[i]));
+ cap->bufs[i].state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ ctrl->mem.curr = ctrl->mem.base;
+}
+
+int fimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_capinfo *cap = ctrl->cap;
+ int ret = 0, i;
+ int size[4] = { 0, 0, 0, 0};
+ int align = 0;
+
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ fimc_err("%s: invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!cap) {
+ fimc_err("%s: no capture device info\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ if (b->count < 1 || b->count > FIMC_CAPBUFS)
+ return -EINVAL;
+
+ /* It causes flickering as buf_0 and buf_3 refer to same hardware
+ * address.
+ */
+ if (b->count == 3)
+ b->count = 4;
+
+ cap->nr_bufs = b->count;
+
+ fimc_dbg("%s: requested %d buffers\n", __func__, b->count);
+
+ INIT_LIST_HEAD(&cap->inq);
+ fimc_free_buffers(ctrl);
+
+ switch (cap->fmt.pixelformat) {
+ case V4L2_PIX_FMT_RGB32: /* fall through */
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_YUV422P: /* fall through */
+ size[0] = cap->fmt.sizeimage;
+ break;
+
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ size[0] = cap->fmt.width * cap->fmt.height;
+ size[1] = cap->fmt.width * cap->fmt.height;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ size[0] = cap->fmt.width * cap->fmt.height;
+ size[1] = cap->fmt.width * cap->fmt.height/2;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ size[0] = cap->fmt.width * cap->fmt.height;
+ size[1] = cap->fmt.width * cap->fmt.height/2;
+ break;
+ case V4L2_PIX_FMT_NV12T:
+ /* Tiled frame size calculations as per 4x2 tiles
+ * - Width: Has to be aligned to 2 times the tile width
+ * - Height: Has to be aligned to the tile height
+ * - Alignment: Has to be aligned to the size of the
+ * macrotile (size of 4 tiles)
+ *
+ * NOTE: In case of rotation, we need modified calculation as
+ * width and height are aligned to different values.
+ */
+ if (cap->rotate == 90 || cap->rotate == 270) {
+ size[0] = ALIGN(ALIGN(cap->fmt.height, 128) *
+ ALIGN(cap->fmt.width, 32),
+ SZ_8K);
+ size[1] = ALIGN(ALIGN(cap->fmt.height, 128) *
+ ALIGN(cap->fmt.width/2, 32),
+ SZ_8K);
+ } else {
+ size[0] = ALIGN(ALIGN(cap->fmt.width, 128) *
+ ALIGN(cap->fmt.height, 32),
+ SZ_8K);
+ size[1] = ALIGN(ALIGN(cap->fmt.width, 128) *
+ ALIGN(cap->fmt.height/2, 32),
+ SZ_8K);
+ }
+ align = SZ_8K;
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ size[0] = cap->fmt.width * cap->fmt.height;
+ size[1] = cap->fmt.width * cap->fmt.height >> 2;
+ size[2] = cap->fmt.width * cap->fmt.height >> 2;
+ break;
+
+ case V4L2_PIX_FMT_JPEG:
+ size[0] = fimc_camera_get_jpeg_memsize(ctrl);
+ default:
+ break;
+ }
+
+ ret = fimc_alloc_buffers(ctrl, size, align);
+ if (ret) {
+ fimc_err("%s: no memory for "
+ "capture buffer\n", __func__);
+ mutex_unlock(&ctrl->v4l2_lock);
+ return -ENOMEM;
+ }
+
+ for (i = cap->nr_bufs; i < FIMC_PHYBUFS; i++) {
+ memcpy(&cap->bufs[i], \
+ &cap->bufs[i - cap->nr_bufs], sizeof(cap->bufs[i]));
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_querybuf_capture(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ if (!ctrl->cap || !ctrl->cap->bufs) {
+ fimc_err("%s: no capture device info\n", __func__);
+ return -ENODEV;
+ }
+
+ if (ctrl->status != FIMC_STREAMOFF) {
+ fimc_err("fimc is running\n");
+ return -EBUSY;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ b->length = ctrl->cap->bufs[b->index].length[FIMC_ADDR_Y]
+ + ctrl->cap->bufs[b->index].length[FIMC_ADDR_CB]
+ + ctrl->cap->bufs[b->index].length[FIMC_ADDR_CR];
+
+ b->m.offset = b->index * PAGE_SIZE;
+
+ ctrl->cap->bufs[b->index].state = VIDEOBUF_IDLE;
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ fimc_dbg("%s: %d bytes at index: %d\n", __func__, b->length, b->index);
+
+ return 0;
+}
+
+int fimc_g_ctrl_capture(void *fh, struct v4l2_control *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd || !ctrl->cap) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ switch (c->id) {
+ case V4L2_CID_ROTATION:
+ c->value = ctrl->cap->rotate;
+ break;
+
+ case V4L2_CID_HFLIP:
+ c->value = (ctrl->cap->flip & FIMC_XFLIP) ? 1 : 0;
+ break;
+
+ case V4L2_CID_VFLIP:
+ c->value = (ctrl->cap->flip & FIMC_YFLIP) ? 1 : 0;
+ break;
+
+ default:
+ /* get ctrl supported by subdev */
+ mutex_unlock(&ctrl->v4l2_lock);
+ ret = subdev_call(ctrl, core, g_ctrl, c);
+ mutex_lock(&ctrl->v4l2_lock);
+ break;
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+/**
+ * We used s_ctrl API to get the physical address of the buffers.
+ * In g_ctrl, we can pass only one parameter, thus we cannot pass
+ * the index of the buffer.
+ * In order to use g_ctrl for obtaining the physical address, we
+ * will have to create CID ids for all values (4 ids for Y0~Y3 and 4 ids
+ * for C0~C3). Currently, we will continue with the existing
+ * implementation till we get any better idea to implement.
+ */
+int fimc_s_ctrl_capture(void *fh, struct v4l2_control *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ fimc_info2("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd || !ctrl->cap || !ctrl->cap->bufs) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ switch (c->id) {
+ case V4L2_CID_ROTATION:
+ ctrl->cap->rotate = c->value;
+ break;
+
+ case V4L2_CID_HFLIP: /* fall through */
+ ctrl->cap->flip |= FIMC_XFLIP;
+ break;
+ case V4L2_CID_VFLIP:
+ ctrl->cap->flip |= FIMC_YFLIP;
+ break;
+
+ case V4L2_CID_PADDR_Y:
+ c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_Y];
+ break;
+
+ case V4L2_CID_PADDR_CB: /* fall through */
+ case V4L2_CID_PADDR_CBCR:
+ c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_CB];
+ break;
+
+ case V4L2_CID_PADDR_CR:
+ c->value = ctrl->cap->bufs[c->value].base[FIMC_ADDR_CR];
+ break;
+
+ /* Implementation as per C100 FIMC driver */
+ case V4L2_CID_STREAM_PAUSE:
+ fimc_hwset_stop_processing(ctrl);
+ break;
+
+ case V4L2_CID_IMAGE_EFFECT_APPLY:
+ ctrl->fe.ie_on = c->value ? 1 : 0;
+ ctrl->fe.ie_after_sc = 0;
+ ret = fimc_hwset_image_effect(ctrl);
+ break;
+
+ case V4L2_CID_IMAGE_EFFECT_FN:
+ if (c->value < 0 || c->value > FIMC_EFFECT_FIN_SILHOUETTE)
+ return -EINVAL;
+ ctrl->fe.fin = c->value;
+ ret = 0;
+ break;
+
+ case V4L2_CID_IMAGE_EFFECT_CB:
+ ctrl->fe.pat_cb = c->value & 0xFF;
+ ret = 0;
+ break;
+
+ case V4L2_CID_IMAGE_EFFECT_CR:
+ ctrl->fe.pat_cr = c->value & 0xFF;
+ ret = 0;
+ break;
+
+ default:
+ /* try on subdev */
+ mutex_unlock(&ctrl->v4l2_lock);
+ if (2 != ctrl->id)
+ ret = subdev_call(ctrl, core, s_ctrl, c);
+ else
+ ret = 0;
+ mutex_lock(&ctrl->v4l2_lock);
+ break;
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+int fimc_s_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ /* try on subdev */
+ ret = subdev_call(ctrl, core, s_ext_ctrls, c);
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return ret;
+}
+
+int fimc_cropcap_capture(void *fh, struct v4l2_cropcap *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_capinfo *cap = ctrl->cap;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd || !ctrl->cap) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ /* crop limitations */
+ cap->cropcap.bounds.left = 0;
+ cap->cropcap.bounds.top = 0;
+ cap->cropcap.bounds.width = ctrl->cam->width;
+ cap->cropcap.bounds.height = ctrl->cam->height;
+
+ /* crop default values */
+ cap->cropcap.defrect.left = 0;
+ cap->cropcap.defrect.top = 0;
+ cap->cropcap.defrect.width = ctrl->cam->width;
+ cap->cropcap.defrect.height = ctrl->cam->height;
+
+ a->bounds = cap->cropcap.bounds;
+ a->defrect = cap->cropcap.defrect;
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_g_crop_capture(void *fh, struct v4l2_crop *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cap) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ a->c = ctrl->cap->crop;
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+static int fimc_capture_crop_size_check(struct fimc_control *ctrl)
+{
+ struct fimc_capinfo *cap = ctrl->cap;
+ int win_hor_offset = 0, win_hor_offset2 = 0;
+ int win_ver_offset = 0, win_ver_offset2 = 0;
+ int crop_width = 0, crop_height = 0;
+
+ /* check win_hor_offset, win_hor_offset2 */
+ win_hor_offset = ctrl->cam->window.left;
+ win_hor_offset2 = ctrl->cam->width - ctrl->cam->window.left -
+ ctrl->cam->window.width;
+
+ win_ver_offset = ctrl->cam->window.top;
+ win_ver_offset2 = ctrl->cam->height - ctrl->cam->window.top -
+ ctrl->cam->window.height;
+
+ if (win_hor_offset < 0 || win_hor_offset2 < 0) {
+ fimc_err("%s: Offset (left-side(%d) or right-side(%d) "
+ "is negative.\n", __func__, \
+ win_hor_offset, win_hor_offset2);
+ return -1;
+ }
+
+ if (win_ver_offset < 0 || win_ver_offset2 < 0) {
+ fimc_err("%s: Offset (top-side(%d) or bottom-side(%d)) "
+ "is negative.\n", __func__, \
+ win_ver_offset, win_ver_offset2);
+ return -1;
+ }
+
+ if ((win_hor_offset % 2) || (win_hor_offset2 % 2)) {
+ fimc_err("%s: win_hor_offset must be multiple of 2\n", \
+ __func__);
+ return -1;
+ }
+
+ /* check crop_width, crop_height */
+ crop_width = ctrl->cam->window.width;
+ crop_height = ctrl->cam->window.height;
+
+ if (crop_width % 16) {
+ fimc_err("%s: crop_width must be multiple of 16\n", __func__);
+ return -1;
+ }
+
+ switch (cap->fmt.pixelformat) {
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ if ((crop_height % 2) || (crop_height < 8)) {
+ fimc_err("%s: crop_height error!\n", __func__);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/** Given crop parameters are w.r.t. target resolution. Scale
+ * it w.r.t. camera source resolution.
+ *
+ * Steps:
+ * 1. Scale as camera resolution with fixed-point calculation
+ * 2. Check for overflow condition
+ * 3. Apply FIMC constrainsts
+ */
+static void fimc_capture_update_crop_window(struct fimc_control *ctrl)
+{
+ unsigned int zoom_hor = 0;
+ unsigned int zoom_ver = 0;
+ unsigned int multiplier = 1024;
+
+ if (!ctrl->cam->width || !ctrl->cam->height)
+ return;
+
+ zoom_hor = ctrl->cap->fmt.width * multiplier / ctrl->cam->width;
+ zoom_ver = ctrl->cap->fmt.height * multiplier / ctrl->cam->height;
+
+ if (!zoom_hor || !zoom_ver)
+ return;
+
+ /* Width */
+ ctrl->cam->window.width = ctrl->cap->crop.width * multiplier / zoom_hor;
+ if (ctrl->cam->window.width > ctrl->cam->width)
+ ctrl->cam->window.width = ctrl->cam->width;
+ if (ctrl->cam->window.width % 16)
+ ctrl->cam->window.width =
+ (ctrl->cam->window.width + 0xF) & ~0xF;
+
+ /* Left offset */
+ ctrl->cam->window.left = ctrl->cap->crop.left * multiplier / zoom_hor;
+ if (ctrl->cam->window.width + ctrl->cam->window.left > ctrl->cam->width)
+ ctrl->cam->window.left =
+ (ctrl->cam->width - ctrl->cam->window.width)/2;
+ if (ctrl->cam->window.left % 2)
+ ctrl->cam->window.left--;
+
+ /* Height */
+ ctrl->cam->window.height =
+ (ctrl->cap->crop.height * multiplier) / zoom_ver;
+ if (ctrl->cam->window.top > ctrl->cam->height)
+ ctrl->cam->window.height = ctrl->cam->height;
+ if (ctrl->cam->window.height % 2)
+ ctrl->cam->window.height--;
+
+ /* Top offset */
+ ctrl->cam->window.top = ctrl->cap->crop.top * multiplier / zoom_ver;
+ if (ctrl->cam->window.height + ctrl->cam->window.top >
+ ctrl->cam->height)
+ ctrl->cam->window.top =
+ (ctrl->cam->height - ctrl->cam->window.height)/2;
+ if (ctrl->cam->window.top % 2)
+ ctrl->cam->window.top--;
+
+ fimc_dbg("Cam (%dx%d) Crop: (%d %d %d %d) Win: (%d %d %d %d)\n", \
+ ctrl->cam->width, ctrl->cam->height, \
+ ctrl->cap->crop.left, ctrl->cap->crop.top, \
+ ctrl->cap->crop.width, ctrl->cap->crop.height, \
+ ctrl->cam->window.left, ctrl->cam->window.top, \
+ ctrl->cam->window.width, ctrl->cam->window.height);
+
+}
+
+int fimc_s_crop_capture(void *fh, struct v4l2_crop *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = 0;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cap) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ ctrl->cap->crop = a->c;
+
+ fimc_capture_update_crop_window(ctrl);
+
+ ret = fimc_capture_crop_size_check(ctrl);
+ if (ret < 0) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_err("%s: Invalid crop parameters.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ctrl->status == FIMC_STREAMON &&
+ ctrl->cap->fmt.pixelformat != V4L2_PIX_FMT_JPEG) {
+ fimc_hwset_shadow_disable(ctrl);
+ fimc_hwset_camera_offset(ctrl);
+ fimc_capture_scaler_info(ctrl);
+ fimc_hwset_prescaler(ctrl, &ctrl->sc);
+ fimc_hwset_scaler(ctrl, &ctrl->sc);
+ fimc_hwset_shadow_enable(ctrl);
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_start_capture(struct fimc_control *ctrl)
+{
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->sc.bypass)
+ fimc_hwset_start_scaler(ctrl);
+
+ fimc_hwset_enable_capture(ctrl, ctrl->sc.bypass);
+
+ return 0;
+}
+
+int fimc_stop_capture(struct fimc_control *ctrl)
+{
+ fimc_dbg("%s\n", __func__);
+
+ if (ctrl->cap->lastirq) {
+ fimc_hwset_enable_lastirq(ctrl);
+ fimc_hwset_disable_capture(ctrl);
+ fimc_hwset_disable_lastirq(ctrl);
+ ctrl->cap->lastirq = 0;
+ } else {
+ fimc_hwset_disable_capture(ctrl);
+ }
+
+ fimc_hwset_disable_irq(ctrl);
+ fimc_hwset_clear_irq(ctrl);
+
+ if (!ctrl->sc.bypass)
+ fimc_hwset_stop_scaler(ctrl);
+ else
+ ctrl->sc.bypass = 0;
+
+ fimc_wait_disable_capture(ctrl);
+
+ return 0;
+}
+
+static void fimc_reset_capture(struct fimc_control *ctrl)
+{
+ int i;
+
+ ctrl->status = FIMC_READY_OFF;
+
+ fimc_stop_capture(ctrl);
+
+ for (i = 0; i < FIMC_PINGPONG; i++)
+ fimc_add_inqueue(ctrl, ctrl->cap->outq[i]);
+
+ fimc_hwset_reset(ctrl);
+
+ if (0 != ctrl->id)
+ fimc_clk_en(ctrl, false);
+
+ ctrl->status = FIMC_STREAMOFF;
+}
+
+
+int fimc_streamon_capture(void *fh)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_capinfo *cap = ctrl->cap;
+ int rot;
+ int ret;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture device.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (ctrl->status == FIMC_STREAMON) {
+ fimc_err("%s: Camera already running.\n", __func__);
+ return -EBUSY;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ if (0 != ctrl->id)
+ fimc_clk_en(ctrl, true);
+
+ ctrl->status = FIMC_READY_ON;
+ cap->irq = 0;
+
+ fimc_hwset_enable_irq(ctrl, 0, 1);
+
+ if (!ctrl->cam->initialized)
+ fimc_camera_init(ctrl);
+
+ if (ctrl->id != 2 &&
+ ctrl->cap->fmt.colorspace != V4L2_COLORSPACE_JPEG) {
+ ret = fimc_camera_start(ctrl);
+ if (ret < 0) {
+ fimc_reset_capture(ctrl);
+ mutex_unlock(&ctrl->v4l2_lock);
+ return ret;
+ }
+ }
+
+ fimc_hwset_camera_type(ctrl);
+ fimc_hwset_camera_polarity(ctrl);
+ fimc_update_hwaddr(ctrl);
+
+ if (cap->fmt.pixelformat != V4L2_PIX_FMT_JPEG) {
+ fimc_hwset_camera_source(ctrl);
+ fimc_hwset_camera_offset(ctrl);
+
+ fimc_capture_scaler_info(ctrl);
+ fimc_hwset_prescaler(ctrl, &ctrl->sc);
+ fimc_hwset_scaler(ctrl, &ctrl->sc);
+
+ fimc_hwset_output_colorspace(ctrl, cap->fmt.pixelformat);
+ fimc_hwset_output_addr_style(ctrl, cap->fmt.pixelformat);
+ fimc_hwset_output_area(ctrl, cap->fmt.width, cap->fmt.height);
+
+ if (cap->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
+ cap->fmt.pixelformat == V4L2_PIX_FMT_RGB565)
+ fimc_hwset_output_rgb(ctrl, cap->fmt.pixelformat);
+ else
+ fimc_hwset_output_yuv(ctrl, cap->fmt.pixelformat);
+
+ fimc_hwset_output_size(ctrl, cap->fmt.width, cap->fmt.height);
+
+ fimc_hwset_output_scan(ctrl, &cap->fmt);
+ fimc_hwset_output_rot_flip(ctrl, cap->rotate, cap->flip);
+ rot = fimc_mapping_rot_flip(cap->rotate, cap->flip);
+
+ if (rot & FIMC_ROT) {
+ fimc_hwset_org_output_size(ctrl, cap->fmt.height,
+ cap->fmt.width);
+ } else {
+ fimc_hwset_org_output_size(ctrl, cap->fmt.width,
+ cap->fmt.height);
+ }
+ fimc_hwset_jpeg_mode(ctrl, false);
+ } else {
+ fimc_hwset_output_area_size(ctrl, \
+ fimc_camera_get_jpeg_memsize(ctrl)/2);
+ fimc_hwset_jpeg_mode(ctrl, true);
+ }
+
+ if (ctrl->cap->fmt.colorspace == V4L2_COLORSPACE_JPEG)
+ fimc_hwset_scaler_bypass(ctrl);
+
+ fimc_start_capture(ctrl);
+
+ if (ctrl->cap->fmt.colorspace == V4L2_COLORSPACE_JPEG &&
+ ctrl->id != 2) {
+ struct v4l2_control cam_ctrl;
+
+ cam_ctrl.id = V4L2_CID_CAM_CAPTURE;
+ ret = subdev_call(ctrl, core, s_ctrl, &cam_ctrl);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ fimc_reset_capture(ctrl);
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_err("%s: Error in V4L2_CID_CAM_CAPTURE\n", \
+ __func__);
+ return -EPERM;
+ }
+ }
+
+ ctrl->status = FIMC_STREAMON;
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_streamoff_capture(void *fh)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ fimc_dbg("%s\n", __func__);
+
+ if (!ctrl->cap || !ctrl->cam || !ctrl->cam->sd) {
+ fimc_err("%s: No capture info.\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ fimc_reset_capture(ctrl);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_qbuf_capture(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ if (!ctrl->cap || !ctrl->cap->nr_bufs) {
+ fimc_err("%s: Invalid capture setting.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ fimc_err("%s: invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctrl->v4l2_lock);
+ fimc_add_inqueue(ctrl, b->index);
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_dqbuf_capture(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_capinfo *cap;
+ int pp, ret = 0;
+
+ if (!ctrl->cap || !ctrl->cap->nr_bufs) {
+ fimc_err("%s: Invalid capture setting.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ fimc_err("%s: invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ cap = ctrl->cap;
+
+ mutex_lock(&ctrl->v4l2_lock);
+
+ if (ctrl->status != FIMC_STREAMON) {
+ mutex_unlock(&ctrl->v4l2_lock);
+ fimc_dbg("%s: FIMC is not active.\n", __func__);
+ return -EINVAL;
+ }
+
+ /* find out the real index */
+ pp = ((fimc_hwget_frame_count(ctrl) + 2) % 4);
+
+ /* We have read the latest frame, hence should reset availability
+ * flag
+ */
+ cap->irq = 0;
+
+ /* skip even frame: no data */
+ if (cap->fmt.field == V4L2_FIELD_INTERLACED_TB)
+ pp &= ~0x1;
+
+ b->index = cap->outq[pp];
+ fimc_dbg("%s: buffer(%d) outq[%d]\n", __func__, b->index, pp);
+
+ ret = fimc_add_outqueue(ctrl, pp);
+ if (ret) {
+ b->index = -1;
+ fimc_err("%s: no inqueue buffer\n", __func__);
+ }
+
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ /* fimc_dbg("%s: buf_index = %d\n", __func__, b->index); */
+
+ return ret;
+}
+
diff --git a/drivers/media/video/samsung/fimc/fimc_dev.c b/drivers/media/video/samsung/fimc/fimc_dev.c
new file mode 100644
index 0000000..17945d4
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_dev.c
@@ -0,0 +1,1688 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_dev.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Core file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-device.h>
+#include <linux/io.h>
+#include <linux/memory.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <plat/clock.h>
+#include <plat/media.h>
+#include <mach/media.h>
+#include <plat/fimc.h>
+#include <linux/videodev2_samsung.h>
+#include <linux/delay.h>
+#include <plat/regs-fimc.h>
+
+#include "fimc.h"
+
+struct fimc_global *fimc_dev;
+
+int fimc_dma_alloc(struct fimc_control *ctrl, struct fimc_buf_set *bs,
+ int i, int align)
+{
+ dma_addr_t end, *curr;
+
+ mutex_lock(&ctrl->alloc_lock);
+
+ end = ctrl->mem.base + ctrl->mem.size;
+ curr = &ctrl->mem.curr;
+
+ if (!bs->length[i])
+ return -EINVAL;
+
+ if (!align) {
+ if (*curr + bs->length[i] > end) {
+ goto overflow;
+ } else {
+ bs->base[i] = *curr;
+ bs->garbage[i] = 0;
+ *curr += bs->length[i];
+ }
+ } else {
+ if (ALIGN(*curr, align) + bs->length[i] > end)
+ goto overflow;
+ else {
+ bs->base[i] = ALIGN(*curr, align);
+ bs->garbage[i] = ALIGN(*curr, align) - *curr;
+ *curr += (bs->length[i] + bs->garbage[i]);
+ }
+ }
+
+ mutex_unlock(&ctrl->alloc_lock);
+
+ return 0;
+
+overflow:
+ bs->base[i] = 0;
+ bs->length[i] = 0;
+ bs->garbage[i] = 0;
+
+ mutex_unlock(&ctrl->alloc_lock);
+
+ return -ENOMEM;
+}
+
+void fimc_dma_free(struct fimc_control *ctrl, struct fimc_buf_set *bs, int i)
+{
+ int total = bs->length[i] + bs->garbage[i];
+ mutex_lock(&ctrl->alloc_lock);
+
+ if (bs->base[i]) {
+ if (ctrl->mem.curr - total >= ctrl->mem.base)
+ ctrl->mem.curr -= total;
+
+ bs->base[i] = 0;
+ bs->length[i] = 0;
+ bs->garbage[i] = 0;
+ }
+
+ mutex_unlock(&ctrl->alloc_lock);
+}
+
+void fimc_clk_en(struct fimc_control *ctrl, bool on)
+{
+ struct platform_device *pdev;
+ struct s3c_platform_fimc *pdata;
+ struct clk *lclk;
+
+ pdev = to_platform_device(ctrl->dev);
+ pdata = to_fimc_plat(ctrl->dev);
+ lclk = clk_get(&pdev->dev, pdata->lclk_name);
+
+ if (on) {
+ if (!lclk->usage) {
+ if (!ctrl->out)
+ fimc_info1("(%d) Clock %s(%d) enabled.\n",
+ ctrl->id, ctrl->clk->name,
+ ctrl->clk->id);
+
+ /* Turn on fimc power domain regulator */
+ regulator_enable(ctrl->regulator);
+ clk_enable(lclk);
+ }
+ } else {
+ while (lclk->usage > 0) {
+ if (!ctrl->out)
+ fimc_info1("(%d) Clock %s(%d) disabled.\n",
+ ctrl->id, ctrl->clk->name,
+ ctrl->clk->id);
+ clk_disable(lclk);
+ /* Turn off fimc power domain regulator */
+ regulator_disable(ctrl->regulator);
+ }
+ }
+
+}
+
+static inline u32 fimc_irq_out_single_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ int ret = -1, ctx_num, next;
+ u32 wakeup = 1;
+
+ if (ctx->status == FIMC_READY_OFF) {
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctx->status = FIMC_STREAMOFF;
+ ctrl->status = FIMC_STREAMOFF;
+
+ return wakeup;
+ }
+
+ ctx->status = FIMC_STREAMON_IDLE;
+
+ /* Attach done buffer to outgoing queue. */
+ ret = fimc_push_outq(ctrl, ctx, ctrl->out->idxs.active.idx);
+ if (ret < 0)
+ fimc_err("Failed: fimc_push_outq\n");
+
+ /* Detach buffer from incomming queue. */
+ ret = fimc_pop_inq(ctrl, &ctx_num, &next);
+ if (ret == 0) { /* There is a buffer in incomming queue. */
+ if (ctx_num != ctrl->out->last_ctx) {
+ ctx = &ctrl->out->ctx[ctx_num];
+ ctrl->out->last_ctx = ctx->ctx_num;
+ fimc_outdev_set_ctx_param(ctrl, ctx);
+ }
+
+ fimc_outdev_set_src_addr(ctrl, ctx->src[next].base);
+
+ fimc_output_set_dst_addr(ctrl, ctx, next);
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0)
+ fimc_err("Fail: fimc_start_camif\n");
+
+ ctrl->out->idxs.active.ctx = ctx_num;
+ ctrl->out->idxs.active.idx = next;
+ ctx->status = FIMC_STREAMON;
+ ctrl->status = FIMC_STREAMON;
+ } else { /* There is no buffer in incomming queue. */
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctx->status = FIMC_STREAMON_IDLE;
+ ctrl->status = FIMC_STREAMON_IDLE;
+ }
+
+ return wakeup;
+}
+
+static inline u32 fimc_irq_out_multi_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ int ret = -1, ctx_num, next;
+ u32 wakeup = 1;
+
+ if (ctx->status == FIMC_READY_OFF) {
+ if (ctrl->out->idxs.active.ctx == ctx->ctx_num) {
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ }
+
+ ctx->status = FIMC_STREAMOFF;
+
+ return wakeup;
+ }
+
+ /* Attach done buffer to outgoing queue. */
+ ret = fimc_push_outq(ctrl, ctx, ctrl->out->idxs.active.idx);
+ if (ret < 0)
+ fimc_err("Failed: fimc_push_outq\n");
+
+ /* Detach buffer from incomming queue. */
+ ret = fimc_pop_inq(ctrl, &ctx_num, &next);
+ if (ret == 0) { /* There is a buffer in incomming queue. */
+ if (ctx_num != ctrl->out->last_ctx) {
+ ctx = &ctrl->out->ctx[ctx_num];
+ ctrl->out->last_ctx = ctx->ctx_num;
+ fimc_outdev_set_ctx_param(ctrl, ctx);
+ }
+
+ fimc_outdev_set_src_addr(ctrl, ctx->src[next].base);
+
+ fimc_output_set_dst_addr(ctrl, ctx, next);
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0)
+ fimc_err("Fail: fimc_start_camif\n");
+
+ ctrl->out->idxs.active.ctx = ctx_num;
+ ctrl->out->idxs.active.idx = next;
+ ctx->status = FIMC_STREAMON;
+ ctrl->status = FIMC_STREAMON;
+ } else { /* There is no buffer in incomming queue. */
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctx->status = FIMC_STREAMON_IDLE;
+ ctrl->status = FIMC_STREAMON_IDLE;
+ }
+
+ return wakeup;
+}
+
+static inline u32 fimc_irq_out_dma(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct fimc_buf_set buf_set;
+ int idx = ctrl->out->idxs.active.idx;
+ int ret = -1, i, ctx_num, next;
+ u32 wakeup = 1;
+
+ if (ctx->status == FIMC_READY_OFF) {
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctx->status = FIMC_STREAMOFF;
+ ctrl->status = FIMC_STREAMOFF;
+ return wakeup;
+ }
+
+ /* Attach done buffer to outgoing queue. */
+ ret = fimc_push_outq(ctrl, ctx, idx);
+ if (ret < 0)
+ fimc_err("Failed: fimc_push_outq\n");
+
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) {
+ struct s3cfb_window *win;
+ struct fb_info *fbinfo;
+
+ fbinfo = registered_fb[ctx->overlay.fb_id];
+ win = (struct s3cfb_window *)fbinfo->par;
+
+ win->other_mem_addr = ctx->dst[idx].base[FIMC_ADDR_Y];
+
+ ret = fb_pan_display(fbinfo, &fbinfo->var);
+ if (ret < 0) {
+ fimc_err("%s: fb_pan_display fail (ret=%d)\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+ }
+
+ /* Detach buffer from incomming queue. */
+ ret = fimc_pop_inq(ctrl, &ctx_num, &next);
+ if (ret == 0) { /* There is a buffer in incomming queue. */
+ ctx = &ctrl->out->ctx[ctx_num];
+ fimc_outdev_set_src_addr(ctrl, ctx->src[next].base);
+
+ memset(&buf_set, 0x00, sizeof(buf_set));
+ buf_set.base[FIMC_ADDR_Y] = ctx->dst[next].base[FIMC_ADDR_Y];
+
+ for (i = 0; i < FIMC_PHYBUFS; i++)
+ fimc_hwset_output_address(ctrl, &buf_set, i);
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0)
+ fimc_err("Fail: fimc_start_camif\n");
+
+ ctrl->out->idxs.active.ctx = ctx_num;
+ ctrl->out->idxs.active.idx = next;
+
+ ctx->status = FIMC_STREAMON;
+ ctrl->status = FIMC_STREAMON;
+ } else { /* There is no buffer in incomming queue. */
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+
+ ctx->status = FIMC_STREAMON_IDLE;
+ ctrl->status = FIMC_STREAMON_IDLE;
+ }
+
+ return wakeup;
+}
+
+static inline u32 fimc_irq_out_fimd(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct fimc_idx prev;
+ int ret = -1, ctx_num, next;
+ u32 wakeup = 0;
+
+ /* Attach done buffer to outgoing queue. */
+ if (ctrl->out->idxs.prev.idx != -1) {
+ ret = fimc_push_outq(ctrl, ctx, ctrl->out->idxs.prev.idx);
+ if (ret < 0) {
+ fimc_err("Failed: fimc_push_outq\n");
+ } else {
+ ctrl->out->idxs.prev.ctx = -1;
+ ctrl->out->idxs.prev.idx = -1;
+ wakeup = 1; /* To wake up fimc_v4l2_dqbuf */
+ }
+ }
+
+ /* Update index structure. */
+ if (ctrl->out->idxs.next.idx != -1) {
+ ctrl->out->idxs.active.ctx = ctrl->out->idxs.next.ctx;
+ ctrl->out->idxs.active.idx = ctrl->out->idxs.next.idx;
+ ctrl->out->idxs.next.idx = -1;
+ ctrl->out->idxs.next.ctx = -1;
+ }
+
+ /* Detach buffer from incomming queue. */
+ ret = fimc_pop_inq(ctrl, &ctx_num, &next);
+ if (ret == 0) { /* There is a buffer in incomming queue. */
+ prev.ctx = ctrl->out->idxs.active.ctx;
+ prev.idx = ctrl->out->idxs.active.idx;
+
+ ctrl->out->idxs.prev.ctx = prev.ctx;
+ ctrl->out->idxs.prev.idx = prev.idx;
+
+ ctrl->out->idxs.next.ctx = ctx_num;
+ ctrl->out->idxs.next.idx = next;
+
+ /* set source address */
+ fimc_outdev_set_src_addr(ctrl, ctx->src[next].base);
+ }
+
+ return wakeup;
+}
+
+static inline void fimc_irq_out(struct fimc_control *ctrl)
+{
+ struct fimc_ctx *ctx;
+ u32 wakeup = 1;
+ int ctx_num = ctrl->out->idxs.active.ctx;
+
+ /* Interrupt pendding clear */
+ fimc_hwset_clear_irq(ctrl);
+
+ /* check context num */
+ if (ctx_num < 0 || ctx_num >= FIMC_MAX_CTXS) {
+ fimc_err("fimc_irq_out: invalid ctx (ctx=%d)\n", ctx_num);
+ wake_up(&ctrl->wq);
+ return;
+ }
+
+ ctx = &ctrl->out->ctx[ctx_num];
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_NONE_SINGLE_BUF:
+ wakeup = fimc_irq_out_single_buf(ctrl, ctx);
+ break;
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ wakeup = fimc_irq_out_multi_buf(ctrl, ctx);
+ break;
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ wakeup = fimc_irq_out_dma(ctrl, ctx);
+ break;
+ default:
+ fimc_err("[ctx=%d] fimc_irq_out: wrong overlay.mode (%d)\n",
+ ctx_num, ctx->overlay.mode);
+ break;
+ }
+
+ if (wakeup == 1)
+ wake_up(&ctrl->wq);
+}
+
+static inline void fimc_irq_cap(struct fimc_control *ctrl)
+{
+ struct fimc_capinfo *cap = ctrl->cap;
+ int pp;
+ u32 cfg;
+
+ fimc_hwset_clear_irq(ctrl);
+ if (fimc_hwget_overflow_state(ctrl)) {
+ /* s/w reset -- added for recovering module in ESD state*/
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg |= (S3C_CIGCTRL_SWRST);
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+ msleep(1);
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~S3C_CIGCTRL_SWRST;
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+ }
+ pp = ((fimc_hwget_frame_count(ctrl) + 2) % 4);
+ if (cap->fmt.field == V4L2_FIELD_INTERLACED_TB) {
+ /* odd value of pp means one frame is made with top/bottom */
+ if (pp & 0x1) {
+ cap->irq = 1;
+ wake_up(&ctrl->wq);
+ }
+ } else {
+ cap->irq = 1;
+ wake_up(&ctrl->wq);
+ }
+}
+
+static irqreturn_t fimc_irq(int irq, void *dev_id)
+{
+ struct fimc_control *ctrl = (struct fimc_control *) dev_id;
+
+ if (ctrl->cap)
+ fimc_irq_cap(ctrl);
+ else if (ctrl->out)
+ fimc_irq_out(ctrl);
+
+ return IRQ_HANDLED;
+}
+
+static
+struct fimc_control *fimc_register_controller(struct platform_device *pdev)
+{
+ struct s3c_platform_fimc *pdata;
+ struct fimc_control *ctrl;
+ struct resource *res;
+ int id, mdev_id;
+
+ id = pdev->id;
+ mdev_id = S5P_MDEV_FIMC0 + id;
+ pdata = to_fimc_plat(&pdev->dev);
+
+ ctrl = get_fimc_ctrl(id);
+ ctrl->id = id;
+ ctrl->dev = &pdev->dev;
+ ctrl->vd = &fimc_video_device[id];
+ ctrl->vd->minor = id;
+
+ /* alloc from bank1 as default */
+ ctrl->mem.base = pdata->pmem_start;
+ ctrl->mem.size = pdata->pmem_size;
+ ctrl->mem.curr = ctrl->mem.base;
+
+ ctrl->status = FIMC_STREAMOFF;
+ switch (pdata->hw_ver) {
+ case 0x40:
+ ctrl->limit = &fimc40_limits[id];
+ break;
+ case 0x43:
+ case 0x45:
+ ctrl->limit = &fimc43_limits[id];
+ break;
+ case 0x50:
+ ctrl->limit = &fimc50_limits[id];
+ break;
+ }
+
+ ctrl->log = FIMC_LOG_DEFAULT;
+
+ sprintf(ctrl->name, "%s%d", FIMC_NAME, id);
+ strcpy(ctrl->vd->name, ctrl->name);
+
+ atomic_set(&ctrl->in_use, 0);
+ mutex_init(&ctrl->lock);
+ mutex_init(&ctrl->alloc_lock);
+ mutex_init(&ctrl->v4l2_lock);
+ init_waitqueue_head(&ctrl->wq);
+
+ /* get resource for io memory */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ fimc_err("%s: failed to get io memory region\n", __func__);
+ return NULL;
+ }
+
+ /* request mem region */
+ res = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (!res) {
+ fimc_err("%s: failed to request io memory region\n", __func__);
+ return NULL;
+ }
+
+ /* ioremap for register block */
+ ctrl->regs = ioremap(res->start, res->end - res->start + 1);
+ if (!ctrl->regs) {
+ fimc_err("%s: failed to remap io region\n", __func__);
+ return NULL;
+ }
+
+ /* irq */
+ ctrl->irq = platform_get_irq(pdev, 0);
+ if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl))
+ fimc_err("%s: request_irq failed\n", __func__);
+
+ fimc_hwset_reset(ctrl);
+
+ return ctrl;
+}
+
+static int fimc_unregister_controller(struct platform_device *pdev)
+{
+ struct s3c_platform_fimc *pdata;
+ struct fimc_control *ctrl;
+ int id = pdev->id;
+
+ pdata = to_fimc_plat(&pdev->dev);
+ ctrl = get_fimc_ctrl(id);
+
+ free_irq(ctrl->irq, ctrl);
+ mutex_destroy(&ctrl->lock);
+ mutex_destroy(&ctrl->alloc_lock);
+ mutex_destroy(&ctrl->v4l2_lock);
+
+ fimc_clk_en(ctrl, false);
+
+ iounmap(ctrl->regs);
+ memset(ctrl, 0, sizeof(*ctrl));
+
+ return 0;
+}
+
+static void fimc_mmap_open(struct vm_area_struct *vma)
+{
+ struct fimc_global *dev = fimc_dev;
+ int pri_data = (int)vma->vm_private_data;
+ u32 id = pri_data / 0x100;
+ u32 ctx = (pri_data - (id * 0x100)) / 0x10;
+ u32 idx = pri_data % 0x10;
+
+ atomic_inc(&dev->ctrl[id].out->ctx[ctx].src[idx].mapped_cnt);
+}
+
+static void fimc_mmap_close(struct vm_area_struct *vma)
+{
+ struct fimc_global *dev = fimc_dev;
+ int pri_data = (int)vma->vm_private_data;
+ u32 id = pri_data / 0x100;
+ u32 ctx = (pri_data - (id * 0x100)) / 0x10;
+ u32 idx = pri_data % 0x10;
+
+ atomic_dec(&dev->ctrl[id].out->ctx[ctx].src[idx].mapped_cnt);
+}
+
+static struct vm_operations_struct fimc_mmap_ops = {
+ .open = fimc_mmap_open,
+ .close = fimc_mmap_close,
+};
+
+static inline
+int fimc_mmap_out_src(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ int ctx_id = prv_data->ctx_id;
+ struct fimc_ctx *ctx = &ctrl->out->ctx[ctx_id];
+ u32 start_phy_addr = 0;
+ u32 size = vma->vm_end - vma->vm_start;
+ u32 pfn, idx = vma->vm_pgoff;
+ u32 buf_length = 0;
+ int pri_data = 0;
+
+ buf_length = PAGE_ALIGN(ctx->src[idx].length[FIMC_ADDR_Y] +
+ ctx->src[idx].length[FIMC_ADDR_CB] +
+ ctx->src[idx].length[FIMC_ADDR_CR]);
+ if (size > PAGE_ALIGN(buf_length)) {
+ fimc_err("Requested mmap size is too big\n");
+ return -EINVAL;
+ }
+
+ pri_data = (ctrl->id * 0x100) + (ctx_id * 0x10) + idx;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_ops = &fimc_mmap_ops;
+ vma->vm_private_data = (void *)pri_data;
+
+ if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
+ fimc_err("writable mapping must be shared\n");
+ return -EINVAL;
+ }
+
+ start_phy_addr = ctx->src[idx].base[FIMC_ADDR_Y];
+ pfn = __phys_to_pfn(start_phy_addr);
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
+ fimc_err("mmap fail\n");
+ return -EINVAL;
+ }
+
+ vma->vm_ops->open(vma);
+
+ ctx->src[idx].flags |= V4L2_BUF_FLAG_MAPPED;
+
+ return 0;
+}
+
+static inline
+int fimc_mmap_out_dst(struct file *filp, struct vm_area_struct *vma, u32 idx)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ int ctx_id = prv_data->ctx_id;
+ unsigned long pfn = 0, size;
+ int ret = 0;
+
+ size = vma->vm_end - vma->vm_start;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED;
+
+ pfn = __phys_to_pfn(ctrl->out->ctx[ctx_id].dst[idx].base[0]);
+ ret = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
+ if (ret != 0)
+ fimc_err("remap_pfn_range fail.\n");
+
+ return ret;
+}
+
+static inline int fimc_mmap_out(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ int ctx_id = prv_data->ctx_id;
+ int idx = ctrl->out->ctx[ctx_id].overlay.req_idx;
+ int ret = -1;
+
+ if (idx >= 0)
+ ret = fimc_mmap_out_dst(filp, vma, idx);
+ else if (idx == FIMC_MMAP_IDX)
+ ret = fimc_mmap_out_src(filp, vma);
+
+ return ret;
+}
+
+static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ u32 size = vma->vm_end - vma->vm_start;
+ u32 pfn, idx = vma->vm_pgoff;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED;
+
+ /*
+ * page frame number of the address for a source frame
+ * to be stored at.
+ */
+ pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);
+
+ if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
+ fimc_err("%s: writable mapping must be shared\n", __func__);
+ return -EINVAL;
+ }
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
+ fimc_err("%s: mmap fail\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ int ret;
+
+ if (ctrl->cap)
+ ret = fimc_mmap_cap(filp, vma);
+ else
+ ret = fimc_mmap_out(filp, vma);
+
+ return ret;
+}
+
+static u32 fimc_poll(struct file *filp, poll_table *wait)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ struct fimc_capinfo *cap = ctrl->cap;
+ u32 mask = 0;
+
+ if (cap) {
+ if (cap->irq || (ctrl->status != FIMC_STREAMON)) {
+ mask = POLLIN | POLLRDNORM;
+ cap->irq = 0;
+ } else {
+ poll_wait(filp, &ctrl->wq, wait);
+ }
+ }
+
+ return mask;
+}
+
+static
+ssize_t fimc_read(struct file *filp, char *buf, size_t count, loff_t *pos)
+{
+ return 0;
+}
+
+static
+ssize_t fimc_write(struct file *filp, const char *b, size_t c, loff_t *offset)
+{
+ return 0;
+}
+
+u32 fimc_mapping_rot_flip(u32 rot, u32 flip)
+{
+ u32 ret = 0;
+
+ switch (rot) {
+ case 0:
+ if (flip & FIMC_XFLIP)
+ ret |= FIMC_XFLIP;
+
+ if (flip & FIMC_YFLIP)
+ ret |= FIMC_YFLIP;
+ break;
+
+ case 90:
+ ret = FIMC_ROT;
+ if (flip & FIMC_XFLIP)
+ ret |= FIMC_XFLIP;
+
+ if (flip & FIMC_YFLIP)
+ ret |= FIMC_YFLIP;
+ break;
+
+ case 180:
+ ret = (FIMC_XFLIP | FIMC_YFLIP);
+ if (flip & FIMC_XFLIP)
+ ret &= ~FIMC_XFLIP;
+
+ if (flip & FIMC_YFLIP)
+ ret &= ~FIMC_YFLIP;
+ break;
+
+ case 270:
+ ret = (FIMC_XFLIP | FIMC_YFLIP | FIMC_ROT);
+ if (flip & FIMC_XFLIP)
+ ret &= ~FIMC_XFLIP;
+
+ if (flip & FIMC_YFLIP)
+ ret &= ~FIMC_YFLIP;
+ break;
+ }
+
+ return ret;
+}
+
+int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+ if (src >= tar * 64) {
+ return -EINVAL;
+ } else if (src >= tar * 32) {
+ *ratio = 32;
+ *shift = 5;
+ } else if (src >= tar * 16) {
+ *ratio = 16;
+ *shift = 4;
+ } else if (src >= tar * 8) {
+ *ratio = 8;
+ *shift = 3;
+ } else if (src >= tar * 4) {
+ *ratio = 4;
+ *shift = 2;
+ } else if (src >= tar * 2) {
+ *ratio = 2;
+ *shift = 1;
+ } else {
+ *ratio = 1;
+ *shift = 0;
+ }
+
+ return 0;
+}
+
+void fimc_get_nv12t_size(int img_hres, int img_vres,
+ int *y_size, int *cb_size)
+{
+ int remain;
+ int y_hres_byte, y_vres_byte;
+ int cb_hres_byte, cb_vres_byte;
+ int y_hres_roundup, y_vres_roundup;
+ int cb_hres_roundup, cb_vres_roundup;
+
+ /* to make 'img_hres and img_vres' be 16 multiple */
+ remain = img_hres % 16;
+ if (remain != 0) {
+ remain = 16 - remain;
+ img_hres = img_hres + remain;
+ }
+ remain = img_vres % 16;
+ if (remain != 0) {
+ remain = 16 - remain;
+ img_vres = img_vres + remain;
+ }
+
+ cb_hres_byte = img_hres;
+ cb_vres_byte = img_vres;
+
+ y_hres_byte = img_hres - 1;
+ y_vres_byte = img_vres - 1;
+ y_hres_roundup = ((y_hres_byte >> 4) >> 3) + 1;
+ y_vres_roundup = ((y_vres_byte >> 4) >> 2) + 1;
+ if ((y_vres_byte & 0x20) == 0) {
+ y_hres_byte = y_hres_byte & 0x7f00;
+ y_hres_byte = y_hres_byte >> 8;
+ y_hres_byte = y_hres_byte & 0x7f;
+
+ y_vres_byte = y_vres_byte & 0x7fc0;
+ y_vres_byte = y_vres_byte >> 6;
+ y_vres_byte = y_vres_byte & 0x1ff;
+
+ *y_size = y_hres_byte +\
+ (y_vres_byte * y_hres_roundup) + 1;
+ } else {
+ *y_size = y_hres_roundup * y_vres_roundup;
+ }
+
+ *y_size = *(y_size) << 13;
+
+ cb_hres_byte = img_hres - 1;
+ cb_vres_byte = (img_vres >> 1) - 1;
+ cb_hres_roundup = ((cb_hres_byte >> 4) >> 3) + 1;
+ cb_vres_roundup = ((cb_vres_byte >> 4) >> 2) + 1;
+ if ((cb_vres_byte & 0x20) == 0) {
+ cb_hres_byte = cb_hres_byte & 0x7f00;
+ cb_hres_byte = cb_hres_byte >> 8;
+ cb_hres_byte = cb_hres_byte & 0x7f;
+
+ cb_vres_byte = cb_vres_byte & 0x7fc0;
+ cb_vres_byte = cb_vres_byte >> 6;
+ cb_vres_byte = cb_vres_byte & 0x1ff;
+
+ *cb_size = cb_hres_byte + (cb_vres_byte * cb_hres_roundup) + 1;
+ } else {
+ *cb_size = cb_hres_roundup * cb_vres_roundup;
+ }
+ *cb_size = (*cb_size) << 13;
+
+}
+
+static int fimc_get_free_ctx(struct fimc_control *ctrl)
+{
+ int i;
+
+ if (1 != ctrl->id)
+ return 0;
+
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ if (ctrl->ctx_busy[i] == 0) {
+ ctrl->ctx_busy[i] = 1;
+ fimc_info1("Current context is %d\n", i);
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int fimc_open(struct file *filp)
+{
+ struct fimc_control *ctrl;
+ struct s3c_platform_fimc *pdata;
+ struct fimc_prv_data *prv_data;
+ int in_use;
+ int ret;
+
+ ctrl = video_get_drvdata(video_devdata(filp));
+ pdata = to_fimc_plat(ctrl->dev);
+
+ mutex_lock(&ctrl->lock);
+
+ in_use = atomic_read(&ctrl->in_use);
+ if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) {
+ fimc_err("%s: Device busy.\n", __func__);
+ ret = -EBUSY;
+ goto resource_busy;
+ } else {
+ atomic_inc(&ctrl->in_use);
+ }
+ in_use = atomic_read(&ctrl->in_use);
+
+ prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL);
+ if (!prv_data) {
+ fimc_err("%s: not enough memory\n", __func__);
+ ret = -ENOMEM;
+ goto kzalloc_err;
+ }
+
+ prv_data->ctx_id = fimc_get_free_ctx(ctrl);
+ if (prv_data->ctx_id < 0) {
+ fimc_err("%s: Context busy flag not reset.\n", __func__);
+ ret = -EBUSY;
+ goto ctx_err;
+ }
+ prv_data->ctrl = ctrl;
+ filp->private_data = prv_data;
+
+ if (in_use == 1) {
+ fimc_clk_en(ctrl, true);
+
+ if (pdata->hw_ver == 0x40)
+ fimc_hw_reset_camera(ctrl);
+
+ /* Apply things to interface register */
+ fimc_hwset_reset(ctrl);
+
+ if (num_registered_fb > 0) {
+ struct fb_info *fbinfo = registered_fb[0];
+ ctrl->fb.lcd_hres = (int)fbinfo->var.xres;
+ ctrl->fb.lcd_vres = (int)fbinfo->var.yres;
+ fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d\n",
+ __func__, ctrl->fb.lcd_hres,
+ ctrl->fb.lcd_vres);
+ }
+
+ ctrl->mem.curr = ctrl->mem.base;
+ ctrl->status = FIMC_STREAMOFF;
+
+ if (0 != ctrl->id)
+ fimc_clk_en(ctrl, false);
+ }
+
+ mutex_unlock(&ctrl->lock);
+
+ fimc_info1("%s opened.\n", ctrl->name);
+
+ return 0;
+
+ctx_err:
+ kfree(prv_data);
+
+kzalloc_err:
+ atomic_dec(&ctrl->in_use);
+
+resource_busy:
+ mutex_unlock(&ctrl->lock);
+ return ret;
+}
+
+static int fimc_release(struct file *filp)
+{
+ struct fimc_prv_data *prv_data =
+ (struct fimc_prv_data *)filp->private_data;
+ struct fimc_control *ctrl = prv_data->ctrl;
+ int ctx_id = prv_data->ctx_id;
+ struct s3c_platform_fimc *pdata;
+ struct fimc_overlay_buf *buf;
+ struct mm_struct *mm = current->mm;
+ struct fimc_ctx *ctx;
+ int ret = 0, i;
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ pdata = to_fimc_plat(ctrl->dev);
+
+ mutex_lock(&ctrl->lock);
+ atomic_dec(&ctrl->in_use);
+
+ /* FIXME: turning off actual working camera */
+ if (ctrl->cam && ctrl->id != 2) {
+ /* Unload the subdev (camera sensor) module,
+ * reset related status flags
+ */
+ fimc_release_subdev(ctrl);
+ }
+
+ if (ctrl->cap) {
+ ctrl->mem.curr = ctrl->mem.base;
+ kfree(filp->private_data);
+ filp->private_data = NULL;
+
+ for (i = 0; i < FIMC_CAPBUFS; i++) {
+ fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 0);
+ fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 1);
+ fimc_dma_free(ctrl, &ctrl->cap->bufs[i], 2);
+ }
+
+ fimc_clk_en(ctrl, false);
+
+ kfree(ctrl->cap);
+ ctrl->cap = NULL;
+ }
+
+ if (ctrl->out) {
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_clk_en(ctrl, true);
+ ret = fimc_outdev_stop_streaming(ctrl, ctx);
+ fimc_clk_en(ctrl, false);
+ if (ret < 0)
+ fimc_err("Fail: fimc_stop_streaming\n");
+
+ ret = fimc_init_in_queue(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_init_in_queue\n");
+ ret = -EINVAL;
+ goto release_err;
+ }
+
+ ret = fimc_init_out_queue(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_init_out_queue\n");
+ ret = -EINVAL;
+ goto release_err;
+ }
+
+ /* Make all buffers DQUEUED state. */
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].state = VIDEOBUF_IDLE;
+ ctx->src[i].flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) {
+ ctrl->mem.curr = ctx->dst[0].base[FIMC_ADDR_Y];
+
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->dst[i].base[FIMC_ADDR_Y] = 0;
+ ctx->dst[i].length[FIMC_ADDR_Y] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CB] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CB] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CR] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CR] = 0;
+ }
+ }
+
+ ctx->status = FIMC_STREAMOFF;
+ }
+
+ buf = &ctx->overlay.buf;
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ if (buf->vir_addr[i]) {
+ ret = do_munmap(mm, buf->vir_addr[i],
+ buf->size[i]);
+ if (ret < 0)
+ fimc_err("%s: do_munmap fail\n", \
+ __func__);
+ }
+ }
+
+ ctrl->ctx_busy[ctx_id] = 0;
+ memset(ctx, 0x00, sizeof(struct fimc_ctx));
+
+ if (atomic_read(&ctrl->in_use) == 0) {
+ ctrl->status = FIMC_STREAMOFF;
+ fimc_outdev_init_idxs(ctrl);
+
+ fimc_clk_en(ctrl, false);
+
+ ctrl->mem.curr = ctrl->mem.base;
+
+ kfree(ctrl->out);
+ ctrl->out = NULL;
+
+ kfree(filp->private_data);
+ filp->private_data = NULL;
+ }
+ }
+
+ /*
+ * it remain afterimage when I play movie using overlay and exit
+ */
+ if (ctrl->fb.is_enable == 1) {
+ fimc_info2("WIN_OFF for FIMC%d\n", ctrl->id);
+ ret = fb_blank(registered_fb[ctx->overlay.fb_id],
+ FB_BLANK_POWERDOWN);
+ if (ret < 0) {
+ fimc_err("%s: fb_blank: fb[%d] " \
+ "mode=FB_BLANK_POWERDOWN\n",
+ __func__, ctx->overlay.fb_id);
+ ret = -EINVAL;
+ goto release_err;
+ }
+
+ ctrl->fb.is_enable = 0;
+ }
+
+ mutex_unlock(&ctrl->lock);
+
+ fimc_info1("%s released.\n", ctrl->name);
+
+ return 0;
+
+release_err:
+ mutex_unlock(&ctrl->lock);
+ return ret;
+
+}
+
+static const struct v4l2_file_operations fimc_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_open,
+ .release = fimc_release,
+ .ioctl = video_ioctl2,
+ .read = fimc_read,
+ .write = fimc_write,
+ .mmap = fimc_mmap,
+ .poll = fimc_poll,
+};
+
+static void fimc_vdev_release(struct video_device *vdev)
+{
+ kfree(vdev);
+}
+
+struct video_device fimc_video_device[FIMC_DEVICES] = {
+ [0] = {
+ .fops = &fimc_fops,
+ .ioctl_ops = &fimc_v4l2_ops,
+ .release = fimc_vdev_release,
+ },
+ [1] = {
+ .fops = &fimc_fops,
+ .ioctl_ops = &fimc_v4l2_ops,
+ .release = fimc_vdev_release,
+ },
+ [2] = {
+ .fops = &fimc_fops,
+ .ioctl_ops = &fimc_v4l2_ops,
+ .release = fimc_vdev_release,
+ },
+};
+
+static int fimc_init_global(struct platform_device *pdev)
+{
+ struct s3c_platform_fimc *pdata;
+ struct s3c_platform_camera *cam;
+ int i;
+
+ pdata = to_fimc_plat(&pdev->dev);
+
+ /* Registering external camera modules. re-arrange order to be sure */
+ for (i = 0; i < FIMC_MAXCAMS; i++) {
+ cam = pdata->camera[i];
+ if (!cam)
+ break;
+
+ cam->srclk = clk_get(&pdev->dev, cam->srclk_name);
+ if (IS_ERR(cam->srclk)) {
+ dev_err(&pdev->dev, "%s: failed to get mclk source\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* mclk */
+ cam->clk = clk_get(&pdev->dev, cam->clk_name);
+ if (IS_ERR(cam->clk)) {
+ dev_err(&pdev->dev, "%s: failed to get mclk source\n",
+ __func__);
+ clk_put(cam->srclk);
+ return -EINVAL;
+ }
+
+ clk_put(cam->clk);
+ clk_put(cam->srclk);
+
+ /* Assign camera device to fimc */
+ memcpy(&fimc_dev->camera[i], cam, sizeof(*cam));
+ fimc_dev->camera_isvalid[i] = 1;
+ fimc_dev->camera[i].initialized = 0;
+ }
+
+ fimc_dev->active_camera = -1;
+ fimc_dev->initialized = 1;
+
+ return 0;
+}
+
+static int fimc_show_log_level(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fimc_control *ctrl;
+ struct platform_device *pdev;
+ int id = -1;
+
+ char temp[150];
+
+ pdev = to_platform_device(dev);
+ id = pdev->id;
+ ctrl = get_fimc_ctrl(id);
+
+ sprintf(temp, "\t");
+ strcat(buf, temp);
+ if (ctrl->log & FIMC_LOG_DEBUG) {
+ sprintf(temp, "FIMC_LOG_DEBUG | ");
+ strcat(buf, temp);
+ }
+
+ if (ctrl->log & FIMC_LOG_INFO_L2) {
+ sprintf(temp, "FIMC_LOG_INFO_L2 | ");
+ strcat(buf, temp);
+ }
+
+ if (ctrl->log & FIMC_LOG_INFO_L1) {
+ sprintf(temp, "FIMC_LOG_INFO_L1 | ");
+ strcat(buf, temp);
+ }
+
+ if (ctrl->log & FIMC_LOG_WARN) {
+ sprintf(temp, "FIMC_LOG_WARN | ");
+ strcat(buf, temp);
+ }
+
+ if (ctrl->log & FIMC_LOG_ERR) {
+ sprintf(temp, "FIMC_LOG_ERR\n");
+ strcat(buf, temp);
+ }
+
+ return strlen(buf);
+}
+
+static int fimc_store_log_level(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct fimc_control *ctrl;
+ struct platform_device *pdev;
+
+ const char *p = buf;
+ char msg[150] = {0, };
+ int id = -1;
+ u32 match = 0;
+
+ pdev = to_platform_device(dev);
+ id = pdev->id;
+ ctrl = get_fimc_ctrl(id);
+
+ while (*p != '\0') {
+ if (!isspace(*p))
+ strncat(msg, p, 1);
+ p++;
+ }
+
+ ctrl->log = 0;
+ printk(KERN_INFO "FIMC.%d log level is set as below.\n", id);
+
+ if (strstr(msg, "FIMC_LOG_ERR") != NULL) {
+ ctrl->log |= FIMC_LOG_ERR;
+ match = 1;
+ printk(KERN_INFO "\tFIMC_LOG_ERR\n");
+ }
+
+ if (strstr(msg, "FIMC_LOG_WARN") != NULL) {
+ ctrl->log |= FIMC_LOG_WARN;
+ match = 1;
+ printk(KERN_INFO "\tFIMC_LOG_WARN\n");
+ }
+
+ if (strstr(msg, "FIMC_LOG_INFO_L1") != NULL) {
+ ctrl->log |= FIMC_LOG_INFO_L1;
+ match = 1;
+ printk(KERN_INFO "\tFIMC_LOG_INFO_L1\n");
+ }
+
+ if (strstr(msg, "FIMC_LOG_INFO_L2") != NULL) {
+ ctrl->log |= FIMC_LOG_INFO_L2;
+ match = 1;
+ printk(KERN_INFO "\tFIMC_LOG_INFO_L2\n");
+ }
+
+ if (strstr(msg, "FIMC_LOG_DEBUG") != NULL) {
+ ctrl->log |= FIMC_LOG_DEBUG;
+ match = 1;
+ printk(KERN_INFO "\tFIMC_LOG_DEBUG\n");
+ }
+
+ if (!match) {
+ printk(KERN_INFO "FIMC_LOG_ERR \t: Error condition.\n");
+ printk(KERN_INFO "FIMC_LOG_WARN \t: WARNING condition.\n");
+ printk(KERN_INFO "FIMC_LOG_INFO_L1 \t: V4L2 API without QBUF, DQBUF.\n");
+ printk(KERN_INFO "FIMC_LOG_INFO_L2 \t: V4L2 API QBUF, DQBUF.\n");
+ printk(KERN_INFO "FIMC_LOG_DEBUG \t: Queue status report.\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(log_level, 0644, \
+ fimc_show_log_level,
+ fimc_store_log_level);
+
+static int __devinit fimc_probe(struct platform_device *pdev)
+{
+ struct s3c_platform_fimc *pdata;
+ struct fimc_control *ctrl;
+ struct clk *srclk;
+ int ret;
+
+ if (!fimc_dev) {
+ fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);
+ if (!fimc_dev) {
+ dev_err(&pdev->dev, "%s: not enough memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+
+ ctrl = fimc_register_controller(pdev);
+ if (!ctrl) {
+ printk(KERN_ERR "%s: cannot register fimc\n", __func__);
+ goto err_alloc;
+ }
+
+ pdata = to_fimc_plat(&pdev->dev);
+ if (pdata->cfg_gpio)
+ pdata->cfg_gpio(pdev);
+
+ /* Get fimc power domain regulator */
+ ctrl->regulator = regulator_get(&pdev->dev, "pd");
+ if (IS_ERR(ctrl->regulator)) {
+ fimc_err("%s: failed to get resource %s\n",
+ __func__, "s3c-fimc");
+ return PTR_ERR(ctrl->regulator);
+ }
+
+ /* fimc source clock */
+ srclk = clk_get(&pdev->dev, pdata->srclk_name);
+ if (IS_ERR(srclk)) {
+ fimc_err("%s: failed to get source clock of fimc\n",
+ __func__);
+ goto err_v4l2;
+ }
+
+ /* fimc clock */
+ ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);
+ if (IS_ERR(ctrl->clk)) {
+ fimc_err("%s: failed to get fimc clock source\n",
+ __func__);
+ goto err_v4l2;
+ }
+
+ /* set parent for mclk */
+ clk_set_parent(ctrl->clk, srclk);
+
+ /* set rate for mclk */
+ clk_set_rate(ctrl->clk, pdata->clk_rate);
+
+ /* V4L2 device-subdev registration */
+ ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
+ if (ret) {
+ fimc_err("%s: v4l2 device register failed\n", __func__);
+ goto err_fimc;
+ }
+
+ /* things to initialize once */
+ if (!fimc_dev->initialized) {
+ ret = fimc_init_global(pdev);
+ if (ret)
+ goto err_v4l2;
+ }
+
+ /* video device register */
+ ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
+ if (ret) {
+ fimc_err("%s: cannot register video driver\n", __func__);
+ goto err_v4l2;
+ }
+
+ video_set_drvdata(ctrl->vd, ctrl);
+
+ ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
+ if (ret < 0) {
+ fimc_err("failed to add sysfs entries\n");
+ goto err_global;
+ }
+ printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);
+
+ return 0;
+
+err_global:
+ video_unregister_device(ctrl->vd);
+
+err_v4l2:
+ v4l2_device_unregister(&ctrl->v4l2_dev);
+
+err_fimc:
+ fimc_unregister_controller(pdev);
+
+err_alloc:
+ kfree(fimc_dev);
+ return -EINVAL;
+
+}
+
+static int fimc_remove(struct platform_device *pdev)
+{
+ fimc_unregister_controller(pdev);
+
+ device_remove_file(&(pdev->dev), &dev_attr_log_level);
+
+ kfree(fimc_dev);
+ fimc_dev = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static inline void fimc_suspend_out_ctx(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF: /* fall through */
+ case FIMC_OVLY_NONE_SINGLE_BUF:
+ if (ctx->status == FIMC_STREAMON) {
+ if (ctx->inq[0] != -1)
+ fimc_err("%s : %d in queue unstable\n",
+ __func__, __LINE__);
+
+ fimc_outdev_stop_streaming(ctrl, ctx);
+ ctx->status = FIMC_ON_SLEEP;
+ } else if (ctx->status == FIMC_STREAMON_IDLE) {
+ fimc_outdev_stop_streaming(ctrl, ctx);
+ ctx->status = FIMC_ON_IDLE_SLEEP;
+ } else {
+ ctx->status = FIMC_OFF_SLEEP;
+ }
+
+ break;
+ case FIMC_OVLY_NOT_FIXED:
+ ctx->status = FIMC_OFF_SLEEP;
+ break;
+ }
+}
+
+static inline int fimc_suspend_out(struct fimc_control *ctrl)
+{
+ struct fimc_ctx *ctx;
+ int i, on_sleep = 0, idle_sleep = 0, off_sleep = 0;
+
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ ctx = &ctrl->out->ctx[i];
+ fimc_suspend_out_ctx(ctrl, ctx);
+
+ switch (ctx->status) {
+ case FIMC_ON_SLEEP:
+ on_sleep++;
+ break;
+ case FIMC_ON_IDLE_SLEEP:
+ idle_sleep++;
+ break;
+ case FIMC_OFF_SLEEP:
+ off_sleep++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (on_sleep)
+ ctrl->status = FIMC_ON_SLEEP;
+ else if (idle_sleep)
+ ctrl->status = FIMC_ON_IDLE_SLEEP;
+ else
+ ctrl->status = FIMC_OFF_SLEEP;
+
+ ctrl->out->last_ctx = -1;
+
+ return 0;
+}
+
+static inline int fimc_suspend_cap(struct fimc_control *ctrl)
+{
+ if (ctrl->cam->id == CAMERA_WB && ctrl->status == FIMC_STREAMON)
+ fimc_streamoff_capture((void *)ctrl);
+ ctrl->status = FIMC_ON_SLEEP;
+
+ return 0;
+}
+
+int fimc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fimc_control *ctrl;
+ struct s3c_platform_fimc *pdata;
+ int id;
+
+ id = pdev->id;
+ ctrl = get_fimc_ctrl(id);
+ pdata = to_fimc_plat(ctrl->dev);
+
+ if (ctrl->out)
+ fimc_suspend_out(ctrl);
+ else if (ctrl->cap)
+ fimc_suspend_cap(ctrl);
+ else
+ ctrl->status = FIMC_OFF_SLEEP;
+
+ if (atomic_read(&ctrl->in_use) && ctrl->status != FIMC_OFF_SLEEP)
+ fimc_clk_en(ctrl, false);
+
+ return 0;
+}
+
+static inline void fimc_resume_out_ctx(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ int ret = -1;
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO:
+ if (ctx->status == FIMC_ON_IDLE_SLEEP) {
+ fimc_outdev_resume_dma(ctrl, ctx);
+ ret = fimc_outdev_set_ctx_param(ctrl, ctx);
+ if (ret < 0)
+ fimc_err("Fail: fimc_outdev_set_ctx_param\n");
+
+ ctx->status = FIMC_STREAMON_IDLE;
+ } else if (ctx->status == FIMC_OFF_SLEEP) {
+ ctx->status = FIMC_STREAMOFF;
+ } else {
+ fimc_err("%s: Abnormal (%d)\n", __func__, ctx->status);
+ }
+
+ break;
+ case FIMC_OVLY_DMA_MANUAL:
+ if (ctx->status == FIMC_ON_IDLE_SLEEP) {
+ ret = fimc_outdev_set_ctx_param(ctrl, ctx);
+ if (ret < 0)
+ fimc_err("Fail: fimc_outdev_set_ctx_param\n");
+
+ ctx->status = FIMC_STREAMON_IDLE;
+
+ } else if (ctx->status == FIMC_OFF_SLEEP) {
+ ctx->status = FIMC_STREAMOFF;
+ } else {
+ fimc_err("%s: Abnormal (%d)\n", __func__, ctx->status);
+ }
+
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ if (ctx->status == FIMC_ON_IDLE_SLEEP) {
+ ret = fimc_outdev_set_ctx_param(ctrl, ctx);
+ if (ret < 0)
+ fimc_err("Fail: fimc_outdev_set_ctx_param\n");
+
+ ctx->status = FIMC_STREAMON_IDLE;
+ } else if (ctx->status == FIMC_OFF_SLEEP) {
+ ctx->status = FIMC_STREAMOFF;
+ } else {
+ fimc_err("%s: Abnormal (%d)\n", __func__, ctx->status);
+ }
+
+ break;
+ default:
+ ctx->status = FIMC_STREAMOFF;
+ break;
+ }
+}
+
+static inline int fimc_resume_out(struct fimc_control *ctrl)
+{
+ struct fimc_ctx *ctx;
+ int i;
+ u32 state = 0;
+
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ ctx = &ctrl->out->ctx[i];
+ fimc_resume_out_ctx(ctrl, ctx);
+
+ switch (ctx->status) {
+ case FIMC_STREAMON:
+ state |= FIMC_STREAMON;
+ break;
+ case FIMC_STREAMON_IDLE:
+ state |= FIMC_STREAMON_IDLE;
+ break;
+ case FIMC_STREAMOFF:
+ state |= FIMC_STREAMOFF;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((state & FIMC_STREAMON) == FIMC_STREAMON)
+ ctrl->status = FIMC_STREAMON;
+ else if ((state & FIMC_STREAMON_IDLE) == FIMC_STREAMON_IDLE)
+ ctrl->status = FIMC_STREAMON_IDLE;
+ else
+ ctrl->status = FIMC_STREAMOFF;
+
+ return 0;
+}
+
+static inline int fimc_resume_cap(struct fimc_control *ctrl)
+{
+ if (ctrl->cam->id == CAMERA_WB)
+ fimc_streamon_capture((void *)ctrl);
+
+ return 0;
+}
+
+int fimc_resume(struct platform_device *pdev)
+{
+ struct fimc_control *ctrl;
+ struct s3c_platform_fimc *pdata;
+ int in_use, id = pdev->id;
+
+ ctrl = get_fimc_ctrl(id);
+ pdata = to_fimc_plat(ctrl->dev);
+
+ in_use = atomic_read(&ctrl->in_use);
+
+ if (in_use && ctrl->status != FIMC_OFF_SLEEP)
+ fimc_clk_en(ctrl, true);
+
+ if (ctrl->out)
+ fimc_resume_out(ctrl);
+ else if (ctrl->cap)
+ fimc_resume_cap(ctrl);
+ else
+ ctrl->status = FIMC_STREAMOFF;
+
+ if (in_use && 0 != ctrl->id)
+ fimc_clk_en(ctrl, false);
+
+ return 0;
+}
+#else
+#define fimc_suspend NULL
+#define fimc_resume NULL
+#endif
+
+static struct platform_driver fimc_driver = {
+ .probe = fimc_probe,
+ .remove = fimc_remove,
+ .suspend = fimc_suspend,
+ .resume = fimc_resume,
+ .driver = {
+ .name = FIMC_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int fimc_register(void)
+{
+ platform_driver_register(&fimc_driver);
+
+ return 0;
+}
+
+static void fimc_unregister(void)
+{
+ platform_driver_unregister(&fimc_driver);
+}
+
+late_initcall(fimc_register);
+module_exit(fimc_unregister);
+
+MODULE_AUTHOR("Dongsoo, Kim <dongsoo45.kim@samsung.com>");
+MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@samsung.com>");
+MODULE_AUTHOR("Jonghun, Han <jonghun.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung Camera Interface (FIMC) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/samsung/fimc/fimc_output.c b/drivers/media/video/samsung/fimc/fimc_output.c
new file mode 100644
index 0000000..2269d6a
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_output.c
@@ -0,0 +1,2563 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_output.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * V4L2 Output device support file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/slab.h>
+#include <linux/bootmem.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_samsung.h>
+#include <media/videobuf-core.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/mman.h>
+#include <plat/media.h>
+#include <linux/clk.h>
+
+#include "fimc.h"
+
+static __u32 fimc_get_pixel_format_type(__u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_RGB565:
+ return FIMC_RGB;
+
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12T:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ return FIMC_YUV420;
+
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YUV422P:
+ return FIMC_YUV422;
+
+ default:
+ return FIMC_YUV444;
+ }
+}
+
+void fimc_outdev_set_src_addr(struct fimc_control *ctrl, dma_addr_t *base)
+{
+ fimc_hwset_addr_change_disable(ctrl);
+ fimc_hwset_input_address(ctrl, base);
+ fimc_hwset_addr_change_enable(ctrl);
+}
+
+int fimc_outdev_start_camif(void *param)
+{
+ struct fimc_control *ctrl = (struct fimc_control *)param;
+
+ fimc_hwset_start_scaler(ctrl);
+ fimc_hwset_enable_capture(ctrl, 0); /* bypass disable */
+ fimc_hwset_start_input_dma(ctrl);
+
+ return 0;
+}
+
+static int fimc_outdev_stop_camif(void *param)
+{
+ struct fimc_control *ctrl = (struct fimc_control *)param;
+
+ fimc_hwset_stop_input_dma(ctrl);
+ fimc_hwset_disable_autoload(ctrl);
+ fimc_hwset_stop_scaler(ctrl);
+ fimc_hwset_disable_capture(ctrl);
+ fimc_hwset_sw_reset(ctrl);
+
+ fimc_clk_en(ctrl, false);
+ return 0;
+}
+
+int fimc_outdev_stop_streaming(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ int ret = 0;
+
+ fimc_dbg("%s: called\n", __func__);
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ if (ctx->status == FIMC_STREAMON_IDLE)
+ ctx->status = FIMC_STREAMOFF;
+ else
+ ctx->status = FIMC_READY_OFF;
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ if (ctx->status == FIMC_STREAMON_IDLE)
+ ctx->status = FIMC_STREAMOFF;
+ else
+ ctx->status = FIMC_READY_OFF;
+ ret = wait_event_timeout(ctrl->wq,
+ (ctx->status == FIMC_STREAMOFF),
+ FIMC_ONESHOT_TIMEOUT);
+ if (ret == 0) {
+ fimc_dump_context(ctrl, ctx);
+ fimc_err("fail %s: %d\n", __func__, ctx->ctx_num);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int fimc_outdev_resume_dma(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ struct v4l2_rect fimd_rect;
+ struct fb_var_screeninfo var;
+ struct fb_info *fbinfo;
+ struct s3cfb_window *win;
+ int ret = -1, idx;
+
+ fbinfo = registered_fb[ctx->overlay.fb_id];
+ win = (struct s3cfb_window *)fbinfo->par;
+
+ memcpy(&var, &fbinfo->var, sizeof(struct fb_var_screeninfo));
+ memset(&fimd_rect, 0, sizeof(struct v4l2_rect));
+ ret = fimc_fimd_rect(ctrl, ctx, &fimd_rect);
+ if (ret < 0) {
+ fimc_err("fimc_fimd_rect fail\n");
+ return -EINVAL;
+ }
+
+ /* set window path & owner */
+ win->path = DATA_PATH_DMA;
+ win->owner = DMA_MEM_OTHER;
+ win->other_mem_addr = ctx->dst[1].base[FIMC_ADDR_Y];
+ win->other_mem_size = ctx->dst[1].length[FIMC_ADDR_Y];
+
+ /* Update WIN size */
+ var.xres_virtual = fimd_rect.width;
+ var.yres_virtual = fimd_rect.height;
+ var.xres = fimd_rect.width;
+ var.yres = fimd_rect.height;
+
+ /* Update WIN position */
+ win->x = fimd_rect.left;
+ win->y = fimd_rect.top;
+
+ var.activate = FB_ACTIVATE_FORCE;
+ ret = fb_set_var(fbinfo, &var);
+ if (ret < 0) {
+ fimc_err("fb_set_var fail (ret=%d)\n", ret);
+ return -EINVAL;
+ }
+
+ idx = ctx->outq[0];
+ if (idx == -1) {
+ fimc_err("out going queue is empty.\n");
+ return -EINVAL;
+ }
+
+ win->other_mem_addr = ctx->dst[idx].base[FIMC_ADDR_Y];
+ ret = fb_pan_display(fbinfo, &fbinfo->var);
+ if (ret < 0) {
+ fimc_err("%s: fb_pan_display fail (ret=%d)\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ ctrl->fb.is_enable = 1;
+
+ return 0;
+}
+
+static void fimc_init_out_buf(struct fimc_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].state = VIDEOBUF_IDLE;
+ ctx->src[i].flags = 0x0;
+
+ ctx->inq[i] = -1;
+ ctx->outq[i] = -1;
+ }
+}
+
+static int fimc_outdev_set_src_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ u32 width = ctx->pix.width;
+ u32 height = ctx->pix.height;
+ u32 format = ctx->pix.pixelformat;
+ u32 y_size = width * height;
+ u32 cb_size = 0, cr_size = 0;
+ u32 i, size;
+ dma_addr_t *curr = &ctrl->mem.curr;
+
+ switch (format) {
+ case V4L2_PIX_FMT_RGB32:
+ size = PAGE_ALIGN(y_size << 2);
+ break;
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YUYV:
+ size = PAGE_ALIGN(y_size << 1);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ cb_size = y_size >> 2;
+ cr_size = y_size >> 2;
+ size = PAGE_ALIGN(y_size + cb_size + cr_size);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ cb_size = y_size >> 1;
+ size = PAGE_ALIGN(y_size + cb_size);
+ break;
+ case V4L2_PIX_FMT_NV12T:
+ fimc_get_nv12t_size(width, height, &y_size, &cb_size);
+ size = PAGE_ALIGN(y_size + cb_size);
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ cb_size = y_size;
+ size = PAGE_ALIGN(y_size + cb_size);
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n", __func__, format);
+ return -EINVAL;
+ }
+
+ if ((*curr + size * FIMC_OUTBUFS) > (ctrl->mem.base + ctrl->mem.size)) {
+ fimc_err("%s: Reserved memory is not sufficient\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Initialize source buffer addr */
+ switch (format) {
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_RGB32: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YUYV:
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].base[FIMC_ADDR_Y] = *curr;
+ ctx->src[i].length[FIMC_ADDR_Y] = size;
+ ctx->src[i].base[FIMC_ADDR_CB] = 0;
+ ctx->src[i].length[FIMC_ADDR_CB] = 0;
+ ctx->src[i].base[FIMC_ADDR_CR] = 0;
+ ctx->src[i].length[FIMC_ADDR_CR] = 0;
+ *curr += size;
+ }
+ break;
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].base[FIMC_ADDR_Y] = *curr;
+ ctx->src[i].length[FIMC_ADDR_Y] = y_size;
+ ctx->src[i].base[FIMC_ADDR_CB] = *curr + y_size;
+ ctx->src[i].length[FIMC_ADDR_CB] = cb_size;
+ ctx->src[i].base[FIMC_ADDR_CR] = 0;
+ ctx->src[i].length[FIMC_ADDR_CR] = 0;
+ *curr += size;
+ }
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].base[FIMC_ADDR_Y] = *curr;
+ ctx->src[i].base[FIMC_ADDR_CB] = *curr + y_size;
+ ctx->src[i].base[FIMC_ADDR_CR] = *curr + y_size +
+ cb_size;
+ ctx->src[i].length[FIMC_ADDR_Y] = y_size;
+ ctx->src[i].length[FIMC_ADDR_CB] = cb_size;
+ ctx->src[i].length[FIMC_ADDR_CR] = cr_size;
+ *curr += size;
+ }
+ break;
+
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n", __func__, format);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_outdev_set_dst_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ dma_addr_t *curr = &ctrl->mem.curr;
+ dma_addr_t end;
+ u32 width = ctrl->fb.lcd_hres;
+ u32 height = ctrl->fb.lcd_vres;
+ u32 i, size;
+
+ end = ctrl->mem.base + ctrl->mem.size;
+ size = PAGE_ALIGN(width * height * 4);
+
+ if ((*curr + (size * FIMC_OUTBUFS)) > end) {
+ fimc_err("%s: Reserved memory is not sufficient\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Initialize destination buffer addr */
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->dst[i].base[FIMC_ADDR_Y] = *curr;
+ ctx->dst[i].length[FIMC_ADDR_Y] = size;
+ ctx->dst[i].base[FIMC_ADDR_CB] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CB] = 0;
+ ctx->dst[i].base[FIMC_ADDR_CR] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CR] = 0;
+ *curr += size;
+ }
+
+ return 0;
+}
+
+static int fimc_set_rot_degree(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ int degree)
+{
+ switch (degree) {
+ case 0: /* fall through */
+ case 90: /* fall through */
+ case 180: /* fall through */
+ case 270:
+ ctx->rotate = degree;
+ break;
+
+ default:
+ fimc_err("Invalid rotate value : %d\n", degree);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int fimc_outdev_check_param(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_rect dst, bound;
+ u32 rot = 0;
+ int ret = 0, i, exclusive = 0;
+
+ rot = fimc_mapping_rot_flip(ctx->rotate, ctx->flip);
+ dst.top = ctx->win.w.top;
+ dst.left = ctx->win.w.left;
+ dst.width = ctx->win.w.width;
+ dst.height = ctx->win.w.height;
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ if (rot & FIMC_ROT) {
+ bound.width = ctrl->fb.lcd_vres;
+ bound.height = ctrl->fb.lcd_hres;
+ } else {
+ bound.width = ctrl->fb.lcd_hres;
+ bound.height = ctrl->fb.lcd_vres;
+ }
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ bound.width = ctx->fbuf.fmt.width;
+ bound.height = ctx->fbuf.fmt.height;
+ break;
+
+ default:
+ fimc_err("%s: invalid ovelay mode.\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((dst.left + dst.width) > bound.width) {
+ fimc_err("Horizontal position setting is failed\n");
+ fimc_err("\tleft = %d, width = %d, bound width = %d,\n",
+ dst.left, dst.width, bound.width);
+ ret = -EINVAL;
+ } else if ((dst.top + dst.height) > bound.height) {
+ fimc_err("Vertical position setting is failed\n");
+ fimc_err("\ttop = %d, height = %d, bound height = %d,\n",
+ dst.top, dst.height, bound.height);
+ ret = -EINVAL;
+ }
+
+ if ((ctx->status != FIMC_STREAMOFF) &&
+ (ctx->status != FIMC_READY_ON) &&
+ (ctx->status != FIMC_ON_IDLE_SLEEP)) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ /* check other open instance */
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ switch (ctrl->out->ctx[i].overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ exclusive++;
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF: /* fall through */
+ case FIMC_OVLY_NOT_FIXED:
+ break;
+ }
+ }
+
+ if (exclusive > 1) {
+ for (i = 0; i < FIMC_MAX_CTXS; i++)
+ fimc_err("%s: ctx %d mode = %d", __func__, i,
+ ctrl->out->ctx[i].overlay.mode);
+ return -EBUSY;
+ }
+
+ return ret;
+}
+
+static void fimc_outdev_set_src_format(struct fimc_control *ctrl,
+ u32 pixfmt, enum v4l2_field field)
+{
+ fimc_hwset_input_burst_cnt(ctrl, 4);
+ fimc_hwset_input_colorspace(ctrl, pixfmt);
+ fimc_hwset_input_yuv(ctrl, pixfmt);
+ fimc_hwset_input_rgb(ctrl, pixfmt);
+ fimc_hwset_intput_field(ctrl, field);
+ fimc_hwset_ext_rgb(ctrl, 1);
+ fimc_hwset_input_addr_style(ctrl, pixfmt);
+}
+
+static void fimc_outdev_set_dst_format(struct fimc_control *ctrl,
+ struct v4l2_pix_format *pixfmt)
+{
+ fimc_hwset_output_colorspace(ctrl, pixfmt->pixelformat);
+ fimc_hwset_output_yuv(ctrl, pixfmt->pixelformat);
+ fimc_hwset_output_rgb(ctrl, pixfmt->pixelformat);
+ fimc_hwset_output_scan(ctrl, pixfmt);
+ fimc_hwset_output_addr_style(ctrl, pixfmt->pixelformat);
+}
+
+static void fimc_outdev_set_format(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_pix_format pixfmt;
+ memset(&pixfmt, 0, sizeof(pixfmt));
+
+ fimc_outdev_set_src_format(ctrl, ctx->pix.pixelformat, ctx->pix.field);
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL: /* Non-destructive overlay mode */
+ if (ctx->pix.field == V4L2_FIELD_NONE) {
+ pixfmt.pixelformat = V4L2_PIX_FMT_RGB32;
+ pixfmt.field = V4L2_FIELD_NONE;
+ } else if (ctx->pix.field == V4L2_FIELD_INTERLACED_TB) {
+ pixfmt.pixelformat = V4L2_PIX_FMT_YUV444;
+ pixfmt.field = V4L2_FIELD_INTERLACED_TB;
+ } else if (ctx->pix.field == V4L2_FIELD_ANY) {
+ pixfmt.pixelformat = V4L2_PIX_FMT_RGB32;
+ pixfmt.field = V4L2_FIELD_NONE;
+ }
+
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF: /* Destructive overlay mode */
+ pixfmt.pixelformat = ctx->fbuf.fmt.pixelformat;
+ pixfmt.field = V4L2_FIELD_NONE;
+
+ break;
+ default:
+ fimc_err("Invalid overlay mode %d\n", ctx->overlay.mode);
+ break;
+ }
+
+ fimc_outdev_set_dst_format(ctrl, &pixfmt);
+}
+
+static void fimc_outdev_set_path(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ /* source path */
+ fimc_hwset_input_source(ctrl, FIMC_SRC_MSDMA);
+
+ fimc_hwset_disable_lcdfifo(ctrl);
+ fimc_hwset_disable_autoload(ctrl);
+}
+
+static void fimc_outdev_set_rot(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ u32 rot = ctx->rotate;
+ u32 flip = ctx->flip;
+
+ fimc_hwset_input_rot(ctrl, 0, 0);
+ fimc_hwset_input_flip(ctrl, 0, 0);
+ fimc_hwset_output_rot_flip(ctrl, rot, flip);
+}
+
+static void fimc_outdev_set_src_dma_offset(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_rect bound, crop;
+ u32 pixfmt = ctx->pix.pixelformat;
+
+ bound.width = ctx->pix.width;
+ bound.height = ctx->pix.height;
+
+ crop.left = ctx->crop.left;
+ crop.top = ctx->crop.top;
+ crop.width = ctx->crop.width;
+ crop.height = ctx->crop.height;
+
+ fimc_hwset_input_offset(ctrl, pixfmt, &bound, &crop);
+}
+
+static int fimc4x_outdev_check_src_size(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_rect *real,
+ struct v4l2_rect *org)
+{
+ /* No Input Rotator */
+ if (real->height < 8) {
+ fimc_err("SRC Real_H: Min 8\n");
+ return -EINVAL;
+ }
+
+ if (real->width < 16) {
+ fimc_err("SRC Real_W: Min 16\n");
+ return -EINVAL;
+ }
+
+ if (real->width > ctrl->limit->real_w_no_rot) {
+ fimc_err("SRC REAL_W: Real_W <= %d\n",
+ ctrl->limit->real_w_no_rot);
+ return -EINVAL;
+ }
+
+ if (org->height < real->height) {
+ fimc_err("SRC Org_H: larger than Real_H\n");
+ return -EINVAL;
+ }
+
+ if (org->width < real->width) {
+ fimc_err("SRC Org_W: Org_W >= Real_W\n");
+ return -EINVAL;
+ }
+
+ if (ctx->sc.pre_vratio) {
+ if (real->height % ctx->sc.pre_vratio) {
+ fimc_err("SRC Real_H: multiple of pre_vratio!\n");
+ return -EINVAL;
+ }
+ }
+
+ if (real->width % 16) {
+ fimc_err("SRC Real_W: multiple of 16 !\n");
+ return -EINVAL;
+ }
+
+ if (ctx->sc.pre_hratio) {
+ if (real->width % (ctx->sc.pre_hratio * 4)) {
+ fimc_err("SRC Real_W: multiple of 4 * pre_hratio!\n");
+ return -EINVAL;
+ }
+ }
+
+ if (org->width % 16) {
+ fimc_err("SRC Org_W: multiple of 16\n");
+ return -EINVAL;
+ }
+
+ if (org->height < 8) {
+ fimc_err("SRC Org_H: Min 8\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc50_outdev_check_src_size(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_rect *real,
+ struct v4l2_rect *org)
+{
+ u32 pixelformat = ctx->pix.pixelformat;
+
+ /* No Input Rotator */
+ if (real->height < 8) {
+ fimc_err("SRC Real_H: Min 8\n");
+ return -EINVAL;
+ }
+
+ if (real->width < 16) {
+ fimc_err("SRC Real_W: Min 16\n");
+ return -EINVAL;
+ }
+
+ if (real->width > ctrl->limit->real_w_no_rot) {
+ fimc_err("SRC REAL_W: Real_W <= %d\n",
+ ctrl->limit->real_w_no_rot);
+ return -EINVAL;
+ }
+
+ if (org->height < real->height) {
+ fimc_err("SRC Org_H: larger than Real_H\n");
+ return -EINVAL;
+ }
+
+ if (org->width < real->width) {
+ fimc_err("SRC Org_W: Org_W >= Real_W\n");
+ return -EINVAL;
+ }
+
+ if (ctx->pix.field == V4L2_FIELD_INTERLACED_TB) {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV444: /* fall through */
+ case V4L2_PIX_FMT_RGB32:
+ if (real->height % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ }
+ case V4L2_PIX_FMT_YUV422P:
+ if (real->height % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ } else if (real->width % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ }
+ case V4L2_PIX_FMT_YVU420:
+ if (real->height % 4) {
+ fimc_err("SRC Real_H: multiple of 4\n");
+ return -EINVAL;
+ } else if (real->width % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ }
+ }
+ } else if (ctx->pix.field == V4L2_FIELD_NONE) {
+ if (pixelformat == V4L2_PIX_FMT_YUV422P) {
+ if (real->height % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ } else if (real->width % 2) {
+ fimc_err("SRC Real_H: multiple of 2\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int fimc_outdev_set_src_dma_size(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ struct v4l2_rect real, org;
+ int ret = 0;
+
+ real.width = ctx->crop.width;
+ real.height = ctx->crop.height;
+ org.width = ctx->pix.width;
+ org.height = ctx->pix.height;
+
+ if (pdata->hw_ver == 0x50)
+ ret = fimc50_outdev_check_src_size(ctrl, ctx, &real, &org);
+ else
+ ret = fimc4x_outdev_check_src_size(ctrl, ctx, &real, &org);
+
+ if (ret < 0)
+ return ret;
+
+ fimc_hwset_org_input_size(ctrl, org.width, org.height);
+ fimc_hwset_real_input_size(ctrl, real.width, real.height);
+
+ return 0;
+}
+
+static void fimc_outdev_set_dst_dma_offset(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_rect bound, win;
+ struct v4l2_rect *w = &ctx->win.w;
+ u32 pixfmt = ctx->fbuf.fmt.pixelformat;
+
+ memset(&bound, 0, sizeof(bound));
+ memset(&win, 0, sizeof(win));
+
+ switch (ctx->rotate) {
+ case 0:
+ bound.width = ctx->fbuf.fmt.width;
+ bound.height = ctx->fbuf.fmt.height;
+
+ win.left = w->left;
+ win.top = w->top;
+ win.width = w->width;
+ win.height = w->height;
+
+ break;
+ case 90:
+ bound.width = ctx->fbuf.fmt.height;
+ bound.height = ctx->fbuf.fmt.width;
+
+ win.left = ctx->fbuf.fmt.height - (w->height + w->top);
+ win.top = w->left;
+ win.width = w->height;
+ win.height = w->width;
+
+ break;
+ case 180:
+ bound.width = ctx->fbuf.fmt.width;
+ bound.height = ctx->fbuf.fmt.height;
+
+ win.left = ctx->fbuf.fmt.width - (w->left + w->width);
+ win.top = ctx->fbuf.fmt.height - (w->top + w->height);
+ win.width = w->width;
+ win.height = w->height;
+
+ break;
+ case 270:
+ bound.width = ctx->fbuf.fmt.height;
+ bound.height = ctx->fbuf.fmt.width;
+
+ win.left = ctx->win.w.top;
+ win.top = ctx->fbuf.fmt.width - (w->left + w->width);
+ win.width = w->height;
+ win.height = w->width;
+
+ break;
+ default:
+ fimc_err("Rotation degree is invalid\n");
+ break;
+ }
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ memset(&bound, 0, sizeof(bound));
+ memset(&win, 0, sizeof(win));
+ fimc_hwset_output_offset(ctrl, pixfmt, &bound, &win);
+ break;
+ default:
+ fimc_hwset_output_offset(ctrl, pixfmt, &bound, &win);
+ break;
+ }
+
+ fimc_dbg("bound:width(%d), height(%d)\n", bound.width, bound.height);
+ fimc_dbg("win:width(%d), height(%d)\n", win.width, win.height);
+ fimc_dbg("win:top(%d), left(%d)\n", win.top, win.left);
+}
+
+static int fimc_outdev_check_dst_size(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_rect *real,
+ struct v4l2_rect *org)
+{
+ u32 rot = ctx->rotate;
+ __u32 pixel_type;
+
+ pixel_type = fimc_get_pixel_format_type(ctx->fbuf.fmt.pixelformat);
+
+ if (FIMC_YUV420 == pixel_type && real->height % 2) {
+ fimc_err("DST Real_H: even number for YUV420 formats\n");
+ return -EINVAL;
+ }
+
+ if ((rot == 90) || (rot == 270)) {
+ /* Use Output Rotator */
+ if (org->height < real->width) {
+ fimc_err("DST Org_H: Org_H(%d) >= Real_W(%d)\n",
+ org->height, real->width);
+ return -EINVAL;
+ }
+
+ if (org->width < real->height) {
+ fimc_err("DST Org_W: Org_W(%d) >= Real_H(%d)\n",
+ org->width, real->height);
+ return -EINVAL;
+ }
+
+ if (real->height > ctrl->limit->trg_h_rot) {
+ fimc_err("DST REAL_H: Real_H <= %d\n",
+ ctrl->limit->trg_h_rot);
+ return -EINVAL;
+ }
+ } else {
+ /* No Output Rotator */
+ if (org->height < 8) {
+ fimc_err("DST Org_H: Min 8\n");
+ return -EINVAL;
+ }
+
+ if (org->height < real->height) {
+ fimc_err("DST Org_H: Org_H >= Real_H\n");
+ return -EINVAL;
+ }
+
+ if (org->width % 8) {
+ fimc_err("DST Org_W: multiple of 8\n");
+ return -EINVAL;
+ }
+
+ if (org->width < real->width) {
+ fimc_err("DST Org_W: Org_W >= Real_W\n");
+ return -EINVAL;
+ }
+
+ if (real->height > ctrl->limit->trg_h_no_rot) {
+ fimc_err("DST REAL_H: Real_H <= %d\n",
+ ctrl->limit->trg_h_no_rot);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int fimc_outdev_set_dst_dma_size(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_rect org, real;
+ int ret = -1;
+
+ memset(&org, 0, sizeof(org));
+ memset(&real, 0, sizeof(real));
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_NONE_MULTI_BUF: /* fall through */
+ case FIMC_OVLY_NONE_SINGLE_BUF:
+ real.width = ctx->win.w.width;
+ real.height = ctx->win.w.height;
+
+ switch (ctx->rotate) {
+ case 0: /* fall through */
+ case 180:
+ org.width = ctx->fbuf.fmt.width;
+ org.height = ctx->fbuf.fmt.height;
+ break;
+ case 90: /* fall through */
+ case 270:
+ org.width = ctx->fbuf.fmt.height;
+ org.height = ctx->fbuf.fmt.width;
+ break;
+ default:
+ fimc_err("Rotation degree is invalid\n");
+ break;
+ }
+
+ break;
+
+ case FIMC_OVLY_DMA_MANUAL: /* fall through */
+ case FIMC_OVLY_DMA_AUTO:
+ real.width = ctx->win.w.width;
+ real.height = ctx->win.w.height;
+
+ switch (ctx->rotate) {
+ case 0: /* fall through */
+ case 180:
+ org.width = ctx->win.w.width;
+ org.height = ctx->win.w.height;
+ break;
+ case 90: /* fall through */
+ case 270:
+ org.width = ctx->win.w.height;
+ org.height = ctx->win.w.width;
+ break;
+ default:
+ fimc_err("Rotation degree is invalid\n");
+ break;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ fimc_dbg("DST org: width(%d), height(%d)\n", org.width, org.height);
+ fimc_dbg("DST real: width(%d), height(%d)\n", real.width, real.height);
+
+ ret = fimc_outdev_check_dst_size(ctrl, ctx, &real, &org);
+ if (ret < 0)
+ return ret;
+
+ fimc_hwset_output_size(ctrl, real.width, real.height);
+ fimc_hwset_output_area(ctrl, real.width, real.height);
+ fimc_hwset_org_output_size(ctrl, org.width, org.height);
+ fimc_hwset_ext_output_size(ctrl, real.width, real.height);
+
+ return 0;
+}
+
+static void fimc_outdev_calibrate_scale_info(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_rect *src,
+ struct v4l2_rect *dst)
+{
+ /* OUTPUT ROTATOR */
+ src->width = ctx->crop.width;
+ src->height = ctx->crop.height;
+ dst->width = ctx->win.w.width;
+ dst->height = ctx->win.w.height;
+
+ fimc_dbg("src->width(%d), src->height(%d)\n", src->width, src->height);
+ fimc_dbg("dst->width(%d), dst->height(%d)\n", dst->width, dst->height);
+}
+
+static int fimc_outdev_check_scaler(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_rect *src,
+ struct v4l2_rect *dst)
+{
+ u32 pixels = 0, dstfmt = 0;
+
+ /* Check scaler limitation */
+ if (ctx->sc.pre_dst_width > ctrl->limit->pre_dst_w) {
+ fimc_err("FIMC%d : MAX PreDstWidth is %d\n",
+ ctrl->id, ctrl->limit->pre_dst_w);
+ return -EDOM;
+ }
+
+ /* SRC width double boundary check */
+ switch (ctx->pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB32:
+ pixels = 1;
+ break;
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ pixels = 2;
+ break;
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ pixels = 8;
+ break;
+ default:
+ fimc_err("Invalid color format\n");
+ return -EINVAL;
+ }
+
+ if (src->width % pixels) {
+ fimc_err("source width multiple of %d pixels\n", pixels);
+ return -EDOM;
+ }
+
+ /* DST width double boundary check */
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ dstfmt = V4L2_PIX_FMT_RGB32;
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF: /* fall through */
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ dstfmt = ctx->fbuf.fmt.pixelformat;
+ break;
+ default:
+ break;
+ }
+
+ switch (dstfmt) {
+ case V4L2_PIX_FMT_RGB32:
+ pixels = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ pixels = 2;
+ break;
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ pixels = 8;
+ break;
+ default:
+ fimc_err("Invalid color format\n");
+ return -EINVAL;
+ }
+
+ if (dst->width % pixels) {
+ fimc_err("source width multiple of %d pixels\n", pixels);
+ return -EDOM;
+ }
+
+ return 0;
+}
+
+static int fimc_outdev_set_scaler(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ struct v4l2_rect src, dst;
+ int ret = 0;
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+
+ memset(&src, 0, sizeof(src));
+ memset(&dst, 0, sizeof(dst));
+
+ fimc_outdev_calibrate_scale_info(ctrl, ctx, &src, &dst);
+
+ ret = fimc_get_scaler_factor(src.width, dst.width,
+ &ctx->sc.pre_hratio, &ctx->sc.hfactor);
+ if (ret < 0) {
+ fimc_err("Fail : Out of Width scale range\n");
+ return ret;
+ }
+
+ ret = fimc_get_scaler_factor(src.height, dst.height,
+ &ctx->sc.pre_vratio, &ctx->sc.vfactor);
+ if (ret < 0) {
+ fimc_err("Fail : Out of Height scale range\n");
+ return ret;
+ }
+
+ ctx->sc.pre_dst_width = src.width / ctx->sc.pre_hratio;
+ ctx->sc.pre_dst_height = src.height / ctx->sc.pre_vratio;
+
+ if (pdata->hw_ver == 0x50) {
+ ctx->sc.main_hratio = (src.width << 14) /
+ (dst.width << ctx->sc.hfactor);
+ ctx->sc.main_vratio = (src.height << 14) /
+ (dst.height << ctx->sc.vfactor);
+ } else {
+ ctx->sc.main_hratio = (src.width << 8) /
+ (dst.width << ctx->sc.hfactor);
+ ctx->sc.main_vratio = (src.height << 8) /
+ (dst.height << ctx->sc.vfactor);
+ }
+
+ fimc_dbg("pre_hratio(%d), hfactor(%d), pre_vratio(%d), vfactor(%d)\n",
+ ctx->sc.pre_hratio, ctx->sc.hfactor,
+ ctx->sc.pre_vratio, ctx->sc.vfactor);
+
+
+ fimc_dbg("pre_dst_width(%d), main_hratio(%d), "
+ "pre_dst_height(%d), main_vratio(%d)\n",
+ ctx->sc.pre_dst_width, ctx->sc.main_hratio,
+ ctx->sc.pre_dst_height, ctx->sc.main_vratio);
+
+ ctx->sc.bypass = 0; /* Input DMA cannot support scaler bypass. */
+ ctx->sc.scaleup_h = (dst.width >= src.width) ? 1 : 0;
+ ctx->sc.scaleup_v = (dst.height >= src.height) ? 1 : 0;
+ ctx->sc.shfactor = 10 - (ctx->sc.hfactor + ctx->sc.vfactor);
+
+ if (pdata->hw_ver != 0x50) {
+ ret = fimc_outdev_check_scaler(ctrl, ctx, &src, &dst);
+ if (ret < 0)
+ return ret;
+ }
+
+ fimc_hwset_prescaler(ctrl, &ctx->sc);
+ fimc_hwset_scaler(ctrl, &ctx->sc);
+
+ return 0;
+}
+
+int fimc_outdev_set_ctx_param(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ int ret;
+
+ if (ctrl->status == FIMC_READY_ON || ctrl->status == FIMC_STREAMON_IDLE)
+ fimc_hwset_enable_irq(ctrl, 0, 1);
+
+ fimc_outdev_set_format(ctrl, ctx);
+ fimc_outdev_set_path(ctrl, ctx);
+ fimc_outdev_set_rot(ctrl, ctx);
+
+ fimc_outdev_set_src_dma_offset(ctrl, ctx);
+ ret = fimc_outdev_set_src_dma_size(ctrl, ctx);
+ if (ret < 0)
+ return ret;
+
+ fimc_outdev_set_dst_dma_offset(ctrl, ctx);
+
+ ret = fimc_outdev_set_dst_dma_size(ctrl, ctx);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_outdev_set_scaler(ctrl, ctx);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int fimc_fimd_rect(const struct fimc_control *ctrl,
+ const struct fimc_ctx *ctx,
+ struct v4l2_rect *fimd_rect)
+{
+ switch (ctx->rotate) {
+ case 0:
+ fimd_rect->left = ctx->win.w.left;
+ fimd_rect->top = ctx->win.w.top;
+ fimd_rect->width = ctx->win.w.width;
+ fimd_rect->height = ctx->win.w.height;
+
+ break;
+
+ case 90:
+ fimd_rect->left = ctrl->fb.lcd_hres -
+ (ctx->win.w.top + ctx->win.w.height);
+ fimd_rect->top = ctx->win.w.left;
+ fimd_rect->width = ctx->win.w.height;
+ fimd_rect->height = ctx->win.w.width;
+
+ break;
+
+ case 180:
+ fimd_rect->left = ctrl->fb.lcd_hres -
+ (ctx->win.w.left + ctx->win.w.width);
+ fimd_rect->top = ctrl->fb.lcd_vres -
+ (ctx->win.w.top + ctx->win.w.height);
+ fimd_rect->width = ctx->win.w.width;
+ fimd_rect->height = ctx->win.w.height;
+
+ break;
+
+ case 270:
+ fimd_rect->left = ctx->win.w.top;
+ fimd_rect->top = ctrl->fb.lcd_vres -
+ (ctx->win.w.left + ctx->win.w.width);
+ fimd_rect->width = ctx->win.w.height;
+ fimd_rect->height = ctx->win.w.width;
+
+ break;
+
+ default:
+ fimc_err("Rotation degree is invalid\n");
+ return -EINVAL;
+
+ break;
+ }
+
+ return 0;
+}
+
+int fimc_outdev_overlay_buf(struct file *filp,
+ struct fimc_control *ctrl,
+ struct fimc_ctx *ctx)
+{
+ int ret = 0, i;
+ struct fimc_overlay_buf *buf;
+
+ buf = &ctx->overlay.buf;
+
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->overlay.req_idx = i;
+ buf->size[i] = ctx->dst[i].length[0];
+ buf->phy_addr[i] = ctx->dst[i].base[0];
+ buf->vir_addr[i] = do_mmap(filp, 0, buf->size[i],
+ PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+ if (buf->vir_addr[i] == -EINVAL) {
+ fimc_err("%s: fail\n", __func__);
+ return -EINVAL;
+ }
+
+ fimc_dbg("idx : %d, size(0x%08x), phy_addr(0x%08x), "
+ "vir_addr(0x%08x)\n", i, buf->size[i],
+ buf->phy_addr[i], buf->vir_addr[i]);
+ }
+
+ ctx->overlay.req_idx = -1;
+
+ return ret;
+}
+
+int fimc_reqbufs_output(void *fh, struct v4l2_requestbuffers *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_ctx *ctx;
+ struct fimc_overlay_buf *buf;
+ struct mm_struct *mm = current->mm;
+ enum fimc_overlay_mode mode;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int ret = -1, i;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ buf = &ctx->overlay.buf;
+ mode = ctx->overlay.mode;
+
+ fimc_info1("%s: called\n", __func__);
+
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ if (ctx->is_requested == 1 && b->count != 0) {
+ fimc_err("Buffers were already requested\n");
+ return -EBUSY;
+ }
+
+ if (b->count > FIMC_OUTBUFS) {
+ fimc_warn("The buffer count is modified by driver "
+ "from %d to %d\n", b->count, FIMC_OUTBUFS);
+ b->count = FIMC_OUTBUFS;
+ }
+
+ fimc_init_out_buf(ctx);
+ ctx->is_requested = 0;
+
+ if (b->count == 0) {
+ ctrl->mem.curr = ctrl->mem.base;
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO: /* fall through */
+ case FIMC_OVLY_DMA_MANUAL:
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ if (buf->vir_addr[i]) {
+ ret = do_munmap(mm,
+ buf->vir_addr[i],
+ buf->size[i]);
+ if (ret < 0)
+ fimc_err("%s: do_munmap fail. "
+ "vir_addr[%d](0x%08x)\n",
+ __func__, i, buf->vir_addr[i]);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* initialize source buffers */
+ if (b->memory == V4L2_MEMORY_MMAP) {
+ ret = fimc_outdev_set_src_buf(ctrl, ctx);
+ ctx->overlay.req_idx = FIMC_MMAP_IDX;
+ if (ret)
+ return ret;
+ } else if (b->memory == V4L2_MEMORY_USERPTR) {
+ if (mode == FIMC_OVLY_DMA_AUTO)
+ ctx->overlay.req_idx = FIMC_USERPTR_IDX;
+ }
+ ctx->is_requested = 1;
+ }
+
+ ctx->buf_num = b->count;
+
+ return 0;
+}
+
+int fimc_querybuf_output(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_ctx *ctx;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ u32 buf_length = 0;
+
+ fimc_info1("%s: called\n", __func__);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ if (b->index >= ctx->buf_num) {
+ fimc_err("The index is out of bounds. You requested %d buffers."
+ "But requested index is %d\n", ctx->buf_num, b->index);
+ return -EINVAL;
+ }
+
+ b->flags = ctx->src[b->index].flags;
+ b->m.offset = b->index * PAGE_SIZE;
+ buf_length = ctx->src[b->index].length[FIMC_ADDR_Y] +
+ ctx->src[b->index].length[FIMC_ADDR_CB] +
+ ctx->src[b->index].length[FIMC_ADDR_CR];
+ b->length = PAGE_ALIGN(buf_length);
+
+ return 0;
+}
+
+int fimc_g_ctrl_output(void *fh, struct v4l2_control *c)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ switch (c->id) {
+ case V4L2_CID_ROTATION:
+ c->value = ctx->rotate;
+ break;
+
+ case V4L2_CID_HFLIP:
+ if (ctx->flip & V4L2_CID_HFLIP)
+ c->value = 1;
+ else
+ c->value = 0;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (ctx->flip & V4L2_CID_VFLIP)
+ c->value = 1;
+ else
+ c->value = 0;
+ break;
+
+ case V4L2_CID_OVERLAY_VADDR0:
+ c->value = ctx->overlay.buf.vir_addr[0];
+ break;
+
+ case V4L2_CID_OVERLAY_VADDR1:
+ c->value = ctx->overlay.buf.vir_addr[1];
+ break;
+
+ case V4L2_CID_OVERLAY_VADDR2:
+ c->value = ctx->overlay.buf.vir_addr[2];
+ break;
+
+ case V4L2_CID_OVERLAY_AUTO:
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO)
+ c->value = 1;
+ else
+ c->value = 0;
+ break;
+
+ case V4L2_CID_RESERVED_MEM_BASE_ADDR:
+ c->value = ctrl->mem.base;
+ if (2 == ctrl->id) {
+ /* Clearing the buffer for FIMC-2
+ * This is required because the same buffer is used
+ * for both Camcorder recording and HDMI display.
+ */
+ char *fimc_mem = NULL;
+ fimc_mem = (char *) ioremap(ctrl->mem.base, \
+ ctrl->mem.size);
+ if (fimc_mem) {
+ memset(fimc_mem, 0x00, ctrl->mem.size);
+ iounmap(fimc_mem);
+ }
+ }
+ break;
+
+ case V4L2_CID_FIMC_VERSION:
+ c->value = pdata->hw_ver;
+ break;
+
+ default:
+ fimc_err("Invalid control id: %d\n", c->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_set_dst_info(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct fimc_buf *fimc_buf)
+{
+ struct fimc_buf *buf;
+ int i;
+
+ for (i = 0; i < ctx->buf_num; i++) {
+ buf = &fimc_buf[i];
+
+ ctx->dst[i].base[FIMC_ADDR_Y] = buf->base[FIMC_ADDR_Y];
+ ctx->dst[i].length[FIMC_ADDR_Y] = buf->length[FIMC_ADDR_Y];
+
+ ctx->dst[i].base[FIMC_ADDR_CB] = buf->base[FIMC_ADDR_CB];
+ ctx->dst[i].length[FIMC_ADDR_CB] = buf->length[FIMC_ADDR_CB];
+
+ ctx->dst[i].base[FIMC_ADDR_CR] = buf->base[FIMC_ADDR_CR];
+ ctx->dst[i].length[FIMC_ADDR_CR] = buf->length[FIMC_ADDR_CR];
+ }
+
+ for (i = ctx->buf_num; i < FIMC_OUTBUFS; i++) {
+ ctx->dst[i].base[FIMC_ADDR_Y] = 0;
+ ctx->dst[i].length[FIMC_ADDR_Y] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CB] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CB] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CR] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CR] = 0;
+ }
+
+ /* for debugging */
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ fimc_dbg("dst[%d]: base[0]=0x%08x, size[0]=0x%08x\n",
+ i, ctx->dst[i].base[0], ctx->dst[i].length[0]);
+
+ fimc_dbg("dst[%d]: base[1]=0x%08x, size[1]=0x%08x\n",
+ i, ctx->dst[i].base[1], ctx->dst[i].length[2]);
+
+ fimc_dbg("dst[%d]: base[2]=0x%08x, size[2]=0x%08x\n",
+ i, ctx->dst[i].base[1], ctx->dst[i].length[2]);
+ }
+
+ return 0;
+}
+int fimc_s_ctrl_output(struct file *filp, void *fh, struct v4l2_control *c)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int ret = 0;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ switch (c->id) {
+ case V4L2_CID_ROTATION:
+ ret = fimc_set_rot_degree(ctrl, ctx, c->value);
+
+ break;
+ case V4L2_CID_HFLIP:
+ if (c->value)
+ ctx->flip |= FIMC_XFLIP;
+ else
+ ctx->flip &= ~FIMC_XFLIP;
+
+ break;
+ case V4L2_CID_VFLIP:
+ if (c->value)
+ ctx->flip |= FIMC_YFLIP;
+ else
+ ctx->flip &= ~FIMC_YFLIP;
+
+ break;
+ case V4L2_CID_OVERLAY_AUTO:
+ if (c->value == 1) {
+ ctx->overlay.mode = FIMC_OVLY_DMA_AUTO;
+ } else {
+ ctx->overlay.mode = FIMC_OVLY_DMA_MANUAL;
+ ret = fimc_outdev_set_dst_buf(ctrl, ctx);
+ fimc_outdev_overlay_buf(filp, ctrl, ctx);
+ }
+
+ break;
+ case V4L2_CID_OVLY_MODE:
+ ctx->overlay.mode = c->value;
+
+ break;
+ case V4L2_CID_DST_INFO:
+ ret = fimc_set_dst_info(ctrl, ctx,
+ (struct fimc_buf *)c->value);
+ break;
+ default:
+ fimc_err("Invalid control id: %d\n", c->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int fimc_cropcap_output(void *fh, struct v4l2_cropcap *a)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ u32 is_rotate = 0, max_w = 0, max_h = 0, pixelformat;
+
+ fimc_info1("%s: called\n", __func__);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ pixelformat = ctx->pix.pixelformat;
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ is_rotate = fimc_mapping_rot_flip(ctx->rotate, ctx->flip);
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_YUV420:
+ max_w = FIMC_SRC_MAX_W;
+ max_h = FIMC_SRC_MAX_H;
+ case V4L2_PIX_FMT_RGB32: /* fall through */
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ if (is_rotate & FIMC_ROT) { /* Landscape mode */
+ max_w = ctrl->fb.lcd_vres;
+ max_h = ctrl->fb.lcd_hres;
+ } else { /* Portrait */
+ max_w = ctrl->fb.lcd_hres;
+ max_h = ctrl->fb.lcd_vres;
+ }
+
+ break;
+ default:
+ fimc_warn("Supported format : \
+ V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, \
+ V4L2_PIX_FMT_YVYU, V4L2_PIX_FMT_VYUY, \
+ V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV12T, \
+ V4L2_PIX_FMT_NV21, V4L2_PIX_FMT_RGB32, \
+ V4L2_PIX_FMT_RGB565\n");
+ fimc_warn("%s: Received pixel format = %x\n",
+ __func__, pixelformat);
+ return -EINVAL;
+ }
+
+ /* crop bounds */
+ ctx->cropcap.bounds.left = 0;
+ ctx->cropcap.bounds.top = 0;
+ ctx->cropcap.bounds.width = max_w;
+ ctx->cropcap.bounds.height = max_h;
+
+ /* crop default values */
+ ctx->cropcap.defrect.left = 0;
+ ctx->cropcap.defrect.top = 0;
+ ctx->cropcap.defrect.width = max_w;
+ ctx->cropcap.defrect.height = max_h;
+
+ /* crop pixel aspec values */
+ /* To Do : Have to modify but I don't know the meaning. */
+ ctx->cropcap.pixelaspect.numerator = 16;
+ ctx->cropcap.pixelaspect.denominator = 9;
+
+ a->bounds = ctx->cropcap.bounds;
+ a->defrect = ctx->cropcap.defrect;
+ a->pixelaspect = ctx->cropcap.pixelaspect;
+
+ return 0;
+}
+
+int fimc_g_crop_output(void *fh, struct v4l2_crop *a)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: called\n", __func__);
+
+ mutex_lock(&ctrl->v4l2_lock);
+ a->c.left = ctx->crop.left;
+ a->c.top = ctx->crop.top;
+ a->c.width = ctx->crop.width;
+ a->c.height = ctx->crop.height;
+ mutex_unlock(&ctrl->v4l2_lock);
+
+ return 0;
+}
+
+int fimc_s_crop_output(void *fh, struct v4l2_crop *a)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+
+ fimc_info1("%s: called: left(%d), top(%d), width(%d), height(%d),\n",
+ __func__, a->c.left, a->c.top, a->c.width, a->c.height);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ /* Check arguments : widht and height */
+ if ((a->c.width < 0) || (a->c.height < 0)) {
+ fimc_err("The crop rect must be bigger than 0\n");
+ return -EINVAL;
+ }
+
+ if ((a->c.width > FIMC_SRC_MAX_W) || (a->c.height > FIMC_SRC_MAX_H)) {
+ fimc_err("The crop width/height must be smaller than "
+ "%d and %d\n", FIMC_SRC_MAX_W, FIMC_SRC_MAX_H);
+ return -EINVAL;
+ }
+
+ /* Check arguments : left and top */
+ if ((a->c.left < 0) || (a->c.top < 0)) {
+ fimc_err("The crop left, top must be bigger than 0\n");
+ return -EINVAL;
+ }
+
+ if ((a->c.left > FIMC_SRC_MAX_W) || (a->c.top > FIMC_SRC_MAX_H)) {
+ fimc_err("The crop left, top must be smaller than %d, %d\n",
+ FIMC_SRC_MAX_W, FIMC_SRC_MAX_H);
+ return -EINVAL;
+ }
+
+ if ((a->c.left + a->c.width) > FIMC_SRC_MAX_W) {
+ fimc_err("The crop rect must be in bound rect\n");
+ return -EINVAL;
+ }
+
+ if ((a->c.top + a->c.height) > FIMC_SRC_MAX_H) {
+ fimc_err("The crop rect must be in bound rect\n");
+ return -EINVAL;
+ }
+
+ ctx->crop.left = a->c.left;
+ ctx->crop.top = a->c.top;
+ ctx->crop.width = a->c.width;
+ ctx->crop.height = a->c.height;
+
+ return 0;
+}
+
+int fimc_streamon_output(void *fh)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int ret = -1;
+
+ fimc_info1("%s: called\n", __func__);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->overlay.mode == FIMC_OVLY_NOT_FIXED)
+ ctx->overlay.mode = FIMC_OVLY_MODE;
+
+ /* initialize destination buffers */
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) {
+ ret = fimc_outdev_set_dst_buf(ctrl, ctx);
+ if (ret)
+ return ret;
+ }
+
+ ret = fimc_outdev_check_param(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_outdev_check_param\n");
+ return ret;
+ }
+
+ ctx->status = FIMC_READY_ON;
+ if (ctrl->status == FIMC_STREAMOFF)
+ ctrl->status = FIMC_READY_ON;
+
+ return ret;
+}
+
+void fimc_outdev_init_idxs(struct fimc_control *ctrl)
+{
+ ctrl->out->idxs.prev.ctx = -1;
+ ctrl->out->idxs.prev.idx = -1;
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctrl->out->idxs.next.ctx = -1;
+ ctrl->out->idxs.next.idx = -1;
+}
+
+int fimc_streamoff_output(void *fh)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int ret = -1, i = 0, off_cnt = 0;
+
+ fimc_info1("%s: called\n", __func__);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ ret = fimc_outdev_stop_streaming(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_outdev_stop_streaming\n");
+ return -EINVAL;
+ }
+
+ ret = fimc_init_in_queue(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_init_in_queue\n");
+ return -EINVAL;
+ }
+
+ ret = fimc_init_out_queue(ctrl, ctx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_init_out_queue\n");
+ return -EINVAL;
+ }
+
+ /* Make all buffers DQUEUED state. */
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->src[i].state = VIDEOBUF_IDLE;
+ ctx->src[i].flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ ctx->status = FIMC_STREAMOFF;
+
+ if (ctrl->out->last_ctx == ctx->ctx_num)
+ ctrl->out->last_ctx = -1;
+
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO) {
+ ctrl->mem.curr = ctx->dst[0].base[FIMC_ADDR_Y];
+
+ for (i = 0; i < FIMC_OUTBUFS; i++) {
+ ctx->dst[i].base[FIMC_ADDR_Y] = 0;
+ ctx->dst[i].length[FIMC_ADDR_Y] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CB] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CB] = 0;
+
+ ctx->dst[i].base[FIMC_ADDR_CR] = 0;
+ ctx->dst[i].length[FIMC_ADDR_CR] = 0;
+ }
+ }
+
+ /* check all ctx to change ctrl->status from streamon to streamoff */
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ if (ctrl->out->ctx[i].status == FIMC_STREAMOFF)
+ off_cnt++;
+ }
+
+ if (off_cnt == FIMC_MAX_CTXS) {
+ ctrl->status = FIMC_STREAMOFF;
+ fimc_outdev_init_idxs(ctrl);
+ fimc_outdev_stop_camif(ctrl);
+ }
+
+ if (ctx_id == ctrl->out->last_ctx)
+ ctrl->out->last_ctx = -1;
+
+ mutex_lock(&ctrl->lock);
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO && ctrl->fb.is_enable == 1) {
+ fimc_info2("WIN_OFF for FIMC%d\n", ctrl->id);
+ ret = fb_blank(registered_fb[ctx->overlay.fb_id],
+ FB_BLANK_POWERDOWN);
+ if (ret < 0) {
+ fimc_err("%s: fb_blank: fb[%d] " \
+ "mode=FB_BLANK_POWERDOWN\n",
+ __func__, ctx->overlay.fb_id);
+ mutex_unlock(&ctrl->lock);
+ return -EINVAL;
+ }
+
+ ctrl->fb.is_enable = 0;
+ }
+ mutex_unlock(&ctrl->lock);
+
+ return 0;
+}
+
+
+int fimc_output_set_dst_addr(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx, int idx)
+{
+ struct fimc_buf_set buf_set; /* destination addr */
+ u32 format = ctx->fbuf.fmt.pixelformat;
+ u32 width = ctx->fbuf.fmt.width;
+ u32 height = ctx->fbuf.fmt.height;
+ u32 y_size = width * height;
+ u32 c_size = y_size >> 2;
+ int i;
+
+ memset(&buf_set, 0x00, sizeof(buf_set));
+
+ if (V4L2_PIX_FMT_NV12T == format)
+ fimc_get_nv12t_size(width, height, &y_size, &c_size);
+
+ switch (format) {
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF)
+ buf_set.base[FIMC_ADDR_Y] =
+ (dma_addr_t)ctx->fbuf.base;
+ else
+ buf_set.base[FIMC_ADDR_Y] =
+ ctx->dst[idx].base[FIMC_ADDR_Y];
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) {
+ buf_set.base[FIMC_ADDR_Y] =
+ (dma_addr_t)ctx->fbuf.base;
+ buf_set.base[FIMC_ADDR_CB] =
+ buf_set.base[FIMC_ADDR_Y] + y_size;
+ buf_set.base[FIMC_ADDR_CR] =
+ buf_set.base[FIMC_ADDR_CB] + c_size;
+ } else {
+ buf_set.base[FIMC_ADDR_Y] =
+ ctx->dst[idx].base[FIMC_ADDR_Y];
+ buf_set.base[FIMC_ADDR_CB] =
+ ctx->dst[idx].base[FIMC_ADDR_CB];
+ buf_set.base[FIMC_ADDR_CR] =
+ ctx->dst[idx].base[FIMC_ADDR_CR];
+ }
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV12T:
+ if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF) {
+ buf_set.base[FIMC_ADDR_Y] =
+ (dma_addr_t)ctx->fbuf.base;
+ buf_set.base[FIMC_ADDR_CB] =
+ buf_set.base[FIMC_ADDR_Y] + y_size;
+ } else {
+ buf_set.base[FIMC_ADDR_Y] =
+ ctx->dst[idx].base[FIMC_ADDR_Y];
+ buf_set.base[FIMC_ADDR_CB] =
+ ctx->dst[idx].base[FIMC_ADDR_CB];
+ }
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n", \
+ __func__, format);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < FIMC_PHYBUFS; i++)
+ fimc_hwset_output_address(ctrl, &buf_set, i);
+
+ return 0;
+}
+
+
+static int fimc_qbuf_output_single_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ int idx)
+{
+ int ret = -1;
+
+ fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base);
+
+ ret = fimc_output_set_dst_addr(ctrl, ctx, idx);
+ if (ret < 0) {
+ fimc_err("%s: Fail: fimc_output_set_dst_addr\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl->out->idxs.active.idx = idx;
+ ctrl->out->idxs.active.ctx = ctx->ctx_num;
+
+ ctrl->status = FIMC_STREAMON;
+ ctx->status = FIMC_STREAMON;
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_start_camif\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_qbuf_output_multi_buf(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ int idx)
+{
+ int ret = -1;
+
+ fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base);
+
+ fimc_output_set_dst_addr(ctrl, ctx, idx);
+ if (ret < 0) {
+ fimc_err("%s: Fail: fimc_output_set_dst_addr\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_start_camif\n");
+ return -EINVAL;
+ }
+
+ ctrl->out->idxs.active.idx = idx;
+ ctrl->out->idxs.active.ctx = ctx->ctx_num;
+
+ ctrl->status = FIMC_STREAMON;
+ ctx->status = FIMC_STREAMON;
+
+ return 0;
+}
+
+static int fimc_qbuf_output_dma_auto(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ int idx)
+{
+ struct fb_var_screeninfo var;
+ struct fb_info *fbinfo;
+ struct s3cfb_window *win;
+ struct v4l2_rect fimd_rect;
+ struct fimc_buf_set buf_set; /* destination addr */
+ int ret = -1, i;
+
+ switch (ctx->status) {
+ case FIMC_READY_ON:
+ fbinfo = registered_fb[ctx->overlay.fb_id];
+ win = (struct s3cfb_window *)fbinfo->par;
+
+ memcpy(&var, &fbinfo->var, sizeof(struct fb_var_screeninfo));
+ memset(&fimd_rect, 0, sizeof(struct v4l2_rect));
+ ret = fimc_fimd_rect(ctrl, ctx, &fimd_rect);
+ if (ret < 0) {
+ fimc_err("fimc_fimd_rect fail\n");
+ return -EINVAL;
+ }
+
+ /* set window path & owner */
+ win->path = DATA_PATH_DMA;
+ win->owner = DMA_MEM_OTHER;
+ win->other_mem_addr = ctx->dst[1].base[FIMC_ADDR_Y];
+ win->other_mem_size = ctx->dst[1].length[FIMC_ADDR_Y];
+
+ /* Update WIN size */
+ var.xres_virtual = fimd_rect.width;
+ var.yres_virtual = fimd_rect.height;
+ var.xres = fimd_rect.width;
+ var.yres = fimd_rect.height;
+
+ /* Update WIN position */
+ win->x = fimd_rect.left;
+ win->y = fimd_rect.top;
+
+ var.activate = FB_ACTIVATE_FORCE;
+ ret = fb_set_var(fbinfo, &var);
+ if (ret < 0) {
+ fimc_err("fb_set_var fail (ret=%d)\n", ret);
+ return -EINVAL;
+ }
+
+ /* fall through */
+
+ case FIMC_STREAMON_IDLE:
+ fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base);
+
+ memset(&buf_set, 0x00, sizeof(buf_set));
+ buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y];
+
+ for (i = 0; i < FIMC_PHYBUFS; i++)
+ fimc_hwset_output_address(ctrl, &buf_set, i);
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_start_camif\n");
+ return -EINVAL;
+ }
+
+ ctrl->out->idxs.active.idx = idx;
+ ctrl->out->idxs.active.ctx = ctx->ctx_num;
+
+ ctrl->status = FIMC_STREAMON;
+ ctx->status = FIMC_STREAMON;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fimc_qbuf_output_dma_manual(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ int idx)
+{
+ struct fimc_buf_set buf_set; /* destination addr */
+ int ret = -1, i;
+
+ fimc_outdev_set_src_addr(ctrl, ctx->src[idx].base);
+
+ memset(&buf_set, 0x00, sizeof(buf_set));
+ buf_set.base[FIMC_ADDR_Y] = ctx->dst[idx].base[FIMC_ADDR_Y];
+
+ for (i = 0; i < FIMC_PHYBUFS; i++)
+ fimc_hwset_output_address(ctrl, &buf_set, i);
+
+ ret = fimc_outdev_start_camif(ctrl);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_start_camif\n");
+ return -EINVAL;
+ }
+
+ ctrl->out->idxs.active.idx = idx;
+ ctrl->out->idxs.active.ctx = ctx->ctx_num;
+
+ ctrl->status = FIMC_STREAMON;
+ ctx->status = FIMC_STREAMON;
+
+ return 0;
+}
+
+static int fimc_update_in_queue_addr(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ u32 idx, dma_addr_t *addr)
+{
+ if (idx >= FIMC_OUTBUFS) {
+ fimc_err("%s: Failed\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx->src[idx].base[FIMC_ADDR_Y] = addr[FIMC_ADDR_Y];
+ ctx->src[idx].base[FIMC_ADDR_CB] = addr[FIMC_ADDR_CB];
+ ctx->src[idx].base[FIMC_ADDR_CR] = addr[FIMC_ADDR_CR];
+
+ return 0;
+}
+
+int fimc_qbuf_output(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_buf *buf = (struct fimc_buf *)b->m.userptr;
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int idx, ctx_num;
+ int ret = -1;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ fimc_info2("ctx(%d) queued idx = %d\n", ctx->ctx_num, b->index);
+
+ if (b->index >= ctx->buf_num) {
+ fimc_err("The index is out of bounds. "
+ "You requested %d buffers. "
+ "But you set the index as %d\n",
+ ctx->buf_num, b->index);
+ return -EINVAL;
+ }
+
+ /* Check the buffer state if the state is VIDEOBUF_IDLE. */
+ if (ctx->src[b->index].state != VIDEOBUF_IDLE) {
+ fimc_err("The index(%d) buffer must be dequeued state(%d)\n",
+ b->index, ctx->src[b->index].state);
+ return -EINVAL;
+ }
+
+ if (b->memory == V4L2_MEMORY_USERPTR) {
+ ret = fimc_update_in_queue_addr(ctrl, ctx, b->index, buf->base);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Attach the buffer to the incoming queue. */
+ ret = fimc_push_inq(ctrl, ctx, b->index);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_push_inq\n");
+ return -EINVAL;
+ }
+
+ if ((ctrl->status == FIMC_READY_ON) ||
+ (ctrl->status == FIMC_STREAMON_IDLE)) {
+ ret = fimc_pop_inq(ctrl, &ctx_num, &idx);
+ if (ret < 0) {
+ fimc_err("Fail: fimc_pop_inq\n");
+ return -EINVAL;
+ }
+
+ fimc_clk_en(ctrl, true);
+
+ ctx = &ctrl->out->ctx[ctx_num];
+ if (ctx_num != ctrl->out->last_ctx) {
+ ctrl->out->last_ctx = ctx->ctx_num;
+ fimc_outdev_set_ctx_param(ctrl, ctx);
+ }
+
+ switch (ctx->overlay.mode) {
+ case FIMC_OVLY_DMA_AUTO:
+ ret = fimc_qbuf_output_dma_auto(ctrl, ctx, idx);
+ break;
+ case FIMC_OVLY_DMA_MANUAL:
+ ret = fimc_qbuf_output_dma_manual(ctrl, ctx, idx);
+ break;
+ case FIMC_OVLY_NONE_SINGLE_BUF:
+ ret = fimc_qbuf_output_single_buf(ctrl, ctx, idx);
+ break;
+ case FIMC_OVLY_NONE_MULTI_BUF:
+ ret = fimc_qbuf_output_multi_buf(ctrl, ctx, idx);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int fimc_dqbuf_output(void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_ctx *ctx;
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int idx = -1, ret = -1;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ ret = fimc_pop_outq(ctrl, ctx, &idx);
+ if (ret < 0) {
+ ret = wait_event_timeout(ctrl->wq, (ctx->outq[0] != -1),
+ FIMC_DQUEUE_TIMEOUT);
+ if (ret == 0) {
+ fimc_dump_context(ctrl, ctx);
+ fimc_err("[0] out_queue is empty\n");
+ ctx->status = FIMC_STREAMON_IDLE;
+ return -EAGAIN;
+ } else if (ret == -ERESTARTSYS) {
+ fimc_print_signal(ctrl);
+ } else {
+ /* Normal case */
+ ret = fimc_pop_outq(ctrl, ctx, &idx);
+ if (ret < 0) {
+ fimc_err("[1] out_queue is empty\n");
+ fimc_dump_context(ctrl, ctx);
+ return -EINVAL;
+ }
+ }
+ }
+
+ mutex_lock(&ctrl->lock);
+ if (ctx->overlay.mode == FIMC_OVLY_DMA_AUTO && ctrl->fb.is_enable == 0) {
+ ret = fb_blank(registered_fb[ctx->overlay.fb_id],
+ FB_BLANK_UNBLANK);
+ if (ret < 0) {
+ fimc_err("%s: fb_blank: fb[%d] " \
+ "mode=FB_BLANK_UNBLANK\n",
+ __func__, ctx->overlay.fb_id);
+ mutex_unlock(&ctrl->lock);
+ return -EINVAL;
+ }
+ ctrl->fb.is_enable = 1;
+ }
+ mutex_unlock(&ctrl->lock);
+
+ b->index = idx;
+
+ fimc_info2("ctx(%d) dqueued idx = %d\n", ctx->ctx_num, b->index);
+
+ return ret;
+}
+
+int fimc_g_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct fimc_outinfo *out = ctrl->out;
+ struct fimc_ctx *ctx;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ int i, j;
+
+ fimc_info1("%s: called\n", __func__);
+
+ if (ctrl->cap) {
+ fimc_err("%s: fimc is already used for capture mode\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!out) {
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out) {
+ fimc_err("%s: no memory for outdev info\n", __func__);
+ return -ENOMEM;
+ }
+ ctrl->out = out;
+
+ /* init: struct fimc_outinfo */
+ out->last_ctx = -1;
+
+ spin_lock_init(&ctrl->out->lock_in);
+ spin_lock_init(&ctrl->out->lock_out);
+
+ for (i = 0; i < FIMC_INQUEUES; i++) {
+ ctrl->out->inq[i].ctx = -1;
+ ctrl->out->inq[i].idx = -1;
+ }
+
+ for (i = 0; i < FIMC_MAX_CTXS; i++) {
+ ctx = &ctrl->out->ctx[i];
+ ctx->ctx_num = i;
+ ctx->overlay.mode = FIMC_OVLY_NOT_FIXED;
+ ctx->status = FIMC_STREAMOFF;
+
+ for (j = 0; j < FIMC_OUTBUFS; j++) {
+ ctx->inq[j] = -1;
+ ctx->outq[j] = -1;
+ }
+ }
+
+ ctrl->out->idxs.prev.ctx = -1;
+ ctrl->out->idxs.prev.idx = -1;
+ ctrl->out->idxs.active.ctx = -1;
+ ctrl->out->idxs.active.idx = -1;
+ ctrl->out->idxs.next.ctx = -1;
+ ctrl->out->idxs.next.idx = -1;
+ }
+
+ f->fmt.pix = ctrl->out->ctx[ctx_id].pix;
+
+ return 0;
+}
+
+int fimc_try_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+ u32 format = f->fmt.pix.pixelformat;
+
+ fimc_info1("%s: called. width(%d), height(%d)\n", __func__,
+ f->fmt.pix.width, f->fmt.pix.height);
+
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ /* Check pixel format */
+ switch (format) {
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_RGB32: /* fall through */
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_YUV420:
+ break;
+ default:
+ fimc_warn("Supported format : \
+ V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, \
+ V4L2_PIX_FMT_YVYU, V4L2_PIX_FMT_VYUY, \
+ V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV12T, \
+ V4L2_PIX_FMT_NV21, V4L2_PIX_FMT_RGB32, \
+ V4L2_PIX_FMT_RGB565\n");
+ fimc_warn("Changed format : V4L2_PIX_FMT_RGB32\n");
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
+ return -EINVAL;
+ }
+
+ /* Fill the return value. */
+ switch (format) {
+ case V4L2_PIX_FMT_RGB32:
+ f->fmt.pix.bytesperline = f->fmt.pix.width << 2;
+ break;
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ f->fmt.pix.bytesperline = f->fmt.pix.width << 1;
+ break;
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * 3) >> 1;
+ break;
+
+ default:
+ /* dummy value*/
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ }
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+
+ return 0;
+}
+
+int fimc_s_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+ int ret = -1;
+
+ fimc_info1("%s: called\n", __func__);
+
+ /* Check stream status */
+ ctx = &ctrl->out->ctx[ctx_id];
+ if (ctx->status != FIMC_STREAMOFF) {
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ ret = fimc_try_fmt_vid_out(filp, fh, f);
+ if (ret < 0)
+ return ret;
+
+ ctx->pix = f->fmt.pix;
+
+ return ret;
+}
+
+int fimc_init_in_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ struct fimc_idx swap_queue[FIMC_INQUEUES];
+ int swap_cnt = 0, i;
+ unsigned long spin_flags;
+
+ spin_lock_irqsave(&ctrl->out->lock_in, spin_flags);
+
+ /* init incoming queue */
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ ctx->inq[i] = -1;
+
+ /* init common incoming queue */
+ for (i = 0; i < FIMC_INQUEUES; i++) {
+ if (ctrl->out->inq[i].ctx != ctx->ctx_num) {
+ swap_queue[swap_cnt].ctx = ctrl->out->inq[i].ctx;
+ swap_queue[swap_cnt].idx = ctrl->out->inq[i].idx;
+ swap_cnt++;
+ }
+
+ ctrl->out->inq[i].ctx = -1;
+ ctrl->out->inq[i].idx = -1;
+ }
+
+ /* restore common incoming queue */
+ for (i = 0; i < swap_cnt; i++) {
+ ctrl->out->inq[i].ctx = swap_queue[i].ctx;
+ ctrl->out->inq[i].idx = swap_queue[i].idx;
+ }
+
+ spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags);
+
+ return 0;
+}
+
+int fimc_init_out_queue(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ unsigned long spin_flags;
+ int i;
+
+ spin_lock_irqsave(&ctrl->out->lock_out, spin_flags);
+
+ /* Init incoming queue */
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ ctx->outq[i] = -1;
+
+ spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags);
+
+ return 0;
+}
+
+int fimc_push_inq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx)
+{
+ struct fimc_idx swap_common_inq[FIMC_INQUEUES];
+ int swap_queue[FIMC_OUTBUFS];
+ int i;
+ unsigned long spin_flags;
+
+ fimc_dbg("%s: idx = %d\n", __func__, idx);
+
+ if (ctrl->out->inq[FIMC_INQUEUES-1].idx != -1) {
+ fimc_err("FULL: common incoming queue\n");
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&ctrl->out->lock_in, spin_flags);
+
+ /* ctx own incoming queue */
+ /* Backup original queue */
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ swap_queue[i] = ctx->inq[i];
+
+ /* Attach new idx */
+ ctx->inq[0] = idx;
+ ctx->src[idx].state = VIDEOBUF_QUEUED;
+ ctx->src[idx].flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
+
+ /* Shift the origonal queue */
+ for (i = 1; i < FIMC_OUTBUFS; i++)
+ ctx->inq[i] = swap_queue[i-1];
+
+ /* Common incoming queue */
+ /* Backup original queue */
+ for (i = 0; i < FIMC_INQUEUES; i++) {
+ swap_common_inq[i].ctx = ctrl->out->inq[i].ctx;
+ swap_common_inq[i].idx = ctrl->out->inq[i].idx;
+ }
+
+ /* Attach new idx */
+ ctrl->out->inq[0].ctx = ctx->ctx_num;
+ ctrl->out->inq[0].idx = idx;
+
+ /* Shift the origonal queue */
+ for (i = 1; i < FIMC_INQUEUES; i++) {
+ ctrl->out->inq[i].ctx = swap_common_inq[i-1].ctx;
+ ctrl->out->inq[i].idx = swap_common_inq[i-1].idx;
+ }
+
+ spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags);
+
+ return 0;
+}
+
+int fimc_pop_inq(struct fimc_control *ctrl, int *ctx_num, int *idx)
+{
+ struct fimc_ctx *ctx;
+ unsigned long spin_flags;
+ int i, ret = 0;
+ int ctx_idx = -1;
+
+ spin_lock_irqsave(&ctrl->out->lock_in, spin_flags);
+
+ /* find valid index from common incoming queue */
+ for (i = (FIMC_INQUEUES-1); i >= 0; i--) {
+ if (ctrl->out->inq[i].ctx != -1) {
+ *ctx_num = ctrl->out->inq[i].ctx;
+ *idx = ctrl->out->inq[i].idx;
+ ctrl->out->inq[i].ctx = -1;
+ ctrl->out->inq[i].idx = -1;
+ break;
+ }
+ }
+
+ /* common incoming queue is empty. */
+ if (i < 0) {
+ spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags);
+ return -EINVAL;
+ }
+
+ /* find valid index from incoming queue. */
+ ctx = &ctrl->out->ctx[*ctx_num];
+ for (i = (FIMC_OUTBUFS-1); i >= 0; i--) {
+ if (ctx->inq[i] != -1) {
+ ctx_idx = ctx->inq[i];
+ ctx->inq[i] = -1;
+ ctx->src[ctx_idx].state = VIDEOBUF_ACTIVE;
+ ctx->src[ctx_idx].flags = V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+
+ if (*idx != ctx_idx)
+ fimc_err("common inq(%d) vs inq(%d) mismatch\n", *idx, ctx_idx);
+
+ /* incoming queue is empty. */
+ if (i < 0)
+ ret = -EINVAL;
+ else
+ fimc_dbg("%s: index = %d\n", __func__, *idx);
+
+ spin_unlock_irqrestore(&ctrl->out->lock_in, spin_flags);
+
+ return ret;
+}
+
+int fimc_push_outq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int idx)
+{
+ unsigned long spin_flags;
+ int swap_queue[FIMC_OUTBUFS];
+ int i;
+
+ fimc_dbg("%s: index = %d\n", __func__, idx);
+
+ spin_lock_irqsave(&ctrl->out->lock_out, spin_flags);
+
+ /* Backup original queue */
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ swap_queue[i] = ctx->outq[i];
+
+ /* Attach new index */
+ ctx->outq[0] = idx;
+ ctx->src[idx].state = VIDEOBUF_DONE;
+ ctx->src[idx].flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+
+ /* Shift the origonal queue */
+ for (i = 1; i < FIMC_OUTBUFS; i++)
+ ctx->outq[i] = swap_queue[i-1];
+
+ spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags);
+
+ return 0;
+}
+
+int fimc_pop_outq(struct fimc_control *ctrl, struct fimc_ctx *ctx, int *idx)
+{
+ unsigned long spin_flags;
+ int i, ret = 0;
+
+ spin_lock_irqsave(&ctrl->out->lock_out, spin_flags);
+
+ /* Find last valid idx in outgoing queue. */
+ for (i = (FIMC_OUTBUFS-1); i >= 0; i--) {
+ if (ctx->outq[i] != -1) {
+ *idx = ctx->outq[i];
+ ctx->outq[i] = -1;
+ ctx->src[*idx].state = VIDEOBUF_IDLE;
+ ctx->src[*idx].flags = V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+
+ /* outgoing queue is empty. */
+ if (i < 0) {
+ ret = -EINVAL;
+ fimc_dbg("%s: outgoing queue : %d, %d, %d\n", __func__,
+ ctx->outq[0], ctx->outq[1], ctx->outq[2]);
+ } else
+ fimc_dbg("%s: idx = %d\n", __func__, *idx);
+
+
+ spin_unlock_irqrestore(&ctrl->out->lock_out, spin_flags);
+
+ return ret;
+}
+
+void fimc_dump_context(struct fimc_control *ctrl, struct fimc_ctx *ctx)
+{
+ int i = 0;
+
+ fimc_err("ctx%d, ctrl->status: %d, ctx->status: %d\n",
+ ctx->ctx_num, ctrl->status, ctx->status);
+
+ for (i = 0; i < FIMC_INQUEUES; i++)
+ fimc_err("ctrl->inq[%d]: ctx(%d) idx(%d)\n",
+ i, ctrl->out->inq[i].ctx, ctrl->out->inq[i].idx);
+
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ fimc_err("inq[%d] = %d\n", i, ctx->inq[i]);
+
+ for (i = 0; i < FIMC_OUTBUFS; i++)
+ fimc_err("outq[%d] = %d\n", i, ctx->outq[i]);
+
+ fimc_err("state : prev.ctx(%d), prev.idx(%d) "
+ "active.ctx(%d), active.idx(%d) "
+ "next.ctx(%d), next.idx(%d)\n",
+ ctrl->out->idxs.prev.ctx, ctrl->out->idxs.prev.idx,
+ ctrl->out->idxs.active.ctx, ctrl->out->idxs.active.idx,
+ ctrl->out->idxs.next.ctx, ctrl->out->idxs.next.idx);
+}
+
+void fimc_print_signal(struct fimc_control *ctrl)
+{
+ if (signal_pending(current)) {
+ fimc_dbg(".pend=%.8lx shpend=%.8lx\n",
+ current->pending.signal.sig[0],
+ current->signal->shared_pending.signal.sig[0]);
+ } else {
+ fimc_dbg(":pend=%.8lx shpend=%.8lx\n",
+ current->pending.signal.sig[0],
+ current->signal->shared_pending.signal.sig[0]);
+ }
+}
diff --git a/drivers/media/video/samsung/fimc/fimc_overlay.c b/drivers/media/video/samsung/fimc/fimc_overlay.c
new file mode 100644
index 0000000..ff43c2e
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_overlay.c
@@ -0,0 +1,312 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_overlay.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * V4L2 Overlay device support file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/slab.h>
+#include <linux/bootmem.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <plat/media.h>
+
+#include "fimc.h"
+
+int fimc_try_fmt_overlay(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+
+ u32 is_rotate = 0;
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: top(%d) left(%d) width(%d) height(%d)\n", __func__,
+ f->fmt.win.w.top, f->fmt.win.w.left,
+ f->fmt.win.w.width, f->fmt.win.w.height);
+
+ if (ctx->overlay.mode == FIMC_OVLY_NONE_SINGLE_BUF ||
+ (ctx->overlay.mode == FIMC_OVLY_NONE_MULTI_BUF))
+ return 0;
+
+ /* Check Overlay Size : Overlay size must be smaller than LCD size. */
+ is_rotate = fimc_mapping_rot_flip(ctx->rotate, ctx->flip);
+ if (is_rotate & FIMC_ROT) { /* Landscape mode */
+ if (f->fmt.win.w.width > ctrl->fb.lcd_vres) {
+ fimc_warn("The width is changed %d -> %d\n",
+ f->fmt.win.w.width, ctrl->fb.lcd_vres);
+ f->fmt.win.w.width = ctrl->fb.lcd_vres;
+ }
+
+ if (f->fmt.win.w.height > ctrl->fb.lcd_hres) {
+ fimc_warn("The height is changed %d -> %d\n",
+ f->fmt.win.w.height, ctrl->fb.lcd_hres);
+ f->fmt.win.w.height = ctrl->fb.lcd_hres;
+ }
+ } else { /* Portrait mode */
+ if (f->fmt.win.w.width > ctrl->fb.lcd_hres) {
+ fimc_warn("The width is changed %d -> %d\n",
+ f->fmt.win.w.width, ctrl->fb.lcd_hres);
+ f->fmt.win.w.width = ctrl->fb.lcd_hres;
+ }
+
+ if (f->fmt.win.w.height > ctrl->fb.lcd_vres) {
+ fimc_warn("The height is changed %d -> %d\n",
+ f->fmt.win.w.height, ctrl->fb.lcd_vres);
+ f->fmt.win.w.height = ctrl->fb.lcd_vres;
+ }
+ }
+
+ return 0;
+}
+
+int fimc_g_fmt_vid_overlay(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: called\n", __func__);
+
+ f->fmt.win = ctx->win;
+
+ return 0;
+}
+
+static int fimc_check_pos(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx,
+ struct v4l2_format *f)
+{
+ if (ctx->win.w.width != f->fmt.win.w.width) {
+ fimc_err("%s: cannot change width\n", __func__);
+ return -EINVAL;
+ } else if (ctx->win.w.height != f->fmt.win.w.height) {
+ fimc_err("%s: cannot change height\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_change_fifo_position(struct fimc_control *ctrl,
+ struct fimc_ctx *ctx) {
+ struct v4l2_rect fimd_rect;
+ struct fb_info *fbinfo;
+ struct s3cfb_window *win;
+ int ret = -1;
+
+ fbinfo = registered_fb[ctx->overlay.fb_id];
+
+ memset(&fimd_rect, 0, sizeof(struct v4l2_rect));
+
+ ret = fimc_fimd_rect(ctrl, ctx, &fimd_rect);
+ if (ret < 0) {
+ fimc_err("fimc_fimd_rect fail\n");
+ return -EINVAL;
+ }
+
+ /* Update WIN position */
+ win->x = fimd_rect.left;
+ win->y = fimd_rect.top;
+
+ fbinfo->var.activate = FB_ACTIVATE_FORCE;
+ ret = fb_set_var(fbinfo, &fbinfo->var);
+ if (ret < 0) {
+ fimc_err("fb_set_var fail (ret=%d)\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int fimc_s_fmt_vid_overlay(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+ int ret = -1;
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: called\n", __func__);
+
+ switch (ctx->status) {
+ case FIMC_STREAMON:
+ ret = fimc_check_pos(ctrl, ctx, f);
+ if (ret < 0) {
+ fimc_err("When FIMC is running, "
+ "you can only move the position.\n");
+ return -EBUSY;
+ }
+
+ ret = fimc_try_fmt_overlay(filp, fh, f);
+ if (ret < 0)
+ return ret;
+
+ ctx->win = f->fmt.win;
+ fimc_change_fifo_position(ctrl, ctx);
+
+ break;
+ case FIMC_STREAMOFF:
+ ret = fimc_try_fmt_overlay(filp, fh, f);
+ if (ret < 0)
+ return ret;
+ ctx->win = f->fmt.win;
+
+ break;
+
+ default:
+ fimc_err("FIMC is running\n");
+ return -EBUSY;
+ }
+
+ return ret;
+}
+
+int fimc_g_fbuf(struct file *filp, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+ u32 bpp = 1, format;
+
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: called\n", __func__);
+
+ fb->capability = ctx->fbuf.capability;
+ fb->flags = 0;
+ fb->base = ctx->fbuf.base;
+
+ fb->fmt.width = ctx->fbuf.fmt.width;
+ fb->fmt.height = ctx->fbuf.fmt.height;
+ fb->fmt.pixelformat = ctx->fbuf.fmt.pixelformat;
+ format = ctx->fbuf.fmt.pixelformat;
+
+ switch (format) {
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12:
+ bpp = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 2;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 4;
+ break;
+ }
+
+ ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp;
+ fb->fmt.bytesperline = ctx->fbuf.fmt.bytesperline;
+ fb->fmt.sizeimage = ctx->fbuf.fmt.sizeimage;
+ fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fb->fmt.priv = 0;
+
+ return 0;
+}
+
+int fimc_s_fbuf(struct file *filp, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ctx_id = ((struct fimc_prv_data *)fh)->ctx_id;
+ struct fimc_ctx *ctx;
+ u32 bpp = 1;
+ u32 format = fb->fmt.pixelformat;
+ ctx = &ctrl->out->ctx[ctx_id];
+
+ fimc_info1("%s: called. width(%d), height(%d)\n",
+ __func__, fb->fmt.width, fb->fmt.height);
+
+ ctx->fbuf.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ ctx->fbuf.flags = 0;
+ ctx->fbuf.base = fb->base;
+
+ if (ctx->overlay.mode == FIMC_OVLY_NONE_MULTI_BUF) {
+ ctx->fbuf.fmt.width = fb->fmt.width;
+ ctx->fbuf.fmt.height = fb->fmt.height;
+ ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat;
+
+ switch (format) {
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12:
+ bpp = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 2;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 4;
+ break;
+ }
+
+ ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp;
+ ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage;
+ ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ ctx->fbuf.fmt.priv = 0;
+ } else if (fb->base) {
+ ctx->fbuf.fmt.width = fb->fmt.width;
+ ctx->fbuf.fmt.height = fb->fmt.height;
+ ctx->fbuf.fmt.pixelformat = fb->fmt.pixelformat;
+
+ switch (format) {
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12:
+ bpp = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 2;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 4;
+ break;
+ }
+
+ ctx->fbuf.fmt.bytesperline = fb->fmt.width * bpp;
+ ctx->fbuf.fmt.sizeimage = fb->fmt.sizeimage;
+ ctx->fbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ ctx->fbuf.fmt.priv = 0;
+
+ ctx->overlay.mode = FIMC_OVLY_NONE_SINGLE_BUF;
+ } else {
+ int i;
+ struct s3cfb_window *win = NULL;
+ ctx->overlay.fb_id = -1;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ win = (struct s3cfb_window *)registered_fb[i]->par;
+ if (win->id == ctrl->id) {
+ ctx->overlay.fb_id = i;
+ fimc_info2("%s: overlay.fb_id = %d\n",
+ __func__, ctx->overlay.fb_id);
+ break;
+ }
+ }
+
+ if (-1 == ctx->overlay.fb_id) {
+ fimc_err("%s: fb[%d] is not registered. " \
+ "must be registered for overlay\n",
+ __func__, ctrl->id);
+ return -1;
+ }
+
+ if (1 == win->enabled) {
+ fimc_err("%s: fb[%d] is already being used. " \
+ "must be not used for overlay\n",
+ __func__, ctrl->id);
+ return -1;
+ }
+
+ ctx->overlay.mode = FIMC_OVLY_NOT_FIXED;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/samsung/fimc/fimc_regs.c b/drivers/media/video/samsung/fimc/fimc_regs.c
new file mode 100644
index 0000000..227945a
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_regs.c
@@ -0,0 +1,1801 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_regs.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Register interface file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_samsung.h>
+#include <linux/io.h>
+#include <mach/map.h>
+#include <plat/regs-fimc.h>
+#include <plat/fimc.h>
+
+#include "fimc.h"
+
+/* struct fimc_limit: Limits for FIMC */
+struct fimc_limit fimc40_limits[FIMC_DEVICES] = {
+ {
+ .pre_dst_w = 3264,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 3264,
+ .trg_h_rot = 1280,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1280,
+ }, {
+ .pre_dst_w = 1280,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 1280,
+ .trg_h_rot = 8192,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 768,
+ }, {
+ .pre_dst_w = 1440,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 1440,
+ .trg_h_rot = 0,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 0,
+ },
+};
+
+struct fimc_limit fimc43_limits[FIMC_DEVICES] = {
+ {
+ .pre_dst_w = 4224,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 4224,
+ .trg_h_rot = 1920,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1920,
+ }, {
+ .pre_dst_w = 4224,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 4224,
+ .trg_h_rot = 1920,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1920,
+ }, {
+ .pre_dst_w = 1920,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 1920,
+ .trg_h_rot = 1280,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1280,
+ },
+};
+
+struct fimc_limit fimc50_limits[FIMC_DEVICES] = {
+ {
+ .pre_dst_w = 4224,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 4224,
+ .trg_h_rot = 1920,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1920,
+ }, {
+ .pre_dst_w = 4224,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 4224,
+ .trg_h_rot = 1920,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1920,
+ }, {
+ .pre_dst_w = 1920,
+ .bypass_w = 8192,
+ .trg_h_no_rot = 1920,
+ .trg_h_rot = 1280,
+ .real_w_no_rot = 8192,
+ .real_h_rot = 1280,
+ },
+};
+
+int fimc_hwset_camera_source(struct fimc_control *ctrl)
+{
+ struct s3c_platform_camera *cam = ctrl->cam;
+ u32 cfg = 0;
+
+ /* for now, we support only ITU601 8 bit mode */
+ cfg |= S3C_CISRCFMT_ITU601_8BIT;
+ cfg |= cam->order422;
+
+ if (cam->type == CAM_TYPE_ITU)
+ cfg |= cam->fmt;
+
+ cfg |= S3C_CISRCFMT_SOURCEHSIZE(cam->width);
+ cfg |= S3C_CISRCFMT_SOURCEVSIZE(cam->height);
+
+ writel(cfg, ctrl->regs + S3C_CISRCFMT);
+
+ return 0;
+}
+
+int fimc_hwset_enable_irq(struct fimc_control *ctrl, int overflow, int level)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg &= ~(S3C_CIGCTRL_IRQ_OVFEN | S3C_CIGCTRL_IRQ_LEVEL);
+ cfg |= S3C_CIGCTRL_IRQ_ENABLE;
+
+ if (overflow)
+ cfg |= S3C_CIGCTRL_IRQ_OVFEN;
+
+ if (level)
+ cfg |= S3C_CIGCTRL_IRQ_LEVEL;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_disable_irq(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg &= ~(S3C_CIGCTRL_IRQ_OVFEN | S3C_CIGCTRL_IRQ_ENABLE);
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_clear_irq(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg |= S3C_CIGCTRL_IRQ_CLR;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_output_area_size(struct fimc_control *ctrl, u32 size)
+{
+ u32 cfg = 0;
+
+ cfg = S3C_CITAREA_TARGET_AREA(size);
+
+ writel(cfg, ctrl->regs + S3C_CITAREA);
+
+ return 0;
+}
+
+void fimc_wait_disable_capture(struct fimc_control *ctrl)
+{
+ unsigned long timeo = jiffies + 20; /* timeout of 100 ms */
+ u32 cfg;
+
+ if (!ctrl || !ctrl->cap ||
+ ctrl->cap->fmt.colorspace == V4L2_COLORSPACE_JPEG)
+ return;
+
+ while (time_before(jiffies, timeo)) {
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+
+ if (0 == (cfg & S3C_CISTATUS_IMGCPTEN))
+ break;
+
+ msleep(10);
+ }
+
+ dev_dbg(ctrl->dev, "IMGCPTEN: Wait time = %d ms\n",
+ jiffies_to_msecs(jiffies - timeo + 20));
+
+ return;
+}
+
+int fimc_hwset_image_effect(struct fimc_control *ctrl)
+{
+ u32 cfg = 0;
+
+ if (ctrl->fe.ie_on) {
+ if (ctrl->fe.ie_after_sc)
+ cfg |= S3C_CIIMGEFF_IE_SC_AFTER;
+
+ cfg |= S3C_CIIMGEFF_FIN(ctrl->fe.fin);
+
+ if (ctrl->fe.fin == FIMC_EFFECT_FIN_ARBITRARY_CBCR)
+ cfg |= S3C_CIIMGEFF_PAT_CB(ctrl->fe.pat_cb) |
+ S3C_CIIMGEFF_PAT_CR(ctrl->fe.pat_cr);
+
+ cfg |= S3C_CIIMGEFF_IE_ENABLE;
+ }
+
+ writel(cfg, ctrl->regs + S3C_CIIMGEFF);
+
+ return 0;
+}
+
+int fimc_hwset_shadow_enable(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg &= ~S3C_CIGCTRL_SHADOW_DISABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_shadow_disable(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg |= S3C_CIGCTRL_SHADOW_DISABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+static void fimc_reset_cfg(struct fimc_control *ctrl)
+{
+ int i;
+ u32 cfg[][2] = {
+ { 0x018, 0x00000000 }, { 0x01c, 0x00000000 },
+ { 0x020, 0x00000000 }, { 0x024, 0x00000000 },
+ { 0x028, 0x00000000 }, { 0x02c, 0x00000000 },
+ { 0x030, 0x00000000 }, { 0x034, 0x00000000 },
+ { 0x038, 0x00000000 }, { 0x03c, 0x00000000 },
+ { 0x040, 0x00000000 }, { 0x044, 0x00000000 },
+ { 0x048, 0x00000000 }, { 0x04c, 0x00000000 },
+ { 0x050, 0x00000000 }, { 0x054, 0x00000000 },
+ { 0x058, 0x18000000 }, { 0x05c, 0x00000000 },
+ { 0x0c0, 0x00000000 }, { 0x0c4, 0xffffffff },
+ { 0x0d0, 0x00100080 }, { 0x0d4, 0x00000000 },
+ { 0x0d8, 0x00000000 }, { 0x0dc, 0x00000000 },
+ { 0x0f8, 0x00000000 }, { 0x0fc, 0x04000000 },
+ { 0x168, 0x00000000 }, { 0x16c, 0x00000000 },
+ { 0x170, 0x00000000 }, { 0x174, 0x00000000 },
+ { 0x178, 0x00000000 }, { 0x17c, 0x00000000 },
+ { 0x180, 0x00000000 }, { 0x184, 0x00000000 },
+ { 0x188, 0x00000000 }, { 0x18c, 0x00000000 },
+ { 0x194, 0x0000001e },
+ };
+
+ for (i = 0; i < sizeof(cfg) / 8; i++)
+ writel(cfg[i][1], ctrl->regs + cfg[i][0]);
+}
+
+int fimc_hwset_reset(struct fimc_control *ctrl)
+{
+ u32 cfg = 0;
+
+ cfg = readl(ctrl->regs + S3C_CISRCFMT);
+ cfg |= S3C_CISRCFMT_ITU601_8BIT;
+ writel(cfg, ctrl->regs + S3C_CISRCFMT);
+
+ /* s/w reset */
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg |= (S3C_CIGCTRL_SWRST);
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+ mdelay(1);
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~S3C_CIGCTRL_SWRST;
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ /* in case of ITU656, CISRCFMT[31] should be 0 */
+ if ((ctrl->cap != NULL) && (ctrl->cam->fmt == ITU_656_YCBCR422_8BIT)) {
+ cfg = readl(ctrl->regs + S3C_CISRCFMT);
+ cfg &= ~S3C_CISRCFMT_ITU601_8BIT;
+ writel(cfg, ctrl->regs + S3C_CISRCFMT);
+ }
+
+ fimc_reset_cfg(ctrl);
+
+ return 0;
+}
+
+int fimc_hwset_sw_reset(struct fimc_control *ctrl)
+{
+ u32 cfg = 0;
+
+ /* s/w reset */
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg |= (S3C_CIGCTRL_SWRST);
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~S3C_CIGCTRL_SWRST;
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_clksrc(struct fimc_control *ctrl, int src_clk)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MISC_FIMC);
+ cfg &= ~S3C_CLKSRC_HCLK_MASK;
+
+ if (src_clk == FIMC_HCLK)
+ cfg |= S3C_CLKSRC_HCLK;
+ else if (src_clk == FIMC_SCLK)
+ cfg |= S3C_CLKSRC_SCLK;
+
+ writel(cfg, ctrl->regs + S3C_MISC_FIMC);
+ return 0;
+}
+
+int fimc_hwget_overflow_state(struct fimc_control *ctrl)
+{
+ u32 cfg, status, flag;
+
+ status = readl(ctrl->regs + S3C_CISTATUS);
+ flag = S3C_CISTATUS_OVFIY | S3C_CISTATUS_OVFICB | S3C_CISTATUS_OVFICR;
+
+ if (status & flag) {
+ cfg = readl(ctrl->regs + S3C_CIWDOFST);
+ cfg |= (S3C_CIWDOFST_CLROVFIY | S3C_CIWDOFST_CLROVFICB |
+ S3C_CIWDOFST_CLROVFICR);
+ writel(cfg, ctrl->regs + S3C_CIWDOFST);
+
+ cfg = readl(ctrl->regs + S3C_CIWDOFST);
+ cfg &= ~(S3C_CIWDOFST_CLROVFIY | S3C_CIWDOFST_CLROVFICB |
+ S3C_CIWDOFST_CLROVFICR);
+ writel(cfg, ctrl->regs + S3C_CIWDOFST);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int fimc_hwset_camera_offset(struct fimc_control *ctrl)
+{
+ struct s3c_platform_camera *cam = ctrl->cam;
+ struct v4l2_rect *rect = &cam->window;
+ u32 cfg, h1, h2, v1, v2;
+
+ if (!cam) {
+ fimc_err("%s: no active camera\n", __func__);
+ return -ENODEV;
+ }
+
+ h1 = rect->left;
+ h2 = cam->width - rect->width - rect->left;
+ v1 = rect->top;
+ v2 = cam->height - rect->height - rect->top;
+
+ cfg = readl(ctrl->regs + S3C_CIWDOFST);
+ cfg &= ~(S3C_CIWDOFST_WINHOROFST_MASK | S3C_CIWDOFST_WINVEROFST_MASK);
+ cfg |= S3C_CIWDOFST_WINHOROFST(h1);
+ cfg |= S3C_CIWDOFST_WINVEROFST(v1);
+ cfg |= S3C_CIWDOFST_WINOFSEN;
+ writel(cfg, ctrl->regs + S3C_CIWDOFST);
+
+ cfg = 0;
+ cfg |= S3C_CIWDOFST2_WINHOROFST2(h2);
+ cfg |= S3C_CIWDOFST2_WINVEROFST2(v2);
+ writel(cfg, ctrl->regs + S3C_CIWDOFST2);
+
+ return 0;
+}
+
+int fimc_hwset_camera_polarity(struct fimc_control *ctrl)
+{
+ struct s3c_platform_camera *cam = ctrl->cam;
+ u32 cfg;
+
+ if (!cam) {
+ fimc_err("%s: no active camera\n", __func__);
+ return -ENODEV;
+ }
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ cfg &= ~(S3C_CIGCTRL_INVPOLPCLK | S3C_CIGCTRL_INVPOLVSYNC |
+ S3C_CIGCTRL_INVPOLHREF | S3C_CIGCTRL_INVPOLHSYNC);
+
+ if (cam->inv_pclk)
+ cfg |= S3C_CIGCTRL_INVPOLPCLK;
+
+ if (cam->inv_vsync)
+ cfg |= S3C_CIGCTRL_INVPOLVSYNC;
+
+ if (cam->inv_href)
+ cfg |= S3C_CIGCTRL_INVPOLHREF;
+
+ if (cam->inv_hsync)
+ cfg |= S3C_CIGCTRL_INVPOLHSYNC;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc40_hwset_camera_type(struct fimc_control *ctrl)
+{
+ struct s3c_platform_camera *cam = ctrl->cam;
+ u32 cfg;
+
+ if (!cam) {
+ fimc_err("%s: no active camera\n", __func__);
+ return -ENODEV;
+ }
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~(S3C_CIGCTRL_TESTPATTERN_MASK | S3C_CIGCTRL_SELCAM_ITU_MASK |
+ S3C_CIGCTRL_SELCAM_FIMC_MASK);
+
+ /* Interface selection */
+ if (cam->type == CAM_TYPE_MIPI) {
+ cfg |= S3C_CIGCTRL_SELCAM_FIMC_MIPI;
+ writel(cam->fmt, ctrl->regs + S3C_CSIIMGFMT);
+ } else if (cam->type == CAM_TYPE_ITU) {
+ if (cam->id == CAMERA_PAR_A)
+ cfg |= S3C_CIGCTRL_SELCAM_ITU_A;
+ else
+ cfg |= S3C_CIGCTRL_SELCAM_ITU_B;
+ /* switch to ITU interface */
+ cfg |= S3C_CIGCTRL_SELCAM_FIMC_ITU;
+ } else {
+ fimc_err("%s: invalid camera bus type selected\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc43_hwset_camera_type(struct fimc_control *ctrl)
+{
+ struct s3c_platform_camera *cam = ctrl->cam;
+ u32 cfg;
+
+ if (!cam) {
+ fimc_err("%s: no active camera\n", __func__);
+ return -ENODEV;
+ }
+
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~(S3C_CIGCTRL_TESTPATTERN_MASK | S3C_CIGCTRL_SELCAM_ITU_MASK |
+ S3C_CIGCTRL_SELCAM_MIPI_MASK | S3C_CIGCTRL_SELCAM_FIMC_MASK |
+ S3C_CIGCTRL_SELWB_CAMIF_MASK);
+
+ /* Interface selection */
+ if (cam->id == CAMERA_WB) {
+ cfg |= S3C_CIGCTRL_SELWB_CAMIF_WRITEBACK;
+ } else if (cam->type == CAM_TYPE_MIPI) {
+ cfg |= S3C_CIGCTRL_SELCAM_FIMC_MIPI;
+
+ /* C110/V210 Support only MIPI A support */
+ cfg |= S3C_CIGCTRL_SELCAM_MIPI_A;
+
+ /* FIXME: Temporary MIPI CSIS Data 32 bit aligned */
+ if (ctrl->cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG)
+ writel((MIPI_USER_DEF_PACKET_1 | (0x1 << 8)),
+ ctrl->regs + S3C_CSIIMGFMT);
+ else
+ writel(cam->fmt | (0x1 << 8),
+ ctrl->regs + S3C_CSIIMGFMT);
+ } else if (cam->type == CAM_TYPE_ITU) {
+ if (cam->id == CAMERA_PAR_A)
+ cfg |= S3C_CIGCTRL_SELCAM_ITU_A;
+ else
+ cfg |= S3C_CIGCTRL_SELCAM_ITU_B;
+ /* switch to ITU interface */
+ cfg |= S3C_CIGCTRL_SELCAM_FIMC_ITU;
+ } else {
+ fimc_err("%s: invalid camera bus type selected\n", __func__);
+ return -EINVAL;
+ }
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_camera_type(struct fimc_control *ctrl)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+
+ switch (pdata->hw_ver) {
+ case 0x40:
+ fimc40_hwset_camera_type(ctrl);
+ break;
+ case 0x43:
+ case 0x45:
+ fimc43_hwset_camera_type(ctrl);
+ break;
+ default:
+ fimc43_hwset_camera_type(ctrl);
+ break;
+ }
+
+ return 0;
+}
+
+
+int fimc_hwset_jpeg_mode(struct fimc_control *ctrl, bool enable)
+{
+ u32 cfg;
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+
+ if (enable)
+ cfg |= S3C_CIGCTRL_CAM_JPEG;
+ else
+ cfg &= ~S3C_CIGCTRL_CAM_JPEG;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_output_size(struct fimc_control *ctrl, int width, int height)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CITRGFMT);
+
+ cfg &= ~(S3C_CITRGFMT_TARGETH_MASK | S3C_CITRGFMT_TARGETV_MASK);
+
+ cfg |= S3C_CITRGFMT_TARGETHSIZE(width);
+ cfg |= S3C_CITRGFMT_TARGETVSIZE(height);
+
+ writel(cfg, ctrl->regs + S3C_CITRGFMT);
+
+ return 0;
+}
+
+int fimc_hwset_output_colorspace(struct fimc_control *ctrl, u32 pixelformat)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg;
+
+ if (pdata->hw_ver != 0x40) {
+ if (pixelformat == V4L2_PIX_FMT_YUV444) {
+ cfg = readl(ctrl->regs + S3C_CIEXTEN);
+ cfg |= S3C_CIEXTEN_YUV444_OUT;
+ writel(cfg, ctrl->regs + S3C_CIEXTEN);
+
+ return 0;
+ } else {
+ cfg = readl(ctrl->regs + S3C_CIEXTEN);
+ cfg &= ~S3C_CIEXTEN_YUV444_OUT;
+ writel(cfg, ctrl->regs + S3C_CIEXTEN);
+ }
+ }
+
+ cfg = readl(ctrl->regs + S3C_CITRGFMT);
+ cfg &= ~S3C_CITRGFMT_OUTFORMAT_MASK;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_JPEG:
+ break;
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_RGB32:
+ cfg |= S3C_CITRGFMT_OUTFORMAT_RGB;
+ break;
+
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YVYU:
+ cfg |= S3C_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE;
+ break;
+
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61: /* fall through */
+ case V4L2_PIX_FMT_YUV422P:
+ cfg |= S3C_CITRGFMT_OUTFORMAT_YCBCR422;
+ break;
+
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_NV21:
+ cfg |= S3C_CITRGFMT_OUTFORMAT_YCBCR420;
+ break;
+
+ default:
+ fimc_err("%s: invalid pixel format\n", __func__);
+ break;
+ }
+
+ writel(cfg, ctrl->regs + S3C_CITRGFMT);
+
+ return 0;
+}
+
+int fimc_hwset_output_rot_flip(struct fimc_control *ctrl, u32 rot, u32 flip)
+{
+ u32 cfg, val;
+
+ cfg = readl(ctrl->regs + S3C_CITRGFMT);
+ cfg &= ~S3C_CITRGFMT_FLIP_MASK;
+ cfg &= ~S3C_CITRGFMT_OUTROT90_CLOCKWISE;
+
+ val = fimc_mapping_rot_flip(rot, flip);
+
+ if (val & FIMC_ROT)
+ cfg |= S3C_CITRGFMT_OUTROT90_CLOCKWISE;
+
+ if (val & FIMC_XFLIP)
+ cfg |= S3C_CITRGFMT_FLIP_X_MIRROR;
+
+ if (val & FIMC_YFLIP)
+ cfg |= S3C_CITRGFMT_FLIP_Y_MIRROR;
+
+ writel(cfg, ctrl->regs + S3C_CITRGFMT);
+
+ return 0;
+}
+
+int fimc_hwset_output_area(struct fimc_control *ctrl, u32 width, u32 height)
+{
+ u32 cfg = 0;
+
+ cfg = S3C_CITAREA_TARGET_AREA(width * height);
+ writel(cfg, ctrl->regs + S3C_CITAREA);
+
+ return 0;
+}
+
+int fimc_hwset_enable_lastirq(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIOCTRL);
+
+ cfg |= S3C_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, ctrl->regs + S3C_CIOCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_disable_lastirq(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIOCTRL);
+
+ cfg &= ~S3C_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, ctrl->regs + S3C_CIOCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_prescaler(struct fimc_control *ctrl, struct fimc_scaler *sc)
+{
+ u32 cfg = 0, shfactor;
+
+ shfactor = 10 - (sc->hfactor + sc->vfactor);
+
+ cfg |= S3C_CISCPRERATIO_SHFACTOR(shfactor);
+ cfg |= S3C_CISCPRERATIO_PREHORRATIO(sc->pre_hratio);
+ cfg |= S3C_CISCPRERATIO_PREVERRATIO(sc->pre_vratio);
+
+ writel(cfg, ctrl->regs + S3C_CISCPRERATIO);
+
+ cfg = 0;
+ cfg |= S3C_CISCPREDST_PREDSTWIDTH(sc->pre_dst_width);
+ cfg |= S3C_CISCPREDST_PREDSTHEIGHT(sc->pre_dst_height);
+
+ writel(cfg, ctrl->regs + S3C_CISCPREDST);
+
+ return 0;
+}
+
+int fimc_hwset_output_address(struct fimc_control *ctrl,
+ struct fimc_buf_set *bs, int id)
+{
+ writel(bs->base[FIMC_ADDR_Y], ctrl->regs + S3C_CIOYSA(id));
+ writel(bs->base[FIMC_ADDR_CB], ctrl->regs + S3C_CIOCBSA(id));
+ writel(bs->base[FIMC_ADDR_CR], ctrl->regs + S3C_CIOCRSA(id));
+
+ return 0;
+}
+
+int fimc_hwset_output_yuv(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg;
+
+ cfg = readl(ctrl->regs + S3C_CIOCTRL);
+ cfg &= ~(S3C_CIOCTRL_ORDER2P_MASK | S3C_CIOCTRL_ORDER422_MASK |
+ S3C_CIOCTRL_YCBCR_PLANE_MASK);
+
+ switch (pixelformat) {
+ /* 1 plane formats */
+ case V4L2_PIX_FMT_YUYV:
+ cfg |= S3C_CIOCTRL_ORDER422_YCBYCR;
+ break;
+
+ case V4L2_PIX_FMT_UYVY:
+ cfg |= S3C_CIOCTRL_ORDER422_CBYCRY;
+ break;
+
+ case V4L2_PIX_FMT_VYUY:
+ cfg |= S3C_CIOCTRL_ORDER422_CRYCBY;
+ break;
+
+ case V4L2_PIX_FMT_YVYU:
+ cfg |= S3C_CIOCTRL_ORDER422_YCRYCB;
+ break;
+
+ /* 2 plane formats */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_NV16:
+ cfg |= S3C_CIOCTRL_ORDER2P_LSB_CBCR;
+ cfg |= S3C_CIOCTRL_YCBCR_2PLANE;
+ break;
+
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ cfg |= S3C_CIOCTRL_ORDER2P_LSB_CRCB;
+ cfg |= S3C_CIOCTRL_YCBCR_2PLANE;
+ break;
+
+ /* 3 plane formats */
+ case V4L2_PIX_FMT_YUV422P: /* fall through */
+ case V4L2_PIX_FMT_YUV420:
+ cfg |= S3C_CIOCTRL_YCBCR_3PLANE;
+ break;
+ }
+
+ writel(cfg, ctrl->regs + S3C_CIOCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_output_scan(struct fimc_control *ctrl,
+ struct v4l2_pix_format *fmt)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg;
+
+ /* nothing to do: FIMC40 not supported interlaced and weave output */
+ if (pdata->hw_ver == 0x40)
+ return 0;
+
+ cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ cfg &= ~S3C_CISCCTRL_SCAN_MASK;
+
+ if (fmt->field == V4L2_FIELD_INTERLACED ||
+ fmt->field == V4L2_FIELD_INTERLACED_TB)
+ cfg |= S3C_CISCCTRL_INTERLACE;
+ else
+ cfg |= S3C_CISCCTRL_PROGRESSIVE;
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ cfg = readl(ctrl->regs + S3C_CIOCTRL);
+ cfg &= ~S3C_CIOCTRL_WEAVE_MASK;
+
+ if ((ctrl->cap) && (fmt->field == V4L2_FIELD_INTERLACED_TB))
+ cfg |= S3C_CIOCTRL_WEAVE_OUT;
+
+ writel(cfg, ctrl->regs + S3C_CIOCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_rot(struct fimc_control *ctrl, u32 rot, u32 flip)
+{
+ u32 cfg, val;
+
+ cfg = readl(ctrl->regs + S3C_CITRGFMT);
+ cfg &= ~S3C_CITRGFMT_INROT90_CLOCKWISE;
+
+ val = fimc_mapping_rot_flip(rot, flip);
+
+ if (val & FIMC_ROT)
+ cfg |= S3C_CITRGFMT_INROT90_CLOCKWISE;
+
+ writel(cfg, ctrl->regs + S3C_CITRGFMT);
+
+ return 0;
+}
+
+int fimc40_hwset_scaler(struct fimc_control *ctrl, struct fimc_scaler *sc)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg &= ~(S3C_CISCCTRL_SCALERBYPASS |
+ S3C_CISCCTRL_SCALEUP_H | S3C_CISCCTRL_SCALEUP_V |
+ S3C_CISCCTRL_MAIN_V_RATIO_MASK |
+ S3C_CISCCTRL_MAIN_H_RATIO_MASK |
+ S3C_CISCCTRL_CSCR2Y_WIDE |
+ S3C_CISCCTRL_CSCY2R_WIDE);
+
+#ifdef CONFIG_VIDEO_FIMC_RANGE_WIDE
+ cfg |= (S3C_CISCCTRL_CSCR2Y_WIDE | S3C_CISCCTRL_CSCY2R_WIDE);
+#endif
+
+ if (sc->bypass)
+ cfg |= S3C_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S3C_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S3C_CISCCTRL_SCALEUP_V;
+
+ cfg |= S3C_CISCCTRL_MAINHORRATIO(sc->main_hratio);
+ cfg |= S3C_CISCCTRL_MAINVERRATIO(sc->main_vratio);
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc43_hwset_scaler(struct fimc_control *ctrl, struct fimc_scaler *sc)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ u32 cfg_ext = readl(ctrl->regs + S3C_CIEXTEN);
+
+ cfg &= ~(S3C_CISCCTRL_SCALERBYPASS |
+ S3C_CISCCTRL_SCALEUP_H | S3C_CISCCTRL_SCALEUP_V |
+ S3C_CISCCTRL_MAIN_V_RATIO_MASK |
+ S3C_CISCCTRL_MAIN_H_RATIO_MASK |
+ S3C_CISCCTRL_CSCR2Y_WIDE |
+ S3C_CISCCTRL_CSCY2R_WIDE);
+
+#ifdef CONFIG_VIDEO_FIMC_RANGE_WIDE
+ cfg |= (S3C_CISCCTRL_CSCR2Y_WIDE | S3C_CISCCTRL_CSCY2R_WIDE);
+#endif
+
+ if (sc->bypass)
+ cfg |= S3C_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S3C_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S3C_CISCCTRL_SCALEUP_V;
+
+ cfg |= S3C_CISCCTRL_MAINHORRATIO(sc->main_hratio);
+ cfg |= S3C_CISCCTRL_MAINVERRATIO(sc->main_vratio);
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ cfg_ext &= ~S3C_CIEXTEN_MAINHORRATIO_EXT_MASK;
+ cfg_ext &= ~S3C_CIEXTEN_MAINVERRATIO_EXT_MASK;
+
+ cfg_ext |= S3C_CIEXTEN_MAINHORRATIO_EXT(sc->main_hratio);
+ cfg_ext |= S3C_CIEXTEN_MAINVERRATIO_EXT(sc->main_vratio);
+
+ writel(cfg_ext, ctrl->regs + S3C_CIEXTEN);
+
+ return 0;
+}
+
+int fimc50_hwset_scaler(struct fimc_control *ctrl, struct fimc_scaler *sc)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ u32 cfg_ext = readl(ctrl->regs + S3C_CIEXTEN);
+
+ cfg &= ~(S3C_CISCCTRL_SCALERBYPASS |
+ S3C_CISCCTRL_SCALEUP_H | S3C_CISCCTRL_SCALEUP_V |
+ S3C_CISCCTRL_MAIN_V_RATIO_MASK |
+ S3C_CISCCTRL_MAIN_H_RATIO_MASK |
+ S3C_CISCCTRL_CSCR2Y_WIDE |
+ S3C_CISCCTRL_CSCY2R_WIDE);
+
+#ifdef CONFIG_VIDEO_FIMC_RANGE_WIDE
+ cfg |= (S3C_CISCCTRL_CSCR2Y_WIDE | S3C_CISCCTRL_CSCY2R_WIDE);
+#endif
+
+ if (sc->bypass)
+ cfg |= S3C_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S3C_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S3C_CISCCTRL_SCALEUP_V;
+
+ cfg |= S3C_CISCCTRL_MAINHORRATIO((sc->main_hratio >> 6));
+ cfg |= S3C_CISCCTRL_MAINVERRATIO((sc->main_vratio >> 6));
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ cfg_ext &= ~S3C_CIEXTEN_MAINHORRATIO_EXT_MASK;
+ cfg_ext &= ~S3C_CIEXTEN_MAINVERRATIO_EXT_MASK;
+
+ cfg_ext |= S3C_CIEXTEN_MAINHORRATIO_EXT(sc->main_hratio);
+ cfg_ext |= S3C_CIEXTEN_MAINVERRATIO_EXT(sc->main_vratio);
+
+ writel(cfg_ext, ctrl->regs + S3C_CIEXTEN);
+
+ return 0;
+}
+
+int fimc_hwset_scaler(struct fimc_control *ctrl, struct fimc_scaler *sc)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+
+ switch (pdata->hw_ver) {
+ case 0x40:
+ fimc40_hwset_scaler(ctrl, sc);
+ break;
+ case 0x43:
+ case 0x45:
+ fimc43_hwset_scaler(ctrl, sc);
+ break;
+ case 0x50:
+ fimc50_hwset_scaler(ctrl, sc);
+ break;
+ default:
+ fimc43_hwset_scaler(ctrl, sc);
+ break;
+ }
+
+ return 0;
+}
+
+
+int fimc_hwset_scaler_bypass(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg |= S3C_CISCCTRL_SCALERBYPASS;
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_enable_lcdfifo(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg |= S3C_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_disable_lcdfifo(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg &= ~S3C_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwget_frame_count(struct fimc_control *ctrl)
+{
+ return S3C_CISTATUS_GET_FRAME_COUNT(readl(ctrl->regs + S3C_CISTATUS));
+}
+
+int fimc_hwget_frame_end(struct fimc_control *ctrl)
+{
+ unsigned long timeo = jiffies;
+ u32 cfg;
+
+ timeo += 20; /* waiting for 100ms */
+ while (time_before(jiffies, timeo)) {
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+
+ if (S3C_CISTATUS_GET_FRAME_END(cfg)) {
+ cfg &= ~S3C_CISTATUS_FRAMEEND;
+ writel(cfg, ctrl->regs + S3C_CISTATUS);
+ break;
+ }
+ cond_resched();
+ }
+
+ return 0;
+}
+
+int fimc_hwget_last_frame_end(struct fimc_control *ctrl)
+{
+ unsigned long timeo = jiffies;
+ u32 cfg;
+
+ timeo += 20; /* waiting for 100ms */
+ while (time_before(jiffies, timeo)) {
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+
+ if (S3C_CISTATUS_GET_LAST_CAPTURE_END(cfg)) {
+ cfg &= ~S3C_CISTATUS_LASTCAPTUREEND;
+ writel(cfg, ctrl->regs + S3C_CISTATUS);
+ break;
+ }
+ cond_resched();
+ }
+
+ return 0;
+}
+
+int fimc_hwset_start_scaler(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg |= S3C_CISCCTRL_SCALERSTART;
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_stop_scaler(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+
+ cfg &= ~S3C_CISCCTRL_SCALERSTART;
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_rgb(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ cfg &= ~S3C_CISCCTRL_INRGB_FMT_RGB_MASK;
+
+ if (pixelformat == V4L2_PIX_FMT_RGB32)
+ cfg |= S3C_CISCCTRL_INRGB_FMT_RGB888;
+ else if (pixelformat == V4L2_PIX_FMT_RGB565)
+ cfg |= S3C_CISCCTRL_INRGB_FMT_RGB565;
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_intput_field(struct fimc_control *ctrl, enum v4l2_field field)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg;
+
+ if (pdata->hw_ver == 0x40)
+ return 0;
+
+ cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~S3C_MSCTRL_FIELD_MASK;
+
+ if (field == V4L2_FIELD_NONE)
+ cfg |= S3C_MSCTRL_FIELD_NORMAL;
+ else if (field == V4L2_FIELD_INTERLACED_TB)
+ cfg |= S3C_MSCTRL_FIELD_WEAVE;
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_output_rgb(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ cfg &= ~S3C_CISCCTRL_OUTRGB_FMT_RGB_MASK;
+
+ if (pixelformat == V4L2_PIX_FMT_RGB32)
+ cfg |= S3C_CISCCTRL_OUTRGB_FMT_RGB888;
+ else if (pixelformat == V4L2_PIX_FMT_RGB565)
+ cfg |= S3C_CISCCTRL_OUTRGB_FMT_RGB565;
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_ext_rgb(struct fimc_control *ctrl, int enable)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CISCCTRL);
+ cfg &= ~S3C_CISCCTRL_EXTRGB_EXTENSION;
+
+ if (enable)
+ cfg |= S3C_CISCCTRL_EXTRGB_EXTENSION;
+
+ writel(cfg, ctrl->regs + S3C_CISCCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_enable_capture(struct fimc_control *ctrl, u32 bypass)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIIMGCPT);
+ cfg &= ~S3C_CIIMGCPT_IMGCPTEN_SC;
+ cfg |= S3C_CIIMGCPT_IMGCPTEN;
+
+ if (!bypass)
+ cfg |= S3C_CIIMGCPT_IMGCPTEN_SC;
+
+ writel(cfg, ctrl->regs + S3C_CIIMGCPT);
+
+ return 0;
+}
+
+int fimc_hwset_disable_capture(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIIMGCPT);
+
+ cfg &= ~(S3C_CIIMGCPT_IMGCPTEN_SC | S3C_CIIMGCPT_IMGCPTEN);
+
+ writel(cfg, ctrl->regs + S3C_CIIMGCPT);
+
+ return 0;
+}
+
+int fimc_hwset_input_address(struct fimc_control *ctrl, dma_addr_t *base)
+{
+ writel(base[FIMC_ADDR_Y], ctrl->regs + S3C_CIIYSA0);
+ writel(base[FIMC_ADDR_CB], ctrl->regs + S3C_CIICBSA0);
+ writel(base[FIMC_ADDR_CR], ctrl->regs + S3C_CIICRSA0);
+
+ return 0;
+}
+
+int fimc_hwset_enable_autoload(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIREAL_ISIZE);
+
+ cfg |= S3C_CIREAL_ISIZE_AUTOLOAD_ENABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIREAL_ISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_disable_autoload(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIREAL_ISIZE);
+
+ cfg &= ~S3C_CIREAL_ISIZE_AUTOLOAD_ENABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIREAL_ISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_real_input_size(struct fimc_control *ctrl, u32 width, u32 height)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIREAL_ISIZE);
+ cfg &= ~(S3C_CIREAL_ISIZE_HEIGHT_MASK | S3C_CIREAL_ISIZE_WIDTH_MASK);
+
+ cfg |= S3C_CIREAL_ISIZE_WIDTH(width);
+ cfg |= S3C_CIREAL_ISIZE_HEIGHT(height);
+
+ writel(cfg, ctrl->regs + S3C_CIREAL_ISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_addr_change_enable(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIREAL_ISIZE);
+
+ cfg &= ~S3C_CIREAL_ISIZE_ADDR_CH_DISABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIREAL_ISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_addr_change_disable(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIREAL_ISIZE);
+
+ cfg |= S3C_CIREAL_ISIZE_ADDR_CH_DISABLE;
+
+ writel(cfg, ctrl->regs + S3C_CIREAL_ISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_input_burst_cnt(struct fimc_control *ctrl, u32 cnt)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~S3C_MSCTRL_BURST_CNT_MASK;
+
+ if (cnt > 4)
+ cnt = 4;
+ else if (cnt == 0)
+ cnt = 4;
+
+ cfg |= S3C_MSCTRL_SUCCESSIVE_COUNT(cnt);
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_colorspace(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~S3C_MSCTRL_INFORMAT_RGB;
+
+ /* Color format setting */
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420: /* fall through */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV21: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ cfg |= S3C_MSCTRL_INFORMAT_YCBCR420;
+ break;
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY:
+ cfg |= S3C_MSCTRL_INFORMAT_YCBCR422_1PLANE;
+ break;
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ cfg |= S3C_MSCTRL_INFORMAT_YCBCR422;
+ break;
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_RGB32:
+ cfg |= S3C_MSCTRL_INFORMAT_RGB;
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n",
+ __func__, pixelformat);
+ return -EINVAL;
+ }
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_yuv(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~(S3C_MSCTRL_ORDER2P_SHIFT_MASK | S3C_MSCTRL_C_INT_IN_2PLANE |
+ S3C_MSCTRL_ORDER422_YCBYCR);
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ cfg |= S3C_MSCTRL_C_INT_IN_3PLANE;
+ break;
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ cfg |= S3C_MSCTRL_ORDER422_YCBYCR;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ cfg |= S3C_MSCTRL_ORDER422_CBYCRY;
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ cfg |= S3C_MSCTRL_ORDER422_YCRYCB;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ cfg |= S3C_MSCTRL_ORDER422_CRYCBY;
+ break;
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ cfg |= S3C_MSCTRL_ORDER2P_LSB_CBCR;
+ cfg |= S3C_MSCTRL_C_INT_IN_2PLANE;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ cfg |= S3C_MSCTRL_ORDER2P_LSB_CRCB;
+ cfg |= S3C_MSCTRL_C_INT_IN_2PLANE;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ cfg |= S3C_MSCTRL_ORDER2P_LSB_CBCR;
+ cfg |= S3C_MSCTRL_C_INT_IN_2PLANE;
+ break;
+ case V4L2_PIX_FMT_NV61:
+ cfg |= S3C_MSCTRL_ORDER2P_LSB_CRCB;
+ cfg |= S3C_MSCTRL_C_INT_IN_2PLANE;
+ break;
+ case V4L2_PIX_FMT_RGB565: /* fall through */
+ case V4L2_PIX_FMT_RGB32:
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n",
+ __func__, pixelformat);
+ }
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_flip(struct fimc_control *ctrl, u32 rot, u32 flip)
+{
+ u32 cfg, val;
+
+ cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~(S3C_MSCTRL_FLIP_X_MIRROR | S3C_MSCTRL_FLIP_Y_MIRROR);
+ val = fimc_mapping_rot_flip(rot, flip);
+
+ if (val & FIMC_XFLIP)
+ cfg |= S3C_MSCTRL_FLIP_X_MIRROR;
+
+ if (val & FIMC_YFLIP)
+ cfg |= S3C_MSCTRL_FLIP_Y_MIRROR;
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_input_source(struct fimc_control *ctrl, enum fimc_input path)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~S3C_MSCTRL_INPUT_MASK;
+
+ if (path == FIMC_SRC_MSDMA)
+ cfg |= S3C_MSCTRL_INPUT_MEMORY;
+ else if (path == FIMC_SRC_CAM)
+ cfg |= S3C_MSCTRL_INPUT_EXTCAM;
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+
+}
+
+int fimc_hwset_start_input_dma(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg |= S3C_MSCTRL_ENVID;
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+int fimc_hwset_stop_input_dma(struct fimc_control *ctrl)
+{
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ cfg &= ~S3C_MSCTRL_ENVID;
+
+ writel(cfg, ctrl->regs + S3C_MSCTRL);
+
+ return 0;
+}
+
+void fimc_wait_stop_processing(struct fimc_control *ctrl)
+{
+ fimc_hwget_frame_end(ctrl);
+ fimc_hwget_last_frame_end(ctrl);
+}
+
+void fimc_hwset_stop_processing(struct fimc_control *ctrl)
+{
+ fimc_wait_stop_processing(ctrl);
+
+ fimc_hwset_stop_scaler(ctrl);
+ fimc_hwset_disable_capture(ctrl);
+ fimc_hwset_stop_input_dma(ctrl);
+
+ /* We need to wait for sometime after processing is stopped.
+ * This is required for obtaining clean buffer for DMA processing. */
+ fimc_wait_stop_processing(ctrl);
+}
+
+int fimc40_hwset_output_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ u32 cfg_y = 0, cfg_cb = 0, cfg_cr = 0;
+
+ if (!crop->left && !crop->top && (bounds->width == crop->width) &&
+ (bounds->height == crop->height))
+ return -EINVAL;
+
+ fimc_dbg("%s: left: %d, top: %d, width: %d, height: %d\n",
+ __func__, crop->left, crop->top, crop->width, crop->height);
+
+ switch (pixelformat) {
+ /* 1 plane, 32 bits per pixel */
+ case V4L2_PIX_FMT_RGB32:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left * 4);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ break;
+
+ /* 1 plane, 16 bits per pixel */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left * 2);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ break;
+
+ /* 2 planes, 16 bits per pixel */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left / 2);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top / 2);
+ break;
+
+ /* 2 planes, 12 bits per pixel */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_NV21:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left / 4);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top / 4);
+ break;
+
+ /* 3 planes, 16 bits per pixel */
+ case V4L2_PIX_FMT_YUV422P:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left / 2);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top / 2);
+ cfg_cr |= S3C_CIOCROFF_HORIZONTAL(crop->left / 2);
+ cfg_cr |= S3C_CIOCROFF_VERTICAL(crop->top / 2);
+ break;
+
+ /* 3 planes, 12 bits per pixel */
+ case V4L2_PIX_FMT_YUV420:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left / 4);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top / 4);
+ cfg_cr |= S3C_CIOCROFF_HORIZONTAL(crop->left / 4);
+ cfg_cr |= S3C_CIOCROFF_VERTICAL(crop->top / 4);
+ break;
+
+ default:
+ break;
+ }
+
+ writel(cfg_y, ctrl->regs + S3C_CIOYOFF);
+ writel(cfg_cb, ctrl->regs + S3C_CIOCBOFF);
+ writel(cfg_cr, ctrl->regs + S3C_CIOCROFF);
+
+ return 0;
+}
+
+int fimc50_hwset_output_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ u32 cfg_y = 0, cfg_cb = 0, cfg_cr = 0;
+
+ if (!crop->left && !crop->top && (bounds->width == crop->width) &&
+ (bounds->height == crop->height))
+ return -EINVAL;
+
+ fimc_dbg("%s: left: %d, top: %d, width: %d, height: %d\n",
+ __func__, crop->left, crop->top, crop->width, crop->height);
+
+ switch (pixelformat) {
+ /* 1 plane, 32 bits per pixel */
+ case V4L2_PIX_FMT_RGB32:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ break;
+
+ /* 1 plane, 16 bits per pixel */
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ break;
+
+ /* 2 planes, 16 bits per pixel */
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top);
+ break;
+
+ /* 2 planes, 12 bits per pixel */
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T: /* fall through */
+ case V4L2_PIX_FMT_NV21:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top);
+ break;
+
+ /* 3 planes, 16 bits per pixel */
+ case V4L2_PIX_FMT_YUV422P:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top);
+ cfg_cr |= S3C_CIOCROFF_HORIZONTAL(crop->left);
+ cfg_cr |= S3C_CIOCROFF_VERTICAL(crop->top);
+ break;
+
+ /* 3 planes, 12 bits per pixel */
+ case V4L2_PIX_FMT_YUV420:
+ cfg_y |= S3C_CIOYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIOYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIOCBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIOCBOFF_VERTICAL(crop->top);
+ cfg_cr |= S3C_CIOCROFF_HORIZONTAL(crop->left);
+ cfg_cr |= S3C_CIOCROFF_VERTICAL(crop->top);
+ break;
+
+ default:
+ break;
+ }
+
+ writel(cfg_y, ctrl->regs + S3C_CIOYOFF);
+ writel(cfg_cb, ctrl->regs + S3C_CIOCBOFF);
+ writel(cfg_cr, ctrl->regs + S3C_CIOCROFF);
+
+ return 0;
+}
+
+int fimc_hwset_output_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+
+ if (pdata->hw_ver == 0x50 || pdata->hw_ver == 0x45)
+ fimc50_hwset_output_offset(ctrl, pixelformat, bounds, crop);
+ else
+ fimc40_hwset_output_offset(ctrl, pixelformat, bounds, crop);
+
+ return 0;
+}
+
+int fimc40_hwset_input_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ u32 cfg_y = 0, cfg_cb = 0;
+
+ if (crop->left || crop->top ||
+ (bounds->width != crop->width) ||
+ (bounds->height != crop->height)) {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left * 2);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left * 4);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_NV12: /* fall through */
+ case V4L2_PIX_FMT_NV12T:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIICBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIICBOFF_VERTICAL(crop->top / 2);
+
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n",
+ __func__, pixelformat);
+ }
+ }
+
+ writel(cfg_y, ctrl->regs + S3C_CIIYOFF);
+ writel(cfg_cb, ctrl->regs + S3C_CIICBOFF);
+
+ return 0;
+}
+
+int fimc50_hwset_input_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ u32 cfg_y = 0, cfg_cb = 0, cfg_cr = 0;
+
+ if (crop->left || crop->top ||
+ (bounds->width != crop->width) ||
+ (bounds->height != crop->height)) {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUYV: /* fall through */
+ case V4L2_PIX_FMT_UYVY: /* fall through */
+ case V4L2_PIX_FMT_YVYU: /* fall through */
+ case V4L2_PIX_FMT_VYUY: /* fall through */
+ case V4L2_PIX_FMT_RGB565:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_NV12: /* fall through*/
+ case V4L2_PIX_FMT_NV21: /* fall through*/
+ case V4L2_PIX_FMT_NV12T:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIICBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIICBOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_NV16: /* fall through */
+ case V4L2_PIX_FMT_NV61:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIICBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIICBOFF_VERTICAL(crop->top);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ cfg_y |= S3C_CIIYOFF_HORIZONTAL(crop->left);
+ cfg_y |= S3C_CIIYOFF_VERTICAL(crop->top);
+ cfg_cb |= S3C_CIICBOFF_HORIZONTAL(crop->left);
+ cfg_cb |= S3C_CIICBOFF_VERTICAL(crop->top);
+ cfg_cr |= S3C_CIICROFF_HORIZONTAL(crop->left);
+ cfg_cr |= S3C_CIICROFF_VERTICAL(crop->top);
+ break;
+ default:
+ fimc_err("%s: Invalid pixelformt : %d\n",
+ __func__, pixelformat);
+ }
+ }
+
+ writel(cfg_y, ctrl->regs + S3C_CIIYOFF);
+ writel(cfg_cb, ctrl->regs + S3C_CIICBOFF);
+ writel(cfg_cr, ctrl->regs + S3C_CIICROFF);
+
+ return 0;
+}
+
+int fimc_hwset_input_offset(struct fimc_control *ctrl, u32 pixelformat,
+ struct v4l2_rect *bounds,
+ struct v4l2_rect *crop)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+
+ if (pdata->hw_ver == 0x50)
+ fimc50_hwset_input_offset(ctrl, pixelformat, bounds, crop);
+ else
+ fimc40_hwset_input_offset(ctrl, pixelformat, bounds, crop);
+
+ return 0;
+}
+
+int fimc_hwset_org_input_size(struct fimc_control *ctrl, u32 width, u32 height)
+{
+ u32 cfg = 0;
+
+ cfg |= S3C_ORGISIZE_HORIZONTAL(width);
+ cfg |= S3C_ORGISIZE_VERTICAL(height);
+
+ writel(cfg, ctrl->regs + S3C_ORGISIZE);
+
+ return 0;
+}
+
+int fimc_hwset_org_output_size(struct fimc_control *ctrl, u32 width, u32 height)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg = 0;
+
+ cfg |= S3C_ORGOSIZE_HORIZONTAL(width);
+ cfg |= S3C_ORGOSIZE_VERTICAL(height);
+
+ writel(cfg, ctrl->regs + S3C_ORGOSIZE);
+
+ if (pdata->hw_ver != 0x40) {
+ cfg = readl(ctrl->regs + S3C_CIGCTRL);
+ cfg &= ~S3C_CIGCTRL_CSC_MASK;
+
+ if (width >= FIMC_HD_WIDTH)
+ cfg |= S3C_CIGCTRL_CSC_ITU709;
+ else
+ cfg |= S3C_CIGCTRL_CSC_ITU601;
+
+ writel(cfg, ctrl->regs + S3C_CIGCTRL);
+ }
+
+ return 0;
+}
+
+int fimc_hwset_ext_output_size(struct fimc_control *ctrl, u32 width, u32 height)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIEXTEN);
+
+ cfg &= ~S3C_CIEXTEN_TARGETH_EXT_MASK;
+ cfg &= ~S3C_CIEXTEN_TARGETV_EXT_MASK;
+ cfg |= S3C_CIEXTEN_TARGETH_EXT(width);
+ cfg |= S3C_CIEXTEN_TARGETV_EXT(height);
+
+ writel(cfg, ctrl->regs + S3C_CIEXTEN);
+
+ return 0;
+}
+
+int fimc_hwset_input_addr_style(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIDMAPARAM);
+ cfg &= ~S3C_CIDMAPARAM_R_MODE_MASK;
+
+ if (pixelformat == V4L2_PIX_FMT_NV12T)
+ cfg |= S3C_CIDMAPARAM_R_MODE_64X32;
+ else
+ cfg |= S3C_CIDMAPARAM_R_MODE_LINEAR;
+
+ writel(cfg, ctrl->regs + S3C_CIDMAPARAM);
+
+ return 0;
+}
+
+int fimc_hwset_output_addr_style(struct fimc_control *ctrl, u32 pixelformat)
+{
+ u32 cfg = readl(ctrl->regs + S3C_CIDMAPARAM);
+ cfg &= ~S3C_CIDMAPARAM_W_MODE_MASK;
+
+ if (pixelformat == V4L2_PIX_FMT_NV12T)
+ cfg |= S3C_CIDMAPARAM_W_MODE_64X32;
+ else
+ cfg |= S3C_CIDMAPARAM_W_MODE_LINEAR;
+
+ writel(cfg, ctrl->regs + S3C_CIDMAPARAM);
+
+ return 0;
+}
+
+int fimc_hw_wait_winoff(struct fimc_control *ctrl)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg = readl(ctrl->regs + S3C_CISTATUS);
+ u32 status = S3C_CISTATUS_GET_LCD_STATUS(cfg);
+ int i = FIMC_FIFOOFF_CNT;
+
+ if (pdata->hw_ver == 0x40)
+ return 0;
+
+ while (status && i--) {
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+ status = S3C_CISTATUS_GET_LCD_STATUS(cfg);
+ }
+
+ if (i < 1) {
+ fimc_err("Fail : %s\n", __func__);
+ return -EBUSY;
+ } else
+ return 0;
+}
+
+int fimc_hw_wait_stop_input_dma(struct fimc_control *ctrl)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg = readl(ctrl->regs + S3C_MSCTRL);
+ u32 status = S3C_MSCTRL_GET_INDMA_STATUS(cfg);
+ int i = FIMC_FIFOOFF_CNT, j = FIMC_FIFOOFF_CNT;
+
+ if (pdata->hw_ver == 0x40)
+ return 0;
+
+ while (status && i--) {
+ cfg = readl(ctrl->regs + S3C_MSCTRL);
+ status = S3C_MSCTRL_GET_INDMA_STATUS(cfg);
+ }
+
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+ status = S3C_CISTATUS_GET_ENVID_STATUS(cfg);
+ while (status && j--) {
+ cfg = readl(ctrl->regs + S3C_CISTATUS);
+ status = S3C_CISTATUS_GET_ENVID_STATUS(cfg);
+ }
+
+ if ((i < 1) || (j < 1)) {
+ fimc_err("Fail : %s\n", __func__);
+ return -EBUSY;
+ } else {
+ return 0;
+ }
+}
+
+int fimc_hwset_input_lineskip(struct fimc_control *ctrl)
+{
+ struct s3c_platform_fimc *pdata = to_fimc_plat(ctrl->dev);
+ u32 cfg = 0;
+
+ if (pdata->hw_ver == 0x40)
+ return 0;
+
+ cfg = S3C_CIILINESKIP(ctrl->sc.skipline);
+
+ writel(cfg, ctrl->regs + S3C_CIILINESKIP_Y);
+ writel(cfg, ctrl->regs + S3C_CIILINESKIP_CB);
+ writel(cfg, ctrl->regs + S3C_CIILINESKIP_CR);
+
+ return 0;
+}
+
+int fimc_hw_reset_camera(struct fimc_control *ctrl)
+{
+ return 0;
+}
diff --git a/drivers/media/video/samsung/fimc/fimc_v4l2.c b/drivers/media/video/samsung/fimc/fimc_v4l2.c
new file mode 100644
index 0000000..3632ded
--- /dev/null
+++ b/drivers/media/video/samsung/fimc/fimc_v4l2.c
@@ -0,0 +1,296 @@
+/* linux/drivers/media/video/samsung/fimc/fimc_v4l2.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * V4L2 interface support file for Samsung Camera Interface (FIMC) driver
+ *
+ * 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/fs.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/videodev2.h>
+#include <linux/videodev2_samsung.h>
+#include <media/v4l2-ioctl.h>
+#include <plat/fimc.h>
+#include <linux/clk.h>
+
+#include "fimc.h"
+
+static int fimc_querycap(struct file *filp, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+
+ fimc_info1("%s: called\n", __func__);
+
+ strcpy(cap->driver, "Samsung FIMC Driver");
+ strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "FIMC AHB-bus");
+
+ cap->version = 0;
+ cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING);
+
+ return 0;
+}
+
+static int fimc_reqbufs(struct file *filp, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_reqbufs_capture(fh, b);
+ } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_reqbufs_output(fh, b);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_querybuf(struct file *filp, void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_querybuf_capture(fh, b);
+ } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_querybuf_output(fh, b);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_g_ctrl(struct file *filp, void *fh, struct v4l2_control *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (ctrl->cap != NULL) {
+ ret = fimc_g_ctrl_capture(fh, c);
+ } else if (ctrl->out != NULL) {
+ ret = fimc_g_ctrl_output(fh, c);
+ } else {
+ fimc_err("%s: Invalid case\n", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_s_ctrl(struct file *filp, void *fh, struct v4l2_control *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (ctrl->cap != NULL) {
+ ret = fimc_s_ctrl_capture(fh, c);
+ } else if (ctrl->out != NULL) {
+ ret = fimc_s_ctrl_output(filp, fh, c);
+ } else {
+ fimc_err("%s: Invalid case\n", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_s_ext_ctrls(struct file *filp, void *fh,
+ struct v4l2_ext_controls *c)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (ctrl->cap != NULL) {
+ ret = fimc_s_ext_ctrls_capture(fh, c);
+ } else {
+ fimc_err("%s: Invalid case\n", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_cropcap(struct file *filp, void *fh, struct v4l2_cropcap *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_cropcap_capture(fh, a);
+ } else if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_cropcap_output(fh, a);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_g_crop(struct file *filp, void *fh, struct v4l2_crop *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_g_crop_capture(fh, a);
+ } else if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_g_crop_output(fh, a);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_s_crop(struct file *filp, void *fh, struct v4l2_crop *a)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_s_crop_capture(fh, a);
+ } else if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_s_crop_output(fh, a);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_streamon(struct file *filp, void *fh, enum v4l2_buf_type i)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct s3c_platform_fimc *pdata;
+ int ret = -1;
+
+ pdata = to_fimc_plat(ctrl->dev);
+
+ if (i == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_streamon_capture(fh);
+ } else if (i == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_streamon_output(fh);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_streamoff(struct file *filp, void *fh, enum v4l2_buf_type i)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ struct s3c_platform_fimc *pdata;
+ int ret = -1;
+
+ pdata = to_fimc_plat(ctrl->dev);
+
+ if (i == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_streamoff_capture(fh);
+ } else if (i == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_streamoff_output(fh);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_qbuf(struct file *filp, void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_qbuf_capture(fh, b);
+ } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_qbuf_output(fh, b);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fimc_dqbuf(struct file *filp, void *fh, struct v4l2_buffer *b)
+{
+ struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
+ int ret = -1;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = fimc_dqbuf_capture(fh, b);
+ } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ret = fimc_dqbuf_output(fh, b);
+ } else {
+ fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "
+ "V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+const struct v4l2_ioctl_ops fimc_v4l2_ops = {
+ .vidioc_querycap = fimc_querycap,
+ .vidioc_reqbufs = fimc_reqbufs,
+ .vidioc_querybuf = fimc_querybuf,
+ .vidioc_g_ctrl = fimc_g_ctrl,
+ .vidioc_s_ctrl = fimc_s_ctrl,
+ .vidioc_s_ext_ctrls = fimc_s_ext_ctrls,
+ .vidioc_cropcap = fimc_cropcap,
+ .vidioc_g_crop = fimc_g_crop,
+ .vidioc_s_crop = fimc_s_crop,
+ .vidioc_streamon = fimc_streamon,
+ .vidioc_streamoff = fimc_streamoff,
+ .vidioc_qbuf = fimc_qbuf,
+ .vidioc_dqbuf = fimc_dqbuf,
+ .vidioc_enum_fmt_vid_cap = fimc_enum_fmt_vid_capture,
+ .vidioc_g_fmt_vid_cap = fimc_g_fmt_vid_capture,
+ .vidioc_s_fmt_vid_cap = fimc_s_fmt_vid_capture,
+ .vidioc_try_fmt_vid_cap = fimc_try_fmt_vid_capture,
+ .vidioc_enum_input = fimc_enum_input,
+ .vidioc_g_input = fimc_g_input,
+ .vidioc_s_input = fimc_s_input,
+ .vidioc_g_parm = fimc_g_parm,
+ .vidioc_s_parm = fimc_s_parm,
+ .vidioc_queryctrl = fimc_queryctrl,
+ .vidioc_querymenu = fimc_querymenu,
+ .vidioc_g_fmt_vid_out = fimc_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = fimc_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = fimc_try_fmt_vid_out,
+ .vidioc_g_fbuf = fimc_g_fbuf,
+ .vidioc_s_fbuf = fimc_s_fbuf,
+ .vidioc_try_fmt_vid_overlay = fimc_try_fmt_overlay,
+ .vidioc_g_fmt_vid_overlay = fimc_g_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = fimc_s_fmt_vid_overlay,
+};