blob: f047a9f1043c2607e268ca8c1e81c2631dbdf16e [file] [log] [blame]
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001/*
2 * S5P/EXYNOS4 SoC series camera host interface media device driver
3 *
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -03004 * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
5 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
Sylwester Nawrockid3953222011-09-01 06:01:08 -03006 *
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>
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -030014#include <linux/clk.h>
15#include <linux/clk-provider.h>
Sylwester Nawrockid3953222011-09-01 06:01:08 -030016#include <linux/device.h>
17#include <linux/errno.h>
18#include <linux/i2c.h>
19#include <linux/kernel.h>
20#include <linux/list.h>
21#include <linux/module.h>
Sylwester Nawrockie2985a22013-03-08 12:59:33 -030022#include <linux/of.h>
23#include <linux/of_platform.h>
24#include <linux/of_device.h>
Sylwester Nawrockid3953222011-09-01 06:01:08 -030025#include <linux/platform_device.h>
26#include <linux/pm_runtime.h>
27#include <linux/types.h>
28#include <linux/slab.h>
Sylwester Nawrocki131b6c62011-08-24 19:25:10 -030029#include <media/v4l2-ctrls.h>
Sylwester Nawrockie2985a22013-03-08 12:59:33 -030030#include <media/v4l2-of.h>
Sylwester Nawrockid3953222011-09-01 06:01:08 -030031#include <media/media-device.h>
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -030032#include <media/s5p_fimc.h>
Sylwester Nawrockid3953222011-09-01 06:01:08 -030033
Sylwester Nawrocki56fa1a62013-03-24 16:54:25 +010034#include "media-dev.h"
Sylwester Nawrockid3953222011-09-01 06:01:08 -030035#include "fimc-core.h"
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -030036#include "fimc-is.h"
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030037#include "fimc-lite.h"
Sylwester Nawrockid3953222011-09-01 06:01:08 -030038#include "mipi-csis.h"
39
40static int __fimc_md_set_camclk(struct fimc_md *fmd,
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -030041 struct fimc_source_info *si,
Sylwester Nawrockid3953222011-09-01 06:01:08 -030042 bool on);
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -030043
44/* Set up image sensor subdev -> FIMC capture node notifications. */
45static void __setup_sensor_notification(struct fimc_md *fmd,
46 struct v4l2_subdev *sensor,
47 struct v4l2_subdev *fimc_sd)
48{
49 struct fimc_source_info *src_inf;
50 struct fimc_sensor_info *md_si;
51 unsigned long flags;
52
53 src_inf = v4l2_get_subdev_hostdata(sensor);
54 if (!src_inf || WARN_ON(fmd == NULL))
55 return;
56
57 md_si = source_to_sensor_info(src_inf);
58 spin_lock_irqsave(&fmd->slock, flags);
59 md_si->host = v4l2_get_subdevdata(fimc_sd);
60 spin_unlock_irqrestore(&fmd->slock, flags);
61}
62
Sylwester Nawrockid3953222011-09-01 06:01:08 -030063/**
64 * fimc_pipeline_prepare - update pipeline information with subdevice pointers
Sylwester Nawrocki39bb6df62013-03-08 08:58:34 -030065 * @me: media entity terminating the pipeline
Sylwester Nawrockid3953222011-09-01 06:01:08 -030066 *
67 * Caller holds the graph mutex.
68 */
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -030069static void fimc_pipeline_prepare(struct fimc_pipeline *p,
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -030070 struct media_entity *me)
Sylwester Nawrockid3953222011-09-01 06:01:08 -030071{
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -030072 struct fimc_md *fmd = entity_to_fimc_mdev(me);
Sylwester Nawrockid3953222011-09-01 06:01:08 -030073 struct v4l2_subdev *sd;
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -030074 struct v4l2_subdev *sensor = NULL;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030075 int i;
Sylwester Nawrockid3953222011-09-01 06:01:08 -030076
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030077 for (i = 0; i < IDX_MAX; i++)
78 p->subdevs[i] = NULL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -030079
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030080 while (1) {
Sylwester Nawrocki39bb6df62013-03-08 08:58:34 -030081 struct media_pad *pad = NULL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -030082
Sylwester Nawrocki39bb6df62013-03-08 08:58:34 -030083 /* Find remote source pad */
84 for (i = 0; i < me->num_pads; i++) {
85 struct media_pad *spad = &me->pads[i];
86 if (!(spad->flags & MEDIA_PAD_FL_SINK))
87 continue;
Andrzej Hajda1bddf1b2013-06-03 05:16:13 -030088 pad = media_entity_remote_pad(spad);
Sylwester Nawrocki39bb6df62013-03-08 08:58:34 -030089 if (pad)
90 break;
91 }
92
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030093 if (pad == NULL ||
94 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
95 break;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -030096 sd = media_entity_to_v4l2_subdev(pad->entity);
97
98 switch (sd->grp_id) {
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -030099 case GRP_ID_SENSOR:
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -0300100 sensor = sd;
101 /* fall through */
102 case GRP_ID_FIMC_IS_SENSOR:
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300103 p->subdevs[IDX_SENSOR] = sd;
104 break;
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300105 case GRP_ID_CSIS:
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300106 p->subdevs[IDX_CSIS] = sd;
107 break;
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300108 case GRP_ID_FLITE:
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300109 p->subdevs[IDX_FLITE] = sd;
110 break;
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300111 case GRP_ID_FIMC:
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -0300112 p->subdevs[IDX_FIMC] = sd;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300113 break;
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300114 case GRP_ID_FIMC_IS:
115 p->subdevs[IDX_IS_ISP] = sd;
116 break;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300117 default:
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300118 break;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300119 }
Sylwester Nawrocki39bb6df62013-03-08 08:58:34 -0300120 me = &sd->entity;
121 if (me->num_pads == 1)
122 break;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300123 }
Sylwester Nawrocki52917bc2013-05-31 11:37:24 -0300124
125 if (sensor && p->subdevs[IDX_FIMC])
126 __setup_sensor_notification(fmd, sensor, p->subdevs[IDX_FIMC]);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300127}
128
129/**
130 * __subdev_set_power - change power state of a single subdev
131 * @sd: subdevice to change power state for
132 * @on: 1 to enable power or 0 to disable
133 *
134 * Return result of s_power subdev operation or -ENXIO if sd argument
135 * is NULL. Return 0 if the subdevice does not implement s_power.
136 */
137static int __subdev_set_power(struct v4l2_subdev *sd, int on)
138{
139 int *use_count;
140 int ret;
141
142 if (sd == NULL)
143 return -ENXIO;
144
145 use_count = &sd->entity.use_count;
146 if (on && (*use_count)++ > 0)
147 return 0;
148 else if (!on && (*use_count == 0 || --(*use_count) > 0))
149 return 0;
150 ret = v4l2_subdev_call(sd, core, s_power, on);
151
152 return ret != -ENOIOCTLCMD ? ret : 0;
153}
154
155/**
156 * fimc_pipeline_s_power - change power state of all pipeline subdevs
157 * @fimc: fimc device terminating the pipeline
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300158 * @state: true to power on, false to power off
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300159 *
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300160 * Needs to be called with the graph mutex held.
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300161 */
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300162static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool on)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300163{
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300164 static const u8 seq[2][IDX_MAX - 1] = {
165 { IDX_IS_ISP, IDX_SENSOR, IDX_CSIS, IDX_FLITE },
166 { IDX_CSIS, IDX_FLITE, IDX_SENSOR, IDX_IS_ISP },
167 };
168 int i, ret = 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300169
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300170 if (p->subdevs[IDX_SENSOR] == NULL)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300171 return -ENXIO;
172
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300173 for (i = 0; i < IDX_MAX - 1; i++) {
174 unsigned int idx = seq[on][i];
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300175
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300176 ret = __subdev_set_power(p->subdevs[idx], on);
177
178
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300179 if (ret < 0 && ret != -ENXIO)
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300180 goto error;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300181 }
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300182 return 0;
Sylwester Nawrockif8bca4f2012-11-20 08:54:22 -0300183error:
184 for (; i >= 0; i--) {
185 unsigned int idx = seq[on][i];
186 __subdev_set_power(p->subdevs[idx], !on);
187 }
188 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300189}
190
191/**
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -0300192 * __fimc_pipeline_open - update the pipeline information, enable power
193 * of all pipeline subdevs and the sensor clock
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300194 * @me: media entity to start graph walk with
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300195 * @prepare: true to walk the current pipeline and acquire all subdevs
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300196 *
Sylwester Nawrocki740ad922012-12-06 10:26:19 -0300197 * Called with the graph mutex held.
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300198 */
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300199static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300200 struct media_entity *me, bool prepare)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300201{
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300202 struct fimc_md *fmd = entity_to_fimc_mdev(me);
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300203 struct fimc_pipeline *p = to_fimc_pipeline(ep);
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300204 struct v4l2_subdev *sd;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300205 int ret;
206
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300207 if (WARN_ON(p == NULL || me == NULL))
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300208 return -EINVAL;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300209
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300210 if (prepare)
211 fimc_pipeline_prepare(p, me);
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300212
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300213 sd = p->subdevs[IDX_SENSOR];
214 if (sd == NULL)
215 return -EINVAL;
216
217 /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */
218 if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) {
219 ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]);
220 if (ret < 0)
221 return ret;
222 }
223 ret = fimc_md_set_camclk(sd, true);
224 if (ret < 0)
225 goto err_wbclk;
226
227 ret = fimc_pipeline_s_power(p, 1);
228 if (!ret)
229 return 0;
230
231 fimc_md_set_camclk(sd, false);
232
233err_wbclk:
234 if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])
235 clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]);
236
237 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300238}
239
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300240/**
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -0300241 * __fimc_pipeline_close - disable the sensor clock and pipeline power
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300242 * @fimc: fimc device terminating the pipeline
243 *
Sylwester Nawrocki740ad922012-12-06 10:26:19 -0300244 * Disable power of all subdevs and turn the external sensor clock off.
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300245 */
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300246static int __fimc_pipeline_close(struct exynos_media_pipeline *ep)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300247{
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300248 struct fimc_pipeline *p = to_fimc_pipeline(ep);
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300249 struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL;
250 struct fimc_md *fmd;
Sylwester Nawrocki36da6fc2013-05-31 11:37:23 -0300251 int ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300252
Sylwester Nawrocki36da6fc2013-05-31 11:37:23 -0300253 if (sd == NULL) {
254 pr_warn("%s(): No sensor subdev\n", __func__);
255 return 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300256 }
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300257
Sylwester Nawrocki36da6fc2013-05-31 11:37:23 -0300258 ret = fimc_pipeline_s_power(p, 0);
259 fimc_md_set_camclk(sd, false);
260
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -0300261 fmd = entity_to_fimc_mdev(&sd->entity);
262
263 /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */
264 if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])
265 clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]);
266
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300267 return ret == -ENXIO ? 0 : ret;
268}
269
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300270/**
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300271 * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300272 * @pipeline: video pipeline structure
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300273 * @on: passed as the s_stream() callback argument
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300274 */
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300275static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300276{
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300277 static const u8 seq[2][IDX_MAX] = {
278 { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE },
279 { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP },
280 };
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300281 struct fimc_pipeline *p = to_fimc_pipeline(ep);
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300282 int i, ret = 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300283
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300284 if (p->subdevs[IDX_SENSOR] == NULL)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300285 return -ENODEV;
286
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300287 for (i = 0; i < IDX_MAX; i++) {
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300288 unsigned int idx = seq[on][i];
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300289
290 ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
291
292 if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300293 goto error;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300294 }
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -0300295 return 0;
Sylwester Nawrocki8d274e72012-11-13 14:35:22 -0300296error:
297 for (; i >= 0; i--) {
298 unsigned int idx = seq[on][i];
299 v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on);
300 }
301 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300302}
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -0300303
304/* Media pipeline operations for the FIMC/FIMC-LITE video device driver */
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300305static const struct exynos_media_pipeline_ops fimc_pipeline_ops = {
Sylwester Nawrocki740ad922012-12-06 10:26:19 -0300306 .open = __fimc_pipeline_open,
307 .close = __fimc_pipeline_close,
308 .set_stream = __fimc_pipeline_s_stream,
Sylwester Nawrockib9ee31e62012-08-14 10:46:58 -0300309};
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300310
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300311static struct exynos_media_pipeline *fimc_md_pipeline_create(
312 struct fimc_md *fmd)
313{
314 struct fimc_pipeline *p;
315
316 p = kzalloc(sizeof(*p), GFP_KERNEL);
317 if (!p)
318 return NULL;
319
320 list_add_tail(&p->list, &fmd->pipelines);
321
322 p->ep.ops = &fimc_pipeline_ops;
323 return &p->ep;
324}
325
326static void fimc_md_pipelines_free(struct fimc_md *fmd)
327{
328 while (!list_empty(&fmd->pipelines)) {
329 struct fimc_pipeline *p;
330
331 p = list_entry(fmd->pipelines.next, typeof(*p), list);
332 list_del(&p->list);
333 kfree(p);
334 }
335}
336
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300337/*
338 * Sensor subdevice helper functions
339 */
340static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300341 struct fimc_source_info *si)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300342{
343 struct i2c_adapter *adapter;
344 struct v4l2_subdev *sd = NULL;
345
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300346 if (!si || !fmd)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300347 return NULL;
Sylwester Nawrocki88fa8312013-03-20 10:44:39 -0300348 /*
349 * If FIMC bus type is not Writeback FIFO assume it is same
350 * as sensor_bus_type.
351 */
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300352 si->fimc_bus_type = si->sensor_bus_type;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300353
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300354 adapter = i2c_get_adapter(si->i2c_bus_num);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300355 if (!adapter) {
356 v4l2_warn(&fmd->v4l2_dev,
357 "Failed to get I2C adapter %d, deferring probe\n",
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300358 si->i2c_bus_num);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300359 return ERR_PTR(-EPROBE_DEFER);
360 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300361 sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300362 si->board_info, NULL);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300363 if (IS_ERR_OR_NULL(sd)) {
Sylwester Nawrocki7acde022011-09-13 14:07:09 -0300364 i2c_put_adapter(adapter);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300365 v4l2_warn(&fmd->v4l2_dev,
366 "Failed to acquire subdev %s, deferring probe\n",
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300367 si->board_info->type);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300368 return ERR_PTR(-EPROBE_DEFER);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300369 }
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300370 v4l2_set_subdev_hostdata(sd, si);
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300371 sd->grp_id = GRP_ID_SENSOR;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300372
373 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300374 sd->name);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300375 return sd;
376}
377
378static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
379{
380 struct i2c_client *client = v4l2_get_subdevdata(sd);
Sylwester Nawrocki7acde022011-09-13 14:07:09 -0300381 struct i2c_adapter *adapter;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300382
383 if (!client)
384 return;
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300385
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300386 v4l2_device_unregister_subdev(sd);
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300387
388 if (!client->dev.of_node) {
389 adapter = client->adapter;
390 i2c_unregister_device(client);
391 if (adapter)
392 i2c_put_adapter(adapter);
393 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300394}
395
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300396#ifdef CONFIG_OF
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300397/* Register I2C client subdev associated with @node. */
398static int fimc_md_of_add_sensor(struct fimc_md *fmd,
399 struct device_node *node, int index)
400{
401 struct fimc_sensor_info *si;
402 struct i2c_client *client;
403 struct v4l2_subdev *sd;
404 int ret;
405
406 if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
407 return -EINVAL;
408 si = &fmd->sensor[index];
409
410 client = of_find_i2c_device_by_node(node);
411 if (!client)
412 return -EPROBE_DEFER;
413
414 device_lock(&client->dev);
415
Lars-Peter Clausenc7110f32013-09-29 10:51:00 +0200416 if (!client->dev.driver ||
417 !try_module_get(client->dev.driver->owner)) {
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300418 ret = -EPROBE_DEFER;
419 v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
420 node->full_name);
421 goto dev_put;
422 }
423
424 /* Enable sensor's master clock */
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300425 ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300426 if (ret < 0)
427 goto mod_put;
428 sd = i2c_get_clientdata(client);
429
430 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300431 __fimc_md_set_camclk(fmd, &si->pdata, false);
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300432 if (ret < 0)
433 goto mod_put;
434
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300435 v4l2_set_subdev_hostdata(sd, &si->pdata);
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300436 if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
437 sd->grp_id = GRP_ID_FIMC_IS_SENSOR;
438 else
439 sd->grp_id = GRP_ID_SENSOR;
440
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300441 si->subdev = sd;
442 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
443 sd->name, fmd->num_sensors);
444 fmd->num_sensors++;
445
446mod_put:
Lars-Peter Clausenc7110f32013-09-29 10:51:00 +0200447 module_put(client->dev.driver->owner);
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300448dev_put:
449 device_unlock(&client->dev);
450 put_device(&client->dev);
451 return ret;
452}
453
454/* Parse port node and register as a sub-device any sensor specified there. */
455static int fimc_md_parse_port_node(struct fimc_md *fmd,
456 struct device_node *port,
457 unsigned int index)
458{
459 struct device_node *rem, *ep, *np;
460 struct fimc_source_info *pd;
461 struct v4l2_of_endpoint endpoint;
462 int ret;
463 u32 val;
464
465 pd = &fmd->sensor[index].pdata;
466
467 /* Assume here a port node can have only one endpoint node. */
468 ep = of_get_next_child(port, NULL);
469 if (!ep)
470 return 0;
471
472 v4l2_of_parse_endpoint(ep, &endpoint);
473 if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS)
474 return -EINVAL;
475
476 pd->mux_id = (endpoint.port - 1) & 0x1;
477
478 rem = v4l2_of_get_remote_port_parent(ep);
479 of_node_put(ep);
480 if (rem == NULL) {
481 v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",
482 ep->full_name);
483 return 0;
484 }
485 if (!of_property_read_u32(rem, "samsung,camclk-out", &val))
486 pd->clk_id = val;
487
488 if (!of_property_read_u32(rem, "clock-frequency", &val))
489 pd->clk_frequency = val;
490
491 if (pd->clk_frequency == 0) {
492 v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
493 rem->full_name);
494 of_node_put(rem);
495 return -EINVAL;
496 }
497
498 if (fimc_input_is_parallel(endpoint.port)) {
499 if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
500 pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
501 else
502 pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
503 pd->flags = endpoint.bus.parallel.flags;
504 } else if (fimc_input_is_mipi_csi(endpoint.port)) {
505 /*
506 * MIPI CSI-2: only input mux selection and
507 * the sensor's clock frequency is needed.
508 */
509 pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
510 } else {
511 v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
512 endpoint.port, rem->full_name);
513 }
514 /*
515 * For FIMC-IS handled sensors, that are placed under i2c-isp device
516 * node, FIMC is connected to the FIMC-IS through its ISP Writeback
517 * input. Sensors are attached to the FIMC-LITE hostdata interface
518 * directly or through MIPI-CSIS, depending on the external media bus
519 * used. This needs to be handled in a more reliable way, not by just
520 * checking parent's node name.
521 */
522 np = of_get_parent(rem);
523
524 if (np && !of_node_cmp(np->name, "i2c-isp"))
525 pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
526 else
527 pd->fimc_bus_type = pd->sensor_bus_type;
528
529 ret = fimc_md_of_add_sensor(fmd, rem, index);
530 of_node_put(rem);
531
532 return ret;
533}
534
535/* Register all SoC external sub-devices */
536static int fimc_md_of_sensors_register(struct fimc_md *fmd,
537 struct device_node *np)
538{
539 struct device_node *parent = fmd->pdev->dev.of_node;
540 struct device_node *node, *ports;
541 int index = 0;
542 int ret;
543
544 /* Attach sensors linked to MIPI CSI-2 receivers */
545 for_each_available_child_of_node(parent, node) {
546 struct device_node *port;
547
548 if (of_node_cmp(node->name, "csis"))
549 continue;
550 /* The csis node can have only port subnode. */
551 port = of_get_next_child(node, NULL);
552 if (!port)
553 continue;
554
555 ret = fimc_md_parse_port_node(fmd, port, index);
556 if (ret < 0)
557 return ret;
558 index++;
559 }
560
561 /* Attach sensors listed in the parallel-ports node */
562 ports = of_get_child_by_name(parent, "parallel-ports");
563 if (!ports)
564 return 0;
565
566 for_each_child_of_node(ports, node) {
567 ret = fimc_md_parse_port_node(fmd, node, index);
568 if (ret < 0)
569 break;
570 index++;
571 }
572
573 return 0;
574}
575
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300576static int __of_get_csis_id(struct device_node *np)
577{
578 u32 reg = 0;
579
580 np = of_get_child_by_name(np, "port");
581 if (!np)
582 return -EINVAL;
583 of_property_read_u32(np, "reg", &reg);
584 return reg - FIMC_INPUT_MIPI_CSI2_0;
585}
586#else
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300587#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS)
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300588#define __of_get_csis_id(np) (-ENOSYS)
589#endif
590
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300591static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
592{
593 struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300594 struct device_node *of_node = fmd->pdev->dev.of_node;
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300595 int num_clients = 0;
596 int ret, i;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300597
598 /*
599 * Runtime resume one of the FIMC entities to make sure
600 * the sclk_cam clocks are not globally disabled.
601 */
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -0300602 if (!fmd->pmf)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300603 return -ENXIO;
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300604
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -0300605 ret = pm_runtime_get_sync(fmd->pmf);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300606 if (ret < 0)
607 return ret;
608
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300609 if (of_node) {
610 fmd->num_sensors = 0;
611 ret = fimc_md_of_sensors_register(fmd, of_node);
612 } else if (pdata) {
613 WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
614 num_clients = min_t(u32, pdata->num_clients,
615 ARRAY_SIZE(fmd->sensor));
616 fmd->num_sensors = num_clients;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300617
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300618 for (i = 0; i < num_clients; i++) {
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300619 struct fimc_sensor_info *si = &fmd->sensor[i];
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300620 struct v4l2_subdev *sd;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300621
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300622 si->pdata = pdata->source_info[i];
623 ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300624 if (ret)
625 break;
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300626 sd = fimc_md_register_sensor(fmd, &si->pdata);
627 ret = __fimc_md_set_camclk(fmd, &si->pdata, false);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300628
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300629 if (IS_ERR(sd)) {
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300630 si->subdev = NULL;
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300631 ret = PTR_ERR(sd);
632 break;
633 }
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300634 si->subdev = sd;
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300635 if (ret)
636 break;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300637 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300638 }
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -0300639
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -0300640 pm_runtime_put(fmd->pmf);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300641 return ret;
642}
643
644/*
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300645 * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300646 */
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300647
648static int register_fimc_lite_entity(struct fimc_md *fmd,
649 struct fimc_lite *fimc_lite)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300650{
Sachin Kamat8163ec02012-10-25 10:56:00 -0300651 struct v4l2_subdev *sd;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300652 struct exynos_media_pipeline *ep;
Sylwester Nawrockiafd73482012-10-25 10:48:44 -0300653 int ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300654
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300655 if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
656 fmd->fimc_lite[fimc_lite->index]))
657 return -EBUSY;
658
659 sd = &fimc_lite->subdev;
660 sd->grp_id = GRP_ID_FLITE;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300661
662 ep = fimc_md_pipeline_create(fmd);
663 if (!ep)
664 return -ENOMEM;
665
666 v4l2_set_subdev_hostdata(sd, ep);
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300667
668 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
669 if (!ret)
670 fmd->fimc_lite[fimc_lite->index] = fimc_lite;
671 else
672 v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n",
673 fimc_lite->index);
674 return ret;
675}
676
677static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
678{
679 struct v4l2_subdev *sd;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300680 struct exynos_media_pipeline *ep;
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300681 int ret;
682
683 if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
684 return -EBUSY;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300685
Sachin Kamat8163ec02012-10-25 10:56:00 -0300686 sd = &fimc->vid_cap.subdev;
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300687 sd->grp_id = GRP_ID_FIMC;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300688
689 ep = fimc_md_pipeline_create(fmd);
690 if (!ep)
691 return -ENOMEM;
692
693 v4l2_set_subdev_hostdata(sd, ep);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300694
695 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300696 if (!ret) {
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -0300697 if (!fmd->pmf && fimc->pdev)
698 fmd->pmf = &fimc->pdev->dev;
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300699 fmd->fimc[fimc->id] = fimc;
700 fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
701 } else {
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300702 v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
703 fimc->id, ret);
704 }
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300705 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300706}
707
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300708static int register_csis_entity(struct fimc_md *fmd,
709 struct platform_device *pdev,
710 struct v4l2_subdev *sd)
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300711{
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300712 struct device_node *node = pdev->dev.of_node;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300713 int id, ret;
714
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300715 id = node ? __of_get_csis_id(node) : max(0, pdev->id);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300716
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300717 if (WARN_ON(id < 0 || id >= CSIS_MAX_ENTITIES))
718 return -ENOENT;
719
720 if (WARN_ON(fmd->csis[id].sd))
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300721 return -EBUSY;
722
Sylwester Nawrocki588c87b2012-11-27 11:57:42 -0300723 sd->grp_id = GRP_ID_CSIS;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300724 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
Sylwester Nawrockiafd73482012-10-25 10:48:44 -0300725 if (!ret)
726 fmd->csis[id].sd = sd;
727 else
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300728 v4l2_err(&fmd->v4l2_dev,
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300729 "Failed to register MIPI-CSIS.%d (%d)\n", id, ret);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300730 return ret;
731}
732
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300733static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
734{
735 struct v4l2_subdev *sd = &is->isp.subdev;
736 int ret;
737
738 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
739 if (ret) {
740 v4l2_err(&fmd->v4l2_dev,
741 "Failed to register FIMC-ISP (%d)\n", ret);
742 return ret;
743 }
744
745 fmd->fimc_is = is;
746 return 0;
747}
748
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300749static int fimc_md_register_platform_entity(struct fimc_md *fmd,
750 struct platform_device *pdev,
751 int plat_entity)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300752{
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300753 struct device *dev = &pdev->dev;
754 int ret = -EPROBE_DEFER;
755 void *drvdata;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300756
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300757 /* Lock to ensure dev->driver won't change. */
758 device_lock(dev);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300759
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300760 if (!dev->driver || !try_module_get(dev->driver->owner))
761 goto dev_unlock;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300762
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300763 drvdata = dev_get_drvdata(dev);
Jonathan McCrohan39c1cb22013-10-20 21:34:01 -0300764 /* Some subdev didn't probe successfully id drvdata is NULL */
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300765 if (drvdata) {
766 switch (plat_entity) {
767 case IDX_FIMC:
768 ret = register_fimc_entity(fmd, drvdata);
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300769 break;
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300770 case IDX_FLITE:
771 ret = register_fimc_lite_entity(fmd, drvdata);
772 break;
773 case IDX_CSIS:
774 ret = register_csis_entity(fmd, pdev, drvdata);
775 break;
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300776 case IDX_IS_ISP:
777 ret = register_fimc_is_entity(fmd, drvdata);
778 break;
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300779 default:
780 ret = -ENODEV;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300781 }
782 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300783
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300784 module_put(dev->driver->owner);
785dev_unlock:
786 device_unlock(dev);
787 if (ret == -EPROBE_DEFER)
788 dev_info(&fmd->pdev->dev, "deferring %s device registration\n",
789 dev_name(dev));
790 else if (ret < 0)
791 dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n",
792 dev_name(dev), ret);
793 return ret;
794}
795
796static int fimc_md_pdev_match(struct device *dev, void *data)
797{
798 struct platform_device *pdev = to_platform_device(dev);
799 int plat_entity = -1;
800 int ret;
801 char *p;
802
803 if (!get_device(dev))
804 return -ENODEV;
805
806 if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) {
807 plat_entity = IDX_CSIS;
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300808 } else {
809 p = strstr(pdev->name, "fimc");
810 if (p && *(p + 4) == 0)
811 plat_entity = IDX_FIMC;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -0300812 }
813
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300814 if (plat_entity >= 0)
815 ret = fimc_md_register_platform_entity(data, pdev,
816 plat_entity);
817 put_device(dev);
818 return 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300819}
820
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300821/* Register FIMC, FIMC-LITE and CSIS media entities */
822#ifdef CONFIG_OF
823static int fimc_md_register_of_platform_entities(struct fimc_md *fmd,
824 struct device_node *parent)
825{
826 struct device_node *node;
827 int ret = 0;
828
829 for_each_available_child_of_node(parent, node) {
830 struct platform_device *pdev;
831 int plat_entity = -1;
832
833 pdev = of_find_device_by_node(node);
834 if (!pdev)
835 continue;
836
837 /* If driver of any entity isn't ready try all again later. */
838 if (!strcmp(node->name, CSIS_OF_NODE_NAME))
839 plat_entity = IDX_CSIS;
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -0300840 else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME))
841 plat_entity = IDX_IS_ISP;
Sylwester Nawrockie2985a22013-03-08 12:59:33 -0300842 else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME))
843 plat_entity = IDX_FLITE;
844 else if (!strcmp(node->name, FIMC_OF_NODE_NAME) &&
845 !of_property_read_bool(node, "samsung,lcd-wb"))
846 plat_entity = IDX_FIMC;
847
848 if (plat_entity >= 0)
849 ret = fimc_md_register_platform_entity(fmd, pdev,
850 plat_entity);
851 put_device(&pdev->dev);
852 if (ret < 0)
853 break;
854 }
855
856 return ret;
857}
858#else
859#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS)
860#endif
861
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300862static void fimc_md_unregister_entities(struct fimc_md *fmd)
863{
864 int i;
865
866 for (i = 0; i < FIMC_MAX_DEVS; i++) {
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300867 struct fimc_dev *dev = fmd->fimc[i];
868 if (dev == NULL)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300869 continue;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300870 v4l2_device_unregister_subdev(&dev->vid_cap.subdev);
871 dev->vid_cap.ve.pipe = NULL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300872 fmd->fimc[i] = NULL;
873 }
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300874 for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300875 struct fimc_lite *dev = fmd->fimc_lite[i];
876 if (dev == NULL)
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300877 continue;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -0300878 v4l2_device_unregister_subdev(&dev->subdev);
879 dev->ve.pipe = NULL;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300880 fmd->fimc_lite[i] = NULL;
881 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300882 for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
883 if (fmd->csis[i].sd == NULL)
884 continue;
885 v4l2_device_unregister_subdev(fmd->csis[i].sd);
886 fmd->csis[i].sd = NULL;
887 }
888 for (i = 0; i < fmd->num_sensors; i++) {
889 if (fmd->sensor[i].subdev == NULL)
890 continue;
891 fimc_md_unregister_sensor(fmd->sensor[i].subdev);
892 fmd->sensor[i].subdev = NULL;
893 }
Sylwester Nawrockie41a35c2013-04-18 09:05:54 -0300894
895 if (fmd->fimc_is)
896 v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev);
897
Sylwester Nawrocki7b43a6f2012-12-12 08:16:05 -0300898 v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n");
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300899}
900
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300901/**
902 * __fimc_md_create_fimc_links - create links to all FIMC entities
903 * @fmd: fimc media device
904 * @source: the source entity to create links to all fimc entities from
905 * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
906 * @pad: the source entity pad index
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -0300907 * @link_mask: bitmask of the fimc devices for which link should be enabled
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300908 */
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300909static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
910 struct media_entity *source,
911 struct v4l2_subdev *sensor,
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -0300912 int pad, int link_mask)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300913{
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300914 struct fimc_source_info *si = NULL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300915 struct media_entity *sink;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300916 unsigned int flags = 0;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300917 int i, ret = 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300918
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300919 if (sensor) {
920 si = v4l2_get_subdev_hostdata(sensor);
921 /* Skip direct FIMC links in the logical FIMC-IS sensor path */
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -0300922 if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300923 ret = 1;
924 }
925
926 for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) {
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300927 if (!fmd->fimc[i])
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300928 continue;
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300929 /*
930 * Some FIMC variants are not fitted with camera capture
931 * interface. Skip creating a link from sensor for those.
932 */
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300933 if (!fmd->fimc[i]->variant->has_cam_if)
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300934 continue;
935
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -0300936 flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300937
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -0300938 sink = &fmd->fimc[i]->vid_cap.subdev.entity;
Sylwester Nawrocki237e0262011-08-24 20:35:30 -0300939 ret = media_entity_create_link(source, pad, sink,
Sylwester Nawrocki88fa8312013-03-20 10:44:39 -0300940 FIMC_SD_PAD_SINK_CAM, flags);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300941 if (ret)
942 return ret;
943
Sylwester Nawrocki237e0262011-08-24 20:35:30 -0300944 /* Notify FIMC capture subdev entity */
945 ret = media_entity_call(sink, link_setup, &sink->pads[0],
946 &source->pads[pad], flags);
947 if (ret)
948 break;
949
Sylwester Nawrocki542fb082012-10-25 05:48:19 -0300950 v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n",
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300951 source->name, flags ? '=' : '-', sink->name);
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300952 }
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300953
954 for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
955 if (!fmd->fimc_lite[i])
956 continue;
957
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300958 sink = &fmd->fimc_lite[i]->subdev.entity;
959 ret = media_entity_create_link(source, pad, sink,
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300960 FLITE_SD_PAD_SINK, 0);
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300961 if (ret)
962 return ret;
963
964 /* Notify FIMC-LITE subdev entity */
965 ret = media_entity_call(sink, link_setup, &sink->pads[0],
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300966 &source->pads[pad], 0);
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300967 if (ret)
968 break;
969
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300970 v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n",
971 source->name, sink->name);
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300972 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -0300973 return 0;
974}
975
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300976/* Create links from FIMC-LITE source pads to other entities */
977static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
978{
979 struct media_entity *source, *sink;
Sylwester Nawrockia26860b2012-12-05 13:43:05 -0300980 int i, ret = 0;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300981
982 for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
983 struct fimc_lite *fimc = fmd->fimc_lite[i];
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300984
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300985 if (fimc == NULL)
986 continue;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300987
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300988 source = &fimc->subdev.entity;
Sylwester Nawrockibc7584b2013-05-31 11:37:18 -0300989 sink = &fimc->ve.vdev.entity;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300990 /* FIMC-LITE's subdev and video node */
Sylwester Nawrocki6319d6a2012-11-28 15:41:01 -0300991 ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA,
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300992 sink, 0, 0);
Sylwester Nawrocki4af81312012-04-27 05:29:05 -0300993 if (ret)
994 break;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -0300995 /* Link from FIMC-LITE to IS-ISP subdev */
996 sink = &fmd->fimc_is->isp.subdev.entity;
997 ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP,
998 sink, 0, 0);
999 if (ret)
1000 break;
1001 }
1002
1003 return ret;
1004}
1005
1006/* Create FIMC-IS links */
1007static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
1008{
1009 struct media_entity *source, *sink;
1010 int i, ret;
1011
1012 source = &fmd->fimc_is->isp.subdev.entity;
1013
1014 for (i = 0; i < FIMC_MAX_DEVS; i++) {
1015 if (fmd->fimc[i] == NULL)
1016 continue;
1017
1018 /* Link from IS-ISP subdev to FIMC */
1019 sink = &fmd->fimc[i]->vid_cap.subdev.entity;
1020 ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
1021 sink, FIMC_SD_PAD_SINK_FIFO, 0);
1022 if (ret)
1023 return ret;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001024 }
1025
1026 return ret;
1027}
1028
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001029/**
1030 * fimc_md_create_links - create default links between registered entities
1031 *
1032 * Parallel interface sensor entities are connected directly to FIMC capture
1033 * entities. The sensors using MIPI CSIS bus are connected through immutable
1034 * link with CSI receiver entity specified by mux_id. Any registered CSIS
1035 * entity has a link to each registered FIMC capture entity. Enabled links
1036 * are created by default between each subsequent registered sensor and
1037 * subsequent FIMC capture entity. The number of default active links is
1038 * determined by the number of available sensors or FIMC entities,
1039 * whichever is less.
1040 */
1041static int fimc_md_create_links(struct fimc_md *fmd)
1042{
Sylwester Nawrockia8697ec2012-12-05 13:40:04 -03001043 struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL };
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001044 struct v4l2_subdev *sensor, *csis;
Sylwester Nawrocki56bc9112013-02-01 15:00:40 -03001045 struct fimc_source_info *pdata;
Sylwester Nawrocki237e0262011-08-24 20:35:30 -03001046 struct media_entity *source, *sink;
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -03001047 int i, pad, fimc_id = 0, ret = 0;
1048 u32 flags, link_mask = 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001049
1050 for (i = 0; i < fmd->num_sensors; i++) {
1051 if (fmd->sensor[i].subdev == NULL)
1052 continue;
1053
1054 sensor = fmd->sensor[i].subdev;
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001055 pdata = v4l2_get_subdev_hostdata(sensor);
1056 if (!pdata)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001057 continue;
1058
1059 source = NULL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001060
Sylwester Nawrocki56bc9112013-02-01 15:00:40 -03001061 switch (pdata->sensor_bus_type) {
1062 case FIMC_BUS_TYPE_MIPI_CSI2:
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001063 if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
1064 "Wrong CSI channel id: %d\n", pdata->mux_id))
1065 return -EINVAL;
1066
1067 csis = fmd->csis[pdata->mux_id].sd;
1068 if (WARN(csis == NULL,
1069 "MIPI-CSI interface specified "
1070 "but s5p-csis module is not loaded!\n"))
Sylwester Nawrockid12392e2011-10-20 05:15:59 -03001071 return -EINVAL;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001072
Andrzej Hajda1c9f5bd2012-11-22 12:13:27 -03001073 pad = sensor->entity.num_pads - 1;
1074 ret = media_entity_create_link(&sensor->entity, pad,
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001075 &csis->entity, CSIS_PAD_SINK,
1076 MEDIA_LNK_FL_IMMUTABLE |
1077 MEDIA_LNK_FL_ENABLED);
1078 if (ret)
1079 return ret;
1080
Sylwester Nawrocki969e8772012-12-07 16:40:08 -03001081 v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n",
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001082 sensor->entity.name, csis->entity.name);
1083
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001084 source = NULL;
Sylwester Nawrocki5d33ee92012-08-16 14:06:41 -03001085 csi_sensors[pdata->mux_id] = sensor;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001086 break;
1087
Sylwester Nawrocki56bc9112013-02-01 15:00:40 -03001088 case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656:
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001089 source = &sensor->entity;
1090 pad = 0;
1091 break;
1092
1093 default:
1094 v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
Sylwester Nawrocki56bc9112013-02-01 15:00:40 -03001095 pdata->sensor_bus_type);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001096 return -EINVAL;
1097 }
1098 if (source == NULL)
1099 continue;
1100
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -03001101 link_mask = 1 << fimc_id++;
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001102 ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -03001103 pad, link_mask);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001104 }
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001105
Sylwester Nawrockia8697ec2012-12-05 13:40:04 -03001106 for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001107 if (fmd->csis[i].sd == NULL)
1108 continue;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -03001109
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001110 source = &fmd->csis[i].sd->entity;
1111 pad = CSIS_PAD_SOURCE;
Sylwester Nawrocki5d33ee92012-08-16 14:06:41 -03001112 sensor = csi_sensors[i];
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001113
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -03001114 link_mask = 1 << fimc_id++;
Sylwester Nawrocki5d33ee92012-08-16 14:06:41 -03001115 ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
Sylwester Nawrockid0da3c32012-05-16 13:08:30 -03001116 pad, link_mask);
Sylwester Nawrocki4af81312012-04-27 05:29:05 -03001117 }
1118
Sylwester Nawrocki237e0262011-08-24 20:35:30 -03001119 /* Create immutable links between each FIMC's subdev and video node */
1120 flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
1121 for (i = 0; i < FIMC_MAX_DEVS; i++) {
1122 if (!fmd->fimc[i])
1123 continue;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -03001124
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001125 source = &fmd->fimc[i]->vid_cap.subdev.entity;
Sylwester Nawrockibc7584b2013-05-31 11:37:18 -03001126 sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity;
Sylwester Nawrockif998bb72012-11-27 13:29:48 -03001127
Sylwester Nawrocki237e0262011-08-24 20:35:30 -03001128 ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
1129 sink, 0, flags);
1130 if (ret)
1131 break;
1132 }
1133
Sylwester Nawrockif998bb72012-11-27 13:29:48 -03001134 ret = __fimc_md_create_flite_source_links(fmd);
1135 if (ret < 0)
1136 return ret;
1137
1138 if (fmd->use_isp)
1139 ret = __fimc_md_create_fimc_is_links(fmd);
1140
1141 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001142}
1143
1144/*
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -03001145 * The peripheral sensor and CAM_BLK (PIXELASYNCMx) clocks management.
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001146 */
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001147static void fimc_md_put_clocks(struct fimc_md *fmd)
1148{
1149 int i = FIMC_MAX_CAMCLKS;
1150
1151 while (--i >= 0) {
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001152 if (IS_ERR(fmd->camclk[i].clock))
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001153 continue;
1154 clk_put(fmd->camclk[i].clock);
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001155 fmd->camclk[i].clock = ERR_PTR(-EINVAL);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001156 }
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -03001157
1158 /* Writeback (PIXELASYNCMx) clocks */
1159 for (i = 0; i < FIMC_MAX_WBCLKS; i++) {
1160 if (IS_ERR(fmd->wbclk[i]))
1161 continue;
1162 clk_put(fmd->wbclk[i]);
1163 fmd->wbclk[i] = ERR_PTR(-EINVAL);
1164 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001165}
1166
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001167static int fimc_md_get_clocks(struct fimc_md *fmd)
1168{
1169 struct device *dev = NULL;
1170 char clk_name[32];
1171 struct clk *clock;
Sylwester Nawrocki044c3722013-07-19 10:05:17 -03001172 int i, ret = 0;
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001173
1174 for (i = 0; i < FIMC_MAX_CAMCLKS; i++)
1175 fmd->camclk[i].clock = ERR_PTR(-EINVAL);
1176
1177 if (fmd->pdev->dev.of_node)
1178 dev = &fmd->pdev->dev;
1179
1180 for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
1181 snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
1182 clock = clk_get(dev, clk_name);
1183
1184 if (IS_ERR(clock)) {
1185 dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n",
1186 clk_name);
1187 ret = PTR_ERR(clock);
1188 break;
1189 }
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001190 fmd->camclk[i].clock = clock;
1191 }
1192 if (ret)
1193 fimc_md_put_clocks(fmd);
1194
Sylwester Nawrocki056f4f32013-03-21 14:43:17 -03001195 if (!fmd->use_isp)
1196 return 0;
1197 /*
1198 * For now get only PIXELASYNCM1 clock (Writeback B/ISP),
1199 * leave PIXELASYNCM0 out for the LCD Writeback driver.
1200 */
1201 fmd->wbclk[CLK_IDX_WB_A] = ERR_PTR(-EINVAL);
1202
1203 for (i = CLK_IDX_WB_B; i < FIMC_MAX_WBCLKS; i++) {
1204 snprintf(clk_name, sizeof(clk_name), "pxl_async%u", i);
1205 clock = clk_get(dev, clk_name);
1206 if (IS_ERR(clock)) {
1207 v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s\n",
1208 clk_name);
1209 ret = PTR_ERR(clock);
1210 break;
1211 }
1212 fmd->wbclk[i] = clock;
1213 }
1214 if (ret)
1215 fimc_md_put_clocks(fmd);
1216
Sylwester Nawrocki0e23cbb2013-01-18 15:34:37 -03001217 return ret;
1218}
1219
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001220static int __fimc_md_set_camclk(struct fimc_md *fmd,
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001221 struct fimc_source_info *si,
Sylwester Nawrockie3fc82e2012-05-17 14:22:10 -03001222 bool on)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001223{
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001224 struct fimc_camclk_info *camclk;
1225 int ret = 0;
1226
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001227 if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001228 return -EINVAL;
1229
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001230 camclk = &fmd->camclk[si->clk_id];
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001231
Sylwester Nawrockie3fc82e2012-05-17 14:22:10 -03001232 dbg("camclk %d, f: %lu, use_count: %d, on: %d",
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001233 si->clk_id, si->clk_frequency, camclk->use_count, on);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001234
1235 if (on) {
1236 if (camclk->use_count > 0 &&
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001237 camclk->frequency != si->clk_frequency)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001238 return -EINVAL;
1239
1240 if (camclk->use_count++ == 0) {
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001241 clk_set_rate(camclk->clock, si->clk_frequency);
1242 camclk->frequency = si->clk_frequency;
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -03001243 ret = pm_runtime_get_sync(fmd->pmf);
1244 if (ret < 0)
1245 return ret;
Sylwester Nawrocki044c3722013-07-19 10:05:17 -03001246 ret = clk_prepare_enable(camclk->clock);
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001247 dbg("Enabled camclk %d: f: %lu", si->clk_id,
Sylwester Nawrockie3fc82e2012-05-17 14:22:10 -03001248 clk_get_rate(camclk->clock));
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001249 }
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001250 return ret;
1251 }
1252
1253 if (WARN_ON(camclk->use_count == 0))
1254 return 0;
1255
1256 if (--camclk->use_count == 0) {
Sylwester Nawrocki044c3722013-07-19 10:05:17 -03001257 clk_disable_unprepare(camclk->clock);
Sylwester Nawrocki3e20c342013-03-21 14:47:17 -03001258 pm_runtime_put(fmd->pmf);
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001259 dbg("Disabled camclk %d", si->clk_id);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001260 }
1261 return ret;
1262}
1263
1264/**
1265 * fimc_md_set_camclk - peripheral sensor clock setup
1266 * @sd: sensor subdev to configure sclk_cam clock for
1267 * @on: 1 to enable or 0 to disable the clock
1268 *
1269 * There are 2 separate clock outputs available in the SoC for external
1270 * image processors. These clocks are shared between all registered FIMC
1271 * devices to which sensors can be attached, either directly or through
1272 * the MIPI CSI receiver. The clock is allowed here to be used by
1273 * multiple sensors concurrently if they use same frequency.
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001274 * This function should only be called when the graph mutex is held.
1275 */
1276int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
1277{
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001278 struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001279 struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
1280
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001281 /*
1282 * If there is a clock provider registered the sensors will
1283 * handle their clock themselves, no need to control it on
1284 * the host interface side.
1285 */
1286 if (fmd->clk_provider.num_clocks > 0)
1287 return 0;
1288
Sylwester Nawrocki4c8f0622013-04-09 11:11:58 -03001289 return __fimc_md_set_camclk(fmd, si, on);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001290}
1291
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001292static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001293{
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -03001294 struct exynos_video_entity *ve;
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001295 struct fimc_pipeline *p;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -03001296 struct video_device *vdev;
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001297 int ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001298
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001299 vdev = media_entity_to_video_device(entity);
1300 if (vdev->entity.use_count == 0)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001301 return 0;
1302
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -03001303 ve = vdev_to_exynos_video_entity(vdev);
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001304 p = to_fimc_pipeline(ve->pipe);
1305 /*
1306 * Nothing to do if we are disabling the pipeline, some link
1307 * has been disconnected and p->subdevs array is cleared now.
1308 */
1309 if (!enable && p->subdevs[IDX_SENSOR] == NULL)
1310 return 0;
Sylwester Nawrocki0f735f52012-04-27 09:33:10 -03001311
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001312 if (enable)
1313 ret = __fimc_pipeline_open(ve->pipe, entity, true);
1314 else
1315 ret = __fimc_pipeline_close(ve->pipe);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001316
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001317 if (ret == 0 && !enable)
1318 memset(p->subdevs, 0, sizeof(p->subdevs));
1319
1320 return ret;
1321}
1322
1323/* Locking: called with entity->parent->graph_mutex mutex held. */
1324static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
1325{
1326 struct media_entity *entity_err = entity;
1327 struct media_entity_graph graph;
1328 int ret;
1329
1330 /*
1331 * Walk current graph and call the pipeline open/close routine for each
1332 * opened video node that belongs to the graph of entities connected
1333 * through active links. This is needed as we cannot power on/off the
1334 * subdevs in random order.
1335 */
1336 media_entity_graph_walk_start(&graph, entity);
1337
1338 while ((entity = media_entity_graph_walk_next(&graph))) {
1339 if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
1340 continue;
1341
1342 ret = __fimc_md_modify_pipeline(entity, enable);
1343
1344 if (ret < 0)
1345 goto err;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001346 }
Sylwester Nawrocki740ad922012-12-06 10:26:19 -03001347
Sylwester Nawrockid3775fa2013-05-31 10:37:27 -03001348 return 0;
1349 err:
1350 media_entity_graph_walk_start(&graph, entity_err);
1351
1352 while ((entity_err = media_entity_graph_walk_next(&graph))) {
1353 if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE)
1354 continue;
1355
1356 __fimc_md_modify_pipeline(entity_err, !enable);
1357
1358 if (entity_err == entity)
1359 break;
1360 }
1361
1362 return ret;
1363}
1364
1365static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
1366 unsigned int notification)
1367{
1368 struct media_entity *sink = link->sink->entity;
1369 int ret = 0;
1370
1371 /* Before link disconnection */
1372 if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
1373 if (!(flags & MEDIA_LNK_FL_ENABLED))
1374 ret = __fimc_md_modify_pipelines(sink, false);
1375 else
1376 ; /* TODO: Link state change validation */
1377 /* After link activation */
1378 } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
1379 (link->flags & MEDIA_LNK_FL_ENABLED)) {
1380 ret = __fimc_md_modify_pipelines(sink, true);
1381 }
1382
1383 return ret ? -EPIPE : 0;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001384}
1385
1386static ssize_t fimc_md_sysfs_show(struct device *dev,
1387 struct device_attribute *attr, char *buf)
1388{
1389 struct platform_device *pdev = to_platform_device(dev);
1390 struct fimc_md *fmd = platform_get_drvdata(pdev);
1391
1392 if (fmd->user_subdev_api)
1393 return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
1394
1395 return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
1396}
1397
1398static ssize_t fimc_md_sysfs_store(struct device *dev,
1399 struct device_attribute *attr,
1400 const char *buf, size_t count)
1401{
1402 struct platform_device *pdev = to_platform_device(dev);
1403 struct fimc_md *fmd = platform_get_drvdata(pdev);
1404 bool subdev_api;
1405 int i;
1406
1407 if (!strcmp(buf, "vid-dev\n"))
1408 subdev_api = false;
1409 else if (!strcmp(buf, "sub-dev\n"))
1410 subdev_api = true;
1411 else
1412 return count;
1413
1414 fmd->user_subdev_api = subdev_api;
1415 for (i = 0; i < FIMC_MAX_DEVS; i++)
1416 if (fmd->fimc[i])
1417 fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
1418 return count;
1419}
1420/*
1421 * This device attribute is to select video pipeline configuration method.
1422 * There are following valid values:
1423 * vid-dev - for V4L2 video node API only, subdevice will be configured
1424 * by the host driver.
1425 * sub-dev - for media controller API, subdevs must be configured in user
1426 * space before starting streaming.
1427 */
1428static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
1429 fimc_md_sysfs_show, fimc_md_sysfs_store);
1430
Sylwester Nawrocki41638512013-03-08 10:41:47 -03001431static int fimc_md_get_pinctrl(struct fimc_md *fmd)
1432{
1433 struct device *dev = &fmd->pdev->dev;
1434 struct fimc_pinctrl *pctl = &fmd->pinctl;
1435
1436 pctl->pinctrl = devm_pinctrl_get(dev);
1437 if (IS_ERR(pctl->pinctrl))
1438 return PTR_ERR(pctl->pinctrl);
1439
1440 pctl->state_default = pinctrl_lookup_state(pctl->pinctrl,
1441 PINCTRL_STATE_DEFAULT);
1442 if (IS_ERR(pctl->state_default))
1443 return PTR_ERR(pctl->state_default);
1444
1445 pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl,
1446 PINCTRL_STATE_IDLE);
1447 return 0;
1448}
1449
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001450#ifdef CONFIG_OF
1451static int cam_clk_prepare(struct clk_hw *hw)
1452{
1453 struct cam_clk *camclk = to_cam_clk(hw);
1454 int ret;
1455
1456 if (camclk->fmd->pmf == NULL)
1457 return -ENODEV;
1458
1459 ret = pm_runtime_get_sync(camclk->fmd->pmf);
1460 return ret < 0 ? ret : 0;
1461}
1462
1463static void cam_clk_unprepare(struct clk_hw *hw)
1464{
1465 struct cam_clk *camclk = to_cam_clk(hw);
1466
1467 if (camclk->fmd->pmf == NULL)
1468 return;
1469
1470 pm_runtime_put_sync(camclk->fmd->pmf);
1471}
1472
1473static const struct clk_ops cam_clk_ops = {
1474 .prepare = cam_clk_prepare,
1475 .unprepare = cam_clk_unprepare,
1476};
1477
1478static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
1479{
1480 struct cam_clk_provider *cp = &fmd->clk_provider;
1481 unsigned int i;
1482
1483 if (cp->of_node)
1484 of_clk_del_provider(cp->of_node);
1485
1486 for (i = 0; i < cp->num_clocks; i++)
1487 clk_unregister(cp->clks[i]);
1488}
1489
1490static int fimc_md_register_clk_provider(struct fimc_md *fmd)
1491{
1492 struct cam_clk_provider *cp = &fmd->clk_provider;
1493 struct device *dev = &fmd->pdev->dev;
1494 int i, ret;
1495
1496 for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
1497 struct cam_clk *camclk = &cp->camclk[i];
1498 struct clk_init_data init;
1499 const char *p_name;
1500
1501 ret = of_property_read_string_index(dev->of_node,
1502 "clock-output-names", i, &init.name);
1503 if (ret < 0)
1504 break;
1505
1506 p_name = __clk_get_name(fmd->camclk[i].clock);
1507
1508 /* It's safe since clk_register() will duplicate the string. */
1509 init.parent_names = &p_name;
1510 init.num_parents = 1;
1511 init.ops = &cam_clk_ops;
1512 init.flags = CLK_SET_RATE_PARENT;
1513 camclk->hw.init = &init;
1514 camclk->fmd = fmd;
1515
1516 cp->clks[i] = clk_register(NULL, &camclk->hw);
1517 if (IS_ERR(cp->clks[i])) {
1518 dev_err(dev, "failed to register clock: %s (%ld)\n",
1519 init.name, PTR_ERR(cp->clks[i]));
1520 ret = PTR_ERR(cp->clks[i]);
1521 goto err;
1522 }
1523 cp->num_clocks++;
1524 }
1525
1526 if (cp->num_clocks == 0) {
1527 dev_warn(dev, "clk provider not registered\n");
1528 return 0;
1529 }
1530
1531 cp->clk_data.clks = cp->clks;
1532 cp->clk_data.clk_num = cp->num_clocks;
1533 cp->of_node = dev->of_node;
1534 ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
1535 &cp->clk_data);
1536 if (ret == 0)
1537 return 0;
1538err:
1539 fimc_md_unregister_clk_provider(fmd);
1540 return ret;
1541}
1542#else
1543#define fimc_md_register_clk_provider(fmd) (0)
1544#define fimc_md_unregister_clk_provider(fmd) (0)
1545#endif
1546
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -03001547static int fimc_md_probe(struct platform_device *pdev)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001548{
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001549 struct device *dev = &pdev->dev;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001550 struct v4l2_device *v4l2_dev;
1551 struct fimc_md *fmd;
1552 int ret;
1553
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001554 fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001555 if (!fmd)
1556 return -ENOMEM;
1557
1558 spin_lock_init(&fmd->slock);
1559 fmd->pdev = pdev;
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -03001560 INIT_LIST_HEAD(&fmd->pipelines);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001561
1562 strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
1563 sizeof(fmd->media_dev.model));
1564 fmd->media_dev.link_notify = fimc_md_link_notify;
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001565 fmd->media_dev.dev = dev;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001566
1567 v4l2_dev = &fmd->v4l2_dev;
1568 v4l2_dev->mdev = &fmd->media_dev;
Sylwester Nawrockie1d72f42011-06-10 15:36:58 -03001569 v4l2_dev->notify = fimc_sensor_notify;
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001570 strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name));
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001571
Sylwester Nawrockie781bbe32013-03-21 14:49:16 -03001572 fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
1573
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001574 ret = fimc_md_register_clk_provider(fmd);
1575 if (ret < 0) {
1576 v4l2_err(v4l2_dev, "clock provider registration failed\n");
1577 return ret;
1578 }
1579
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001580 ret = v4l2_device_register(dev, &fmd->v4l2_dev);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001581 if (ret < 0) {
1582 v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
Sylwester Nawrocki6d91a512012-01-30 11:37:59 -03001583 return ret;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001584 }
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001585
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001586 ret = media_device_register(&fmd->media_dev);
1587 if (ret < 0) {
1588 v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001589 goto err_md;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001590 }
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001591
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001592 ret = fimc_md_get_clocks(fmd);
1593 if (ret)
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001594 goto err_clk;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001595
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001596 fmd->user_subdev_api = (dev->of_node != NULL);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001597
1598 /* Protect the media graph while we're registering entities */
1599 mutex_lock(&fmd->media_dev.graph_mutex);
1600
Sylwester Nawrocki41638512013-03-08 10:41:47 -03001601 ret = fimc_md_get_pinctrl(fmd);
1602 if (ret < 0) {
1603 if (ret != EPROBE_DEFER)
1604 dev_err(dev, "Failed to get pinctrl: %d\n", ret);
1605 goto err_unlock;
1606 }
1607
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001608 if (dev->of_node)
1609 ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
1610 else
1611 ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
1612 fimc_md_pdev_match);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001613 if (ret)
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001614 goto err_unlock;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001615
Sylwester Nawrocki2b13f7d2013-03-29 14:12:39 -03001616 if (dev->platform_data || dev->of_node) {
Sylwester Nawrocki5cbf6f162011-10-07 07:40:00 -03001617 ret = fimc_md_register_sensor_entities(fmd);
1618 if (ret)
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001619 goto err_unlock;
Sylwester Nawrocki5cbf6f162011-10-07 07:40:00 -03001620 }
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001621
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001622 ret = fimc_md_create_links(fmd);
1623 if (ret)
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001624 goto err_unlock;
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001625
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001626 ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
1627 if (ret)
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001628 goto err_unlock;
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001629
1630 ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001631 if (ret)
1632 goto err_unlock;
1633
1634 platform_set_drvdata(pdev, fmd);
1635 mutex_unlock(&fmd->media_dev.graph_mutex);
1636 return 0;
1637
1638err_unlock:
1639 mutex_unlock(&fmd->media_dev.graph_mutex);
1640err_clk:
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001641 fimc_md_put_clocks(fmd);
1642 fimc_md_unregister_entities(fmd);
Sylwester Nawrockid2b903b2013-07-29 06:53:59 -03001643 media_device_unregister(&fmd->media_dev);
Sylwester Nawrocki693f5c42012-04-20 18:57:25 -03001644err_md:
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001645 v4l2_device_unregister(&fmd->v4l2_dev);
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001646 fimc_md_unregister_clk_provider(fmd);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001647 return ret;
1648}
1649
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -08001650static int fimc_md_remove(struct platform_device *pdev)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001651{
1652 struct fimc_md *fmd = platform_get_drvdata(pdev);
1653
1654 if (!fmd)
1655 return 0;
Sylwester Nawrockib74bee12013-08-09 14:23:53 -03001656
Sylwester Nawrockid3f5e0c2013-12-20 18:53:53 -03001657 fimc_md_unregister_clk_provider(fmd);
Sylwester Nawrockib74bee12013-08-09 14:23:53 -03001658 v4l2_device_unregister(&fmd->v4l2_dev);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001659 device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
1660 fimc_md_unregister_entities(fmd);
Sylwester Nawrocki403dfbe2013-05-31 11:37:22 -03001661 fimc_md_pipelines_free(fmd);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001662 media_device_unregister(&fmd->media_dev);
1663 fimc_md_put_clocks(fmd);
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001664 return 0;
1665}
1666
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001667static struct platform_device_id fimc_driver_ids[] __always_unused = {
1668 { .name = "s5p-fimc-md" },
1669 { },
1670};
1671MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
1672
1673static const struct of_device_id fimc_md_of_match[] = {
1674 { .compatible = "samsung,fimc" },
1675 { },
1676};
1677MODULE_DEVICE_TABLE(of, fimc_md_of_match);
1678
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001679static struct platform_driver fimc_md_driver = {
1680 .probe = fimc_md_probe,
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -08001681 .remove = fimc_md_remove,
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001682 .driver = {
Sylwester Nawrockie2985a22013-03-08 12:59:33 -03001683 .of_match_table = of_match_ptr(fimc_md_of_match),
1684 .name = "s5p-fimc-md",
1685 .owner = THIS_MODULE,
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001686 }
1687};
1688
Sachin Kamat7e566be2012-05-26 11:11:54 -03001689static int __init fimc_md_init(void)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001690{
1691 int ret;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -03001692
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001693 request_module("s5p-csis");
1694 ret = fimc_register_driver();
1695 if (ret)
1696 return ret;
Sylwester Nawrockiecd9acb2012-03-21 09:58:09 -03001697
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001698 return platform_driver_register(&fimc_md_driver);
1699}
Sachin Kamat7e566be2012-05-26 11:11:54 -03001700
1701static void __exit fimc_md_exit(void)
Sylwester Nawrockid3953222011-09-01 06:01:08 -03001702{
1703 platform_driver_unregister(&fimc_md_driver);
1704 fimc_unregister_driver();
1705}
1706
1707module_init(fimc_md_init);
1708module_exit(fimc_md_exit);
1709
1710MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
1711MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
1712MODULE_LICENSE("GPL");
1713MODULE_VERSION("2.0.1");