diff options
Diffstat (limited to 'drivers/media/video/s5p-fimc/fimc-mdevice.c')
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-mdevice.c | 512 |
1 files changed, 343 insertions, 169 deletions
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 62ed37e40149..52cef4865423 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -25,6 +25,7 @@ #include <media/media-device.h> #include "fimc-core.h" +#include "fimc-lite.h" #include "fimc-mdevice.h" #include "mipi-csis.h" @@ -37,22 +38,46 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * * Caller holds the graph mutex. */ -void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me) +void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { - struct media_entity_graph graph; + struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; + int i; - media_entity_graph_walk_start(&graph, me); + for (i = 0; i < IDX_MAX; i++) + p->subdevs[i] = NULL; - while ((me = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV) - continue; - sd = media_entity_to_v4l2_subdev(me); + while (1) { + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); - if (sd->grp_id == SENSOR_GROUP_ID) - fimc->pipeline.sensor = sd; - else if (sd->grp_id == CSIS_GROUP_ID) - fimc->pipeline.csis = sd; + switch (sd->grp_id) { + case SENSOR_GROUP_ID: + p->subdevs[IDX_SENSOR] = sd; + break; + case CSIS_GROUP_ID: + p->subdevs[IDX_CSIS] = sd; + break; + case FLITE_GROUP_ID: + p->subdevs[IDX_FLITE] = sd; + break; + case FIMC_GROUP_ID: + /* No need to control FIMC subdev through subdev ops */ + break; + default: + pr_warn("%s: Unknown subdev grp_id: %#x\n", + __func__, sd->grp_id); + } + /* sink pad */ + pad = &sd->entity.pads[0]; } } @@ -85,30 +110,27 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on) /** * fimc_pipeline_s_power - change power state of all pipeline subdevs * @fimc: fimc device terminating the pipeline - * @state: 1 to enable power or 0 for power down + * @state: true to power on, false to power off * - * Need to be called with the graph mutex held. + * Needs to be called with the graph mutex held. */ -int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) +int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) { - int ret = 0; + unsigned int i; + int ret; - if (fimc->pipeline.sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENXIO; - if (state) { - ret = __subdev_set_power(fimc->pipeline.csis, 1); - if (ret && ret != -ENXIO) + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = state ? (IDX_MAX - 1) - i : i; + + ret = __subdev_set_power(p->subdevs[idx], state); + if (ret < 0 && ret != -ENXIO) return ret; - return __subdev_set_power(fimc->pipeline.sensor, 1); } - ret = __subdev_set_power(fimc->pipeline.sensor, 0); - if (ret) - return ret; - ret = __subdev_set_power(fimc->pipeline.csis, 0); - - return ret == -ENXIO ? 0 : ret; + return 0; } /** @@ -119,32 +141,36 @@ int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) * * This function must be called with the graph mutex held. */ -static int __fimc_pipeline_initialize(struct fimc_dev *fimc, +static int __fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; if (prep) - fimc_pipeline_prepare(fimc, me); - if (fimc->pipeline.sensor == NULL) + fimc_pipeline_prepare(p, me); + + if (p->subdevs[IDX_SENSOR] == NULL) return -EINVAL; - ret = fimc_md_set_camclk(fimc->pipeline.sensor, true); + + ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true); if (ret) return ret; - return fimc_pipeline_s_power(fimc, 1); + + return fimc_pipeline_s_power(p, 1); } -int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, +int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_initialize(fimc, me, prep); + ret = __fimc_pipeline_initialize(p, me, prep); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_initialize); /** * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power @@ -154,52 +180,59 @@ int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, * sensor clock. * Called with the graph mutex held. */ -int __fimc_pipeline_shutdown(struct fimc_dev *fimc) +int __fimc_pipeline_shutdown(struct fimc_pipeline *p) { int ret = 0; - if (fimc->pipeline.sensor) { - ret = fimc_pipeline_s_power(fimc, 0); - fimc_md_set_camclk(fimc->pipeline.sensor, false); + if (p->subdevs[IDX_SENSOR]) { + ret = fimc_pipeline_s_power(p, 0); + fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); } return ret == -ENXIO ? 0 : ret; } -int fimc_pipeline_shutdown(struct fimc_dev *fimc) +int fimc_pipeline_shutdown(struct fimc_pipeline *p) { - struct media_entity *me = &fimc->vid_cap.vfd->entity; + struct media_entity *me; int ret; + if (!p || !p->subdevs[IDX_SENSOR]) + return -EINVAL; + + me = &p->subdevs[IDX_SENSOR]->entity; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_shutdown(fimc); + ret = __fimc_pipeline_shutdown(p); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_shutdown); /** * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs - * @fimc: fimc device terminating the pipeline + * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on) +int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { - struct fimc_pipeline *p = &fimc->pipeline; - int ret = 0; + int i, ret; - if (p->sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; - if ((on && p->csis) || !on) - ret = v4l2_subdev_call(on ? p->csis : p->sensor, - video, s_stream, on); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - if ((!on && p->csis) || on) - ret = v4l2_subdev_call(on ? p->sensor : p->csis, - video, s_stream, on); - return ret == -ENOIOCTLCMD ? 0 : ret; + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = on ? (IDX_MAX - 1) - i : i; + + ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + } + + return 0; + } +EXPORT_SYMBOL_GPL(fimc_pipeline_s_stream); /* * Sensor subdevice helper functions @@ -214,14 +247,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return NULL; adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); - if (!adapter) - return NULL; + if (!adapter) { + v4l2_warn(&fmd->v4l2_dev, + "Failed to get I2C adapter %d, deferring probe\n", + s_info->pdata->i2c_bus_num); + return ERR_PTR(-EPROBE_DEFER); + } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, s_info->pdata->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); - v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); - return NULL; + v4l2_warn(&fmd->v4l2_dev, + "Failed to acquire subdev %s, deferring probe\n", + s_info->pdata->board_info->type); + return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); sd->grp_id = SENSOR_GROUP_ID; @@ -269,13 +308,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = num_clients; for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; + fmd->sensor[i].pdata = &pdata->isp_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; - fmd->sensor[i].subdev = - fimc_md_register_sensor(fmd, &fmd->sensor[i]); + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (!IS_ERR(sd)) { + fmd->sensor[i].subdev = sd; + } else { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } if (ret) break; } @@ -289,21 +337,50 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) static int fimc_register_callback(struct device *dev, void *p) { struct fimc_dev *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; struct fimc_md *fmd = p; - int ret; + int ret = 0; if (!fimc || !fimc->pdev) return 0; + if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) return 0; fmd->fimc[fimc->pdev->id] = fimc; - ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev); - if (ret) - return ret; - ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev); - if (!ret) - fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + sd->grp_id = FIMC_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", + fimc->id, ret); + } + + return ret; +} + +static int fimc_lite_register_callback(struct device *dev, void *p) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->subdev; + struct fimc_md *fmd = p; + int ret; + + if (fimc == NULL) + return 0; + + if (fimc->index >= FIMC_LITE_MAX_DEVS) + return 0; + + fmd->fimc_lite[fimc->index] = fimc; + sd->grp_id = FLITE_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, + "Failed to register FIMC-LITE.%d (%d)\n", + fimc->index, ret); + } return ret; } @@ -336,22 +413,56 @@ static int csis_register_callback(struct device *dev, void *p) */ static int fimc_md_register_platform_entities(struct fimc_md *fmd) { + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; struct device_driver *driver; - int ret; + int ret, i; driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) - return -ENODEV; + if (!driver) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + FIMC_MODULE_NAME); + return -EPROBE_DEFER; + } + ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); if (ret) return ret; - driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) + driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); + if (driver && try_module_get(driver->owner)) { ret = driver_for_each_device(driver, NULL, fmd, - csis_register_callback); - return ret; + fimc_lite_register_callback); + if (ret) + return ret; + module_put(driver->owner); + } + /* + * Check if there is any sensor on the MIPI-CSI2 bus and + * if not skip the s5p-csis module loading. + */ + if (pdata == NULL) + return 0; + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { + ret = 1; + break; + } + } + if (!ret) + return 0; + + driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); + if (!driver || !try_module_get(driver->owner)) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + CSIS_DRIVER_NAME); + return -EPROBE_DEFER; + } + + return driver_for_each_device(driver, NULL, fmd, + csis_register_callback); } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -361,14 +472,20 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (fmd->fimc[i] == NULL) continue; - fimc_unregister_m2m_device(fmd->fimc[i]); - fimc_unregister_capture_device(fmd->fimc[i]); + v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); fmd->fimc[i] = NULL; } + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (fmd->fimc_lite[i] == NULL) + continue; + v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); + fmd->fimc_lite[i] = NULL; + } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -379,66 +496,37 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) } } -static int fimc_md_register_video_nodes(struct fimc_md *fmd) -{ - struct video_device *vdev; - int i, ret = 0; - - for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) { - if (!fmd->fimc[i]) - continue; - - vdev = fmd->fimc[i]->m2m.vfd; - if (vdev) { - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (ret) - break; - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - vdev = fmd->fimc[i]->vid_cap.vfd; - if (vdev == NULL) - continue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - return ret; -} - /** * __fimc_md_create_fimc_links - create links to all FIMC entities * @fmd: fimc media device * @source: the source entity to create links to all fimc entities from * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null * @pad: the source entity pad index - * @fimc_id: index of the fimc device for which link should be enabled + * @link_mask: bitmask of the fimc devices for which link should be enabled */ -static int __fimc_md_create_fimc_links(struct fimc_md *fmd, - struct media_entity *source, - struct v4l2_subdev *sensor, - int pad, int fimc_id) +static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, + struct media_entity *source, + struct v4l2_subdev *sensor, + int pad, int link_mask) { struct fimc_sensor_info *s_info; struct media_entity *sink; - unsigned int flags; + unsigned int flags = 0; int ret, i; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) - break; + continue; /* * Some FIMC variants are not fitted with camera capture * interface. Skip creating a link from sensor for those. */ - if (sensor->grp_id == SENSOR_GROUP_ID && - !fmd->fimc[i]->variant->has_cam_if) + if (!fmd->fimc[i]->variant->has_cam_if) continue; - flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; - sink = &fmd->fimc[i]->vid_cap.subdev->entity; + flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; + + sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, FIMC_SD_PAD_SINK, flags); if (ret) @@ -453,7 +541,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", source->name, flags ? '=' : '-', sink->name); - if (flags == 0) + if (flags == 0 || sensor == NULL) continue; s_info = v4l2_get_subdev_hostdata(sensor); if (!WARN_ON(s_info == NULL)) { @@ -463,9 +551,58 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, spin_unlock_irqrestore(&fmd->slock, irq_flags); } } + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (!fmd->fimc_lite[i]) + continue; + + if (link_mask & (1 << (i + FIMC_MAX_DEVS))) + flags = MEDIA_LNK_FL_ENABLED; + else + flags = 0; + + sink = &fmd->fimc_lite[i]->subdev.entity; + ret = media_entity_create_link(source, pad, sink, + FLITE_SD_PAD_SINK, flags); + if (ret) + return ret; + + /* Notify FIMC-LITE subdev entity */ + ret = media_entity_call(sink, link_setup, &sink->pads[0], + &source->pads[pad], flags); + if (ret) + break; + + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + source->name, flags ? '=' : '-', sink->name); + } return 0; } +/* Create links from FIMC-LITE source pads to other entities */ +static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) +{ + struct media_entity *source, *sink; + unsigned int flags = MEDIA_LNK_FL_ENABLED; + int i, ret; + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + struct fimc_lite *fimc = fmd->fimc_lite[i]; + if (fimc == NULL) + continue; + source = &fimc->subdev.entity; + sink = &fimc->vfd->entity; + /* FIMC-LITE's subdev and video node */ + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + sink, 0, flags); + if (ret) + break; + /* TODO: create links to other entities */ + } + + return ret; +} + /** * fimc_md_create_links - create default links between registered entities * @@ -484,9 +621,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) struct s5p_fimc_isp_info *pdata; struct fimc_sensor_info *s_info; struct media_entity *source, *sink; - int i, pad, fimc_id = 0; - int ret = 0; - u32 flags; + int i, pad, fimc_id = 0, ret = 0; + u32 flags, link_mask = 0; for (i = 0; i < fmd->num_sensors; i++) { if (fmd->sensor[i].subdev == NULL) @@ -522,8 +658,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", sensor->entity.name, csis->entity.name); - source = &csis->entity; - pad = CSIS_PAD_SOURCE; + source = NULL; break; case FIMC_ITU_601...FIMC_ITU_656: @@ -539,15 +674,28 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (source == NULL) continue; - ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, - fimc_id++); + link_mask = 1 << fimc_id++; + ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, + pad, link_mask); } + + for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + if (fmd->csis[i].sd == NULL) + continue; + source = &fmd->csis[i].sd->entity; + pad = CSIS_PAD_SOURCE; + + link_mask = 1 << fimc_id++; + ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL, + pad, link_mask); + } + /* Create immutable links between each FIMC's subdev and video node */ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; - source = &fmd->fimc[i]->vid_cap.subdev->entity; + source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.vfd->entity; ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); @@ -555,7 +703,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) break; } - return ret; + return __fimc_md_create_flite_source_links(fmd); } /* @@ -593,8 +741,8 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) } static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, - bool on) + struct fimc_sensor_info *s_info, + bool on) { struct s5p_fimc_isp_info *pdata = s_info->pdata; struct fimc_camclk_info *camclk; @@ -603,12 +751,10 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL) return -EINVAL; - if (s_info->clk_on == on) - return 0; camclk = &fmd->camclk[pdata->clk_id]; - dbg("camclk %d, f: %lu, clk: %p, on: %d", - pdata->clk_id, pdata->clk_frequency, camclk, on); + dbg("camclk %d, f: %lu, use_count: %d, on: %d", + pdata->clk_id, pdata->clk_frequency, camclk->use_count, on); if (on) { if (camclk->use_count > 0 && @@ -619,11 +765,9 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, clk_set_rate(camclk->clock, pdata->clk_frequency); camclk->frequency = pdata->clk_frequency; ret = clk_enable(camclk->clock); + dbg("Enabled camclk %d: f: %lu", pdata->clk_id, + clk_get_rate(camclk->clock)); } - s_info->clk_on = 1; - dbg("Enabled camclk %d: f: %lu", pdata->clk_id, - clk_get_rate(camclk->clock)); - return ret; } @@ -632,7 +776,6 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (--camclk->use_count == 0) { clk_disable(camclk->clock); - s_info->clk_on = 0; dbg("Disabled camclk %d", pdata->clk_id); } return ret; @@ -648,8 +791,6 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * devices to which sensors can be attached, either directly or through * the MIPI CSI receiver. The clock is allowed here to be used by * multiple sensors concurrently if they use same frequency. - * The per sensor subdev clk_on attribute helps to synchronize accesses - * to the sclk_cam clocks from the video and media device nodes. * This function should only be called when the graph mutex is held. */ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) @@ -663,24 +804,40 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { + struct fimc_lite *fimc_lite = NULL; + struct fimc_dev *fimc = NULL; + struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; - struct fimc_dev *fimc; int ret = 0; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; sd = media_entity_to_v4l2_subdev(sink->entity); - fimc = v4l2_get_subdevdata(sd); - if (!(flags & MEDIA_LNK_FL_ENABLED)) { - ret = __fimc_pipeline_shutdown(fimc); - fimc->pipeline.sensor = NULL; - fimc->pipeline.csis = NULL; + switch (sd->grp_id) { + case FLITE_GROUP_ID: + fimc_lite = v4l2_get_subdevdata(sd); + pipeline = &fimc_lite->pipeline; + break; + case FIMC_GROUP_ID: + fimc = v4l2_get_subdevdata(sd); + pipeline = &fimc->pipeline; + break; + default: + return 0; + } - mutex_lock(&fimc->lock); - fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ret = __fimc_pipeline_shutdown(pipeline); + pipeline->subdevs[IDX_SENSOR] = NULL; + pipeline->subdevs[IDX_CSIS] = NULL; + + if (fimc) { + mutex_lock(&fimc->lock); + fimc_ctrls_delete(fimc->vid_cap.ctx); + mutex_unlock(&fimc->lock); + } return ret; } /* @@ -688,14 +845,23 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_initialize(fimc, source->entity, true); + if (fimc) { + mutex_lock(&fimc->lock); + if (fimc->vid_cap.refcnt > 0) { + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); + } + mutex_unlock(&fimc->lock); + } else { + mutex_lock(&fimc_lite->lock); + if (fimc_lite->ref_count > 0) { + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); + } + mutex_unlock(&fimc_lite->lock); } - mutex_unlock(&fimc->lock); - return ret ? -EPIPE : ret; } @@ -744,7 +910,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int __devinit fimc_md_probe(struct platform_device *pdev) +static int fimc_md_probe(struct platform_device *pdev) { struct v4l2_device *v4l2_dev; struct fimc_md *fmd; @@ -776,42 +942,48 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) ret = media_device_register(&fmd->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); - goto err2; + goto err_md; } ret = fimc_md_get_clocks(fmd); if (ret) - goto err3; + goto err_clk; fmd->user_subdev_api = false; + + /* Protect the media graph while we're registering entities */ + mutex_lock(&fmd->media_dev.graph_mutex); + ret = fimc_md_register_platform_entities(fmd); if (ret) - goto err3; + goto err_unlock; if (pdev->dev.platform_data) { ret = fimc_md_register_sensor_entities(fmd); if (ret) - goto err3; + goto err_unlock; } ret = fimc_md_create_links(fmd); if (ret) - goto err3; + goto err_unlock; ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret) - goto err3; - ret = fimc_md_register_video_nodes(fmd); - if (ret) - goto err3; + goto err_unlock; ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); - if (!ret) { - platform_set_drvdata(pdev, fmd); - return 0; - } -err3: + if (ret) + goto err_unlock; + + platform_set_drvdata(pdev, fmd); + mutex_unlock(&fmd->media_dev.graph_mutex); + return 0; + +err_unlock: + mutex_unlock(&fmd->media_dev.graph_mutex); +err_clk: media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); fimc_md_unregister_entities(fmd); -err2: +err_md: v4l2_device_unregister(&fmd->v4l2_dev); return ret; } @@ -841,10 +1013,12 @@ static struct platform_driver fimc_md_driver = { int __init fimc_md_init(void) { int ret; + request_module("s5p-csis"); ret = fimc_register_driver(); if (ret) return ret; + return platform_driver_register(&fimc_md_driver); } void __exit fimc_md_exit(void) |