aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/media/samsung-fimc.txt86
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.c240
-rw-r--r--include/media/s5p_fimc.h3
3 files changed, 303 insertions, 26 deletions
diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 22e2889162c3..bcb0de7462a2 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -72,18 +72,95 @@ Optional properties:
72 writeback input. 72 writeback input.
73 73
74 74
75'parallel-ports' node
76---------------------
77
78This node should contain child 'port' nodes specifying active parallel video
79input ports. It includes camera A and camera B inputs. 'reg' property in the
80port nodes specifies data input - 0, 1 indicates input A, B respectively.
81
82Optional properties
83
84- samsung,camclk-out : specifies clock output for remote sensor,
85 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT;
86
87Image sensor nodes
88------------------
89
90The sensor device nodes should be added to their control bus controller (e.g.
91I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
92using the common video interfaces bindings, defined in video-interfaces.txt.
93The implementation of this bindings requires clock-frequency property to be
94present in the sensor device nodes.
95
75Example: 96Example:
76 97
77 aliases { 98 aliases {
78 fimc0 = &fimc_0; 99 fimc0 = &fimc_0;
79 }; 100 };
80 101
102 /* Parallel bus IF sensor */
103 i2c_0: i2c@13860000 {
104 s5k6aa: sensor@3c {
105 compatible = "samsung,s5k6aafx";
106 reg = <0x3c>;
107 vddio-supply = <...>;
108
109 clock-frequency = <24000000>;
110 clocks = <...>;
111 clock-names = "mclk";
112
113 port {
114 s5k6aa_ep: endpoint {
115 remote-endpoint = <&fimc0_ep>;
116 bus-width = <8>;
117 hsync-active = <0>;
118 vsync-active = <1>;
119 pclk-sample = <1>;
120 };
121 };
122 };
123 };
124
125 /* MIPI CSI-2 bus IF sensor */
126 s5c73m3: sensor@0x1a {
127 compatible = "samsung,s5c73m3";
128 reg = <0x1a>;
129 vddio-supply = <...>;
130
131 clock-frequency = <24000000>;
132 clocks = <...>;
133 clock-names = "mclk";
134
135 port {
136 s5c73m3_1: endpoint {
137 data-lanes = <1 2 3 4>;
138 remote-endpoint = <&csis0_ep>;
139 };
140 };
141 };
142
81 camera { 143 camera {
82 compatible = "samsung,fimc", "simple-bus"; 144 compatible = "samsung,fimc", "simple-bus";
83 #address-cells = <1>; 145 #address-cells = <1>;
84 #size-cells = <1>; 146 #size-cells = <1>;
85 status = "okay"; 147 status = "okay";
86 148
149 /* parallel camera ports */
150 parallel-ports {
151 /* camera A input */
152 port@0 {
153 reg = <0>;
154 fimc0_ep: endpoint {
155 remote-endpoint = <&s5k6aa_ep>;
156 bus-width = <8>;
157 hsync-active = <0>;
158 vsync-active = <1>;
159 pclk-sample = <1>;
160 };
161 };
162 };
163
87 fimc_0: fimc@11800000 { 164 fimc_0: fimc@11800000 {
88 compatible = "samsung,exynos4210-fimc"; 165 compatible = "samsung,exynos4210-fimc";
89 reg = <0x11800000 0x1000>; 166 reg = <0x11800000 0x1000>;
@@ -95,6 +172,15 @@ Example:
95 compatible = "samsung,exynos4210-csis"; 172 compatible = "samsung,exynos4210-csis";
96 reg = <0x11880000 0x1000>; 173 reg = <0x11880000 0x1000>;
97 interrupts = <0 78 0>; 174 interrupts = <0 78 0>;
175 /* camera C input */
176 port@3 {
177 reg = <3>;
178 csis0_ep: endpoint {
179 remote-endpoint = <&s5c73m3_ep>;
180 data-lanes = <1 2 3 4>;
181 samsung,csis-hs-settle = <12>;
182 };
183 };
98 }; 184 };
99 }; 185 };
100 186
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
index d34106c66427..bfa02abd94c8 100644
--- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
@@ -251,7 +251,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
251 sd->grp_id = GRP_ID_SENSOR; 251 sd->grp_id = GRP_ID_SENSOR;
252 252
253 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", 253 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
254 s_info->pdata.board_info->type); 254 sd->name);
255 return sd; 255 return sd;
256} 256}
257 257
@@ -263,13 +263,191 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
263 if (!client) 263 if (!client)
264 return; 264 return;
265 v4l2_device_unregister_subdev(sd); 265 v4l2_device_unregister_subdev(sd);
266 adapter = client->adapter; 266
267 i2c_unregister_device(client); 267 if (!client->dev.of_node) {
268 if (adapter) 268 adapter = client->adapter;
269 i2c_put_adapter(adapter); 269 i2c_unregister_device(client);
270 if (adapter)
271 i2c_put_adapter(adapter);
272 }
270} 273}
271 274
272#ifdef CONFIG_OF 275#ifdef CONFIG_OF
276/* Register I2C client subdev associated with @node. */
277static int fimc_md_of_add_sensor(struct fimc_md *fmd,
278 struct device_node *node, int index)
279{
280 struct fimc_sensor_info *si;
281 struct i2c_client *client;
282 struct v4l2_subdev *sd;
283 int ret;
284
285 if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
286 return -EINVAL;
287 si = &fmd->sensor[index];
288
289 client = of_find_i2c_device_by_node(node);
290 if (!client)
291 return -EPROBE_DEFER;
292
293 device_lock(&client->dev);
294
295 if (!client->driver ||
296 !try_module_get(client->driver->driver.owner)) {
297 ret = -EPROBE_DEFER;
298 v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
299 node->full_name);
300 goto dev_put;
301 }
302
303 /* Enable sensor's master clock */
304 ret = __fimc_md_set_camclk(fmd, si, true);
305 if (ret < 0)
306 goto mod_put;
307 sd = i2c_get_clientdata(client);
308
309 ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
310 __fimc_md_set_camclk(fmd, si, false);
311 if (ret < 0)
312 goto mod_put;
313
314 v4l2_set_subdev_hostdata(sd, si);
315 sd->grp_id = GRP_ID_SENSOR;
316 si->subdev = sd;
317 v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
318 sd->name, fmd->num_sensors);
319 fmd->num_sensors++;
320
321mod_put:
322 module_put(client->driver->driver.owner);
323dev_put:
324 device_unlock(&client->dev);
325 put_device(&client->dev);
326 return ret;
327}
328
329/* Parse port node and register as a sub-device any sensor specified there. */
330static int fimc_md_parse_port_node(struct fimc_md *fmd,
331 struct device_node *port,
332 unsigned int index)
333{
334 struct device_node *rem, *ep, *np;
335 struct fimc_source_info *pd;
336 struct v4l2_of_endpoint endpoint;
337 int ret;
338 u32 val;
339
340 pd = &fmd->sensor[index].pdata;
341
342 /* Assume here a port node can have only one endpoint node. */
343 ep = of_get_next_child(port, NULL);
344 if (!ep)
345 return 0;
346
347 v4l2_of_parse_endpoint(ep, &endpoint);
348 if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS)
349 return -EINVAL;
350
351 pd->mux_id = (endpoint.port - 1) & 0x1;
352
353 rem = v4l2_of_get_remote_port_parent(ep);
354 of_node_put(ep);
355 if (rem == NULL) {
356 v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",
357 ep->full_name);
358 return 0;
359 }
360 if (!of_property_read_u32(rem, "samsung,camclk-out", &val))
361 pd->clk_id = val;
362
363 if (!of_property_read_u32(rem, "clock-frequency", &val))
364 pd->clk_frequency = val;
365
366 if (pd->clk_frequency == 0) {
367 v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
368 rem->full_name);
369 of_node_put(rem);
370 return -EINVAL;
371 }
372
373 if (fimc_input_is_parallel(endpoint.port)) {
374 if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
375 pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
376 else
377 pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
378 pd->flags = endpoint.bus.parallel.flags;
379 } else if (fimc_input_is_mipi_csi(endpoint.port)) {
380 /*
381 * MIPI CSI-2: only input mux selection and
382 * the sensor's clock frequency is needed.
383 */
384 pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
385 } else {
386 v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
387 endpoint.port, rem->full_name);
388 }
389 /*
390 * For FIMC-IS handled sensors, that are placed under i2c-isp device
391 * node, FIMC is connected to the FIMC-IS through its ISP Writeback
392 * input. Sensors are attached to the FIMC-LITE hostdata interface
393 * directly or through MIPI-CSIS, depending on the external media bus
394 * used. This needs to be handled in a more reliable way, not by just
395 * checking parent's node name.
396 */
397 np = of_get_parent(rem);
398
399 if (np && !of_node_cmp(np->name, "i2c-isp"))
400 pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
401 else
402 pd->fimc_bus_type = pd->sensor_bus_type;
403
404 ret = fimc_md_of_add_sensor(fmd, rem, index);
405 of_node_put(rem);
406
407 return ret;
408}
409
410/* Register all SoC external sub-devices */
411static int fimc_md_of_sensors_register(struct fimc_md *fmd,
412 struct device_node *np)
413{
414 struct device_node *parent = fmd->pdev->dev.of_node;
415 struct device_node *node, *ports;
416 int index = 0;
417 int ret;
418
419 /* Attach sensors linked to MIPI CSI-2 receivers */
420 for_each_available_child_of_node(parent, node) {
421 struct device_node *port;
422
423 if (of_node_cmp(node->name, "csis"))
424 continue;
425 /* The csis node can have only port subnode. */
426 port = of_get_next_child(node, NULL);
427 if (!port)
428 continue;
429
430 ret = fimc_md_parse_port_node(fmd, port, index);
431 if (ret < 0)
432 return ret;
433 index++;
434 }
435
436 /* Attach sensors listed in the parallel-ports node */
437 ports = of_get_child_by_name(parent, "parallel-ports");
438 if (!ports)
439 return 0;
440
441 for_each_child_of_node(ports, node) {
442 ret = fimc_md_parse_port_node(fmd, node, index);
443 if (ret < 0)
444 break;
445 index++;
446 }
447
448 return 0;
449}
450
273static int __of_get_csis_id(struct device_node *np) 451static int __of_get_csis_id(struct device_node *np)
274{ 452{
275 u32 reg = 0; 453 u32 reg = 0;
@@ -281,14 +459,17 @@ static int __of_get_csis_id(struct device_node *np)
281 return reg - FIMC_INPUT_MIPI_CSI2_0; 459 return reg - FIMC_INPUT_MIPI_CSI2_0;
282} 460}
283#else 461#else
462#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS)
284#define __of_get_csis_id(np) (-ENOSYS) 463#define __of_get_csis_id(np) (-ENOSYS)
285#endif 464#endif
286 465
287static int fimc_md_register_sensor_entities(struct fimc_md *fmd) 466static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
288{ 467{
289 struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; 468 struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
469 struct device_node *of_node = fmd->pdev->dev.of_node;
290 struct fimc_dev *fd = NULL; 470 struct fimc_dev *fd = NULL;
291 int num_clients, ret, i; 471 int num_clients = 0;
472 int ret, i;
292 473
293 /* 474 /*
294 * Runtime resume one of the FIMC entities to make sure 475 * Runtime resume one of the FIMC entities to make sure
@@ -299,34 +480,41 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
299 fd = fmd->fimc[i]; 480 fd = fmd->fimc[i];
300 if (!fd) 481 if (!fd)
301 return -ENXIO; 482 return -ENXIO;
483
302 ret = pm_runtime_get_sync(&fd->pdev->dev); 484 ret = pm_runtime_get_sync(&fd->pdev->dev);
303 if (ret < 0) 485 if (ret < 0)
304 return ret; 486 return ret;
305 487
306 WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); 488 if (of_node) {
307 num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); 489 fmd->num_sensors = 0;
490 ret = fimc_md_of_sensors_register(fmd, of_node);
491 } else if (pdata) {
492 WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
493 num_clients = min_t(u32, pdata->num_clients,
494 ARRAY_SIZE(fmd->sensor));
495 fmd->num_sensors = num_clients;
308 496
309 fmd->num_sensors = num_clients; 497 for (i = 0; i < num_clients; i++) {
310 for (i = 0; i < num_clients; i++) { 498 struct v4l2_subdev *sd;
311 struct v4l2_subdev *sd;
312 499
313 fmd->sensor[i].pdata = pdata->source_info[i]; 500 fmd->sensor[i].pdata = pdata->source_info[i];
314 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); 501 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
315 if (ret) 502 if (ret)
316 break; 503 break;
317 sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); 504 sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
318 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); 505 ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
319 506
320 if (!IS_ERR(sd)) { 507 if (IS_ERR(sd)) {
508 fmd->sensor[i].subdev = NULL;
509 ret = PTR_ERR(sd);
510 break;
511 }
321 fmd->sensor[i].subdev = sd; 512 fmd->sensor[i].subdev = sd;
322 } else { 513 if (ret)
323 fmd->sensor[i].subdev = NULL; 514 break;
324 ret = PTR_ERR(sd);
325 break;
326 } 515 }
327 if (ret)
328 break;
329 } 516 }
517
330 pm_runtime_put(&fd->pdev->dev); 518 pm_runtime_put(&fd->pdev->dev);
331 return ret; 519 return ret;
332} 520}
@@ -1037,7 +1225,7 @@ static int fimc_md_probe(struct platform_device *pdev)
1037 if (ret) 1225 if (ret)
1038 goto err_unlock; 1226 goto err_unlock;
1039 1227
1040 if (dev->platform_data) { 1228 if (dev->platform_data || dev->of_node) {
1041 ret = fimc_md_register_sensor_entities(fmd); 1229 ret = fimc_md_register_sensor_entities(fmd);
1042 if (ret) 1230 if (ret)
1043 goto err_unlock; 1231 goto err_unlock;
diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h
index e2c598962698..e2434bb0b308 100644
--- a/include/media/s5p_fimc.h
+++ b/include/media/s5p_fimc.h
@@ -45,6 +45,9 @@ enum fimc_bus_type {
45 FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, 45 FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B,
46}; 46};
47 47
48#define fimc_input_is_parallel(x) ((x) == 1 || (x) == 2)
49#define fimc_input_is_mipi_csi(x) ((x) == 3 || (x) == 4)
50
48struct i2c_board_info; 51struct i2c_board_info;
49 52
50/** 53/**