diff options
-rw-r--r-- | Documentation/devicetree/bindings/media/samsung-fimc.txt | 86 | ||||
-rw-r--r-- | drivers/media/platform/s5p-fimc/fimc-mdevice.c | 240 | ||||
-rw-r--r-- | include/media/s5p_fimc.h | 3 |
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 | |||
78 | This node should contain child 'port' nodes specifying active parallel video | ||
79 | input ports. It includes camera A and camera B inputs. 'reg' property in the | ||
80 | port nodes specifies data input - 0, 1 indicates input A, B respectively. | ||
81 | |||
82 | Optional properties | ||
83 | |||
84 | - samsung,camclk-out : specifies clock output for remote sensor, | ||
85 | 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT; | ||
86 | |||
87 | Image sensor nodes | ||
88 | ------------------ | ||
89 | |||
90 | The sensor device nodes should be added to their control bus controller (e.g. | ||
91 | I2C0) nodes and linked to a port node in the csis or the parallel-ports node, | ||
92 | using the common video interfaces bindings, defined in video-interfaces.txt. | ||
93 | The implementation of this bindings requires clock-frequency property to be | ||
94 | present in the sensor device nodes. | ||
95 | |||
75 | Example: | 96 | Example: |
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. */ | ||
277 | static 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 | |||
321 | mod_put: | ||
322 | module_put(client->driver->driver.owner); | ||
323 | dev_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. */ | ||
330 | static 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 */ | ||
411 | static 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 | |||
273 | static int __of_get_csis_id(struct device_node *np) | 451 | static 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 | ||
287 | static int fimc_md_register_sensor_entities(struct fimc_md *fmd) | 466 | static 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 | |||
48 | struct i2c_board_info; | 51 | struct i2c_board_info; |
49 | 52 | ||
50 | /** | 53 | /** |