aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2011-09-01 05:01:08 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-09-06 16:35:52 -0400
commitd3953223b0905437fef7ce60506b5fdfaf98dda6 (patch)
tree12570704e36b7131a03ec31f0746ae459410d251 /drivers/media
parent30c9939d79d6edf64092148842835893d156b672 (diff)
[media] s5p-fimc: Add the media device driver
Add a top level media device driver aggregating FIMC video devnodes, MIPI-CSIS and sensor subdevs. This driver gathers all media entities and creates the possible links between them during initialization. By default some links will be activated to enable access to all available sensors in the system. For example if there are sensors S0, S1 listed in the media device platform data definition they will be by default assigned to FIMC0, FIMC1 respectively, which in turn will corresponds to separate /dev/video?. There is enough FIMC H/W entities to cover all available physical camera interfaces in the system. The fimc media device driver is bound to the "s5p-fimc-md" platform device. Such platform device should be created by board initialization code and camera sensors description array need to be specified as its platform data. The media device driver also implements various video pipeline operations, for enabling subdevs power, streaming, etc., which will be used by the capture video node driver. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/Kconfig5
-rw-r--r--drivers/media/video/s5p-fimc/Makefile2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c33
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h16
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c829
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h118
6 files changed, 981 insertions, 22 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 14326d7da559..6279663bd227 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -949,8 +949,9 @@ config VIDEO_MX2
949 Interface 949 Interface
950 950
951config VIDEO_SAMSUNG_S5P_FIMC 951config VIDEO_SAMSUNG_S5P_FIMC
952 tristate "Samsung S5P and EXYNOS4 camera host interface driver" 952 tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
953 depends on VIDEO_V4L2 && PLAT_S5P && PM_RUNTIME 953 depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
954 VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
954 select VIDEOBUF2_DMA_CONTIG 955 select VIDEOBUF2_DMA_CONTIG
955 select V4L2_MEM2MEM_DEV 956 select V4L2_MEM2MEM_DEV
956 ---help--- 957 ---help---
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
index df6954ab1d99..33dec7f890e7 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -1,4 +1,4 @@
1s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o 1s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o
2s5p-csis-objs := mipi-csis.o 2s5p-csis-objs := mipi-csis.o
3 3
4obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o 4obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 3e2143d10854..16314c94cc12 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -28,6 +28,7 @@
28#include <media/videobuf2-dma-contig.h> 28#include <media/videobuf2-dma-contig.h>
29 29
30#include "fimc-core.h" 30#include "fimc-core.h"
31#include "fimc-mdevice.h"
31 32
32static char *fimc_clocks[MAX_FIMC_CLOCKS] = { 33static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
33 "sclk_fimc", "fimc" 34 "sclk_fimc", "fimc"
@@ -1867,6 +1868,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = {
1867static struct samsung_fimc_variant fimc0_variant_s5p = { 1868static struct samsung_fimc_variant fimc0_variant_s5p = {
1868 .has_inp_rot = 1, 1869 .has_inp_rot = 1,
1869 .has_out_rot = 1, 1870 .has_out_rot = 1,
1871 .has_cam_if = 1,
1870 .min_inp_pixsize = 16, 1872 .min_inp_pixsize = 16,
1871 .min_out_pixsize = 16, 1873 .min_out_pixsize = 16,
1872 .hor_offs_align = 8, 1874 .hor_offs_align = 8,
@@ -1875,6 +1877,7 @@ static struct samsung_fimc_variant fimc0_variant_s5p = {
1875}; 1877};
1876 1878
1877static struct samsung_fimc_variant fimc2_variant_s5p = { 1879static struct samsung_fimc_variant fimc2_variant_s5p = {
1880 .has_cam_if = 1,
1878 .min_inp_pixsize = 16, 1881 .min_inp_pixsize = 16,
1879 .min_out_pixsize = 16, 1882 .min_out_pixsize = 16,
1880 .hor_offs_align = 8, 1883 .hor_offs_align = 8,
@@ -1886,6 +1889,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
1886 .pix_hoff = 1, 1889 .pix_hoff = 1,
1887 .has_inp_rot = 1, 1890 .has_inp_rot = 1,
1888 .has_out_rot = 1, 1891 .has_out_rot = 1,
1892 .has_cam_if = 1,
1889 .min_inp_pixsize = 16, 1893 .min_inp_pixsize = 16,
1890 .min_out_pixsize = 16, 1894 .min_out_pixsize = 16,
1891 .hor_offs_align = 8, 1895 .hor_offs_align = 8,
@@ -1897,6 +1901,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
1897 .pix_hoff = 1, 1901 .pix_hoff = 1,
1898 .has_inp_rot = 1, 1902 .has_inp_rot = 1,
1899 .has_out_rot = 1, 1903 .has_out_rot = 1,
1904 .has_cam_if = 1,
1900 .has_mainscaler_ext = 1, 1905 .has_mainscaler_ext = 1,
1901 .min_inp_pixsize = 16, 1906 .min_inp_pixsize = 16,
1902 .min_out_pixsize = 16, 1907 .min_out_pixsize = 16,
@@ -1906,6 +1911,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
1906}; 1911};
1907 1912
1908static struct samsung_fimc_variant fimc2_variant_s5pv210 = { 1913static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
1914 .has_cam_if = 1,
1909 .pix_hoff = 1, 1915 .pix_hoff = 1,
1910 .min_inp_pixsize = 16, 1916 .min_inp_pixsize = 16,
1911 .min_out_pixsize = 16, 1917 .min_out_pixsize = 16,
@@ -1918,6 +1924,7 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = {
1918 .pix_hoff = 1, 1924 .pix_hoff = 1,
1919 .has_inp_rot = 1, 1925 .has_inp_rot = 1,
1920 .has_out_rot = 1, 1926 .has_out_rot = 1,
1927 .has_cam_if = 1,
1921 .has_cistatus2 = 1, 1928 .has_cistatus2 = 1,
1922 .has_mainscaler_ext = 1, 1929 .has_mainscaler_ext = 1,
1923 .min_inp_pixsize = 16, 1930 .min_inp_pixsize = 16,
@@ -1927,8 +1934,9 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = {
1927 .pix_limit = &s5p_pix_limit[1], 1934 .pix_limit = &s5p_pix_limit[1],
1928}; 1935};
1929 1936
1930static struct samsung_fimc_variant fimc2_variant_exynos4 = { 1937static struct samsung_fimc_variant fimc3_variant_exynos4 = {
1931 .pix_hoff = 1, 1938 .pix_hoff = 1,
1939 .has_cam_if = 1,
1932 .has_cistatus2 = 1, 1940 .has_cistatus2 = 1,
1933 .has_mainscaler_ext = 1, 1941 .has_mainscaler_ext = 1,
1934 .min_inp_pixsize = 16, 1942 .min_inp_pixsize = 16,
@@ -1966,7 +1974,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
1966 [0] = &fimc0_variant_exynos4, 1974 [0] = &fimc0_variant_exynos4,
1967 [1] = &fimc0_variant_exynos4, 1975 [1] = &fimc0_variant_exynos4,
1968 [2] = &fimc0_variant_exynos4, 1976 [2] = &fimc0_variant_exynos4,
1969 [3] = &fimc2_variant_exynos4, 1977 [3] = &fimc3_variant_exynos4,
1970 }, 1978 },
1971 .num_entities = 4, 1979 .num_entities = 4,
1972 .lclk_frequency = 166000000UL, 1980 .lclk_frequency = 166000000UL,
@@ -1994,32 +2002,21 @@ static const struct dev_pm_ops fimc_pm_ops = {
1994 2002
1995static struct platform_driver fimc_driver = { 2003static struct platform_driver fimc_driver = {
1996 .probe = fimc_probe, 2004 .probe = fimc_probe,
1997 .remove = __devexit_p(fimc_remove), 2005 .remove = __devexit_p(fimc_remove),
1998 .id_table = fimc_driver_ids, 2006 .id_table = fimc_driver_ids,
1999 .driver = { 2007 .driver = {
2000 .name = MODULE_NAME, 2008 .name = FIMC_MODULE_NAME,
2001 .owner = THIS_MODULE, 2009 .owner = THIS_MODULE,
2002 .pm = &fimc_pm_ops, 2010 .pm = &fimc_pm_ops,
2003 } 2011 }
2004}; 2012};
2005 2013
2006static int __init fimc_init(void) 2014int __init fimc_register_driver(void)
2007{ 2015{
2008 int ret = platform_driver_register(&fimc_driver); 2016 return platform_driver_probe(&fimc_driver, fimc_probe);
2009 if (ret)
2010 err("platform_driver_register failed: %d\n", ret);
2011 return ret;
2012} 2017}
2013 2018
2014static void __exit fimc_exit(void) 2019void __exit fimc_unregister_driver(void)
2015{ 2020{
2016 platform_driver_unregister(&fimc_driver); 2021 platform_driver_unregister(&fimc_driver);
2017} 2022}
2018
2019module_init(fimc_init);
2020module_exit(fimc_exit);
2021
2022MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
2023MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
2024MODULE_LICENSE("GPL");
2025MODULE_VERSION("1.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index ec2e83bd2dd9..5a6234951e2e 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -36,7 +36,7 @@
36/* Time to wait for next frame VSYNC interrupt while stopping operation. */ 36/* Time to wait for next frame VSYNC interrupt while stopping operation. */
37#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) 37#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
38#define MAX_FIMC_CLOCKS 2 38#define MAX_FIMC_CLOCKS 2
39#define MODULE_NAME "s5p-fimc" 39#define FIMC_MODULE_NAME "s5p-fimc"
40#define FIMC_MAX_DEVS 4 40#define FIMC_MAX_DEVS 4
41#define FIMC_MAX_OUT_BUFS 4 41#define FIMC_MAX_OUT_BUFS 4
42#define SCALER_MAX_HRATIO 64 42#define SCALER_MAX_HRATIO 64
@@ -308,6 +308,7 @@ struct fimc_m2m_device {
308 * @reqbufs_count: the number of buffers requested in REQBUFS ioctl 308 * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
309 * @input_index: input (camera sensor) index 309 * @input_index: input (camera sensor) index
310 * @refcnt: driver's private reference counter 310 * @refcnt: driver's private reference counter
311 * @user_subdev_api: true if subdevs are not configured by the host driver
311 */ 312 */
312struct fimc_vid_cap { 313struct fimc_vid_cap {
313 struct fimc_ctx *ctx; 314 struct fimc_ctx *ctx;
@@ -325,6 +326,7 @@ struct fimc_vid_cap {
325 unsigned int reqbufs_count; 326 unsigned int reqbufs_count;
326 int input_index; 327 int input_index;
327 int refcnt; 328 int refcnt;
329 bool user_subdev_api;
328}; 330};
329 331
330/** 332/**
@@ -355,6 +357,7 @@ struct fimc_pix_limit {
355 * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision 357 * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
356 * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register 358 * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
357 * are present in this IP revision 359 * are present in this IP revision
360 * @has_cam_if: set if this instance has a camera input interface
358 * @pix_limit: pixel size constraints for the scaler 361 * @pix_limit: pixel size constraints for the scaler
359 * @min_inp_pixsize: minimum input pixel size 362 * @min_inp_pixsize: minimum input pixel size
360 * @min_out_pixsize: minimum output pixel size 363 * @min_out_pixsize: minimum output pixel size
@@ -367,6 +370,7 @@ struct samsung_fimc_variant {
367 unsigned int has_out_rot:1; 370 unsigned int has_out_rot:1;
368 unsigned int has_cistatus2:1; 371 unsigned int has_cistatus2:1;
369 unsigned int has_mainscaler_ext:1; 372 unsigned int has_mainscaler_ext:1;
373 unsigned int has_cam_if:1;
370 struct fimc_pix_limit *pix_limit; 374 struct fimc_pix_limit *pix_limit;
371 u16 min_inp_pixsize; 375 u16 min_inp_pixsize;
372 u16 min_out_pixsize; 376 u16 min_out_pixsize;
@@ -387,6 +391,12 @@ struct samsung_fimc_driverdata {
387 int num_entities; 391 int num_entities;
388}; 392};
389 393
394struct fimc_pipeline {
395 struct media_pipeline *pipe;
396 struct v4l2_subdev *sensor;
397 struct v4l2_subdev *csis;
398};
399
390struct fimc_ctx; 400struct fimc_ctx;
391 401
392/** 402/**
@@ -408,6 +418,7 @@ struct fimc_ctx;
408 * @vid_cap: camera capture device information 418 * @vid_cap: camera capture device information
409 * @state: flags used to synchronize m2m and capture mode operation 419 * @state: flags used to synchronize m2m and capture mode operation
410 * @alloc_ctx: videobuf2 memory allocator context 420 * @alloc_ctx: videobuf2 memory allocator context
421 * @pipeline: fimc video capture pipeline data structure
411 */ 422 */
412struct fimc_dev { 423struct fimc_dev {
413 spinlock_t slock; 424 spinlock_t slock;
@@ -427,6 +438,7 @@ struct fimc_dev {
427 struct fimc_vid_cap vid_cap; 438 struct fimc_vid_cap vid_cap;
428 unsigned long state; 439 unsigned long state;
429 struct vb2_alloc_ctx *alloc_ctx; 440 struct vb2_alloc_ctx *alloc_ctx;
441 struct fimc_pipeline pipeline;
430}; 442};
431 443
432/** 444/**
@@ -645,6 +657,8 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
645int fimc_register_m2m_device(struct fimc_dev *fimc, 657int fimc_register_m2m_device(struct fimc_dev *fimc,
646 struct v4l2_device *v4l2_dev); 658 struct v4l2_device *v4l2_dev);
647void fimc_unregister_m2m_device(struct fimc_dev *fimc); 659void fimc_unregister_m2m_device(struct fimc_dev *fimc);
660int fimc_register_driver(void);
661void fimc_unregister_driver(void);
648 662
649/* -----------------------------------------------------*/ 663/* -----------------------------------------------------*/
650/* fimc-capture.c */ 664/* fimc-capture.c */
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
new file mode 100644
index 000000000000..50f3fcaa4e77
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -0,0 +1,829 @@
1/*
2 * S5P/EXYNOS4 SoC series camera host interface media device driver
3 *
4 * Copyright (C) 2011 Samsung Electronics Co., Ltd.
5 * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation, either version 2 of the License,
10 * or (at your option) any later version.
11 */
12
13#include <linux/bug.h>
14#include <linux/device.h>
15#include <linux/errno.h>
16#include <linux/i2c.h>
17#include <linux/kernel.h>
18#include <linux/list.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/pm_runtime.h>
22#include <linux/types.h>
23#include <linux/slab.h>
24#include <linux/version.h>
25#include <media/media-device.h>
26
27#include "fimc-core.h"
28#include "fimc-mdevice.h"
29#include "mipi-csis.h"
30
31static int __fimc_md_set_camclk(struct fimc_md *fmd,
32 struct fimc_sensor_info *s_info,
33 bool on);
34/**
35 * fimc_pipeline_prepare - update pipeline information with subdevice pointers
36 * @fimc: fimc device terminating the pipeline
37 *
38 * Caller holds the graph mutex.
39 */
40void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me)
41{
42 struct media_entity_graph graph;
43 struct v4l2_subdev *sd;
44
45 media_entity_graph_walk_start(&graph, me);
46
47 while ((me = media_entity_graph_walk_next(&graph))) {
48 if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
49 continue;
50 sd = media_entity_to_v4l2_subdev(me);
51
52 if (sd->grp_id == SENSOR_GROUP_ID)
53 fimc->pipeline.sensor = sd;
54 else if (sd->grp_id == CSIS_GROUP_ID)
55 fimc->pipeline.csis = sd;
56 }
57}
58
59/**
60 * __subdev_set_power - change power state of a single subdev
61 * @sd: subdevice to change power state for
62 * @on: 1 to enable power or 0 to disable
63 *
64 * Return result of s_power subdev operation or -ENXIO if sd argument
65 * is NULL. Return 0 if the subdevice does not implement s_power.
66 */
67static int __subdev_set_power(struct v4l2_subdev *sd, int on)
68{
69 int *use_count;
70 int ret;
71
72 if (sd == NULL)
73 return -ENXIO;
74
75 use_count = &sd->entity.use_count;
76 if (on && (*use_count)++ > 0)
77 return 0;
78 else if (!on && (*use_count == 0 || --(*use_count) > 0))
79 return 0;
80 ret = v4l2_subdev_call(sd, core, s_power, on);
81
82 return ret != -ENOIOCTLCMD ? ret : 0;
83}
84
85/**
86 * fimc_pipeline_s_power - change power state of all pipeline subdevs
87 * @fimc: fimc device terminating the pipeline
88 * @state: 1 to enable power or 0 for power down
89 *
90 * Need to be called with the graph mutex held.
91 */
92int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
93{
94 int ret = 0;
95
96 if (fimc->pipeline.sensor == NULL)
97 return -ENXIO;
98
99 if (state) {
100 ret = __subdev_set_power(fimc->pipeline.csis, 1);
101 if (ret && ret != -ENXIO)
102 return ret;
103 return __subdev_set_power(fimc->pipeline.sensor, 1);
104 }
105
106 ret = __subdev_set_power(fimc->pipeline.sensor, 0);
107 if (ret)
108 return ret;
109 ret = __subdev_set_power(fimc->pipeline.csis, 0);
110
111 return ret == -ENXIO ? 0 : ret;
112}
113
114/**
115 * __fimc_pipeline_initialize - update the pipeline information, enable power
116 * of all pipeline subdevs and the sensor clock
117 * @me: media entity to start graph walk with
118 * @prep: true to acquire sensor (and csis) subdevs
119 *
120 * This function must be called with the graph mutex held.
121 */
122static int __fimc_pipeline_initialize(struct fimc_dev *fimc,
123 struct media_entity *me, bool prep)
124{
125 int ret;
126
127 if (prep)
128 fimc_pipeline_prepare(fimc, me);
129 if (fimc->pipeline.sensor == NULL)
130 return -EINVAL;
131 ret = fimc_md_set_camclk(fimc->pipeline.sensor, true);
132 if (ret)
133 return ret;
134 return fimc_pipeline_s_power(fimc, 1);
135}
136
137int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
138 bool prep)
139{
140 int ret;
141
142 mutex_lock(&me->parent->graph_mutex);
143 ret = __fimc_pipeline_initialize(fimc, me, prep);
144 mutex_unlock(&me->parent->graph_mutex);
145
146 return ret;
147}
148
149/**
150 * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
151 * @fimc: fimc device terminating the pipeline
152 *
153 * Disable power of all subdevs in the pipeline and turn off the external
154 * sensor clock.
155 * Called with the graph mutex held.
156 */
157int __fimc_pipeline_shutdown(struct fimc_dev *fimc)
158{
159 int ret = 0;
160
161 if (fimc->pipeline.sensor) {
162 ret = fimc_pipeline_s_power(fimc, 0);
163 fimc_md_set_camclk(fimc->pipeline.sensor, false);
164 }
165 return ret == -ENXIO ? 0 : ret;
166}
167
168int fimc_pipeline_shutdown(struct fimc_dev *fimc)
169{
170 struct media_entity *me = &fimc->vid_cap.vfd->entity;
171 int ret;
172
173 mutex_lock(&me->parent->graph_mutex);
174 ret = __fimc_pipeline_shutdown(fimc);
175 mutex_unlock(&me->parent->graph_mutex);
176
177 return ret;
178}
179
180/**
181 * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
182 * @fimc: fimc device terminating the pipeline
183 * @on: passed as the s_stream call argument
184 */
185int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on)
186{
187 struct fimc_pipeline *p = &fimc->pipeline;
188 int ret = 0;
189
190 if (p->sensor == NULL)
191 return -ENODEV;
192
193 if ((on && p->csis) || !on)
194 ret = v4l2_subdev_call(on ? p->csis : p->sensor,
195 video, s_stream, on);
196 if (ret && ret != -ENOIOCTLCMD)
197 return ret;
198 if ((!on && p->csis) || on)
199 ret = v4l2_subdev_call(on ? p->sensor : p->csis,
200 video, s_stream, on);
201 return ret == -ENOIOCTLCMD ? 0 : ret;
202}
203
204/*
205 * Sensor subdevice helper functions
206 */
207static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
208 struct fimc_sensor_info *s_info)
209{
210 struct i2c_adapter *adapter;
211 struct v4l2_subdev *sd = NULL;
212
213 if (!s_info || !fmd)
214 return NULL;
215
216 adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
217 if (!adapter)
218 return NULL;
219 sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
220 s_info->pdata->board_info, NULL);
221 if (IS_ERR_OR_NULL(sd)) {
222 v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
223 return NULL;
224 }
225 v4l2_set_subdev_hostdata(sd, s_info);
226 sd->grp_id = SENSOR_GROUP_ID;
227
228 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
229 s_info->pdata->board_info->type);
230 return sd;
231}
232
233static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
234{
235 struct i2c_client *client = v4l2_get_subdevdata(sd);
236
237 if (!client)
238 return;
239 v4l2_device_unregister_subdev(sd);
240 i2c_unregister_device(client);
241 i2c_put_adapter(client->adapter);
242}
243
244static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
245{
246 struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
247 struct fimc_dev *fd = NULL;
248 int num_clients, ret, i;
249
250 /*
251 * Runtime resume one of the FIMC entities to make sure
252 * the sclk_cam clocks are not globally disabled.
253 */
254 for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
255 if (fmd->fimc[i])
256 fd = fmd->fimc[i];
257 if (!fd)
258 return -ENXIO;
259 ret = pm_runtime_get_sync(&fd->pdev->dev);
260 if (ret < 0)
261 return ret;
262
263 WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
264 num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
265
266 fmd->num_sensors = num_clients;
267 for (i = 0; i < num_clients; i++) {
268 fmd->sensor[i].pdata = &pdata->isp_info[i];
269 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
270 if (ret)
271 break;
272 fmd->sensor[i].subdev =
273 fimc_md_register_sensor(fmd, &fmd->sensor[i]);
274 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
275 if (ret)
276 break;
277 }
278 pm_runtime_put(&fd->pdev->dev);
279 return ret;
280}
281
282/*
283 * MIPI CSIS and FIMC platform devices registration.
284 */
285static int fimc_register_callback(struct device *dev, void *p)
286{
287 struct fimc_dev *fimc = dev_get_drvdata(dev);
288 struct fimc_md *fmd = p;
289 int ret;
290
291 if (!fimc || !fimc->pdev)
292 return 0;
293 if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
294 return 0;
295
296 fmd->fimc[fimc->pdev->id] = fimc;
297 ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev);
298 if (ret)
299 return ret;
300 ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev);
301 if (!ret)
302 fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
303 return ret;
304}
305
306static int csis_register_callback(struct device *dev, void *p)
307{
308 struct v4l2_subdev *sd = dev_get_drvdata(dev);
309 struct platform_device *pdev;
310 struct fimc_md *fmd = p;
311 int id, ret;
312
313 if (!sd)
314 return 0;
315 pdev = v4l2_get_subdevdata(sd);
316 if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
317 return 0;
318 v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
319
320 id = pdev->id < 0 ? 0 : pdev->id;
321 fmd->csis[id].sd = sd;
322 sd->grp_id = CSIS_GROUP_ID;
323 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
324 if (ret)
325 v4l2_err(&fmd->v4l2_dev,
326 "Failed to register CSIS subdevice: %d\n", ret);
327 return ret;
328}
329
330/**
331 * fimc_md_register_platform_entities - register FIMC and CSIS media entities
332 */
333static int fimc_md_register_platform_entities(struct fimc_md *fmd)
334{
335 struct device_driver *driver;
336 int ret;
337
338 driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
339 if (!driver)
340 return -ENODEV;
341 ret = driver_for_each_device(driver, NULL, fmd,
342 fimc_register_callback);
343 put_driver(driver);
344 if (ret)
345 return ret;
346
347 driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
348 if (driver) {
349 ret = driver_for_each_device(driver, NULL, fmd,
350 csis_register_callback);
351 put_driver(driver);
352 }
353 return ret;
354}
355
356static void fimc_md_unregister_entities(struct fimc_md *fmd)
357{
358 int i;
359
360 for (i = 0; i < FIMC_MAX_DEVS; i++) {
361 if (fmd->fimc[i] == NULL)
362 continue;
363 fimc_unregister_m2m_device(fmd->fimc[i]);
364 fimc_unregister_capture_device(fmd->fimc[i]);
365 fmd->fimc[i] = NULL;
366 }
367 for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
368 if (fmd->csis[i].sd == NULL)
369 continue;
370 v4l2_device_unregister_subdev(fmd->csis[i].sd);
371 fmd->csis[i].sd = NULL;
372 }
373 for (i = 0; i < fmd->num_sensors; i++) {
374 if (fmd->sensor[i].subdev == NULL)
375 continue;
376 fimc_md_unregister_sensor(fmd->sensor[i].subdev);
377 fmd->sensor[i].subdev = NULL;
378 }
379}
380
381static int fimc_md_register_video_nodes(struct fimc_md *fmd)
382{
383 int i, ret = 0;
384
385 for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
386 if (!fmd->fimc[i])
387 continue;
388
389 if (fmd->fimc[i]->m2m.vfd)
390 ret = video_register_device(fmd->fimc[i]->m2m.vfd,
391 VFL_TYPE_GRABBER, -1);
392 if (ret)
393 break;
394 if (fmd->fimc[i]->vid_cap.vfd)
395 ret = video_register_device(fmd->fimc[i]->vid_cap.vfd,
396 VFL_TYPE_GRABBER, -1);
397 }
398
399 return ret;
400}
401
402/**
403 * __fimc_md_create_fimc_links - create links to all FIMC entities
404 * @fmd: fimc media device
405 * @source: the source entity to create links to all fimc entities from
406 * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
407 * @pad: the source entity pad index
408 * @fimc_id: index of the fimc device for which link should be enabled
409 */
410static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
411 struct media_entity *source,
412 struct v4l2_subdev *sensor,
413 int pad, int fimc_id)
414{
415 struct fimc_sensor_info *s_info;
416 struct media_entity *sink;
417 unsigned int flags;
418 int ret, i, src_pad;
419
420 for (i = 0; i < FIMC_MAX_DEVS; i++) {
421 if (!fmd->fimc[i])
422 break;
423 /*
424 * Some FIMC variants are not fitted with camera capture
425 * interface. Skip creating a link from sensor for those.
426 */
427 if (sensor && sensor->grp_id == SENSOR_GROUP_ID &&
428 !fmd->fimc[i]->variant->has_cam_if)
429 continue;
430
431 flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
432 sink = &fmd->fimc[i]->vid_cap.vfd->entity;
433 ret = media_entity_create_link(source, 0, sink, 0, flags);
434 if (ret)
435 return ret;
436
437 v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
438 source->name, flags ? '=' : '-', sink->name);
439
440 if (flags == 0 || sensor == NULL)
441 continue;
442 s_info = v4l2_get_subdev_hostdata(sensor);
443 if (!WARN_ON(s_info == NULL)) {
444 unsigned long irq_flags;
445 spin_lock_irqsave(&fmd->slock, irq_flags);
446 s_info->host = fmd->fimc[i];
447 spin_unlock_irqrestore(&fmd->slock, irq_flags);
448 }
449 }
450 return 0;
451}
452
453/**
454 * fimc_md_create_links - create default links between registered entities
455 *
456 * Parallel interface sensor entities are connected directly to FIMC capture
457 * entities. The sensors using MIPI CSIS bus are connected through immutable
458 * link with CSI receiver entity specified by mux_id. Any registered CSIS
459 * entity has a link to each registered FIMC capture entity. Enabled links
460 * are created by default between each subsequent registered sensor and
461 * subsequent FIMC capture entity. The number of default active links is
462 * determined by the number of available sensors or FIMC entities,
463 * whichever is less.
464 */
465static int fimc_md_create_links(struct fimc_md *fmd)
466{
467 struct v4l2_subdev *sensor, *csis;
468 struct s5p_fimc_isp_info *pdata;
469 struct fimc_sensor_info *s_info;
470 struct media_entity *source;
471 int fimc_id = 0;
472 int i, pad;
473 int ret = 0;
474
475 for (i = 0; i < fmd->num_sensors; i++) {
476 if (fmd->sensor[i].subdev == NULL)
477 continue;
478
479 sensor = fmd->sensor[i].subdev;
480 s_info = v4l2_get_subdev_hostdata(sensor);
481 if (!s_info || !s_info->pdata)
482 continue;
483
484 source = NULL;
485 pdata = s_info->pdata;
486
487 switch (pdata->bus_type) {
488 case FIMC_MIPI_CSI2:
489 if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
490 "Wrong CSI channel id: %d\n", pdata->mux_id))
491 return -EINVAL;
492
493 csis = fmd->csis[pdata->mux_id].sd;
494 if (WARN(csis == NULL,
495 "MIPI-CSI interface specified "
496 "but s5p-csis module is not loaded!\n"))
497 continue;
498
499 ret = media_entity_create_link(&sensor->entity, 0,
500 &csis->entity, CSIS_PAD_SINK,
501 MEDIA_LNK_FL_IMMUTABLE |
502 MEDIA_LNK_FL_ENABLED);
503 if (ret)
504 return ret;
505
506 v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
507 sensor->entity.name, csis->entity.name);
508
509 sensor = NULL;
510 source = &csis->entity;
511 pad = CSIS_PAD_SOURCE;
512 break;
513
514 case FIMC_ITU_601...FIMC_ITU_656:
515 source = &sensor->entity;
516 pad = 0;
517 break;
518
519 default:
520 v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
521 pdata->bus_type);
522 return -EINVAL;
523 }
524 if (source == NULL)
525 continue;
526
527 ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
528 fimc_id++);
529 }
530 return ret;
531}
532
533/*
534 * The peripheral sensor clock management.
535 */
536static int fimc_md_get_clocks(struct fimc_md *fmd)
537{
538 char clk_name[32];
539 struct clk *clock;
540 int i;
541
542 for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
543 snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
544 clock = clk_get(NULL, clk_name);
545 if (IS_ERR_OR_NULL(clock)) {
546 v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
547 clk_name);
548 return -ENXIO;
549 }
550 fmd->camclk[i].clock = clock;
551 }
552 return 0;
553}
554
555static void fimc_md_put_clocks(struct fimc_md *fmd)
556{
557 int i = FIMC_MAX_CAMCLKS;
558
559 while (--i >= 0) {
560 if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
561 continue;
562 clk_put(fmd->camclk[i].clock);
563 fmd->camclk[i].clock = NULL;
564 }
565}
566
567static int __fimc_md_set_camclk(struct fimc_md *fmd,
568 struct fimc_sensor_info *s_info,
569 bool on)
570{
571 struct s5p_fimc_isp_info *pdata = s_info->pdata;
572 struct fimc_camclk_info *camclk;
573 int ret = 0;
574
575 if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
576 return -EINVAL;
577
578 if (s_info->clk_on == on)
579 return 0;
580 camclk = &fmd->camclk[pdata->clk_id];
581
582 dbg("camclk %d, f: %lu, clk: %p, on: %d",
583 pdata->clk_id, pdata->clk_frequency, camclk, on);
584
585 if (on) {
586 if (camclk->use_count > 0 &&
587 camclk->frequency != pdata->clk_frequency)
588 return -EINVAL;
589
590 if (camclk->use_count++ == 0) {
591 clk_set_rate(camclk->clock, pdata->clk_frequency);
592 camclk->frequency = pdata->clk_frequency;
593 ret = clk_enable(camclk->clock);
594 }
595 s_info->clk_on = 1;
596 dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
597 clk_get_rate(camclk->clock));
598
599 return ret;
600 }
601
602 if (WARN_ON(camclk->use_count == 0))
603 return 0;
604
605 if (--camclk->use_count == 0) {
606 clk_disable(camclk->clock);
607 s_info->clk_on = 0;
608 dbg("Disabled camclk %d", pdata->clk_id);
609 }
610 return ret;
611}
612
613/**
614 * fimc_md_set_camclk - peripheral sensor clock setup
615 * @sd: sensor subdev to configure sclk_cam clock for
616 * @on: 1 to enable or 0 to disable the clock
617 *
618 * There are 2 separate clock outputs available in the SoC for external
619 * image processors. These clocks are shared between all registered FIMC
620 * devices to which sensors can be attached, either directly or through
621 * the MIPI CSI receiver. The clock is allowed here to be used by
622 * multiple sensors concurrently if they use same frequency.
623 * The per sensor subdev clk_on attribute helps to synchronize accesses
624 * to the sclk_cam clocks from the video and media device nodes.
625 * This function should only be called when the graph mutex is held.
626 */
627int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
628{
629 struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
630 struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
631
632 return __fimc_md_set_camclk(fmd, s_info, on);
633}
634
635static int fimc_md_link_notify(struct media_pad *source,
636 struct media_pad *sink, u32 flags)
637{
638 struct video_device *vid_dev;
639 struct fimc_dev *fimc;
640 int ret = 0;
641
642 if (WARN_ON(media_entity_type(sink->entity) != MEDIA_ENT_T_DEVNODE))
643 return 0;
644
645 vid_dev = media_entity_to_video_device(sink->entity);
646 fimc = video_get_drvdata(vid_dev);
647
648 if (!(flags & MEDIA_LNK_FL_ENABLED)) {
649 ret = __fimc_pipeline_shutdown(fimc);
650 fimc->pipeline.sensor = NULL;
651 fimc->pipeline.csis = NULL;
652 return ret;
653 }
654 /*
655 * Link activation. Enable power of pipeline elements only if the
656 * pipeline is already in use, i.e. its video node is opened.
657 */
658 mutex_lock(&fimc->lock);
659 if (fimc->vid_cap.refcnt > 0)
660 ret = __fimc_pipeline_initialize(fimc, source->entity, true);
661 mutex_unlock(&fimc->lock);
662
663 return ret ? -EPIPE : ret;
664}
665
666static ssize_t fimc_md_sysfs_show(struct device *dev,
667 struct device_attribute *attr, char *buf)
668{
669 struct platform_device *pdev = to_platform_device(dev);
670 struct fimc_md *fmd = platform_get_drvdata(pdev);
671
672 if (fmd->user_subdev_api)
673 return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
674
675 return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
676}
677
678static ssize_t fimc_md_sysfs_store(struct device *dev,
679 struct device_attribute *attr,
680 const char *buf, size_t count)
681{
682 struct platform_device *pdev = to_platform_device(dev);
683 struct fimc_md *fmd = platform_get_drvdata(pdev);
684 bool subdev_api;
685 int i;
686
687 if (!strcmp(buf, "vid-dev\n"))
688 subdev_api = false;
689 else if (!strcmp(buf, "sub-dev\n"))
690 subdev_api = true;
691 else
692 return count;
693
694 fmd->user_subdev_api = subdev_api;
695 for (i = 0; i < FIMC_MAX_DEVS; i++)
696 if (fmd->fimc[i])
697 fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
698 return count;
699}
700/*
701 * This device attribute is to select video pipeline configuration method.
702 * There are following valid values:
703 * vid-dev - for V4L2 video node API only, subdevice will be configured
704 * by the host driver.
705 * sub-dev - for media controller API, subdevs must be configured in user
706 * space before starting streaming.
707 */
708static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
709 fimc_md_sysfs_show, fimc_md_sysfs_store);
710
711static int __devinit fimc_md_probe(struct platform_device *pdev)
712{
713 struct v4l2_device *v4l2_dev;
714 struct fimc_md *fmd;
715 int ret;
716
717 if (WARN(!pdev->dev.platform_data, "Platform data not specified!\n"))
718 return -EINVAL;
719
720 fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL);
721 if (!fmd)
722 return -ENOMEM;
723
724 spin_lock_init(&fmd->slock);
725 fmd->pdev = pdev;
726
727 strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
728 sizeof(fmd->media_dev.model));
729 fmd->media_dev.link_notify = fimc_md_link_notify;
730 fmd->media_dev.dev = &pdev->dev;
731
732 v4l2_dev = &fmd->v4l2_dev;
733 v4l2_dev->mdev = &fmd->media_dev;
734 snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
735 dev_name(&pdev->dev));
736
737 ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
738 if (ret < 0) {
739 v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
740 goto err1;
741 }
742 ret = media_device_register(&fmd->media_dev);
743 if (ret < 0) {
744 v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
745 goto err2;
746 }
747 ret = fimc_md_get_clocks(fmd);
748 if (ret)
749 goto err3;
750
751 fmd->user_subdev_api = false;
752 ret = fimc_md_register_platform_entities(fmd);
753 if (ret)
754 goto err3;
755
756 ret = fimc_md_register_sensor_entities(fmd);
757 if (ret)
758 goto err3;
759 ret = fimc_md_create_links(fmd);
760 if (ret)
761 goto err3;
762 ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
763 if (ret)
764 goto err3;
765 ret = fimc_md_register_video_nodes(fmd);
766 if (ret)
767 goto err3;
768
769 ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
770 if (!ret) {
771 platform_set_drvdata(pdev, fmd);
772 return 0;
773 }
774err3:
775 media_device_unregister(&fmd->media_dev);
776 fimc_md_put_clocks(fmd);
777 fimc_md_unregister_entities(fmd);
778err2:
779 v4l2_device_unregister(&fmd->v4l2_dev);
780err1:
781 kfree(fmd);
782 return ret;
783}
784
785static int __devexit fimc_md_remove(struct platform_device *pdev)
786{
787 struct fimc_md *fmd = platform_get_drvdata(pdev);
788
789 if (!fmd)
790 return 0;
791 device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
792 fimc_md_unregister_entities(fmd);
793 media_device_unregister(&fmd->media_dev);
794 fimc_md_put_clocks(fmd);
795 kfree(fmd);
796 return 0;
797}
798
799static struct platform_driver fimc_md_driver = {
800 .probe = fimc_md_probe,
801 .remove = __devexit_p(fimc_md_remove),
802 .driver = {
803 .name = "s5p-fimc-md",
804 .owner = THIS_MODULE,
805 }
806};
807
808int __init fimc_md_init(void)
809{
810 int ret;
811 request_module("s5p-csis");
812 ret = fimc_register_driver();
813 if (ret)
814 return ret;
815 return platform_driver_register(&fimc_md_driver);
816}
817void __exit fimc_md_exit(void)
818{
819 platform_driver_unregister(&fimc_md_driver);
820 fimc_unregister_driver();
821}
822
823module_init(fimc_md_init);
824module_exit(fimc_md_exit);
825
826MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
827MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
828MODULE_LICENSE("GPL");
829MODULE_VERSION("2.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
new file mode 100644
index 000000000000..da3780823e7d
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h
@@ -0,0 +1,118 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co., Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef FIMC_MDEVICE_H_
10#define FIMC_MDEVICE_H_
11
12#include <linux/clk.h>
13#include <linux/platform_device.h>
14#include <linux/mutex.h>
15#include <media/media-device.h>
16#include <media/media-entity.h>
17#include <media/v4l2-device.h>
18#include <media/v4l2-subdev.h>
19
20#include "fimc-core.h"
21#include "mipi-csis.h"
22
23/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */
24#define SENSOR_GROUP_ID (1 << 8)
25#define CSIS_GROUP_ID (1 << 9)
26#define WRITEBACK_GROUP_ID (1 << 10)
27
28#define FIMC_MAX_SENSORS 8
29#define FIMC_MAX_CAMCLKS 2
30
31struct fimc_csis_info {
32 struct v4l2_subdev *sd;
33 int id;
34};
35
36struct fimc_camclk_info {
37 struct clk *clock;
38 int use_count;
39 unsigned long frequency;
40};
41
42/**
43 * struct fimc_sensor_info - image data source subdev information
44 * @pdata: sensor's atrributes passed as media device's platform data
45 * @subdev: image sensor v4l2 subdev
46 * @host: fimc device the sensor is currently linked to
47 * @clk_on: sclk_cam clock's state associated with this subdev
48 *
49 * This data structure applies to image sensor and the writeback subdevs.
50 */
51struct fimc_sensor_info {
52 struct s5p_fimc_isp_info *pdata;
53 struct v4l2_subdev *subdev;
54 struct fimc_dev *host;
55 bool clk_on;
56};
57
58/**
59 * struct fimc_md - fimc media device information
60 * @csis: MIPI CSIS subdevs data
61 * @sensor: array of registered sensor subdevs
62 * @num_sensors: actual number of registered sensors
63 * @camclk: external sensor clock information
64 * @fimc: array of registered fimc devices
65 * @media_dev: top level media device
66 * @v4l2_dev: top level v4l2_device holding up the subdevs
67 * @pdev: platform device this media device is hooked up into
68 * @user_subdev_api: true if subdevs are not configured by the host driver
69 * @slock: spinlock protecting @sensor array
70 */
71struct fimc_md {
72 struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
73 struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
74 int num_sensors;
75 struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
76 struct fimc_dev *fimc[FIMC_MAX_DEVS];
77 struct media_device media_dev;
78 struct v4l2_device v4l2_dev;
79 struct platform_device *pdev;
80 bool user_subdev_api;
81 spinlock_t slock;
82};
83
84#define is_subdev_pad(pad) (pad == NULL || \
85 media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
86
87#define me_subtype(me) \
88 ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
89
90#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
91
92static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
93{
94 return me->parent == NULL ? NULL :
95 container_of(me->parent, struct fimc_md, media_dev);
96}
97
98static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
99{
100 BUG_ON(fimc->vid_cap.vfd == NULL);
101 mutex_lock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
102}
103
104static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
105{
106 BUG_ON(fimc->vid_cap.vfd == NULL);
107 mutex_unlock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
108}
109
110int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
111void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me);
112int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
113 bool resume);
114int fimc_pipeline_shutdown(struct fimc_dev *fimc);
115int fimc_pipeline_s_power(struct fimc_dev *fimc, int state);
116int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state);
117
118#endif