From d3f5e0c54f1bfa5f48e92ac45a279fa8cfdc55b7 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 20 Dec 2013 18:53:53 -0300 Subject: [media] exynos4-is: Add clock provider for the SCLK_CAM clock outputs This patch adds clock provider so the the SCLK_CAM0/1 output clocks can be accessed by image sensor devices through the clk API. Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 118 ++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'drivers/media/platform/exynos4-is/media-dev.c') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index c1bce170df6f..f047a9f1043c 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include #include @@ -1276,6 +1278,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); + /* + * If there is a clock provider registered the sensors will + * handle their clock themselves, no need to control it on + * the host interface side. + */ + if (fmd->clk_provider.num_clocks > 0) + return 0; + return __fimc_md_set_camclk(fmd, si, on); } @@ -1437,6 +1447,103 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) return 0; } +#ifdef CONFIG_OF +static int cam_clk_prepare(struct clk_hw *hw) +{ + struct cam_clk *camclk = to_cam_clk(hw); + int ret; + + if (camclk->fmd->pmf == NULL) + return -ENODEV; + + ret = pm_runtime_get_sync(camclk->fmd->pmf); + return ret < 0 ? ret : 0; +} + +static void cam_clk_unprepare(struct clk_hw *hw) +{ + struct cam_clk *camclk = to_cam_clk(hw); + + if (camclk->fmd->pmf == NULL) + return; + + pm_runtime_put_sync(camclk->fmd->pmf); +} + +static const struct clk_ops cam_clk_ops = { + .prepare = cam_clk_prepare, + .unprepare = cam_clk_unprepare, +}; + +static void fimc_md_unregister_clk_provider(struct fimc_md *fmd) +{ + struct cam_clk_provider *cp = &fmd->clk_provider; + unsigned int i; + + if (cp->of_node) + of_clk_del_provider(cp->of_node); + + for (i = 0; i < cp->num_clocks; i++) + clk_unregister(cp->clks[i]); +} + +static int fimc_md_register_clk_provider(struct fimc_md *fmd) +{ + struct cam_clk_provider *cp = &fmd->clk_provider; + struct device *dev = &fmd->pdev->dev; + int i, ret; + + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { + struct cam_clk *camclk = &cp->camclk[i]; + struct clk_init_data init; + const char *p_name; + + ret = of_property_read_string_index(dev->of_node, + "clock-output-names", i, &init.name); + if (ret < 0) + break; + + p_name = __clk_get_name(fmd->camclk[i].clock); + + /* It's safe since clk_register() will duplicate the string. */ + init.parent_names = &p_name; + init.num_parents = 1; + init.ops = &cam_clk_ops; + init.flags = CLK_SET_RATE_PARENT; + camclk->hw.init = &init; + camclk->fmd = fmd; + + cp->clks[i] = clk_register(NULL, &camclk->hw); + if (IS_ERR(cp->clks[i])) { + dev_err(dev, "failed to register clock: %s (%ld)\n", + init.name, PTR_ERR(cp->clks[i])); + ret = PTR_ERR(cp->clks[i]); + goto err; + } + cp->num_clocks++; + } + + if (cp->num_clocks == 0) { + dev_warn(dev, "clk provider not registered\n"); + return 0; + } + + cp->clk_data.clks = cp->clks; + cp->clk_data.clk_num = cp->num_clocks; + cp->of_node = dev->of_node; + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &cp->clk_data); + if (ret == 0) + return 0; +err: + fimc_md_unregister_clk_provider(fmd); + return ret; +} +#else +#define fimc_md_register_clk_provider(fmd) (0) +#define fimc_md_unregister_clk_provider(fmd) (0) +#endif + static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1464,16 +1571,24 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->use_isp = fimc_md_is_isp_available(dev->of_node); + ret = fimc_md_register_clk_provider(fmd); + if (ret < 0) { + v4l2_err(v4l2_dev, "clock provider registration failed\n"); + return ret; + } + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; } + ret = media_device_register(&fmd->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); goto err_md; } + ret = fimc_md_get_clocks(fmd); if (ret) goto err_clk; @@ -1507,6 +1622,7 @@ static int fimc_md_probe(struct platform_device *pdev) ret = fimc_md_create_links(fmd); if (ret) goto err_unlock; + ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret) goto err_unlock; @@ -1527,6 +1643,7 @@ err_clk: media_device_unregister(&fmd->media_dev); err_md: v4l2_device_unregister(&fmd->v4l2_dev); + fimc_md_unregister_clk_provider(fmd); return ret; } @@ -1537,6 +1654,7 @@ static int fimc_md_remove(struct platform_device *pdev) if (!fmd) return 0; + fimc_md_unregister_clk_provider(fmd); v4l2_device_unregister(&fmd->v4l2_dev); device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); fimc_md_unregister_entities(fmd); -- cgit v1.2.1 From fa91f1056f17c87bc0fa601f80d1b1a4487fd701 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 24 Feb 2014 11:44:50 -0300 Subject: [media] exynos4-is: Add support for asynchronous subdevices registration Add support for registering external sensor subdevs using v4l2-async API. The async API is used only for sensor subdevs and only for booting from DT. Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 238 +++++++++++++++----------- 1 file changed, 135 insertions(+), 103 deletions(-) (limited to 'drivers/media/platform/exynos4-is/media-dev.c') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index f047a9f1043c..c670d67001f7 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, if (ret < 0) return ret; } + ret = fimc_md_set_camclk(sd, true); if (ret < 0) goto err_wbclk; @@ -380,77 +382,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adapter; - if (!client) + if (!client || client->dev.of_node) return; v4l2_device_unregister_subdev(sd); - if (!client->dev.of_node) { - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); - } + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); } #ifdef CONFIG_OF -/* Register I2C client subdev associated with @node. */ -static int fimc_md_of_add_sensor(struct fimc_md *fmd, - struct device_node *node, int index) -{ - struct fimc_sensor_info *si; - struct i2c_client *client; - struct v4l2_subdev *sd; - int ret; - - if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) - return -EINVAL; - si = &fmd->sensor[index]; - - client = of_find_i2c_device_by_node(node); - if (!client) - return -EPROBE_DEFER; - - device_lock(&client->dev); - - if (!client->dev.driver || - !try_module_get(client->dev.driver->owner)) { - ret = -EPROBE_DEFER; - v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", - node->full_name); - goto dev_put; - } - - /* Enable sensor's master clock */ - ret = __fimc_md_set_camclk(fmd, &si->pdata, true); - if (ret < 0) - goto mod_put; - sd = i2c_get_clientdata(client); - - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); - __fimc_md_set_camclk(fmd, &si->pdata, false); - if (ret < 0) - goto mod_put; - - v4l2_set_subdev_hostdata(sd, &si->pdata); - if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) - sd->grp_id = GRP_ID_FIMC_IS_SENSOR; - else - sd->grp_id = GRP_ID_SENSOR; - - si->subdev = sd; - v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", - sd->name, fmd->num_sensors); - fmd->num_sensors++; - -mod_put: - module_put(client->dev.driver->owner); -dev_put: - device_unlock(&client->dev); - put_device(&client->dev); - return ret; -} - /* Parse port node and register as a sub-device any sensor specified there. */ static int fimc_md_parse_port_node(struct fimc_md *fmd, struct device_node *port, @@ -459,7 +402,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, struct device_node *rem, *ep, *np; struct fimc_source_info *pd; struct v4l2_of_endpoint endpoint; - int ret; u32 val; pd = &fmd->sensor[index].pdata; @@ -487,6 +429,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, if (!of_property_read_u32(rem, "clock-frequency", &val)) pd->clk_frequency = val; + else + pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ; if (pd->clk_frequency == 0) { v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", @@ -526,10 +470,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, else pd->fimc_bus_type = pd->sensor_bus_type; - ret = fimc_md_of_add_sensor(fmd, rem, index); - of_node_put(rem); + if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) + return -EINVAL; - return ret; + fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF; + fmd->sensor[index].asd.match.of.node = rem; + fmd->async_subdevs[index] = &fmd->sensor[index].asd; + + fmd->num_sensors++; + + of_node_put(rem); + return 0; } /* Register all SoC external sub-devices */ @@ -885,11 +836,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) v4l2_device_unregister_subdev(fmd->csis[i].sd); fmd->csis[i].sd = NULL; } - for (i = 0; i < fmd->num_sensors; i++) { - if (fmd->sensor[i].subdev == NULL) - continue; - fimc_md_unregister_sensor(fmd->sensor[i].subdev); - fmd->sensor[i].subdev = NULL; + if (fmd->pdev->dev.of_node == NULL) { + for (i = 0; i < fmd->num_sensors; i++) { + if (fmd->sensor[i].subdev == NULL) + continue; + fimc_md_unregister_sensor(fmd->sensor[i].subdev); + fmd->sensor[i].subdev = NULL; + } } if (fmd->fimc_is) @@ -1224,6 +1177,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, struct fimc_camclk_info *camclk; int ret = 0; + /* + * When device tree is used the sensor drivers are supposed to + * control the clock themselves. This whole function will be + * removed once S5PV210 platform is converted to the device tree. + */ + if (fmd->pdev->dev.of_node) + return 0; + if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) return -EINVAL; @@ -1544,6 +1505,56 @@ err: #define fimc_md_unregister_clk_provider(fmd) (0) #endif +static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct fimc_md *fmd = notifier_to_fimc_md(notifier); + struct fimc_sensor_info *si = NULL; + int i; + + /* Find platform data for this sensor subdev */ + for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) + if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node) + si = &fmd->sensor[i]; + + if (si == NULL) + return -EINVAL; + + v4l2_set_subdev_hostdata(subdev, &si->pdata); + + if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) + subdev->grp_id = GRP_ID_FIMC_IS_SENSOR; + else + subdev->grp_id = GRP_ID_SENSOR; + + si->subdev = subdev; + + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", + subdev->name, fmd->num_sensors); + + fmd->num_sensors++; + + return 0; +} + +static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct fimc_md *fmd = notifier_to_fimc_md(notifier); + int ret; + + mutex_lock(&fmd->media_dev.graph_mutex); + + ret = fimc_md_create_links(fmd); + if (ret < 0) + goto unlock; + + ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); +unlock: + mutex_unlock(&fmd->media_dev.graph_mutex); + return ret; +} + static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1571,12 +1582,6 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->use_isp = fimc_md_is_isp_available(dev->of_node); - ret = fimc_md_register_clk_provider(fmd); - if (ret < 0) { - v4l2_err(v4l2_dev, "clock provider registration failed\n"); - return ret; - } - ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); @@ -1586,64 +1591,88 @@ static int 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 err_md; + goto err_v4l2_dev; } ret = fimc_md_get_clocks(fmd); if (ret) - goto err_clk; + goto err_md; fmd->user_subdev_api = (dev->of_node != NULL); - /* Protect the media graph while we're registering entities */ - mutex_lock(&fmd->media_dev.graph_mutex); - ret = fimc_md_get_pinctrl(fmd); if (ret < 0) { if (ret != EPROBE_DEFER) dev_err(dev, "Failed to get pinctrl: %d\n", ret); - goto err_unlock; + goto err_clk; } + platform_set_drvdata(pdev, fmd); + + /* Protect the media graph while we're registering entities */ + mutex_lock(&fmd->media_dev.graph_mutex); + if (dev->of_node) ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); else ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, fimc_md_pdev_match); - if (ret) - goto err_unlock; + if (ret) { + mutex_unlock(&fmd->media_dev.graph_mutex); + goto err_clk; + } if (dev->platform_data || dev->of_node) { ret = fimc_md_register_sensor_entities(fmd); - if (ret) - goto err_unlock; + if (ret) { + mutex_unlock(&fmd->media_dev.graph_mutex); + goto err_m_ent; + } } - ret = fimc_md_create_links(fmd); - if (ret) - goto err_unlock; - - ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); - if (ret) - goto err_unlock; + mutex_unlock(&fmd->media_dev.graph_mutex); ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); if (ret) - goto err_unlock; + goto err_m_ent; + /* + * FIMC platform devices need to be registered before the sclk_cam + * clocks provider, as one of these devices needs to be activated + * to enable the clock. + */ + ret = fimc_md_register_clk_provider(fmd); + if (ret < 0) { + v4l2_err(v4l2_dev, "clock provider registration failed\n"); + goto err_attr; + } + + if (fmd->num_sensors > 0) { + fmd->subdev_notifier.subdevs = fmd->async_subdevs; + fmd->subdev_notifier.num_subdevs = fmd->num_sensors; + fmd->subdev_notifier.bound = subdev_notifier_bound; + fmd->subdev_notifier.complete = subdev_notifier_complete; + fmd->num_sensors = 0; + + ret = v4l2_async_notifier_register(&fmd->v4l2_dev, + &fmd->subdev_notifier); + if (ret) + goto err_clk_p; + } - 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_p: + fimc_md_unregister_clk_provider(fmd); +err_attr: + device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); err_clk: fimc_md_put_clocks(fmd); +err_m_ent: fimc_md_unregister_entities(fmd); - media_device_unregister(&fmd->media_dev); err_md: + media_device_unregister(&fmd->media_dev); +err_v4l2_dev: v4l2_device_unregister(&fmd->v4l2_dev); - fimc_md_unregister_clk_provider(fmd); return ret; } @@ -1655,12 +1684,15 @@ static int fimc_md_remove(struct platform_device *pdev) return 0; fimc_md_unregister_clk_provider(fmd); + v4l2_async_notifier_unregister(&fmd->subdev_notifier); + v4l2_device_unregister(&fmd->v4l2_dev); device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); fimc_md_unregister_entities(fmd); fimc_md_pipelines_free(fmd); media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); + return 0; } -- cgit v1.2.1 From 34947b8aebe3f2d4eceb65fceafa92bf8dc97d96 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 20 Dec 2013 19:35:06 -0300 Subject: [media] exynos4-is: Add the FIMC-IS ISP capture DMA driver Add a video capture node for the FIMC-IS ISP IP block. The Exynos4x12 FIMC-IS ISP IP block has 2 DMA interfaces that allow to capture raw Bayer and YUV data to memory. Currently only the DMA2 output is and raw Bayer data capture is supported. Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/media/platform/exynos4-is/media-dev.c') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index c670d67001f7..9bec34c4ac94 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -684,8 +684,16 @@ static int register_csis_entity(struct fimc_md *fmd, static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) { struct v4l2_subdev *sd = &is->isp.subdev; + struct exynos_media_pipeline *ep; int ret; + /* Allocate pipeline object for the ISP capture video node. */ + ep = fimc_md_pipeline_create(fmd); + if (!ep) + return -ENOMEM; + + v4l2_set_subdev_hostdata(sd, ep); + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (ret) { v4l2_err(&fmd->v4l2_dev, @@ -959,16 +967,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) /* Create FIMC-IS links */ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) { + struct fimc_isp *isp = &fmd->fimc_is->isp; struct media_entity *source, *sink; int i, ret; - source = &fmd->fimc_is->isp.subdev.entity; + source = &isp->subdev.entity; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (fmd->fimc[i] == NULL) continue; - /* Link from IS-ISP subdev to FIMC */ + /* Link from FIMC-IS-ISP subdev to FIMC */ sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, sink, FIMC_SD_PAD_SINK_FIFO, 0); @@ -976,7 +985,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) return ret; } - return ret; + /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */ + sink = &isp->video_capture.ve.vdev.entity; + + /* Skip this link if the fimc-is-isp video node driver isn't built-in */ + if (sink->num_pads == 0) + return 0; + + return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA, + sink, 0, 0); } /** -- cgit v1.2.1