On 03/06/2013 12:53 PM, Shaik Ameer Basha wrote:
This patch adds support for media device for EXYNOS5 SoCs. The current media device supports the following ips to connect through the media controller framework. * MIPI-CSIS Support interconnection(subdev interface) between devices * FIMC-LITE Support capture interface from device(Sensor, MIPI-CSIS) to memory Support interconnection(subdev interface) between devices G-Scaler will be added later to the current media device. * Gscaler: general scaler Support memory to memory interface Support output interface from memory to display device(LCD, TV) Support capture interface from device(FIMC-LITE, FIMD) to memory --> media 0 Camera Capture path consists of MIPI-CSIS, FIMC-LITE and G-Scaler +--------+ +-----------+ +-----------------+ | Sensor | --> | FIMC-LITE | --> | G-Scaler-capture | +--------+ +-----------+ +-----------------+ +--------+ +-----------+ +-----------+ +-----------------+ | Sensor | --> | MIPI-CSIS | --> | FIMC-LITE | --> | G-Scaler-capture | +--------+ +-----------+ +-----------+ +-----------------+ Signed-off-by: Shaik Ameer Basha<shaik.ameer@xxxxxxxxxxx> --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/exynos5-is/Kconfig | 7 + drivers/media/platform/exynos5-is/Makefile | 4 + drivers/media/platform/exynos5-is/exynos5-mdev.c | 1309 ++++++++++++++++++++++ drivers/media/platform/exynos5-is/exynos5-mdev.h | 107 ++ 6 files changed, 1429 insertions(+) create mode 100644 drivers/media/platform/exynos5-is/Kconfig create mode 100644 drivers/media/platform/exynos5-is/Makefile create mode 100644 drivers/media/platform/exynos5-is/exynos5-mdev.c create mode 100644 drivers/media/platform/exynos5-is/exynos5-mdev.h
...
diff --git a/drivers/media/platform/exynos5-is/exynos5-mdev.c b/drivers/media/platform/exynos5-is/exynos5-mdev.c new file mode 100644 index 0000000..1158696 --- /dev/null +++ b/drivers/media/platform/exynos5-is/exynos5-mdev.c @@ -0,0 +1,1309 @@ +/* + * S5P/EXYNOS4 SoC series camera host interface media device driver
EXYNOS5
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki<s.nawrocki@xxxxxxxxxxx>
This is incorrect too, you should add your authorship and a note it is based on other code, with a proper copyright notice.
+ * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + */ + +#include<linux/bug.h> +#include<linux/device.h> +#include<linux/errno.h> +#include<linux/i2c.h> +#include<linux/kernel.h> +#include<linux/list.h> +#include<linux/module.h> +#include<linux/of.h> +#include<linux/of_platform.h> +#include<linux/of_device.h> +#include<linux/of_i2c.h> +#include<linux/pinctrl/consumer.h> +#include<linux/platform_device.h> +#include<linux/pm_runtime.h> +#include<linux/types.h> +#include<linux/slab.h> +#include<media/v4l2-ctrls.h> +#include<media/v4l2-of.h> +#include<media/media-device.h> + +#include "exynos5-mdev.h" + +#define dbg(fmt, args...) \ + pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) + +static struct fimc_md *g_exynos_mdev; + +static int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); +static int __fimc_md_set_camclk(struct fimc_md *fmd, + struct fimc_sensor_info *s_info, + bool on); +/** + * fimc_pipeline_prepare - update pipeline information with subdevice pointers + * @fimc: fimc device terminating the pipeline + * + * Caller holds the graph mutex. + */ +static void fimc_pipeline_prepare(struct exynos5_pipeline0 *p, + struct media_entity *me) +{ + struct media_pad *pad =&me->pads[0];
This will need to be changed to support subdevs with more than 2 pads. I should post relevant patch this week.
+ struct v4l2_subdev *sd; + int i; + + for (i = 0; i< IDX_MAX; i++) + p->subdevs[i] = NULL; + + while (1) { + + if (!(pad->flags& MEDIA_PAD_FL_SINK)) + break; + + /* source pad */ + pad = media_entity_remote_source(pad); + + if (pad != NULL) + pr_err("entity type: %d, entity name: %s\n", + media_entity_type(pad->entity), pad->entity->name); + + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + switch (sd->grp_id) { + case GRP_ID_FIMC_IS_SENSOR: + case GRP_ID_SENSOR: + p->subdevs[IDX_SENSOR] = sd; + break; + case GRP_ID_CSIS: + p->subdevs[IDX_CSIS] = sd; + break; + case GRP_ID_FLITE: + p->subdevs[IDX_FLITE] = sd; + break; + default: + pr_warn("%s: Unknown subdev grp_id: %#x\n", + __func__, sd->grp_id); + } + + /* sink pad */ + pad =&sd->entity.pads[0]; + } +}
...
+/* + * Sensor subdevice helper functions + */ +static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, + struct fimc_sensor_info *s_info) +{ + struct i2c_adapter *adapter; + struct v4l2_subdev *sd = NULL; + + if (!s_info || !fmd) + return NULL; + + adapter = i2c_get_adapter(s_info->pdata.i2c_bus_num); + 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_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 = GRP_ID_SENSOR; + + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", + sd->name); + return sd; +} + +static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_adapter *adapter; + + if (!client) + return; + v4l2_device_unregister_subdev(sd); + + if (!client->dev.of_node) { + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); + } +}
You don't need the above code which is for !CONFIG_OF only.
+#ifdef CONFIG_OF
Just add "depends on OF" in Kconfig for the whole driver and you can drop all #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->driver || + !try_module_get(client->driver->driver.owner)) { + ret = -EPROBE_DEFER; + goto dev_put; + } + + /* Enable sensor's master clock */ + ret = __fimc_md_set_camclk(fmd, si, 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, false); + if (ret< 0) + goto mod_put; + + v4l2_set_subdev_hostdata(sd, si); + 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->driver->driver.owner); +dev_put: + device_unlock(&client->dev); + put_device(&client->dev); + return ret; +}
...
+#else +#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS)
This would never be used since Exynos5 is a dt-only platform.
+#endif + +static int fimc_md_register_sensor_entities(struct fimc_md *fmd) +{ + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; + struct device_node *of_node = fmd->pdev->dev.of_node; + int num_clients = 0; + int ret, i; + + if (of_node) { + fmd->num_sensors = 0; + ret = fimc_md_of_sensors_register(fmd, of_node); + } else if (pdata) {
The code below is for !CONFIG_OF only, no need to repeat it for Exynos5.
+ WARN_ON(pdata->num_clients> ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, + ARRAY_SIZE(fmd->sensor)); + fmd->num_sensors = num_clients; + + fmd->num_sensors = num_clients; + for (i = 0; i< num_clients; i++) { + struct v4l2_subdev *sd; + + fmd->sensor[i].pdata = pdata->source_info[i]; + ret = __fimc_md_set_camclk(fmd,&fmd->sensor[i], true); + if (ret) + break; + 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 = NULL; + ret = PTR_ERR(sd); + break; + } + fmd->sensor[i].subdev = sd; + if (ret) + break; + } + } + + return ret; +} + +/* + * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. + */ + +static int register_fimc_lite_entity(struct fimc_md *fmd, + struct fimc_lite *fimc_lite) +{ + struct v4l2_subdev *sd; + int ret; + + if (WARN_ON(fimc_lite->index>= FIMC_LITE_MAX_DEVS || + fmd->fimc_lite[fimc_lite->index])) + return -EBUSY; + + sd =&fimc_lite->subdev; + sd->grp_id = GRP_ID_FLITE; + v4l2_set_subdev_hostdata(sd, (void *)&exynos5_pipeline0_ops); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (!ret) + fmd->fimc_lite[fimc_lite->index] = fimc_lite; + else + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n", + fimc_lite->index); + return ret; +} + +static int register_csis_entity(struct fimc_md *fmd, + struct platform_device *pdev, + struct v4l2_subdev *sd) +{ + struct device_node *node = pdev->dev.of_node; + int id, ret; + + id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id); + + if (WARN_ON(id>= CSIS_MAX_ENTITIES || fmd->csis[id].sd)) + return -EBUSY; + + if (WARN_ON(id>= CSIS_MAX_ENTITIES)) + return 0; + + sd->grp_id = GRP_ID_CSIS; + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (!ret) + fmd->csis[id].sd = sd; + else + v4l2_err(&fmd->v4l2_dev, + "Failed to register MIPI-CSIS.%d (%d)\n", id, ret); + + return ret; +} + +static int fimc_md_register_platform_entity(struct fimc_md *fmd, + struct platform_device *pdev, + int plat_entity) +{ + struct device *dev =&pdev->dev; + int ret = -EPROBE_DEFER; + void *drvdata; + + /* Lock to ensure dev->driver won't change. */ + device_lock(dev); + + if (!dev->driver || !try_module_get(dev->driver->owner)) + goto dev_unlock; + + drvdata = dev_get_drvdata(dev); + /* Some subdev didn't probe succesfully id drvdata is NULL */ + if (drvdata) { + switch (plat_entity) { + case IDX_FLITE: + ret = register_fimc_lite_entity(fmd, drvdata); + break; + case IDX_CSIS: + ret = register_csis_entity(fmd, pdev, drvdata); + break; + default: + ret = -ENODEV; + } + } + + module_put(dev->driver->owner); +dev_unlock: + device_unlock(dev); + if (ret == -EPROBE_DEFER) + dev_info(&fmd->pdev->dev, "deferring %s device registration\n", + dev_name(dev)); + else if (ret< 0) + dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n", + dev_name(dev), ret); + return ret; +} + +static int fimc_md_pdev_match(struct device *dev, void *data)
This function is also useless in your case.
+{ + struct platform_device *pdev = to_platform_device(dev); + int plat_entity = -1; + int ret; + + if (!get_device(dev)) + return -ENODEV; + + if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) + plat_entity = IDX_CSIS; + else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) + plat_entity = IDX_FLITE; + + if (plat_entity>= 0) + ret = fimc_md_register_platform_entity(data, pdev, + plat_entity); + put_device(dev); + return 0; +} + +/* Register FIMC, FIMC-LITE and CSIS media entities */ +#ifdef CONFIG_OF +static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, + struct device_node *parent) +{ + struct device_node *node; + int ret = 0; + + for_each_available_child_of_node(parent, node) { + struct platform_device *pdev; + int plat_entity = -1; + + pdev = of_find_device_by_node(node); + if (!pdev) + continue; + + /* If driver of any entity isn't ready try all again later. */ + if (!strcmp(node->name, CSIS_OF_NODE_NAME)) + plat_entity = IDX_CSIS; + else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) + plat_entity = IDX_FLITE; + + if (plat_entity>= 0) + ret = fimc_md_register_platform_entity(fmd, pdev, + plat_entity); + put_device(&pdev->dev); + if (ret< 0) + break; + } + + return ret; +} +#else +#define fimc_md_register_platform_entities(fmd) (-ENOSYS) +#endif
...
+static int fimc_md_link_notify(struct media_pad *source, + struct media_pad *sink, u32 flags) +{ + struct fimc_lite *fimc_lite = NULL; + struct exynos_pipeline *pipeline; + struct exynos5_pipeline0 *p; + struct v4l2_subdev *sd; + struct mutex *lock; + int ret = 0; + int ref_count; + + if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return 0; + + sd = media_entity_to_v4l2_subdev(sink->entity); + + switch (sd->grp_id) { + case GRP_ID_FLITE: + fimc_lite = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc_lite == NULL)) + return 0; + pipeline =&fimc_lite->pipeline; + lock =&fimc_lite->lock; + break; + default: + return 0; + } + + p = (struct exynos5_pipeline0 *)pipeline->priv; + if (!p || !p->is_init) + return -EINVAL; + + if (!(flags& MEDIA_LNK_FL_ENABLED)) { + int i; + mutex_lock(lock);
ref_count needs to be checked here as well.
+ ret = __fimc_pipeline_close(pipeline); + for (i = 0; i< IDX_MAX; i++) + p->subdevs[i] = NULL; + mutex_unlock(lock); + return ret; + } + /* + * Link activation. Enable power of pipeline elements only if the + * pipeline is already in use, i.e. its video node is opened. + * Recreate the controls destroyed during the link deactivation.
I think this line should be deleted.
+ */ + mutex_lock(lock); + + ref_count = fimc_lite->ref_count; + if (ref_count> 0) + ret = __fimc_pipeline_open(pipeline, source->entity, true); + + mutex_unlock(lock); + return ret ? -EPIPE : ret; +} + +static ssize_t fimc_md_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fimc_md *fmd = platform_get_drvdata(pdev); + + if (fmd->user_subdev_api) + return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); + + return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); +} + +static ssize_t fimc_md_sysfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fimc_md *fmd = platform_get_drvdata(pdev); + bool subdev_api; + int i; + + if (!strcmp(buf, "vid-dev\n")) + subdev_api = false; + else if (!strcmp(buf, "sub-dev\n")) + subdev_api = true; + else + return count; + + fmd->user_subdev_api = subdev_api; + for (i = 0; i< FIMC_LITE_MAX_DEVS; i++) + if (fmd->fimc_lite[i]) + fmd->fimc_lite[i]->user_subdev_api = subdev_api; + + return count; +} +/* + * This device attribute is to select video pipeline configuration method. + * There are following valid values: + * vid-dev - for V4L2 video node API only, subdevice will be configured + * by the host driver. + * sub-dev - for media controller API, subdevs must be configured in user + * space before starting streaming. + */ +static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, + fimc_md_sysfs_show, fimc_md_sysfs_store);
Please don't copy that. It is a not standard attribute that you should not need in this driver anyway.
+/** + * fimc_md_sensor_notify - v4l2_device notification from a sensor subdev + * @sd: pointer to a subdev generating the notification + * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY + * @arg: pointer to an u32 type integer that stores the frame payload value + * + * Passes the sensor notification to the capture device + */ +static void fimc_md_sensor_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct fimc_md *fmd; + struct exynos_pipeline *ep; + struct exynos5_pipeline0 *p; + unsigned long flags; + + if (sd == NULL) + return; + + ep = media_pipe_to_exynos_pipeline(sd->entity.pipe); + p = (struct exynos5_pipeline0 *)ep->priv; + + spin_lock_irqsave(&fmd->slock, flags); + + if (p->sensor_notify) + p->sensor_notify(sd, notification, arg); + + spin_unlock_irqrestore(&fmd->slock, flags); +} + +static int fimc_md_probe(struct platform_device *pdev) +{ + struct device *dev =&pdev->dev; + struct v4l2_device *v4l2_dev; + struct fimc_md *fmd; + int ret; + + fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL); + if (!fmd) + return -ENOMEM; + + spin_lock_init(&fmd->slock); + fmd->pdev = pdev; + + strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
Please change this to "SAMSUNG EXYNOS5 IS" or something similar. This could confuse user space!
+ sizeof(fmd->media_dev.model)); + fmd->media_dev.link_notify = fimc_md_link_notify; + fmd->media_dev.dev = dev; + + v4l2_dev =&fmd->v4l2_dev; + v4l2_dev->mdev =&fmd->media_dev; + v4l2_dev->notify = fimc_md_sensor_notify; + strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name));
exynos-fimc-md ?
+ + 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 dev: %d\n", ret); + goto err_md; + } + ret = fimc_md_get_clocks(fmd); + if (ret) + goto err_clk; + + fmd->user_subdev_api = (dev->of_node != NULL); + g_exynos_mdev = fmd; + + /* Protect the media graph while we're registering entities */ + mutex_lock(&fmd->media_dev.graph_mutex); + + if (fmd->pdev->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 (dev->platform_data || dev->of_node) {
platform_data will never be used, and dev->of_node would be always not NULL.
+ ret = fimc_md_register_sensor_entities(fmd); + if (ret) + goto err_unlock; + } + + 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; + + ret = device_create_file(&pdev->dev,&dev_attr_subdev_conf_mode); + 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); +err_md: + v4l2_device_unregister(&fmd->v4l2_dev); + return ret; +} +
...
+static struct platform_device_id fimc_driver_ids[] __always_unused = { + { .name = "exynos-fimc-md" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, fimc_driver_ids); + +static const struct of_device_id fimc_md_of_match[] __initconst = {
The attribute is wrong. I would remove that "__initconst".
+ { .compatible = "samsung,exynos5-fimc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, fimc_md_of_match); +
-- Regards, Sylwester -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html