From 3047f3f98eabd655ca2db4e42219a2990052f73a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Mar 2016 05:04:03 -0700 Subject: [PATCH 001/173] [media] media-device: Fix a comment The comment is for the wrong function. Fix it. Reviewed-by: Javier Martinez Canillas Signed-off-by: Mauro Carvalho Chehab --- include/media/media-device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-device.h b/include/media/media-device.h index df74cfa7da4a..6d2860657021 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -494,7 +494,7 @@ int __must_check __media_device_register(struct media_device *mdev, #define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE) /** - * __media_device_unregister() - Unegisters a media device element + * media_device_unregister() - Unregisters a media device element * * @mdev: pointer to struct &media_device * From 952f8eef901b170dbe6b48e80f098be5d835a82c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Mar 2016 05:34:39 -0700 Subject: [PATCH 002/173] [media] media-device: make topology_version u64 The uAPI defines it with 64 bits. Let's change the Kernel implementation too. Signed-off-by: Mauro Carvalho Chehab --- include/media/media-device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-device.h b/include/media/media-device.h index 6d2860657021..07809f698464 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -357,7 +357,7 @@ struct media_device { u32 hw_revision; u32 driver_version; - u32 topology_version; + u64 topology_version; u32 id; struct ida entity_internal_idx; From 88336e174645948da269e1812f138f727cd2896b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 21 Mar 2016 04:33:12 -0700 Subject: [PATCH 003/173] [media] media-devnode: add missing mutex lock in error handler We should protect the device unregister patch too, at the error condition. Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-devnode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 29409f440f1c..64a4b1ef3dcd 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -267,8 +267,11 @@ int __must_check media_devnode_register(struct media_devnode *mdev, return 0; error: + mutex_lock(&media_devnode_lock); cdev_del(&mdev->cdev); clear_bit(mdev->minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + return ret; } From bc5ccdbc990debbcae4602214dddc8d5fd38b01d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Mar 2016 05:04:04 -0700 Subject: [PATCH 004/173] [media] au0828: Unregister notifiers If au0828 gets removed, we need to remove the notifiers. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Javier Martinez Canillas Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-core.c | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index cc22b32776ad..321ea5cf1329 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -131,22 +131,36 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, return status; } +#ifdef CONFIG_MEDIA_CONTROLLER +static void au0828_media_graph_notify(struct media_entity *new, + void *notify_data); +#endif + static void au0828_unregister_media_device(struct au0828_dev *dev) { - #ifdef CONFIG_MEDIA_CONTROLLER - if (dev->media_dev && - media_devnode_is_registered(&dev->media_dev->devnode)) { - /* clear enable_source, disable_source */ - dev->media_dev->source_priv = NULL; - dev->media_dev->enable_source = NULL; - dev->media_dev->disable_source = NULL; + struct media_device *mdev = dev->media_dev; + struct media_entity_notify *notify, *nextp; - media_device_unregister(dev->media_dev); - media_device_cleanup(dev->media_dev); - kfree(dev->media_dev); - dev->media_dev = NULL; + if (!mdev || !media_devnode_is_registered(&mdev->devnode)) + return; + + /* Remove au0828 entity_notify callbacks */ + list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list) { + if (notify->notify != au0828_media_graph_notify) + continue; + media_device_unregister_entity_notify(mdev, notify); } + + /* clear enable_source, disable_source */ + dev->media_dev->source_priv = NULL; + dev->media_dev->enable_source = NULL; + dev->media_dev->disable_source = NULL; + + media_device_unregister(dev->media_dev); + media_device_cleanup(dev->media_dev); + kfree(dev->media_dev); + dev->media_dev = NULL; #endif } From 7e8da343a31623decc7fd0ee66700ba50260aca7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 25 Jan 2016 22:41:46 -0200 Subject: [PATCH 005/173] [media] exynos4-is: Add missing port parent of_node_put on error paths In fimc_md_parse_port_node() remote port parent node is acquired with of_graph_get_remote_port_parent() but it is not put on error path. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index feb521f28e14..48f62dc4453e 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -446,8 +446,10 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, else pd->fimc_bus_type = pd->sensor_bus_type; - if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) + if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { + of_node_put(rem); return -EINVAL; + } fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF; fmd->sensor[index].asd.match.of.node = rem; From 5790a1589f746d5648825d3bc8367f0ce7b12784 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 4 Mar 2016 17:20:12 -0300 Subject: [PATCH 006/173] [media] exynos4-is: Put node before s5pcsis_parse_dt() return error The MIPI CSIS DT parse function return an -ENXIO errno if the port # is outside of the supported values. But it doesn't call of_node_put() to decrement the node's reference counter, that's incremented inside the of_graph_get_next_endpoint() function that was called before. Instead of just returning, go to the error path that already does it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/mipi-csis.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index bd5c46c3d4b7..bf954424e7be 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -757,8 +757,10 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, goto err; state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; - if (state->index >= CSIS_MAX_ENTITIES) - return -ENXIO; + if (state->index >= CSIS_MAX_ENTITIES) { + ret = -ENXIO; + goto err; + } /* Get MIPI CSI-2 bus configration from the endpoint node. */ of_property_read_u32(node, "samsung,csis-hs-settle", From 77401dd7394c5d7b593718361ac6bb8f1aa4db62 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 8 Dec 2015 12:39:08 -0200 Subject: [PATCH 007/173] [media] s5p-jpeg: Adjust buffer size for Exynos 4412 Eliminate iommu fault during encoding by adjusting image size used for buffer size computation and ensuring that the buffer is not overrun. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index c3b13a630edf..caa19b408551 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1548,8 +1548,10 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, struct v4l2_pix_format *pix = &f->fmt.pix; u32 pix_fmt = f->fmt.pix.pixelformat; int w = pix->width, h = pix->height, wh_align; + int padding = 0; if (pix_fmt == V4L2_PIX_FMT_RGB32 || + pix_fmt == V4L2_PIX_FMT_RGB565 || pix_fmt == V4L2_PIX_FMT_NV24 || pix_fmt == V4L2_PIX_FMT_NV42 || pix_fmt == V4L2_PIX_FMT_NV12 || @@ -1564,7 +1566,10 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, &h, S5P_JPEG_MIN_HEIGHT, S5P_JPEG_MAX_HEIGHT, wh_align); - return w * h * fmt_depth >> 3; + if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) + padding = PAGE_SIZE; + + return (w * h * fmt_depth >> 3) + padding; } static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, From c1ac057173ba674d93afc8ddc5c91da1c61a951a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Dec 2015 12:00:13 -0200 Subject: [PATCH 008/173] [media] exynos-gsc: remove non-device-tree init code Exynos platform has been fully converted to device tree, so old platform device based init data can be now removed. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 33 ++++---------------- drivers/media/platform/exynos-gsc/gsc-core.h | 1 - 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 9b9e423e4fc4..032a423fb892 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -967,15 +967,6 @@ static struct gsc_driverdata gsc_v_100_drvdata = { .lclk_frequency = 266000000UL, }; -static const struct platform_device_id gsc_driver_ids[] = { - { - .name = "exynos-gsc", - .driver_data = (unsigned long)&gsc_v_100_drvdata, - }, - {}, -}; -MODULE_DEVICE_TABLE(platform, gsc_driver_ids); - static const struct of_device_id exynos_gsc_match[] = { { .compatible = "samsung,exynos5-gsc", @@ -988,17 +979,11 @@ MODULE_DEVICE_TABLE(of, exynos_gsc_match); static void *gsc_get_drv_data(struct platform_device *pdev) { struct gsc_driverdata *driver_data = NULL; + const struct of_device_id *match; - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(exynos_gsc_match, - pdev->dev.of_node); - if (match) - driver_data = (struct gsc_driverdata *)match->data; - } else { - driver_data = (struct gsc_driverdata *) - platform_get_device_id(pdev)->driver_data; - } + match = of_match_node(exynos_gsc_match, pdev->dev.of_node); + if (match) + driver_data = (struct gsc_driverdata *)match->data; return driver_data; } @@ -1084,19 +1069,14 @@ static int gsc_probe(struct platform_device *pdev) if (!gsc) return -ENOMEM; - if (dev->of_node) - gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); - else - gsc->id = pdev->id; - - if (gsc->id >= drv_data->num_entities) { + gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); + if (gsc->id >= drv_data->num_entities || gsc->id < 0) { dev_err(dev, "Invalid platform device id: %d\n", gsc->id); return -EINVAL; } gsc->variant = drv_data->variant[gsc->id]; gsc->pdev = pdev; - gsc->pdata = dev->platform_data; init_waitqueue_head(&gsc->irq_queue); spin_lock_init(&gsc->slock); @@ -1253,7 +1233,6 @@ static const struct dev_pm_ops gsc_pm_ops = { static struct platform_driver gsc_driver = { .probe = gsc_probe, .remove = gsc_remove, - .id_table = gsc_driver_ids, .driver = { .name = GSC_MODULE_NAME, .pm = &gsc_pm_ops, diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index e93a2336cfa2..ec4000c72172 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -340,7 +340,6 @@ struct gsc_dev { void __iomem *regs; wait_queue_head_t irq_queue; struct gsc_m2m_device m2m; - struct exynos_platform_gscaler *pdata; unsigned long state; struct vb2_alloc_ctx *alloc_ctx; struct video_device vdev; From ef3617c4b887a9b176210b4b80c0fe964cbe8c2a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Dec 2015 12:00:14 -0200 Subject: [PATCH 009/173] [media] s5p-g2d: remove non-device-tree init code Exynos and Samsung S5P platforms has been fully converted to device tree, so old platform device based init data can be now removed. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 27 +++++---------------------- drivers/media/platform/s5p-g2d/g2d.h | 5 ----- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 74bd46ca7942..612d1ea514f1 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -719,16 +719,12 @@ static int g2d_probe(struct platform_device *pdev) def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - if (!pdev->dev.of_node) { - dev->variant = g2d_get_drv_data(pdev); - } else { - of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); - if (!of_id) { - ret = -ENODEV; - goto unreg_video_dev; - } - dev->variant = (struct g2d_variant *)of_id->data; + of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); + if (!of_id) { + ret = -ENODEV; + goto unreg_video_dev; } + dev->variant = (struct g2d_variant *)of_id->data; return 0; @@ -788,22 +784,9 @@ static const struct of_device_id exynos_g2d_match[] = { }; MODULE_DEVICE_TABLE(of, exynos_g2d_match); -static const struct platform_device_id g2d_driver_ids[] = { - { - .name = "s5p-g2d", - .driver_data = (unsigned long)&g2d_drvdata_v3x, - }, { - .name = "s5p-g2d-v4x", - .driver_data = (unsigned long)&g2d_drvdata_v4x, - }, - {}, -}; -MODULE_DEVICE_TABLE(platform, g2d_driver_ids); - static struct platform_driver g2d_pdrv = { .probe = g2d_probe, .remove = g2d_remove, - .id_table = g2d_driver_ids, .driver = { .name = G2D_NAME, .of_match_table = exynos_g2d_match, diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index b0e52ab7ecdb..e31df541aa62 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -89,8 +89,3 @@ void g2d_set_flip(struct g2d_dev *d, u32 r); void g2d_set_v41_stretch(struct g2d_dev *d, struct g2d_frame *src, struct g2d_frame *dst); void g2d_set_cmd(struct g2d_dev *d, u32 c); - -static inline struct g2d_variant *g2d_get_drv_data(struct platform_device *pdev) -{ - return (struct g2d_variant *)platform_get_device_id(pdev)->driver_data; -} From ed3e34ed828c00ac7eafd24079d90a8fabcf07cc Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Dec 2015 12:00:15 -0200 Subject: [PATCH 010/173] [media] s5p-mfc: remove non-device-tree init code Exynos and Samsung S5P platforms has been fully converted to device tree, so old platform device based init data can be now removed. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 37 ++++-------------------- 1 file changed, 5 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 927ab4928779..b16466fe35ee 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1489,27 +1489,6 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = { .fw_name[0] = "s5p-mfc-v8.fw", }; -static const struct platform_device_id mfc_driver_ids[] = { - { - .name = "s5p-mfc", - .driver_data = (unsigned long)&mfc_drvdata_v5, - }, { - .name = "s5p-mfc-v5", - .driver_data = (unsigned long)&mfc_drvdata_v5, - }, { - .name = "s5p-mfc-v6", - .driver_data = (unsigned long)&mfc_drvdata_v6, - }, { - .name = "s5p-mfc-v7", - .driver_data = (unsigned long)&mfc_drvdata_v7, - }, { - .name = "s5p-mfc-v8", - .driver_data = (unsigned long)&mfc_drvdata_v8, - }, - {}, -}; -MODULE_DEVICE_TABLE(platform, mfc_driver_ids); - static const struct of_device_id exynos_mfc_match[] = { { .compatible = "samsung,mfc-v5", @@ -1531,24 +1510,18 @@ MODULE_DEVICE_TABLE(of, exynos_mfc_match); static void *mfc_get_drv_data(struct platform_device *pdev) { struct s5p_mfc_variant *driver_data = NULL; + const struct of_device_id *match; + + match = of_match_node(exynos_mfc_match, pdev->dev.of_node); + if (match) + driver_data = (struct s5p_mfc_variant *)match->data; - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(exynos_mfc_match, - pdev->dev.of_node); - if (match) - driver_data = (struct s5p_mfc_variant *)match->data; - } else { - driver_data = (struct s5p_mfc_variant *) - platform_get_device_id(pdev)->driver_data; - } return driver_data; } static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, .remove = s5p_mfc_remove, - .id_table = mfc_driver_ids, .driver = { .name = S5P_MFC_NAME, .pm = &s5p_mfc_pm_ops, From 4c9c6d86d190caa00028dcb3e9864e57aa9a1df6 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Dec 2015 12:00:16 -0200 Subject: [PATCH 011/173] [media] exynos4-is: remove non-device-tree init code Exynos and Samsung S5P platforms has been fully converted to device tree, so old platform device based init data can be now removed. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-core.c | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index cef2a7f07cdb..b1c1cea82a27 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1154,26 +1154,6 @@ static const struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static const struct fimc_variant fimc0_variant_s5p = { - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 8, - .min_vsize_align = 16, - .pix_limit = &s5p_pix_limit[0], -}; - -static const struct fimc_variant fimc2_variant_s5p = { - .has_cam_if = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 8, - .min_vsize_align = 16, - .pix_limit = &s5p_pix_limit[1], -}; - static const struct fimc_variant fimc0_variant_s5pv210 = { .has_inp_rot = 1, .has_out_rot = 1, @@ -1206,18 +1186,6 @@ static const struct fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -/* S5PC100 */ -static const struct fimc_drvdata fimc_drvdata_s5p = { - .variant = { - [0] = &fimc0_variant_s5p, - [1] = &fimc0_variant_s5p, - [2] = &fimc2_variant_s5p, - }, - .num_entities = 3, - .lclk_frequency = 133000000UL, - .out_buf_count = 4, -}; - /* S5PV210, S5PC110 */ static const struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { @@ -1251,23 +1219,6 @@ static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { .out_buf_count = 32, }; -static const struct platform_device_id fimc_driver_ids[] = { - { - .name = "s5p-fimc", - .driver_data = (unsigned long)&fimc_drvdata_s5p, - }, { - .name = "s5pv210-fimc", - .driver_data = (unsigned long)&fimc_drvdata_s5pv210, - }, { - .name = "exynos4-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4210, - }, { - .name = "exynos4x12-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, - }, - { }, -}; - static const struct of_device_id fimc_of_match[] = { { .compatible = "samsung,s5pv210-fimc", @@ -1290,7 +1241,6 @@ static const struct dev_pm_ops fimc_pm_ops = { static struct platform_driver fimc_driver = { .probe = fimc_probe, .remove = fimc_remove, - .id_table = fimc_driver_ids, .driver = { .of_match_table = fimc_of_match, .name = FIMC_DRIVER_NAME, From adbedc29524a31cc6196d991acc04ee797abc11b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 15 Nov 2015 18:08:23 -0200 Subject: [PATCH 012/173] [media] s5p-tv: constify mxr_layer_ops structures The mxr_layer_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer.h | 2 +- drivers/media/platform/s5p-tv/mixer_grp_layer.c | 2 +- drivers/media/platform/s5p-tv/mixer_video.c | 2 +- drivers/media/platform/s5p-tv/mixer_vp_layer.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index 42cd2709c41c..4dd62a918fcf 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -300,7 +300,7 @@ void mxr_release_video(struct mxr_device *mdev); struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, struct mxr_layer_ops *ops); + int idx, char *name, const struct mxr_layer_ops *ops); void mxr_base_layer_release(struct mxr_layer *layer); void mxr_layer_release(struct mxr_layer *layer); diff --git a/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c index db3163b23ea0..d4d2564f7de7 100644 --- a/drivers/media/platform/s5p-tv/mixer_grp_layer.c +++ b/drivers/media/platform/s5p-tv/mixer_grp_layer.c @@ -235,7 +235,7 @@ struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx) { struct mxr_layer *layer; int ret; - struct mxr_layer_ops ops = { + const struct mxr_layer_ops ops = { .release = mxr_graph_layer_release, .buffer_set = mxr_graph_buffer_set, .stream_set = mxr_graph_stream_set, diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index d9e7f030294c..7ab5578a0405 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -1070,7 +1070,7 @@ static void mxr_vfd_release(struct video_device *vdev) } struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, struct mxr_layer_ops *ops) + int idx, char *name, const struct mxr_layer_ops *ops) { struct mxr_layer *layer; diff --git a/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c index dd002a497dbb..6fa6f673f53b 100644 --- a/drivers/media/platform/s5p-tv/mixer_vp_layer.c +++ b/drivers/media/platform/s5p-tv/mixer_vp_layer.c @@ -207,7 +207,7 @@ struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx) { struct mxr_layer *layer; int ret; - struct mxr_layer_ops ops = { + const struct mxr_layer_ops ops = { .release = mxr_vp_layer_release, .buffer_set = mxr_vp_buffer_set, .stream_set = mxr_vp_stream_set, From 26a7ed9c18193dc7a3dfba33e3c711822f4bdd29 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 13 Apr 2016 16:29:31 -0300 Subject: [PATCH 013/173] [media] exynos-gsc: remove an always false condition As reported by smatch: drivers/media/platform/exynos-gsc/gsc-core.c:1073 gsc_probe() warn: impossible condition '(gsc->id < 0) => (0-65535 < 0)' drivers/media/platform/exynos-gsc/gsc-core.c: In function 'gsc_probe': drivers/media/platform/exynos-gsc/gsc-core.c:1073:51: warning: comparison is always false due to limited range of data type [-Wtype-limits] if (gsc->id >= drv_data->num_entities || gsc->id < 0) { ^ gsc->id is an u16, so it can never be a negative number. So, remove the always false condition. Fixes: c1ac057173ba "[media] exynos-gsc: remove non-device-tree init code" Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 032a423fb892..c595723f5031 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1070,7 +1070,7 @@ static int gsc_probe(struct platform_device *pdev) return -ENOMEM; gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); - if (gsc->id >= drv_data->num_entities || gsc->id < 0) { + if (gsc->id >= drv_data->num_entities) { dev_err(dev, "Invalid platform device id: %d\n", gsc->id); return -EINVAL; } From 7bbe7813290df9fda0c175b7a703325720594912 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Mar 2016 11:57:23 -0300 Subject: [PATCH 014/173] [media] v4l2: add device_caps to struct video_device Instead of letting drivers fill in device_caps at querycap time, let them fill it in when the video device is registered. This has the advantage that in the future the v4l2 core can access the video device's capabilities and take decisions based on that. Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++ include/media/v4l2-dev.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 170dd68d27f4..6bf5a3ecd126 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1020,9 +1020,12 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_capability *cap = (struct v4l2_capability *)arg; + struct video_device *vfd = video_devdata(file); int ret; cap->version = LINUX_VERSION_CODE; + cap->device_caps = vfd->device_caps; + cap->capabilities = vfd->device_caps | V4L2_CAP_DEVICE_CAPS; ret = ops->vidioc_querycap(file, fh, cap); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 76056ab5c5bd..25a3190308fb 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -92,6 +92,9 @@ struct video_device /* device ops */ const struct v4l2_file_operations *fops; + /* device capabilities as used in v4l2_capabilities */ + u32 device_caps; + /* sysfs */ struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ From 3e43cb33f637d77405ccddc81a6d76acd57ff71c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Mar 2016 11:57:24 -0300 Subject: [PATCH 015/173] [media] v4l2-pci-skeleton.c: fill in device_caps in video_device With the new core support for the caps the driver no longer needs to set device_caps and capabilities in the querycap call. Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-pci-skeleton.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/Documentation/video4linux/v4l2-pci-skeleton.c index 79af0c041056..a55cf94ac907 100644 --- a/Documentation/video4linux/v4l2-pci-skeleton.c +++ b/Documentation/video4linux/v4l2-pci-skeleton.c @@ -308,9 +308,6 @@ static int skeleton_querycap(struct file *file, void *priv, strlcpy(cap->card, "V4L2 PCI Skeleton", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(skel->pdev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -872,6 +869,8 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vdev->release = video_device_release_empty; vdev->fops = &skel_fops, vdev->ioctl_ops = &skel_ioctl_ops, + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; /* * The main serialization lock. All ioctls are serialized by this * lock. Exception: if q->lock is set, then the streaming ioctls From 9765a32cd802709d152a0d73d0cbfd07c9f49808 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Mar 2016 11:57:25 -0300 Subject: [PATCH 016/173] [media] vivid: set device_caps in video_device This simplifies the querycap function. Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index ec125becb7af..c14da84af09b 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -200,27 +200,12 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct vivid_dev *dev = video_drvdata(file); - struct video_device *vdev = video_devdata(file); strcpy(cap->driver, "vivid"); strcpy(cap->card, "vivid"); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev->v4l2_dev.name); - if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps = dev->vid_cap_caps; - if (vdev->vfl_type == VFL_TYPE_GRABBER && vdev->vfl_dir == VFL_DIR_TX) - cap->device_caps = dev->vid_out_caps; - else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps = dev->vbi_cap_caps; - else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX) - cap->device_caps = dev->vbi_out_caps; - else if (vdev->vfl_type == VFL_TYPE_SDR) - cap->device_caps = dev->sdr_cap_caps; - else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps = dev->radio_rx_caps; - else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX) - cap->device_caps = dev->radio_tx_caps; cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | dev->vbi_cap_caps | dev->vbi_out_caps | dev->radio_rx_caps | dev->radio_tx_caps | @@ -1135,6 +1120,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name)); vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->vid_cap_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = &dev->vb_vid_cap_q; @@ -1160,6 +1146,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) vfd->vfl_dir = VFL_DIR_TX; vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->vid_out_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = &dev->vb_vid_out_q; @@ -1184,6 +1171,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name)); vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->vbi_cap_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = &dev->vb_vbi_cap_q; @@ -1207,6 +1195,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) vfd->vfl_dir = VFL_DIR_TX; vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->vbi_out_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = &dev->vb_vbi_out_q; @@ -1229,6 +1218,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name)); vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->sdr_cap_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = &dev->vb_sdr_cap_q; @@ -1247,6 +1237,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name)); vfd->fops = &vivid_radio_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->radio_rx_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->lock = &dev->mutex; @@ -1265,6 +1256,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) vfd->vfl_dir = VFL_DIR_TX; vfd->fops = &vivid_radio_fops; vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->radio_tx_caps; vfd->release = video_device_release_empty; vfd->v4l2_dev = &dev->v4l2_dev; vfd->lock = &dev->mutex; From 54ace1cfd4358fd11112f17cc711eea234d5ab9e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Feb 2016 07:16:39 -0300 Subject: [PATCH 017/173] [media] v4l2-ioctl: simplify code Instead of a big if at the beginning, just check if g_selection == NULL and call the cropcap op immediately and return the result. No functional changes in this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 57 ++++++++++++++++------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 6bf5a3ecd126..3cf8d3adab03 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2160,34 +2160,41 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_cropcap *p = arg; + struct v4l2_selection s = { .type = p->type }; + int ret; - if (ops->vidioc_g_selection) { - struct v4l2_selection s = { .type = p->type }; - int ret; - - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; - - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; + if (ops->vidioc_g_selection == NULL) { + /* + * The determine_valid_ioctls() call already should ensure + * that ops->vidioc_cropcap != NULL, but just in case... + */ + if (ops->vidioc_cropcap) + return ops->vidioc_cropcap(file, fh, p); + return -ENOTTY; } + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; + + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; + /* setting trivial pixelaspect */ p->pixelaspect.numerator = 1; p->pixelaspect.denominator = 1; From 1254880834d08ad288b9706b5bd791dc11516e40 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 7 Mar 2016 07:22:23 -0300 Subject: [PATCH 018/173] [media] cx231xx: fix memory leak When we returned on error we missed freeing p_current_fw and p_buffer. Signed-off-by: Sudip Mukherjee Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-417.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index c9320d6c6131..3636d8d0abd3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -966,6 +966,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) p_buffer = vmalloc(4096); if (p_buffer == NULL) { dprintk(2, "FAIL!!!\n"); + vfree(p_current_fw); return -1; } @@ -989,6 +990,8 @@ static int cx231xx_load_firmware(struct cx231xx *dev) if (retval != 0) { dev_err(dev->dev, "%s: Error with mc417_register_write\n", __func__); + vfree(p_current_fw); + vfree(p_buffer); return -1; } @@ -1001,6 +1004,8 @@ static int cx231xx_load_firmware(struct cx231xx *dev) CX231xx_FIRM_IMAGE_NAME); dev_err(dev->dev, "Please fix your hotplug setup, the board will not work without firmware loaded!\n"); + vfree(p_current_fw); + vfree(p_buffer); return -1; } @@ -1009,6 +1014,8 @@ static int cx231xx_load_firmware(struct cx231xx *dev) "ERROR: Firmware size mismatch (have %zd, expected %d)\n", firmware->size, CX231xx_FIRM_IMAGE_SIZE); release_firmware(firmware); + vfree(p_current_fw); + vfree(p_buffer); return -1; } @@ -1016,6 +1023,8 @@ static int cx231xx_load_firmware(struct cx231xx *dev) dev_err(dev->dev, "ERROR: Firmware magic mismatch, wrong file?\n"); release_firmware(firmware); + vfree(p_current_fw); + vfree(p_buffer); return -1; } From ccc5429f7bf9c78d64f1bb03c578f1641ca72fe4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 13 Apr 2016 17:02:20 -0300 Subject: [PATCH 019/173] [media] cx231xx: return proper error codes at cx231xx-417.c Instead of returning -1, return valid error codes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-417.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 3636d8d0abd3..00da024b47a6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -360,7 +360,7 @@ static int wait_for_mci_complete(struct cx231xx *dev) if (count++ > 100) { dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio); - return -1; + return -EIO; } } return 0; @@ -856,7 +856,7 @@ static int cx231xx_find_mailbox(struct cx231xx *dev) } } dprintk(3, "Mailbox signature values not found!\n"); - return -1; + return -EIO; } static void mci_write_memory_to_gpio(struct cx231xx *dev, u32 address, u32 value, @@ -960,14 +960,14 @@ static int cx231xx_load_firmware(struct cx231xx *dev) p_fw = p_current_fw; if (p_current_fw == NULL) { dprintk(2, "FAIL!!!\n"); - return -1; + return -ENOMEM; } p_buffer = vmalloc(4096); if (p_buffer == NULL) { dprintk(2, "FAIL!!!\n"); vfree(p_current_fw); - return -1; + return -ENOMEM; } dprintk(2, "%s()\n", __func__); @@ -992,7 +992,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) "%s: Error with mc417_register_write\n", __func__); vfree(p_current_fw); vfree(p_buffer); - return -1; + return retval; } retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME, @@ -1006,7 +1006,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) "Please fix your hotplug setup, the board will not work without firmware loaded!\n"); vfree(p_current_fw); vfree(p_buffer); - return -1; + return retval; } if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) { @@ -1016,7 +1016,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) release_firmware(firmware); vfree(p_current_fw); vfree(p_buffer); - return -1; + return -EINVAL; } if (0 != memcmp(firmware->data, magic, 8)) { @@ -1025,7 +1025,7 @@ static int cx231xx_load_firmware(struct cx231xx *dev) release_firmware(firmware); vfree(p_current_fw); vfree(p_buffer); - return -1; + return -EINVAL; } initGPIO(dev); @@ -1140,21 +1140,21 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) if (retval < 0) { dev_err(dev->dev, "%s: mailbox < 0, error\n", __func__); - return -1; + return retval; } dev->cx23417_mailbox = retval; retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); if (retval < 0) { dev_err(dev->dev, "ERROR: cx23417 firmware ping failed!\n"); - return -1; + return retval; } retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version); if (retval < 0) { dev_err(dev->dev, "ERROR: cx23417 firmware get encoder: version failed!\n"); - return -1; + return retval; } dprintk(1, "cx23417 firmware version is 0x%08x\n", version); msleep(200); From 2a518f8e87a718b482a5ac7ffa9590cc7d86004f Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 7 Mar 2016 07:26:55 -0300 Subject: [PATCH 020/173] [media] dw2102: fix unreleased firmware On the particular case when the product id is 0x2101 we have requested for a firmware but after processing it we missed releasing it. Signed-off-by: Sudip Mukherjee Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 6d0dd859d684..1f35f3decf39 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1843,6 +1843,9 @@ static int dw2102_load_firmware(struct usb_device *dev, msleep(100); kfree(p); } + + if (le16_to_cpu(dev->descriptor.idProduct) == 0x2101) + release_firmware(fw); return ret; } From baf43c6eace43868e490f18560287fa3481b2159 Mon Sep 17 00:00:00 2001 From: Tiffany Lin Date: Mon, 14 Mar 2016 08:16:14 -0300 Subject: [PATCH 021/173] [media] media: v4l2-compat-ioctl32: fix missing reserved field copy in put_v4l2_create32 In v4l2-compliance utility, test VIDIOC_CREATE_BUFS will check whether reserved filed of v4l2_create_buffers filled with zero Reserved field is filled with zero in v4l_create_bufs. This patch copy reserved field of v4l2_create_buffer from kernel space to user space Signed-off-by: Tiffany Lin Signed-off-by: Hans Verkuil Cc: # for v3.19 and up Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 019644ff627d..bacecbd68a6d 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -280,7 +280,8 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || - copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format))) + copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) || + copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved))) return -EFAULT; return __put_v4l2_format32(&kp->format, &up->format); } From c16352b5b35d8f619028ebf855ce42c1f99649e6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 14 Mar 2016 19:40:07 -0300 Subject: [PATCH 022/173] [media] cobalt: add MTD dependency The cobalt driver fails to link when it is built-in and MTD is disabled or a loadable module: drivers/media/built-in.o: In function `cobalt_flash_probe': :(.text+0xb8b46): undefined reference to `mtd_device_parse_register' :(.text+0xb8b88): undefined reference to `do_map_probe' drivers/media/built-in.o: In function `cobalt_flash_remove': :(.text+0xb8bb4): undefined reference to `mtd_device_unregister' :(.text+0xb8bbe): undefined reference to `map_destroy' This adds a Kconfig dependency to ensure we can call the API. Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig index a01f0cc745cc..70343829a125 100644 --- a/drivers/media/pci/cobalt/Kconfig +++ b/drivers/media/pci/cobalt/Kconfig @@ -4,6 +4,7 @@ config VIDEO_COBALT depends on PCI_MSI && MTD_COMPLEX_MAPPINGS depends on GPIOLIB || COMPILE_TEST depends on SND + depends on MTD select I2C_ALGOBIT select VIDEO_ADV7604 select VIDEO_ADV7511 From 0fb504001192c1df62c847a8bb6558753c36ebef Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 14 Mar 2016 19:40:08 -0300 Subject: [PATCH 023/173] [media] am437x-vfpe: fix typo in vpfe_get_app_input_index gcc-6 points out an obviously silly comparison in vpfe_get_app_input_index(): drivers/media/platform/am437x/am437x-vpfe.c: In function 'vpfe_get_app_input_index': drivers/media/platform/am437x/am437x-vpfe.c:1709:27: warning: self-comparison always evaluats to true [-Wtautological-compare] client->adapter->nr == client->adapter->nr) { ^~ This was introduced in a slighly incorrect conversion, and it's clear that the comparison was meant to compare the iterator to the current subdev instead, as we do in the line above. Fixes: d37232390fd4 ("[media] media: am437x-vpfe: match the OF node/i2c addr instead of name") Signed-off-by: Arnd Bergmann Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index de32e3a3d4d1..e6a7bff4650c 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1706,7 +1706,7 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe, sdinfo = &cfg->sub_devs[i]; client = v4l2_get_subdevdata(sdinfo->sd); if (client->addr == curr_client->addr && - client->adapter->nr == client->adapter->nr) { + client->adapter->nr == curr_client->adapter->nr) { if (vpfe->current_input >= 1) return -1; *app_input_index = j + vpfe->current_input; From e4bccada44c177cde31b9a236b7dfd7f76d403ed Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2016 04:04:12 -0300 Subject: [PATCH 024/173] [media] am437x-vpfe: fix an uninitialized variable bug If we are doing V4L2_FIELD_NONE then "ret" is used uninitialized. Fixes: 417d2e507edc ('[media] media: platform: add VPFE capture driver support for AM437X') Signed-off-by: Dan Carpenter Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index e6a7bff4650c..e749eb7c3be9 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1047,7 +1047,7 @@ static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe, static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) { enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; - int ret; + int ret = 0; vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n"); From 60587bd0680507f48ae3a7360983228fd207de8a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2016 04:05:20 -0300 Subject: [PATCH 025/173] [media] cx23885: uninitialized variable in cx23885_av_work_handler() The "handled" variable could be uninitialized if the interrupt_service_routine() call back hasn't been implimented or if it has been implemented but doesn't initialize "handled" to zero at the start. For example, adv76xx_isr() only sets "handled" to true. Fixes: 44b153ca639f ('[media] m5mols: Add ISO sensitivity controls') Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-av.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c index 877dad89107e..e7d4406f9abd 100644 --- a/drivers/media/pci/cx23885/cx23885-av.c +++ b/drivers/media/pci/cx23885/cx23885-av.c @@ -24,7 +24,7 @@ void cx23885_av_work_handler(struct work_struct *work) { struct cx23885_dev *dev = container_of(work, struct cx23885_dev, cx25840_work); - bool handled; + bool handled = false; v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine, PCI_MSK_AV_CORE, &handled); From cc7666a3948c6a554fa4c39b4fb4ccbdbdce1343 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2016 04:07:10 -0300 Subject: [PATCH 026/173] [media] m5mols: potential uninitialized variable Smatch complains that there are some paths where "status" isn't initialized. The code does assume that m5mols_read_u8() can fail so it seems as if Smatch is correct. Let's initialize it to REG_ISO_AUTO which is zero. Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/m5mols/m5mols_controls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c index a60931e66312..c2218c0a9e6f 100644 --- a/drivers/media/i2c/m5mols/m5mols_controls.c +++ b/drivers/media/i2c/m5mols/m5mols_controls.c @@ -405,7 +405,7 @@ static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); int ret = 0; - u8 status; + u8 status = REG_ISO_AUTO; v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", __func__, ctrl->name, info->isp_ready); From 20674818c4a32ad1bfb1ea798e8c1fbed338ad71 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 6 Apr 2016 00:26:56 -0300 Subject: [PATCH 027/173] [media] au0828: remove unused macro An V4L2_CID_PRIVATE_SHARPNESS macro is defined in the au0828 driver, but never used. Remove it. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 87f32846f1c0..dd7b378fe070 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -55,7 +55,6 @@ #define NTSC_STD_H 480 #define AU0828_INTERLACED_DEFAULT 1 -#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) /* Defination for AU0828 USB transfer */ #define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */ From f343f8484aa9a925b9536b676633af1b9604553e Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 7 Mar 2016 22:03:55 -0300 Subject: [PATCH 028/173] [media] rcar_vin: Use ARCH_RENESAS Make use of ARCH_RENESAS in place of ARCH_SHMOBILE. This is part of an ongoing process to migrate from ARCH_SHMOBILE to ARCH_RENESAS the motivation for which being that RENESAS seems to be a more appropriate name than SHMOBILE for the majority of Renesas ARM based SoCs. Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 355298989dd8..08db3b040bbe 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -28,7 +28,7 @@ config VIDEO_PXA27x config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) support" depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP From 345900be580b52d6993d262ca7052cf9d449ba6c Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 7 Mar 2016 22:03:58 -0300 Subject: [PATCH 029/173] [media] sh_mobile_ceu_camera: Remove dependency on SUPERH A dependency on ARCH_SHMOBILE seems to be the best option for sh_mobile_ceu_camera: * For Super H based SoCs: sh_mobile_ceu is used on SH_AP325RXA, SH_ECOVEC, SH_KFR2R09, SH_MIGOR, and SH_7724_SOLUTION_ENGINE which depend on CPU_SUBTYPE_SH7722, CPU_SUBTYPE_SH7723, or CPU_SUBTYPE_SH7724 which all select ARCH_SHMOBILE. * For ARM Based SoCs: Since the removal of legacy (non-multiplatform) support this driver has not been used by any Renesas ARM based SoCs. The Renesas ARM based SoCs currently select ARCH_SHMOBILE, however, it is planned that this will no longer be the case. This is part of an ongoing process to migrate from ARCH_SHMOBILE to ARCH_RENESAS the motivation for which being that RENESAS seems to be a more appropriate name than SHMOBILE for the majority of Renesas ARM based SoCs. Thanks to Geert Uytterhoeven for analysis and portions of the change log text. Cc: Geert Uytterhoeven Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 08db3b040bbe..83029a4854ae 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -45,7 +45,7 @@ config VIDEO_SH_MOBILE_CSI2 config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK - depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST + depends on ARCH_SHMOBILE || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP From bd546882ff3476718d6df872c1c95aedcc5ed37d Mon Sep 17 00:00:00 2001 From: Yoshihiro Kaneko Date: Mon, 14 Mar 2016 21:40:26 -0300 Subject: [PATCH 030/173] [media] soc_camera: rcar_vin: add R-Car Gen 2 and 3 fallback compatibility strings Add fallback compatibility string for R-Car Gen 1 and 2. In the case of Renesas R-Car hardware we know that there are generations of SoCs, e.g. Gen 2 and 3. But beyond that it's unclear what the relationship between IP blocks might be. For example, I believe that r8a7790 is older than r8a7791 but that doesn't imply that the latter is a descendant of the former or vice versa. We can, however, by examining the documentation and behaviour of the hardware at run-time observe that the current driver implementation appears to be compatible with the IP blocks on SoCs within a given generation. For the above reasons and convenience when enabling new SoCs a per-generation fallback compatibility string scheme being adopted for drivers for Renesas SoCs. Signed-off-by: Yoshihiro Kaneko Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 11 +++++++++-- drivers/media/platform/soc_camera/rcar_vin.c | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 619193ccf7ff..4266123888ed 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -5,7 +5,7 @@ The rcar_vin device provides video input capabilities for the Renesas R-Car family of devices. The current blocks are always slaves and suppot one input channel which can be either RGB, YUYV or BT656. - - compatible: Must be one of the following + - compatible: Must be one or more of the following - "renesas,vin-r8a7795" for the R8A7795 device - "renesas,vin-r8a7794" for the R8A7794 device - "renesas,vin-r8a7793" for the R8A7793 device @@ -13,6 +13,13 @@ channel which can be either RGB, YUYV or BT656. - "renesas,vin-r8a7790" for the R8A7790 device - "renesas,vin-r8a7779" for the R8A7779 device - "renesas,vin-r8a7778" for the R8A7778 device + - "renesas,rcar-gen2-vin" for a generic R-Car Gen2 compatible device. + - "renesas,rcar-gen3-vin" for a generic R-Car Gen3 compatible device. + + When compatible with the generic version nodes must list the + SoC-specific version corresponding to the platform first + followed by the generic version. + - reg: the register base and size for the device registers - interrupts: the interrupt for the device - clocks: Reference to the parent clock @@ -37,7 +44,7 @@ Device node example }; vin0: vin@0xe6ef0000 { - compatible = "renesas,vin-r8a7790"; + compatible = "renesas,vin-r8a7790", "renesas,rcar-gen2-vin"; clocks = <&mstp8_clks R8A7790_CLK_VIN0>; reg = <0 0xe6ef0000 0 0x1000>; interrupts = <0 188 IRQ_TYPE_LEVEL_HIGH>; diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 3b8edf458964..3f9c1b8456c3 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -1845,6 +1845,8 @@ static const struct of_device_id rcar_vin_of_table[] = { { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 }, + { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, { }, }; MODULE_DEVICE_TABLE(of, rcar_vin_of_table); From 1d260123cb5c88574de9b5147eddc243f83b77a8 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 14 Mar 2016 21:40:27 -0300 Subject: [PATCH 031/173] [media] soc_camera: rcar_vin: add device tree support for r8a7792 Simply document new compatibility string. As a previous patch adds a generic R-Car Gen2 compatibility string there appears to be no need for a driver updates. By documenting this compat string it may be used in DTSs shipped, for example as part of ROMs. It must be used in conjunction with the Gen2 fallback compat string. At this time there are no known differences between the r8a7792 IP block and that implemented by the driver for the Gen2 fallback compat string. Thus there is no need to update the driver as the use of the Gen2 fallback compat string will activate the correct code in the current driver while leaving the option for r8a7792-specific driver code to be activated in an updated driver should the need arise. Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 4266123888ed..6a4e61cbe011 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -9,6 +9,7 @@ channel which can be either RGB, YUYV or BT656. - "renesas,vin-r8a7795" for the R8A7795 device - "renesas,vin-r8a7794" for the R8A7794 device - "renesas,vin-r8a7793" for the R8A7793 device + - "renesas,vin-r8a7792" for the R8A7792 device - "renesas,vin-r8a7791" for the R8A7791 device - "renesas,vin-r8a7790" for the R8A7790 device - "renesas,vin-r8a7779" for the R8A7779 device From b76a2a8cb6f6d9da711305d805156b40c698e94f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Feb 2016 08:45:44 -0300 Subject: [PATCH 032/173] [media] media: Add obj_type field to struct media_entity Code that processes media entities can require knowledge of the structure type that embeds a particular media entity instance in order to cast the entity to the proper object type. This needs is shown by the presence of the is_media_entity_v4l2_io and is_media_entity_v4l2_subdev functions. The implementation of those two functions relies on the entity function field, which is both a wrong and an inefficient design, without even mentioning the maintenance issue involved in updating the functions every time a new entity function is added. Fix this by adding add an obj_type field to the media entity structure to carry the information. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 1 + drivers/media/v4l2-core/v4l2-subdev.c | 1 + include/media/media-entity.h | 76 ++++++++++++++------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index d8e5994cccf1..70b559d7ca80 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -735,6 +735,7 @@ static int video_register_media_controller(struct video_device *vdev, int type) if (!vdev->v4l2_dev->mdev) return 0; + vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; vdev->entity.function = MEDIA_ENT_F_UNKNOWN; switch (type) { diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d63083803144..0fa60801a428 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -584,6 +584,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) sd->host_priv = NULL; #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.name = sd->name; + sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; #endif } diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 6dc9e4e8cbd4..1986340a8cf2 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -187,11 +187,39 @@ struct media_entity_operations { int (*link_validate)(struct media_link *link); }; +/** + * enum media_entity_type - Media entity type + * + * @MEDIA_ENTITY_TYPE_BASE: + * The entity isn't embedded in another subsystem structure. + * @MEDIA_ENTITY_TYPE_VIDEO_DEVICE: + * The entity is embedded in a struct video_device instance. + * @MEDIA_ENTITY_TYPE_V4L2_SUBDEV: + * The entity is embedded in a struct v4l2_subdev instance. + * + * Media entity objects are often not instantiated directly, but the media + * entity structure is inherited by (through embedding) other subsystem-specific + * structures. The media entity type identifies the type of the subclass + * structure that implements a media entity instance. + * + * This allows runtime type identification of media entities and safe casting to + * the correct object type. For instance, a media entity structure instance + * embedded in a v4l2_subdev structure instance will have the type + * MEDIA_ENTITY_TYPE_V4L2_SUBDEV and can safely be cast to a v4l2_subdev + * structure using the container_of() macro. + */ +enum media_entity_type { + MEDIA_ENTITY_TYPE_BASE, + MEDIA_ENTITY_TYPE_VIDEO_DEVICE, + MEDIA_ENTITY_TYPE_V4L2_SUBDEV, +}; + /** * struct media_entity - A media entity graph object. * * @graph_obj: Embedded structure containing the media object common data. * @name: Entity name. + * @obj_type: Type of the object that implements the media_entity. * @function: Entity main function, as defined in uapi/media.h * (MEDIA_ENT_F_*) * @flags: Entity flags, as defined in uapi/media.h (MEDIA_ENT_FL_*) @@ -220,6 +248,7 @@ struct media_entity_operations { struct media_entity { struct media_gobj graph_obj; /* must be first field in struct */ const char *name; + enum media_entity_type obj_type; u32 function; unsigned long flags; @@ -329,56 +358,29 @@ static inline u32 media_gobj_gen_id(enum media_gobj_type type, u64 local_id) } /** - * is_media_entity_v4l2_io() - identify if the entity main function - * is a V4L2 I/O - * + * is_media_entity_v4l2_io() - Check if the entity is a video_device * @entity: pointer to entity * - * Return: true if the entity main function is one of the V4L2 I/O types - * (video, VBI or SDR radio); false otherwise. + * Return: true if the entity is an instance of a video_device object and can + * safely be cast to a struct video_device using the container_of() macro, or + * false otherwise. */ static inline bool is_media_entity_v4l2_io(struct media_entity *entity) { - if (!entity) - return false; - - switch (entity->function) { - case MEDIA_ENT_F_IO_V4L: - case MEDIA_ENT_F_IO_VBI: - case MEDIA_ENT_F_IO_SWRADIO: - return true; - default: - return false; - } + return entity && entity->obj_type == MEDIA_ENTITY_TYPE_VIDEO_DEVICE; } /** - * is_media_entity_v4l2_subdev - return true if the entity main function is - * associated with the V4L2 API subdev usage - * + * is_media_entity_v4l2_subdev() - Check if the entity is a v4l2_subdev * @entity: pointer to entity * - * This is an ancillary function used by subdev-based V4L2 drivers. - * It checks if the entity function is one of functions used by a V4L2 subdev, - * e. g. camera-relatef functions, analog TV decoder, TV tuner, V4L2 DSPs. + * Return: true if the entity is an instance of a v4l2_subdev object and can + * safely be cast to a struct v4l2_subdev using the container_of() macro, or + * false otherwise. */ static inline bool is_media_entity_v4l2_subdev(struct media_entity *entity) { - if (!entity) - return false; - - switch (entity->function) { - case MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN: - case MEDIA_ENT_F_CAM_SENSOR: - case MEDIA_ENT_F_FLASH: - case MEDIA_ENT_F_LENS: - case MEDIA_ENT_F_ATV_DECODER: - case MEDIA_ENT_F_TUNER: - return true; - - default: - return false; - } + return entity && entity->obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV; } /** From 45b46879a785678e08953c8f97df945bf634e472 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Feb 2016 08:45:45 -0300 Subject: [PATCH 033/173] [media] media: Rename is_media_entity_v4l2_io to is_media_entity_v4l2_video_device All users of is_media_entity_v4l2_io() (the exynos4-is, omap3isp, davince_vpfe and omap4iss drivers and the v4l2-mc power management code) use the function to check whether entities are video_device instances, either to ensure they can cast the entity to a struct video_device, or to count the number of video nodes users. The purpose of the function is thus to identify whether the media entity instance is an instance of the video_device object, not to check whether it can perform I/O. Rename it accordingly, we will introduce a more specific is_media_entity_v4l2_io() check when needed. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 4 ++-- drivers/media/platform/omap3isp/ispvideo.c | 2 +- drivers/media/v4l2-core/v4l2-mc.c | 2 +- drivers/staging/media/davinci_vpfe/vpfe_video.c | 2 +- drivers/staging/media/omap4iss/iss_video.c | 2 +- include/media/media-entity.h | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 48f62dc4453e..04348b502232 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1132,7 +1132,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, media_entity_graph_walk_start(graph, entity); while ((entity = media_entity_graph_walk_next(graph))) { - if (!is_media_entity_v4l2_io(entity)) + if (!is_media_entity_v4l2_video_device(entity)) continue; ret = __fimc_md_modify_pipeline(entity, enable); @@ -1147,7 +1147,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, media_entity_graph_walk_start(graph, entity_err); while ((entity_err = media_entity_graph_walk_next(graph))) { - if (!is_media_entity_v4l2_io(entity_err)) + if (!is_media_entity_v4l2_video_device(entity_err)) continue; __fimc_md_modify_pipeline(entity_err, !enable); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index ac76d2901501..1b1a95d546f6 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -251,7 +251,7 @@ static int isp_video_get_graph_data(struct isp_video *video, if (entity == &video->video.entity) continue; - if (!is_media_entity_v4l2_io(entity)) + if (!is_media_entity_v4l2_video_device(entity)) continue; __video = to_isp_video(media_entity_to_video_device(entity)); diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 2228cd3a846e..ca94bded3386 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -263,7 +263,7 @@ static int pipeline_pm_use_count(struct media_entity *entity, media_entity_graph_walk_start(graph, entity); while ((entity = media_entity_graph_walk_next(graph))) { - if (is_media_entity_v4l2_io(entity)) + if (is_media_entity_v4l2_video_device(entity)) use += entity->use_count; } diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index b793c04028a3..df4f298617c0 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -154,7 +154,7 @@ static int vpfe_prepare_pipeline(struct vpfe_video_device *video) while ((entity = media_entity_graph_walk_next(&graph))) { if (entity == &video->video_dev.entity) continue; - if (!is_media_entity_v4l2_io(entity)) + if (!is_media_entity_v4l2_video_device(entity)) continue; far_end = to_vpfe_video(media_entity_to_video_device(entity)); if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index f54349bce4de..cf8da23558bb 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -223,7 +223,7 @@ iss_video_far_end(struct iss_video *video) if (entity == &video->video.entity) continue; - if (!is_media_entity_v4l2_io(entity)) + if (!is_media_entity_v4l2_video_device(entity)) continue; far_end = to_iss_video(media_entity_to_video_device(entity)); diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 1986340a8cf2..e0295eefd702 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -358,14 +358,14 @@ static inline u32 media_gobj_gen_id(enum media_gobj_type type, u64 local_id) } /** - * is_media_entity_v4l2_io() - Check if the entity is a video_device + * is_media_entity_v4l2_video_device() - Check if the entity is a video_device * @entity: pointer to entity * * Return: true if the entity is an instance of a video_device object and can * safely be cast to a struct video_device using the container_of() macro, or * false otherwise. */ -static inline bool is_media_entity_v4l2_io(struct media_entity *entity) +static inline bool is_media_entity_v4l2_video_device(struct media_entity *entity) { return entity && entity->obj_type == MEDIA_ENTITY_TYPE_VIDEO_DEVICE; } From 9b02cbb3ede89b5cd84bbe4ef493bd130d76b070 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 24 Apr 2015 20:06:31 -0300 Subject: [PATCH 034/173] [media] v4l: subdev: Add pad config allocator and init Add a new subdev operation to initialize a subdev pad config array, and a helper function to allocate and initialize the array. This can be used by bridge drivers to implement try format based on subdev pad operations. Signed-off-by: Laurent Pinchart Acked-by: Vaibhav Hiremath Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 39 ++++++++++++++++++++++++--- include/media/v4l2-subdev.h | 8 ++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 0fa60801a428..224ea6028b55 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -35,9 +35,11 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); - if (fh->pad == NULL) - return -ENOMEM; + if (sd->entity.num_pads) { + fh->pad = v4l2_subdev_alloc_pad_config(sd); + if (fh->pad == NULL) + return -ENOMEM; + } #endif return 0; } @@ -45,7 +47,7 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->pad); + v4l2_subdev_free_pad_config(fh->pad); fh->pad = NULL; #endif } @@ -569,6 +571,35 @@ int v4l2_subdev_link_validate(struct media_link *link) sink, link, &source_fmt, &sink_fmt); } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); + +struct v4l2_subdev_pad_config * +v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd) +{ + struct v4l2_subdev_pad_config *cfg; + int ret; + + if (!sd->entity.num_pads) + return NULL; + + cfg = kcalloc(sd->entity.num_pads, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return NULL; + + ret = v4l2_subdev_call(sd, pad, init_cfg, cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + kfree(cfg); + return NULL; + } + + return cfg; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_pad_config); + +void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg) +{ + kfree(cfg); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_free_pad_config); #endif /* CONFIG_MEDIA_CONTROLLER */ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 11e2dfec0198..32fc7a4beb5e 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -572,6 +572,7 @@ struct v4l2_subdev_pad_config { /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations * + * @init_cfg: initialize the pad config to default values * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl handler * code. * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl handler @@ -607,6 +608,8 @@ struct v4l2_subdev_pad_config { * may be adjusted by the subdev driver to device capabilities. */ struct v4l2_subdev_pad_ops { + int (*init_cfg)(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg); int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); @@ -801,7 +804,12 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt); int v4l2_subdev_link_validate(struct media_link *link); + +struct v4l2_subdev_pad_config * +v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd); +void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg); #endif /* CONFIG_MEDIA_CONTROLLER */ + void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops); From fa369c9366c1c695a02ccd88abd76d7f68162810 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 19 Feb 2016 23:12:26 -0200 Subject: [PATCH 035/173] [media] v4l: vsp1: Fix vsp1_du_atomic_(begin|flush) declarations The functions are void, make the declaration match the definition. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/media/vsp1.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/media/vsp1.h b/include/media/vsp1.h index cc541753896f..d01f7cb8f691 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -23,11 +23,11 @@ int vsp1_du_init(struct device *dev); int vsp1_du_setup_lif(struct device *dev, unsigned int width, unsigned int height); -int vsp1_du_atomic_begin(struct device *dev); +void vsp1_du_atomic_begin(struct device *dev); int vsp1_du_atomic_update(struct device *dev, unsigned int rpf, u32 pixelformat, unsigned int pitch, dma_addr_t mem[2], const struct v4l2_rect *src, const struct v4l2_rect *dst); -int vsp1_du_atomic_flush(struct device *dev); +void vsp1_du_atomic_flush(struct device *dev); #endif /* __MEDIA_VSP1_H__ */ From c1741af7d1d0f2f9d748939678c4d4cc33783069 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 19 Feb 2016 23:13:45 -0200 Subject: [PATCH 036/173] [media] v4l: vsp1: drm: Include correct header file The VSP1 DRM API is declared in , not . Fix it. This also reverts commit 18922936dc28 ("[media] vsp1_drm.h: add missing prototypes") that added the same declarations in a different header file. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 2 +- drivers/media/platform/vsp1/vsp1_drm.h | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 021fe5778cd1..8cf7c19f4344 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -13,10 +13,10 @@ #include #include -#include #include #include +#include #include "vsp1.h" #include "vsp1_bru.h" diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index f68056838319..7704038c3add 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -35,15 +35,4 @@ int vsp1_drm_init(struct vsp1_device *vsp1); void vsp1_drm_cleanup(struct vsp1_device *vsp1); int vsp1_drm_create_links(struct vsp1_device *vsp1); -int vsp1_du_init(struct device *dev); -int vsp1_du_setup_lif(struct device *dev, unsigned int width, - unsigned int height); -void vsp1_du_atomic_begin(struct device *dev); -int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, - u32 pixelformat, unsigned int pitch, - dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst); -void vsp1_du_atomic_flush(struct device *dev); - - #endif /* __VSP1_DRM_H__ */ From 6b1446bc7c89025e07037f9c238a60b6fbe3c207 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 28 Feb 2016 23:28:38 -0300 Subject: [PATCH 037/173] [media] v4l: vsp1: video: Fix coding style Commit 54b5a749b4f3 ("[media] v4l: vsp1: Use media entity enumeration interface") wasn't aligned with the driver coding style. Fix it by renaming the rval variable to ret. Furthermore shorten lines by accessing the media_device instance in a more straightforward fashion. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 72cc7d3729f8..b97bbdb1a256 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -175,31 +175,30 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, struct vsp1_rwpf *input, struct vsp1_rwpf *output) { - struct vsp1_entity *entity; struct media_entity_enum ent_enum; + struct vsp1_entity *entity; struct media_pad *pad; - int rval; bool bru_found = false; + int ret; input->location.left = 0; input->location.top = 0; - rval = media_entity_enum_init( - &ent_enum, input->entity.pads[RWPF_PAD_SOURCE].graph_obj.mdev); - if (rval) - return rval; + ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev); + if (ret < 0) + return ret; pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); while (1) { if (pad == NULL) { - rval = -EPIPE; + ret = -EPIPE; goto out; } /* We've reached a video node, that shouldn't have happened. */ if (!is_media_entity_v4l2_subdev(pad->entity)) { - rval = -EPIPE; + ret = -EPIPE; goto out; } @@ -229,14 +228,14 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, /* Ensure the branch has no loop. */ if (media_entity_enum_test_and_set(&ent_enum, &entity->subdev.entity)) { - rval = -EPIPE; + ret = -EPIPE; goto out; } /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { if (pipe->uds) { - rval = -EPIPE; + ret = -EPIPE; goto out; } @@ -256,12 +255,12 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, /* The last entity must be the output WPF. */ if (entity != &output->entity) - rval = -EPIPE; + ret = -EPIPE; out: media_entity_enum_cleanup(&ent_enum); - return rval; + return ret; } static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe, From 94d48e56d388f60d045f41749f89ba385f107d69 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 11 Feb 2016 19:32:00 -0200 Subject: [PATCH 038/173] [media] v4l: vsp1: VSPD instances have no LUT on Gen3 Remove the HAS_LUT flag in the corresponding device information entry. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 25750a0e4631..da43e3f35610 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -623,7 +623,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, - .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF, .rpf_count = 5, .wpf_count = 2, .num_bru_inputs = 5, From aa380ea0c54e491f7f31e8180514766dd3e6cd91 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 10:46:25 -0200 Subject: [PATCH 039/173] [media] v4l: vsp1: Use pipeline display list to decide how to write to modules This allows getting rid of the vsp1_device::use_dl field. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 12 ------------ drivers/media/platform/vsp1/vsp1_dl.c | 5 +---- drivers/media/platform/vsp1/vsp1_dl.h | 12 ++---------- drivers/media/platform/vsp1/vsp1_drv.c | 9 +++------ drivers/media/platform/vsp1/vsp1_entity.c | 12 ++++++++++++ drivers/media/platform/vsp1/vsp1_entity.h | 2 ++ 6 files changed, 20 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 910d6b8e8b50..bea232820ead 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -85,8 +85,6 @@ struct vsp1_device { struct media_entity_operations media_ops; struct vsp1_drm *drm; - - bool use_dl; }; int vsp1_device_get(struct vsp1_device *vsp1); @@ -104,14 +102,4 @@ static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data) iowrite32(data, vsp1->mmio + reg); } -#include "vsp1_dl.h" - -static inline void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) -{ - if (e->vsp1->use_dl) - vsp1_dl_add(e, reg, data); - else - vsp1_write(e->vsp1, reg, data); -} - #endif /* __VSP1_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 1a9a58588f84..5518a81f5792 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -18,7 +18,6 @@ #include "vsp1.h" #include "vsp1_dl.h" -#include "vsp1_pipe.h" /* * Global resources @@ -129,10 +128,8 @@ void vsp1_dl_begin(struct vsp1_dl *dl) list->reg_count = 0; } -void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data) +void vsp1_dl_add(struct vsp1_dl *dl, u32 reg, u32 data) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); - struct vsp1_dl *dl = pipe->dl; struct vsp1_dl_list *list = dl->lists.write; list->body[list->reg_count].addr = reg; diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 448c4250e54c..f4116ca59c28 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -13,7 +13,7 @@ #ifndef __VSP1_DL_H__ #define __VSP1_DL_H__ -#include "vsp1_entity.h" +#include struct vsp1_device; struct vsp1_dl; @@ -25,18 +25,10 @@ void vsp1_dl_setup(struct vsp1_device *vsp1); void vsp1_dl_reset(struct vsp1_dl *dl); void vsp1_dl_begin(struct vsp1_dl *dl); -void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data); +void vsp1_dl_add(struct vsp1_dl *dl, u32 reg, u32 data); void vsp1_dl_commit(struct vsp1_dl *dl); void vsp1_dl_irq_display_start(struct vsp1_dl *dl); void vsp1_dl_irq_frame_end(struct vsp1_dl *dl); -static inline void vsp1_dl_mod_write(struct vsp1_entity *e, u32 reg, u32 data) -{ - if (e->vsp1->use_dl) - vsp1_dl_add(e, reg, data); - else - vsp1_write(e->vsp1, reg, data); -} - #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index da43e3f35610..58632d766a2a 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -387,13 +387,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) /* Register subdev nodes if the userspace API is enabled or initialize * the DRM pipeline otherwise. */ - if (vsp1->info->uapi) { - vsp1->use_dl = false; + if (vsp1->info->uapi) ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); - } else { - vsp1->use_dl = true; + else ret = vsp1_drm_init(vsp1); - } if (ret < 0) goto done; @@ -465,7 +462,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); - if (vsp1->use_dl) + if (!vsp1->info->uapi) vsp1_dl_setup(vsp1); return 0; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 20a78fbd3691..4006f0d28bac 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -19,7 +19,19 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_entity.h" +#include "vsp1_pipe.h" + +void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) +{ + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); + + if (pipe->dl) + vsp1_dl_add(pipe->dl, reg, data); + else + vsp1_write(e->vsp1, reg, data); +} bool vsp1_entity_is_streaming(struct vsp1_entity *entity) { diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 83570dfde8ec..311d5b64c9a5 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -103,4 +103,6 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); void vsp1_entity_route_setup(struct vsp1_entity *source); +void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data); + #endif /* __VSP1_ENTITY_H__ */ From 7939fef4d3911695c78cb067f1e4c16056a9f113 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 10:53:22 -0200 Subject: [PATCH 040/173] [media] v4l: vsp1: Always setup the display list Make sure display list usage is correctly disabled by always setting up the corresponding registers, including when the display list feature isn't used. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 7 +++---- drivers/media/platform/vsp1/vsp1_drv.c | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 5518a81f5792..9587c37743b0 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -243,15 +243,14 @@ void vsp1_dl_irq_frame_end(struct vsp1_dl *dl) void vsp1_dl_setup(struct vsp1_device *vsp1) { - u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) - | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 - | VI6_DL_CTRL_DLE; + u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT); /* The DRM pipeline operates with header-less display lists in * Continuous Frame Mode. */ if (vsp1->drm) - ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; + ctrl |= VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 + | VI6_DL_CTRL_DLE | VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; vsp1_write(vsp1, VI6_DL_CTRL, ctrl); vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 58632d766a2a..d657949bac3b 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -462,8 +462,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); - if (!vsp1->info->uapi) - vsp1_dl_setup(vsp1); + vsp1_dl_setup(vsp1); return 0; } From f9df34f8cd0da731f65728480fe2e669391adbd0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 14 Nov 2015 16:33:40 -0200 Subject: [PATCH 041/173] [media] v4l: vsp1: Simplify frame end processing The DRM pipeline, as it runs in automatic restart mode, never sees the pipeline state set to VSP1_PIPELINE_STOPPING or VSP1_PIPELINE_STOPPED when running the frame end interrupt handler. We can thus skip the checks various checks in the handler and return immediately. Similarly the DRM frame end handler calls vsp1_pipeline_run() unnecessarily, as the state there is never VSP1_PIPELINE_STOPPED. Remove the function call and the frame end handler is it's now empty. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 15 --------------- drivers/media/platform/vsp1/vsp1_pipe.c | 9 ++++++--- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 8cf7c19f4344..9ecba4c1332e 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -26,20 +26,6 @@ #include "vsp1_pipe.h" #include "vsp1_rwpf.h" -/* ----------------------------------------------------------------------------- - * Runtime Handling - */ - -static void vsp1_drm_pipeline_frame_end(struct vsp1_pipeline *pipe) -{ - unsigned long flags; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->num_inputs) - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); -} - /* ----------------------------------------------------------------------------- * DU Driver API */ @@ -569,7 +555,6 @@ int vsp1_drm_init(struct vsp1_device *vsp1) pipe = &vsp1->drm->pipe; vsp1_pipeline_init(pipe); - pipe->frame_end = vsp1_drm_pipeline_frame_end; /* The DRM pipeline is static, add entities manually. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 6659f06b1643..78096122a22d 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -289,7 +289,8 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) vsp1_dl_irq_frame_end(pipe->dl); /* Signal frame end to the pipeline handler. */ - pipe->frame_end(pipe); + if (pipe->frame_end) + pipe->frame_end(pipe); spin_lock_irqsave(&pipe->irqlock, flags); @@ -298,8 +299,10 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) /* When using display lists in continuous frame mode the pipeline is * automatically restarted by the hardware. */ - if (!pipe->dl) - pipe->state = VSP1_PIPELINE_STOPPED; + if (pipe->dl) + goto done; + + pipe->state = VSP1_PIPELINE_STOPPED; /* If a stop has been requested, mark the pipeline as stopped and * return. From c2dd2513ea7aafe5cca2460aecaf83cb46128faf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 8 Nov 2015 20:06:57 -0200 Subject: [PATCH 042/173] [media] v4l: vsp1: Split display list manager from display list This clarifies the API and prepares display list support for being used to implement the request API. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 1 - drivers/media/platform/vsp1/vsp1_dl.c | 293 +++++++++++----------- drivers/media/platform/vsp1/vsp1_dl.h | 40 ++- drivers/media/platform/vsp1/vsp1_drm.c | 32 ++- drivers/media/platform/vsp1/vsp1_drm.h | 12 +- drivers/media/platform/vsp1/vsp1_drv.c | 11 +- drivers/media/platform/vsp1/vsp1_entity.c | 2 +- drivers/media/platform/vsp1/vsp1_pipe.c | 13 +- drivers/media/platform/vsp1/vsp1_pipe.h | 5 +- 9 files changed, 208 insertions(+), 201 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index bea232820ead..dae987a11a70 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -26,7 +26,6 @@ struct clk; struct device; -struct vsp1_dl; struct vsp1_drm; struct vsp1_entity; struct vsp1_platform_data; diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 9587c37743b0..14b2ee3c5fba 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -37,117 +37,108 @@ struct vsp1_dl_entry { } __attribute__((__packed__)); struct vsp1_dl_list { - size_t size; - int reg_count; + struct list_head list; - bool in_use; + struct vsp1_dl_manager *dlm; struct vsp1_dl_entry *body; dma_addr_t dma; -}; - -/** - * struct vsp1_dl - Display List manager - * @vsp1: the VSP1 device - * @lock: protects the active, queued and pending lists - * @lists.all: array of all allocate display lists - * @lists.active: list currently being processed (loaded) by hardware - * @lists.queued: list queued to the hardware (written to the DL registers) - * @lists.pending: list waiting to be queued to the hardware - * @lists.write: list being written to by software - */ -struct vsp1_dl { - struct vsp1_device *vsp1; - - spinlock_t lock; - size_t size; - dma_addr_t dma; - void *mem; - struct { - struct vsp1_dl_list all[VSP1_DL_NUM_LISTS]; - - struct vsp1_dl_list *active; - struct vsp1_dl_list *queued; - struct vsp1_dl_list *pending; - struct vsp1_dl_list *write; - } lists; + int reg_count; }; /* ----------------------------------------------------------------------------- * Display List Transaction Management */ -static void vsp1_dl_free_list(struct vsp1_dl_list *list) +static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { - if (!list) + struct vsp1_dl_list *dl; + + dl = kzalloc(sizeof(*dl), GFP_KERNEL); + if (!dl) + return NULL; + + dl->dlm = dlm; + dl->size = VSP1_DL_BODY_SIZE; + + dl->body = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma, GFP_KERNEL); + if (!dl->body) { + kfree(dl); + return NULL; + } + + return dl; +} + +static void vsp1_dl_list_free(struct vsp1_dl_list *dl) +{ + dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->body, dl->dma); + kfree(dl); +} + +/** + * vsp1_dl_list_get - Get a free display list + * @dlm: The display list manager + * + * Get a display list from the pool of free lists and return it. + * + * This function must be called without the display list manager lock held. + */ +struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) +{ + struct vsp1_dl_list *dl = NULL; + unsigned long flags; + + spin_lock_irqsave(&dlm->lock, flags); + + if (!list_empty(&dlm->free)) { + dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); + list_del(&dl->list); + } + + spin_unlock_irqrestore(&dlm->lock, flags); + + return dl; +} + +/** + * vsp1_dl_list_put - Release a display list + * @dl: The display list + * + * Release the display list and return it to the pool of free lists. + * + * This function must be called with the display list manager lock held. + * + * Passing a NULL pointer to this function is safe, in that case no operation + * will be performed. + */ +void vsp1_dl_list_put(struct vsp1_dl_list *dl) +{ + if (!dl) return; - list->in_use = false; + dl->reg_count = 0; + + list_add_tail(&dl->list, &dl->dlm->free); } -void vsp1_dl_reset(struct vsp1_dl *dl) +void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data) { - unsigned int i; - - dl->lists.active = NULL; - dl->lists.queued = NULL; - dl->lists.pending = NULL; - dl->lists.write = NULL; - - for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) - dl->lists.all[i].in_use = false; + dl->body[dl->reg_count].addr = reg; + dl->body[dl->reg_count].data = data; + dl->reg_count++; } -void vsp1_dl_begin(struct vsp1_dl *dl) +void vsp1_dl_list_commit(struct vsp1_dl_list *dl) { - struct vsp1_dl_list *list = NULL; - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&dl->lock, flags); - - for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) { - if (!dl->lists.all[i].in_use) { - list = &dl->lists.all[i]; - break; - } - } - - if (!list) { - list = dl->lists.pending; - dl->lists.pending = NULL; - } - - spin_unlock_irqrestore(&dl->lock, flags); - - dl->lists.write = list; - - list->in_use = true; - list->reg_count = 0; -} - -void vsp1_dl_add(struct vsp1_dl *dl, u32 reg, u32 data) -{ - struct vsp1_dl_list *list = dl->lists.write; - - list->body[list->reg_count].addr = reg; - list->body[list->reg_count].data = data; - list->reg_count++; -} - -void vsp1_dl_commit(struct vsp1_dl *dl) -{ - struct vsp1_device *vsp1 = dl->vsp1; - struct vsp1_dl_list *list; + struct vsp1_dl_manager *dlm = dl->dlm; + struct vsp1_device *vsp1 = dlm->vsp1; unsigned long flags; bool update; - list = dl->lists.write; - dl->lists.write = NULL; - - spin_lock_irqsave(&dl->lock, flags); + spin_lock_irqsave(&dlm->lock, flags); /* Once the UPD bit has been set the hardware can start processing the * display list at any time and we can't touch the address and size @@ -156,8 +147,8 @@ void vsp1_dl_commit(struct vsp1_dl *dl) */ update = !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD); if (update) { - vsp1_dl_free_list(dl->lists.pending); - dl->lists.pending = list; + vsp1_dl_list_put(dlm->pending); + dlm->pending = dl; goto done; } @@ -165,42 +156,44 @@ void vsp1_dl_commit(struct vsp1_dl *dl) * The UPD bit will be cleared by the device when the display list is * processed. */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (list->reg_count * 8)); + (dl->reg_count * 8)); - vsp1_dl_free_list(dl->lists.queued); - dl->lists.queued = list; + vsp1_dl_list_put(dlm->queued); + dlm->queued = dl; done: - spin_unlock_irqrestore(&dl->lock, flags); + spin_unlock_irqrestore(&dlm->lock, flags); } /* ----------------------------------------------------------------------------- - * Interrupt Handling + * Display List Manager */ -void vsp1_dl_irq_display_start(struct vsp1_dl *dl) +/* Interrupt Handling */ +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm) { - spin_lock(&dl->lock); + spin_lock(&dlm->lock); /* The display start interrupt signals the end of the display list * processing by the device. The active display list, if any, won't be * accessed anymore and can be reused. */ - if (dl->lists.active) { - vsp1_dl_free_list(dl->lists.active); - dl->lists.active = NULL; - } + vsp1_dl_list_put(dlm->active); + dlm->active = NULL; - spin_unlock(&dl->lock); + spin_unlock(&dlm->lock); } -void vsp1_dl_irq_frame_end(struct vsp1_dl *dl) +void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { - struct vsp1_device *vsp1 = dl->vsp1; + struct vsp1_device *vsp1 = dlm->vsp1; - spin_lock(&dl->lock); + spin_lock(&dlm->lock); + + vsp1_dl_list_put(dlm->active); + dlm->active = NULL; /* The UPD bit set indicates that the commit operation raced with the * interrupt and occurred after the frame end event and UPD clear but @@ -213,35 +206,31 @@ void vsp1_dl_irq_frame_end(struct vsp1_dl *dl) /* The device starts processing the queued display list right after the * frame end interrupt. The display list thus becomes active. */ - if (dl->lists.queued) { - WARN_ON(dl->lists.active); - dl->lists.active = dl->lists.queued; - dl->lists.queued = NULL; + if (dlm->queued) { + dlm->active = dlm->queued; + dlm->queued = NULL; } /* Now that the UPD bit has been cleared we can queue the next display * list to the hardware if one has been prepared. */ - if (dl->lists.pending) { - struct vsp1_dl_list *list = dl->lists.pending; + if (dlm->pending) { + struct vsp1_dl_list *dl = dlm->pending; - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (list->reg_count * 8)); + (dl->reg_count * 8)); - dl->lists.queued = list; - dl->lists.pending = NULL; + dlm->queued = dl; + dlm->pending = NULL; } done: - spin_unlock(&dl->lock); + spin_unlock(&dlm->lock); } -/* ----------------------------------------------------------------------------- - * Hardware Setup - */ - -void vsp1_dl_setup(struct vsp1_device *vsp1) +/* Hardware Setup */ +void vsp1_dlm_setup(struct vsp1_device *vsp1) { u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT); @@ -256,46 +245,46 @@ void vsp1_dl_setup(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); } -/* ----------------------------------------------------------------------------- - * Initialization and Cleanup - */ - -struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1) +void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) +{ + vsp1_dl_list_put(dlm->active); + vsp1_dl_list_put(dlm->queued); + vsp1_dl_list_put(dlm->pending); + + dlm->active = NULL; + dlm->queued = NULL; + dlm->pending = NULL; +} + +int vsp1_dlm_init(struct vsp1_device *vsp1, struct vsp1_dl_manager *dlm, + unsigned int prealloc) { - struct vsp1_dl *dl; unsigned int i; - dl = kzalloc(sizeof(*dl), GFP_KERNEL); - if (!dl) - return NULL; + dlm->vsp1 = vsp1; - spin_lock_init(&dl->lock); + spin_lock_init(&dlm->lock); + INIT_LIST_HEAD(&dlm->free); - dl->vsp1 = vsp1; - dl->size = VSP1_DL_BODY_SIZE * ARRAY_SIZE(dl->lists.all); + for (i = 0; i < prealloc; ++i) { + struct vsp1_dl_list *dl; - dl->mem = dma_alloc_wc(vsp1->dev, dl->size, &dl->dma, - GFP_KERNEL); - if (!dl->mem) { - kfree(dl); - return NULL; + dl = vsp1_dl_list_alloc(dlm); + if (!dl) + return -ENOMEM; + + list_add_tail(&dl->list, &dlm->free); } - for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) { - struct vsp1_dl_list *list = &dl->lists.all[i]; - - list->size = VSP1_DL_BODY_SIZE; - list->reg_count = 0; - list->in_use = false; - list->dma = dl->dma + VSP1_DL_BODY_SIZE * i; - list->body = dl->mem + VSP1_DL_BODY_SIZE * i; - } - - return dl; + return 0; } -void vsp1_dl_destroy(struct vsp1_dl *dl) +void vsp1_dlm_cleanup(struct vsp1_dl_manager *dlm) { - dma_free_wc(dl->vsp1->dev, dl->size, dl->mem, dl->dma); - kfree(dl); + struct vsp1_dl_list *dl, *next; + + list_for_each_entry_safe(dl, next, &dlm->free, list) { + list_del(&dl->list); + vsp1_dl_list_free(dl); + } } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index f4116ca59c28..caa6a85f6825 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -16,19 +16,39 @@ #include struct vsp1_device; -struct vsp1_dl; +struct vsp1_dl_list; -struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1); -void vsp1_dl_destroy(struct vsp1_dl *dl); +/** + * struct vsp1_dl_manager - Display List manager + * @vsp1: the VSP1 device + * @lock: protects the active, queued and pending lists + * @free: array of all free display lists + * @active: list currently being processed (loaded) by hardware + * @queued: list queued to the hardware (written to the DL registers) + * @pending: list waiting to be queued to the hardware + */ +struct vsp1_dl_manager { + struct vsp1_device *vsp1; -void vsp1_dl_setup(struct vsp1_device *vsp1); + spinlock_t lock; + struct list_head free; + struct vsp1_dl_list *active; + struct vsp1_dl_list *queued; + struct vsp1_dl_list *pending; +}; -void vsp1_dl_reset(struct vsp1_dl *dl); -void vsp1_dl_begin(struct vsp1_dl *dl); -void vsp1_dl_add(struct vsp1_dl *dl, u32 reg, u32 data); -void vsp1_dl_commit(struct vsp1_dl *dl); +void vsp1_dlm_setup(struct vsp1_device *vsp1); -void vsp1_dl_irq_display_start(struct vsp1_dl *dl); -void vsp1_dl_irq_frame_end(struct vsp1_dl *dl); +int vsp1_dlm_init(struct vsp1_device *vsp1, struct vsp1_dl_manager *dlm, + unsigned int prealloc); +void vsp1_dlm_cleanup(struct vsp1_dl_manager *dlm); +void vsp1_dlm_reset(struct vsp1_dl_manager *dlm); +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm); +void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); + +struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); +void vsp1_dl_list_put(struct vsp1_dl_list *dl); +void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data); +void vsp1_dl_list_commit(struct vsp1_dl_list *dl); #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 9ecba4c1332e..a8cd74335f20 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -26,6 +26,18 @@ #include "vsp1_pipe.h" #include "vsp1_rwpf.h" + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + + vsp1_dlm_irq_frame_end(&vsp1->drm->dlm); +} + /* ----------------------------------------------------------------------------- * DU Driver API */ @@ -89,6 +101,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width, pipe->num_inputs = 0; + vsp1_dlm_reset(&vsp1->drm->dlm); vsp1_device_put(vsp1); dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); @@ -96,8 +109,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width, return 0; } - vsp1_dl_reset(vsp1->drm->dl); - /* Configure the format at the BRU sinks and propagate it through the * pipeline. */ @@ -217,7 +228,7 @@ void vsp1_du_atomic_begin(struct device *dev) spin_unlock_irqrestore(&pipe->irqlock, flags); /* Prepare the display list. */ - vsp1_dl_begin(vsp1->drm->dl); + pipe->dl = vsp1_dl_list_get(&vsp1->drm->dlm); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); @@ -467,7 +478,8 @@ void vsp1_du_atomic_flush(struct device *dev) } } - vsp1_dl_commit(vsp1->drm->dl); + vsp1_dl_list_commit(pipe->dl); + pipe->dl = NULL; spin_lock_irqsave(&pipe->irqlock, flags); @@ -543,18 +555,20 @@ int vsp1_drm_init(struct vsp1_device *vsp1) { struct vsp1_pipeline *pipe; unsigned int i; + int ret; vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL); if (!vsp1->drm) return -ENOMEM; - vsp1->drm->dl = vsp1_dl_create(vsp1); - if (!vsp1->drm->dl) - return -ENOMEM; + ret = vsp1_dlm_init(vsp1, &vsp1->drm->dlm, 4); + if (ret < 0) + return ret; pipe = &vsp1->drm->pipe; vsp1_pipeline_init(pipe); + pipe->frame_end = vsp1_drm_frame_end; /* The DRM pipeline is static, add entities manually. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { @@ -571,12 +585,10 @@ int vsp1_drm_init(struct vsp1_device *vsp1) pipe->lif = &vsp1->lif->entity; pipe->output = vsp1->wpf[0]; - pipe->dl = vsp1->drm->dl; - return 0; } void vsp1_drm_cleanup(struct vsp1_device *vsp1) { - vsp1_dl_destroy(vsp1->drm->dl); + vsp1_dlm_cleanup(&vsp1->drm->dlm); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index 7704038c3add..5ef32cff9601 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -13,26 +13,30 @@ #ifndef __VSP1_DRM_H__ #define __VSP1_DRM_H__ +#include "vsp1_dl.h" #include "vsp1_pipe.h" -struct vsp1_dl; - /** * vsp1_drm - State for the API exposed to the DRM driver - * @dl: display list for DRM pipeline operation * @pipe: the VSP1 pipeline used for display * @num_inputs: number of active pipeline inputs at the beginning of an update * @update: the pipeline configuration has been updated + * @dlm: display list manager used for DRM operation */ struct vsp1_drm { - struct vsp1_dl *dl; struct vsp1_pipeline pipe; unsigned int num_inputs; bool update; + struct vsp1_dl_manager dlm; }; int vsp1_drm_init(struct vsp1_device *vsp1); void vsp1_drm_cleanup(struct vsp1_device *vsp1); int vsp1_drm_create_links(struct vsp1_device *vsp1); +static inline void vsp1_drm_display_start(struct vsp1_device *vsp1) +{ + vsp1_dlm_irq_display_start(&vsp1->drm->dlm); +} + #endif /* __VSP1_DRM_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index d657949bac3b..bfdc01c9172d 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -68,14 +68,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST); if (status & VI6_DISP_IRQ_STA_DST) { - struct vsp1_rwpf *wpf = vsp1->wpf[0]; - struct vsp1_pipeline *pipe; - - if (wpf) { - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); - vsp1_pipeline_display_start(pipe); - } - + vsp1_drm_display_start(vsp1); ret = IRQ_HANDLED; } @@ -462,7 +455,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); - vsp1_dl_setup(vsp1); + vsp1_dlm_setup(vsp1); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 4006f0d28bac..83689588900a 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -28,7 +28,7 @@ void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); if (pipe->dl) - vsp1_dl_add(pipe->dl, reg, data); + vsp1_dl_list_write(pipe->dl, reg, data); else vsp1_write(e->vsp1, reg, data); } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 78096122a22d..cb67b8f80635 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -226,7 +226,7 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) unsigned long flags; int ret; - if (pipe->dl) { + if (pipe->lif) { /* When using display lists in continuous frame mode the only * way to stop the pipeline is to reset the hardware. */ @@ -271,12 +271,6 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) return pipe->buffers_ready == mask; } -void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe) -{ - if (pipe->dl) - vsp1_dl_irq_display_start(pipe->dl); -} - void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { enum vsp1_pipeline_state state; @@ -285,9 +279,6 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) if (pipe == NULL) return; - if (pipe->dl) - vsp1_dl_irq_frame_end(pipe->dl); - /* Signal frame end to the pipeline handler. */ if (pipe->frame_end) pipe->frame_end(pipe); @@ -299,7 +290,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) /* When using display lists in continuous frame mode the pipeline is * automatically restarted by the hardware. */ - if (pipe->dl) + if (pipe->lif) goto done; pipe->state = VSP1_PIPELINE_STOPPED; diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index b2f3a8a896c9..f4bdfc943add 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -19,7 +19,7 @@ #include -struct vsp1_dl; +struct vsp1_dl_list; struct vsp1_rwpf; /* @@ -100,7 +100,7 @@ struct vsp1_pipeline { struct list_head entities; - struct vsp1_dl *dl; + struct vsp1_dl_list *dl; }; static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) @@ -119,7 +119,6 @@ bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); -void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, From ef9621bcd6640d48834ec9315dae06e9d7cb5283 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 14 Nov 2015 22:27:52 -0200 Subject: [PATCH 043/173] [media] v4l: vsp1: Store the display list manager in the WPF Each WPF can process display lists independently, move the manager to the WPF to reflect that and prepare for display list support for non-DRM pipelines. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 37 ++++++++++++++++++++--- drivers/media/platform/vsp1/vsp1_dl.h | 26 +++------------- drivers/media/platform/vsp1/vsp1_drm.c | 19 +++++------- drivers/media/platform/vsp1/vsp1_drm.h | 8 +---- drivers/media/platform/vsp1/vsp1_entity.c | 2 ++ drivers/media/platform/vsp1/vsp1_entity.h | 2 ++ drivers/media/platform/vsp1/vsp1_rwpf.h | 3 ++ drivers/media/platform/vsp1/vsp1_wpf.c | 18 +++++++++++ 8 files changed, 70 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 14b2ee3c5fba..e0f4816925d5 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -48,6 +48,25 @@ struct vsp1_dl_list { int reg_count; }; +/** + * struct vsp1_dl_manager - Display List manager + * @vsp1: the VSP1 device + * @lock: protects the active, queued and pending lists + * @free: array of all free display lists + * @active: list currently being processed (loaded) by hardware + * @queued: list queued to the hardware (written to the DL registers) + * @pending: list waiting to be queued to the hardware + */ +struct vsp1_dl_manager { + struct vsp1_device *vsp1; + + spinlock_t lock; + struct list_head free; + struct vsp1_dl_list *active; + struct vsp1_dl_list *queued; + struct vsp1_dl_list *pending; +}; + /* ----------------------------------------------------------------------------- * Display List Transaction Management */ @@ -256,11 +275,16 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -int vsp1_dlm_init(struct vsp1_device *vsp1, struct vsp1_dl_manager *dlm, - unsigned int prealloc) +struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, + unsigned int prealloc) { + struct vsp1_dl_manager *dlm; unsigned int i; + dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL); + if (!dlm) + return NULL; + dlm->vsp1 = vsp1; spin_lock_init(&dlm->lock); @@ -271,18 +295,21 @@ int vsp1_dlm_init(struct vsp1_device *vsp1, struct vsp1_dl_manager *dlm, dl = vsp1_dl_list_alloc(dlm); if (!dl) - return -ENOMEM; + return NULL; list_add_tail(&dl->list, &dlm->free); } - return 0; + return dlm; } -void vsp1_dlm_cleanup(struct vsp1_dl_manager *dlm) +void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl, *next; + if (!dlm) + return; + list_for_each_entry_safe(dl, next, &dlm->free, list) { list_del(&dl->list); vsp1_dl_list_free(dl); diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index caa6a85f6825..46f7ae337374 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -17,31 +17,13 @@ struct vsp1_device; struct vsp1_dl_list; - -/** - * struct vsp1_dl_manager - Display List manager - * @vsp1: the VSP1 device - * @lock: protects the active, queued and pending lists - * @free: array of all free display lists - * @active: list currently being processed (loaded) by hardware - * @queued: list queued to the hardware (written to the DL registers) - * @pending: list waiting to be queued to the hardware - */ -struct vsp1_dl_manager { - struct vsp1_device *vsp1; - - spinlock_t lock; - struct list_head free; - struct vsp1_dl_list *active; - struct vsp1_dl_list *queued; - struct vsp1_dl_list *pending; -}; +struct vsp1_dl_manager; void vsp1_dlm_setup(struct vsp1_device *vsp1); -int vsp1_dlm_init(struct vsp1_device *vsp1, struct vsp1_dl_manager *dlm, - unsigned int prealloc); -void vsp1_dlm_cleanup(struct vsp1_dl_manager *dlm); +struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, + unsigned int prealloc); +void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm); void vsp1_dlm_reset(struct vsp1_dl_manager *dlm); void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm); void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a8cd74335f20..22f67360b750 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -31,11 +31,14 @@ * Interrupt Handling */ +void vsp1_drm_display_start(struct vsp1_device *vsp1) +{ + vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm); +} + void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) { - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - - vsp1_dlm_irq_frame_end(&vsp1->drm->dlm); + vsp1_dlm_irq_frame_end(pipe->output->dlm); } /* ----------------------------------------------------------------------------- @@ -101,7 +104,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width, pipe->num_inputs = 0; - vsp1_dlm_reset(&vsp1->drm->dlm); + vsp1_dlm_reset(pipe->output->dlm); vsp1_device_put(vsp1); dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); @@ -228,7 +231,7 @@ void vsp1_du_atomic_begin(struct device *dev) spin_unlock_irqrestore(&pipe->irqlock, flags); /* Prepare the display list. */ - pipe->dl = vsp1_dl_list_get(&vsp1->drm->dlm); + pipe->dl = vsp1_dl_list_get(pipe->output->dlm); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); @@ -555,16 +558,11 @@ int vsp1_drm_init(struct vsp1_device *vsp1) { struct vsp1_pipeline *pipe; unsigned int i; - int ret; vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL); if (!vsp1->drm) return -ENOMEM; - ret = vsp1_dlm_init(vsp1, &vsp1->drm->dlm, 4); - if (ret < 0) - return ret; - pipe = &vsp1->drm->pipe; vsp1_pipeline_init(pipe); @@ -590,5 +588,4 @@ int vsp1_drm_init(struct vsp1_device *vsp1) void vsp1_drm_cleanup(struct vsp1_device *vsp1) { - vsp1_dlm_cleanup(&vsp1->drm->dlm); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index 5ef32cff9601..e9242f2c870e 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -13,7 +13,6 @@ #ifndef __VSP1_DRM_H__ #define __VSP1_DRM_H__ -#include "vsp1_dl.h" #include "vsp1_pipe.h" /** @@ -21,22 +20,17 @@ * @pipe: the VSP1 pipeline used for display * @num_inputs: number of active pipeline inputs at the beginning of an update * @update: the pipeline configuration has been updated - * @dlm: display list manager used for DRM operation */ struct vsp1_drm { struct vsp1_pipeline pipe; unsigned int num_inputs; bool update; - struct vsp1_dl_manager dlm; }; int vsp1_drm_init(struct vsp1_device *vsp1); void vsp1_drm_cleanup(struct vsp1_device *vsp1); int vsp1_drm_create_links(struct vsp1_device *vsp1); -static inline void vsp1_drm_display_start(struct vsp1_device *vsp1) -{ - vsp1_dlm_irq_display_start(&vsp1->drm->dlm); -} +void vsp1_drm_display_start(struct vsp1_device *vsp1); #endif /* __VSP1_DRM_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 83689588900a..a94f544dcc77 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -244,6 +244,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { + if (entity->destroy) + entity->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 311d5b64c9a5..259880e524fe 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -56,6 +56,8 @@ struct vsp1_route { struct vsp1_entity { struct vsp1_device *vsp1; + void (*destroy)(struct vsp1_entity *); + enum vsp1_entity_type type; unsigned int index; const struct vsp1_route *route; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 8e8235682ada..d04df39b2737 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -24,6 +24,7 @@ #define RWPF_PAD_SOURCE 1 struct v4l2_ctrl; +struct vsp1_dl_manager; struct vsp1_rwpf; struct vsp1_video; @@ -60,6 +61,8 @@ struct vsp1_rwpf { unsigned int offsets[2]; dma_addr_t buf_addr[3]; + + struct vsp1_dl_manager *dlm; }; static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index c78d4af50fcf..6afc9c8d9adc 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -16,6 +16,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -218,6 +219,13 @@ static const struct vsp1_rwpf_operations wpf_vdev_ops = { * Initialization and Cleanup */ +static void vsp1_wpf_destroy(struct vsp1_entity *entity) +{ + struct vsp1_rwpf *wpf = container_of(entity, struct vsp1_rwpf, entity); + + vsp1_dlm_destroy(wpf->dlm); +} + struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) { struct v4l2_subdev *subdev; @@ -233,6 +241,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->max_width = WPF_MAX_WIDTH; wpf->max_height = WPF_MAX_HEIGHT; + wpf->entity.destroy = vsp1_wpf_destroy; wpf->entity.type = VSP1_ENTITY_WPF; wpf->entity.index = index; @@ -240,6 +249,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (ret < 0) return ERR_PTR(ret); + /* Initialize the display list manager if the WPF is used for display */ + if ((vsp1->info->features & VSP1_HAS_LIF) && index == 0) { + wpf->dlm = vsp1_dlm_create(vsp1, 4); + if (!wpf->dlm) { + ret = -ENOMEM; + goto error; + } + } + /* Initialize the V4L2 subdev. */ subdev = &wpf->entity.subdev; v4l2_subdev_init(subdev, &wpf_ops); From f22af945f79a2bd68941efdb57d52e8d81db4d45 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 12:19:42 -0200 Subject: [PATCH 044/173] [media] v4l: vsp1: bru: Don't program background color in control set handler The datasheet clearly states that all but a few registers can't be modified when the device is running. Programming the background color in the control set handler is thus prohibited. Program it when starting the module instead. This requires storing the background color value internally as the module can be started from the frame completion interrupt handler, and accessing control values requires taking a mutex. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 15 +++++++++------ drivers/media/platform/vsp1/vsp1_bru.h | 2 ++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index cb0dbc15ddad..4c1bd0419e12 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -42,13 +42,9 @@ static int bru_s_ctrl(struct v4l2_ctrl *ctrl) struct vsp1_bru *bru = container_of(ctrl->handler, struct vsp1_bru, ctrls); - if (!vsp1_entity_is_streaming(&bru->entity)) - return 0; - switch (ctrl->id) { case V4L2_CID_BG_COLOR: - vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val | - (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); + bru->bgcolor = ctrl->val; break; } @@ -95,12 +91,17 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); - /* Set the background position to cover the whole output image. */ + /* Set the background position to cover the whole output image and + * configure its color. + */ vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); + vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, bru->bgcolor | + (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); + /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP * unit with a NOP operation to make BRU input 1 available as the * Blend/ROP unit B SRC input. @@ -438,6 +439,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, 0, 0xffffff, 1, 0); + bru->bgcolor = 0; + bru->entity.subdev.ctrl_handler = &bru->ctrls; if (bru->ctrls.error) { diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index dbac9686ea69..4e7d2e79b940 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -33,6 +33,8 @@ struct vsp1_bru { struct vsp1_rwpf *rpf; struct v4l2_rect compose; } inputs[VSP1_MAX_RPF]; + + u32 bgcolor; }; static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) From 5fb2107346cfc6d8fe62117a2cbf91fc1f92cc84 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 13 Apr 2016 17:40:48 -0300 Subject: [PATCH 045/173] [media] vsp1: make vsp1_drm_frame_end static As reported by smatch: drivers/media/platform/vsp1/vsp1_drm.c:39:6: warning: no previous prototype for 'vsp1_drm_frame_end' [-Wmissing-prototypes] void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) Fixes: ef9621bcd664 ("[media] v4l: vsp1: Store the display list manager in the WPF") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 22f67360b750..1f08da4b933b 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -36,7 +36,7 @@ void vsp1_drm_display_start(struct vsp1_device *vsp1) vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm); } -void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) +static void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) { vsp1_dlm_irq_frame_end(pipe->output->dlm); } From bd2fdd5aa919e3cb750147c9270034f11d106d94 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 12:19:42 -0200 Subject: [PATCH 046/173] [media] v4l: vsp1: rwpf: Don't program alpha value in control set handler The datasheet clearly states that all but a few registers can't be modified when the device is running. Programming the alpha value in the control set handler is thus prohibited. Program it when starting the module instead. This requires storing the alpha value internally as the module can be started from the frame completion interrupt handler, and accessing control values requires taking a mutex. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_rpf.c | 51 +++-------------------- drivers/media/platform/vsp1/vsp1_rwpf.c | 35 ++++++++++++++++ drivers/media/platform/vsp1/vsp1_rwpf.h | 5 ++- drivers/media/platform/vsp1/vsp1_wpf.c | 55 ++----------------------- 4 files changed, 47 insertions(+), 99 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 5bc1d1574a43..9ccfb572b4a5 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -32,36 +32,6 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) data); } -/* ----------------------------------------------------------------------------- - * Controls - */ - -static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vsp1_rwpf *rpf = - container_of(ctrl->handler, struct vsp1_rwpf, ctrls); - struct vsp1_pipeline *pipe; - - if (!vsp1_entity_is_streaming(&rpf->entity)) - return 0; - - switch (ctrl->id) { - case V4L2_CID_ALPHA_COMPONENT: - vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, - ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - - pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity); - vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val); - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops rpf_ctrl_ops = { - .s_ctrl = rpf_s_ctrl, -}; - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -70,7 +40,6 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *rpf = to_rwpf(subdev); - struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_rect *crop = &rpf->crop; @@ -151,13 +120,10 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); - if (vsp1->info->uapi) - mutex_lock(rpf->ctrls.lock); vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, - rpf->alpha->cur.val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha->cur.val); - if (vsp1->info->uapi) - mutex_unlock(rpf->ctrls.lock); + rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha); vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); @@ -255,17 +221,10 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&rpf->ctrls, 1); - rpf->alpha = v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, - V4L2_CID_ALPHA_COMPONENT, - 0, 255, 1, 255); - - rpf->entity.subdev.ctrl_handler = &rpf->ctrls; - - if (rpf->ctrls.error) { + ret = vsp1_rwpf_init_ctrls(rpf); + if (ret < 0) { dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", index); - ret = rpf->ctrls.error; goto error; } diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 9688c219b30e..ba50386db35c 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -230,3 +230,38 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, return 0; } + +/* ----------------------------------------------------------------------------- + * Controls + */ + +static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *rwpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + rwpf->alpha = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { + .s_ctrl = vsp1_rwpf_s_ctrl, +}; + +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf) +{ + rwpf->alpha = 255; + + v4l2_ctrl_handler_init(&rwpf->ctrls, 1); + v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); + + rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls; + + return rwpf->ctrls.error; +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index d04df39b2737..66af2a06dd8b 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -42,7 +42,6 @@ struct vsp1_rwpf_operations { struct vsp1_rwpf { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *alpha; struct vsp1_video *video; @@ -59,6 +58,8 @@ struct vsp1_rwpf { } location; struct v4l2_rect crop; + unsigned int alpha; + unsigned int offsets[2]; dma_addr_t buf_addr[3]; @@ -73,6 +74,8 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf); + int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6afc9c8d9adc..2135cca2490e 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -27,47 +27,12 @@ * Device Access */ -static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg) -{ - return vsp1_read(wpf->entity.vsp1, - reg + wpf->entity.index * VI6_WPF_OFFSET); -} - static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) { vsp1_mod_write(&wpf->entity, reg + wpf->entity.index * VI6_WPF_OFFSET, data); } -/* ----------------------------------------------------------------------------- - * Controls - */ - -static int wpf_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vsp1_rwpf *wpf = - container_of(ctrl->handler, struct vsp1_rwpf, ctrls); - u32 value; - - if (!vsp1_entity_is_streaming(&wpf->entity)) - return 0; - - switch (ctrl->id) { - case V4L2_CID_ALPHA_COMPONENT: - value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT); - value &= ~VI6_WPF_OUTFMT_PDV_MASK; - value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT; - vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value); - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops wpf_ctrl_ops = { - .s_ctrl = wpf_s_ctrl, -}; - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -153,15 +118,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) wpf->entity.formats[RWPF_PAD_SOURCE].code) outfmt |= VI6_WPF_OUTFMT_CSC; - /* Take the control handler lock to ensure that the PDV value won't be - * changed behind our back by a set control operation. - */ - if (vsp1->info->uapi) - mutex_lock(wpf->ctrls.lock); - outfmt |= wpf->alpha->cur.val << VI6_WPF_OUTFMT_PDV_SHIFT; + outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); - if (vsp1->info->uapi) - mutex_unlock(wpf->ctrls.lock); vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -272,17 +230,10 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&wpf->ctrls, 1); - wpf->alpha = v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, - V4L2_CID_ALPHA_COMPONENT, - 0, 255, 1, 255); - - wpf->entity.subdev.ctrl_handler = &wpf->ctrls; - - if (wpf->ctrls.error) { + ret = vsp1_rwpf_init_ctrls(wpf); + if (ret < 0) { dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", index); - ret = wpf->ctrls.error; goto error; } From d884a8b2a5dc2fad784a60f356d1d8d90cecb436 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 12:19:42 -0200 Subject: [PATCH 047/173] [media] v4l: vsp1: sru: Don't program intensity in control set handler The datasheet clearly states that all but a few registers can't be modified when the device is running. Programming the intensity parameters in the control set handler is thus prohibited. Program it when starting the module instead. This requires storing the intensity value internally as the module can be started from the frame completion interrupt handler, and accessing control values requires taking a mutex. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_sru.c | 35 +++++++------------------- drivers/media/platform/vsp1/vsp1_sru.h | 2 ++ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index cc09efbfb24f..ec4741efc7f8 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -26,11 +26,6 @@ * Device Access */ -static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg) -{ - return vsp1_read(sru->entity.vsp1, reg); -} - static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) { vsp1_write(sru->entity.vsp1, reg, data); @@ -82,20 +77,10 @@ static int sru_s_ctrl(struct v4l2_ctrl *ctrl) { struct vsp1_sru *sru = container_of(ctrl->handler, struct vsp1_sru, ctrls); - const struct vsp1_sru_param *param; - u32 value; switch (ctrl->id) { case V4L2_CID_VSP1_SRU_INTENSITY: - param = &vsp1_sru_params[ctrl->val - 1]; - - value = vsp1_sru_read(sru, VI6_SRU_CTRL0); - value &= ~(VI6_SRU_CTRL0_PARAM0_MASK | - VI6_SRU_CTRL0_PARAM1_MASK); - value |= param->ctrl0; - vsp1_sru_write(sru, VI6_SRU_CTRL0, value); - - vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + sru->intensity = ctrl->val; break; } @@ -123,6 +108,7 @@ static const struct v4l2_ctrl_config sru_intensity_control = { static int sru_s_stream(struct v4l2_subdev *subdev, int enable) { + const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; @@ -148,18 +134,13 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) if (input->width != output->width) ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; - /* Take the control handler lock to ensure that the CTRL0 value won't be - * changed behind our back by a set control operation. - */ - if (sru->entity.vsp1->info->uapi) - mutex_lock(sru->ctrls.lock); - ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0) - & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK); - vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); - if (sru->entity.vsp1->info->uapi) - mutex_unlock(sru->ctrls.lock); + param = &vsp1_sru_params[sru->intensity - 1]; + ctrl0 |= param->ctrl0; + + vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); return 0; } @@ -376,6 +357,8 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) v4l2_ctrl_handler_init(&sru->ctrls, 1); v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); + sru->intensity = 1; + sru->entity.subdev.ctrl_handler = &sru->ctrls; if (sru->ctrls.error) { diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h index b6768bf3dc47..85e241457af2 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -28,6 +28,8 @@ struct vsp1_sru { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; + + unsigned int intensity; }; static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev) From 59d0b2bf1d8de62d3ee8cce5c5b9463608095642 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 12:58:29 -0200 Subject: [PATCH 048/173] [media] v4l: vsp1: Don't setup control handler when starting streaming The control handler set operations don't program the hardware anymore, there's thus no need to call them when starting the stream. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 5 +---- drivers/media/platform/vsp1/vsp1_entity.c | 18 +----------------- drivers/media/platform/vsp1/vsp1_entity.h | 2 +- drivers/media/platform/vsp1/vsp1_rpf.c | 5 +---- drivers/media/platform/vsp1/vsp1_sru.c | 5 +---- drivers/media/platform/vsp1/vsp1_wpf.c | 5 +---- 6 files changed, 6 insertions(+), 34 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 4c1bd0419e12..27a9043b11e2 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -66,11 +66,8 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; - int ret; - ret = vsp1_entity_set_streaming(&bru->entity, enable); - if (ret < 0) - return ret; + vsp1_entity_set_streaming(&bru->entity, enable); if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index a94f544dcc77..6b425ae9aba3 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -45,29 +45,13 @@ bool vsp1_entity_is_streaming(struct vsp1_entity *entity) return streaming; } -int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) +void vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) { unsigned long flags; - int ret; spin_lock_irqsave(&entity->lock, flags); entity->streaming = streaming; spin_unlock_irqrestore(&entity->lock, flags); - - if (!streaming) - return 0; - - if (!entity->vsp1->info->uapi || !entity->subdev.ctrl_handler) - return 0; - - ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); - if (ret < 0) { - spin_lock_irqsave(&entity->lock, flags); - entity->streaming = false; - spin_unlock_irqrestore(&entity->lock, flags); - } - - return ret; } void vsp1_entity_route_setup(struct vsp1_entity *source) diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 259880e524fe..c0d6db82ebfb 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -101,7 +101,7 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); bool vsp1_entity_is_streaming(struct vsp1_entity *entity); -int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); +void vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); void vsp1_entity_route_setup(struct vsp1_entity *source); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 9ccfb572b4a5..48870b257a81 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -45,11 +45,8 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; - int ret; - ret = vsp1_entity_set_streaming(&rpf->entity, enable); - if (ret < 0) - return ret; + vsp1_entity_set_streaming(&rpf->entity, enable); if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index ec4741efc7f8..15fc562a52da 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -113,11 +113,8 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; u32 ctrl0; - int ret; - ret = vsp1_entity_set_streaming(&sru->entity, enable); - if (ret < 0) - return ret; + vsp1_entity_set_streaming(&sru->entity, enable); if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 2135cca2490e..d68c90d45232 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -46,11 +46,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) unsigned int i; u32 srcrpf = 0; u32 outfmt = 0; - int ret; - ret = vsp1_entity_set_streaming(&wpf->entity, enable); - if (ret < 0) - return ret; + vsp1_entity_set_streaming(&wpf->entity, enable); if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); From 773abafe6f7b81f2ff51aaa1d137efdc54c30354 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 12:26:14 -0200 Subject: [PATCH 049/173] [media] v4l: vsp1: Enable display list support for the HS[IT], LUT, SRU and UDS Those modules were left out of display list integration as they're not used by the DRM pipeline. To prepare for display list support in non-DRM pipelines use the module write API to set registers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_hsit.c | 2 +- drivers/media/platform/vsp1/vsp1_lut.c | 2 +- drivers/media/platform/vsp1/vsp1_sru.c | 2 +- drivers/media/platform/vsp1/vsp1_uds.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index c1087cff31a0..e820fe0b4f00 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -28,7 +28,7 @@ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) { - vsp1_write(hsit->entity.vsp1, reg, data); + vsp1_mod_write(&hsit->entity, reg, data); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 4b89095e7b5f..fc9011b12993 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -29,7 +29,7 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) { - vsp1_write(lut->entity.vsp1, reg, data); + vsp1_mod_write(&lut->entity, reg, data); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 15fc562a52da..810c6b376e14 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -28,7 +28,7 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) { - vsp1_write(sru->entity.vsp1, reg, data); + vsp1_mod_write(&sru->entity, reg, data); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index bba67770cf95..c608b06ed677 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -31,8 +31,8 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) { - vsp1_write(uds->entity.vsp1, - reg + uds->entity.index * VI6_UDS_OFFSET, data); + vsp1_mod_write(&uds->entity, reg + uds->entity.index * VI6_UDS_OFFSET, + data); } /* ----------------------------------------------------------------------------- From 4d346be55d415114faf19c0f79c2c15c7cc11242 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 13:48:11 -0200 Subject: [PATCH 050/173] [media] v4l: vsp1: Don't configure RPF memory buffers before calculating offsets The RPF source memory pointers need to be offset to take the crop rectangle into account. Offsets are computed in the RPF stream start, which can happen (when using the DRM pipeline) after calling the RPF .set_memory() operation that programs the buffer addresses. The .set_memory() operation tries to guard against the problem by skipping programming of the registers when the module isn't streaming. This will however only protect the first use of an RPF in a DRM pipeline, as in all subsequent uses the module streaming flag will be set and the .set_memory() operation will use potentially incorrect offsets. Fix this by allowing the caller to decide whether to program the hardware immediately or just cache the addresses. While at it refactor the memory set code and create a new vsp1_rwpf_set_memory() that cache addresses and calls the .set_memory() operation to apply them to the hardware. As a side effect the driver now writes all three DMA address registers regardless of the number of planes, and initializes unused addresses to zero. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 7 +++-- drivers/media/platform/vsp1/vsp1_rpf.c | 37 +++++++----------------- drivers/media/platform/vsp1/vsp1_rwpf.c | 26 +++++++++++++++++ drivers/media/platform/vsp1/vsp1_rwpf.h | 11 +++++-- drivers/media/platform/vsp1/vsp1_video.c | 9 ++++-- drivers/media/platform/vsp1/vsp1_wpf.c | 10 +++---- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 1f08da4b933b..9193b7b7d183 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -420,12 +420,15 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, rpf->location.left = dst->left; rpf->location.top = dst->top; - /* Set the memory buffer address. */ + /* Set the memory buffer address but don't apply the values to the + * hardware as the crop offsets haven't been computed yet. + */ memory.num_planes = fmtinfo->planes; memory.addr[0] = mem[0]; memory.addr[1] = mem[1]; + memory.addr[2] = 0; - rpf->ops->set_memory(rpf, &memory); + vsp1_rwpf_set_memory(rpf, &memory, false); spin_lock_irqsave(&pipe->irqlock, flags); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 48870b257a81..62d898c0ad65 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -69,25 +69,20 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, - rpf->buf_addr[0] + rpf->offsets[0]); - if (format->num_planes > 1) { rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline + crop->left * fmtinfo->bpp[1] / 8; pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; - - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, - rpf->buf_addr[1] + rpf->offsets[1]); - - if (format->num_planes > 2) - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, - rpf->buf_addr[2] + rpf->offsets[1]); + } else { + rpf->offsets[1] = 0; } vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); + /* Now that the offsets have been computed program the DMA addresses. */ + rpf->ops->set_memory(rpf); + /* Format */ infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -154,24 +149,14 @@ static struct v4l2_subdev_ops rpf_ops = { * Video Device Operations */ -static void rpf_set_memory(struct vsp1_rwpf *rpf, struct vsp1_rwpf_memory *mem) +static void rpf_set_memory(struct vsp1_rwpf *rpf) { - unsigned int i; - - for (i = 0; i < 3; ++i) - rpf->buf_addr[i] = mem->addr[i]; - - if (!vsp1_entity_is_streaming(&rpf->entity)) - return; - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, - mem->addr[0] + rpf->offsets[0]); - if (mem->num_planes > 1) - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, - mem->addr[1] + rpf->offsets[1]); - if (mem->num_planes > 2) - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, - mem->addr[2] + rpf->offsets[1]); + rpf->buf_addr[0] + rpf->offsets[0]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + rpf->buf_addr[1] + rpf->offsets[1]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + rpf->buf_addr[2] + rpf->offsets[1]); } static const struct vsp1_rwpf_operations rpf_vdev_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index ba50386db35c..54070ccdc2ff 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -265,3 +265,29 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf) return rwpf->ctrls.error; } + +/* ----------------------------------------------------------------------------- + * Buffers + */ + +/** + * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF + * @rwpf: the [RW]PF instance + * @mem: DMA memory addresses + * @apply: whether to apply the configuration to the hardware + * + * This function stores the DMA addresses for all planes in the rwpf instance + * and optionally applies the configuration to hardware registers if the apply + * argument is set to true. + */ +void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, struct vsp1_rwpf_memory *mem, + bool apply) +{ + unsigned int i; + + for (i = 0; i < 3; ++i) + rwpf->buf_addr[i] = mem->addr[i]; + + if (apply) + rwpf->ops->set_memory(rwpf); +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 66af2a06dd8b..bda0416dc7db 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -34,9 +34,13 @@ struct vsp1_rwpf_memory { unsigned int length[3]; }; +/** + * struct vsp1_rwpf_operations - RPF and WPF operations + * @set_memory: Setup memory buffer access. This operation applies the settings + * stored in the rwpf buf_addr field to the hardware. + */ struct vsp1_rwpf_operations { - void (*set_memory)(struct vsp1_rwpf *rwpf, - struct vsp1_rwpf_memory *mem); + void (*set_memory)(struct vsp1_rwpf *rwpf); }; struct vsp1_rwpf { @@ -93,4 +97,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); +void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, struct vsp1_rwpf_memory *mem, + bool apply); + #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b97bbdb1a256..96b04fcd33ae 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -443,7 +443,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, spin_lock_irqsave(&pipe->irqlock, flags); - video->rwpf->ops->set_memory(video->rwpf, &buf->mem); + vsp1_rwpf_set_memory(video->rwpf, &buf->mem, true); pipe->buffers_ready |= 1 << video->pipe_index; spin_unlock_irqrestore(&pipe->irqlock, flags); @@ -522,6 +522,11 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) return -EINVAL; } + for ( ; i < 3; ++i) { + buf->mem.addr[i] = 0; + buf->mem.length[i] = 0; + } + return 0; } @@ -544,7 +549,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pipe->irqlock, flags); - video->rwpf->ops->set_memory(video->rwpf, &buf->mem); + vsp1_rwpf_set_memory(video->rwpf, &buf->mem, true); pipe->buffers_ready |= 1 << video->pipe_index; if (vb2_is_streaming(&video->queue) && diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d68c90d45232..28654cffeeca 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -157,13 +157,11 @@ static struct v4l2_subdev_ops wpf_ops = { * Video Device Operations */ -static void wpf_set_memory(struct vsp1_rwpf *wpf, struct vsp1_rwpf_memory *mem) +static void wpf_set_memory(struct vsp1_rwpf *wpf) { - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, mem->addr[0]); - if (mem->num_planes > 1) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, mem->addr[1]); - if (mem->num_planes > 2) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, mem->addr[2]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, wpf->buf_addr[0]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, wpf->buf_addr[1]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->buf_addr[2]); } static const struct vsp1_rwpf_operations wpf_vdev_ops = { From 2b09ee4093e98e8eaa908554aa36a5b2ceba6e3d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 13:54:34 -0200 Subject: [PATCH 051/173] [media] v4l: vsp1: Remove unneeded entity streaming flag The flag is set but never read, remove it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 2 -- drivers/media/platform/vsp1/vsp1_entity.c | 23 ----------------------- drivers/media/platform/vsp1/vsp1_entity.h | 6 ------ drivers/media/platform/vsp1/vsp1_rpf.c | 2 -- drivers/media/platform/vsp1/vsp1_sru.c | 2 -- drivers/media/platform/vsp1/vsp1_wpf.c | 2 -- 6 files changed, 37 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 27a9043b11e2..74cc4903e858 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -67,8 +67,6 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) unsigned int flags; unsigned int i; - vsp1_entity_set_streaming(&bru->entity, enable); - if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 6b425ae9aba3..be67727f6f78 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -33,27 +33,6 @@ void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) vsp1_write(e->vsp1, reg, data); } -bool vsp1_entity_is_streaming(struct vsp1_entity *entity) -{ - unsigned long flags; - bool streaming; - - spin_lock_irqsave(&entity->lock, flags); - streaming = entity->streaming; - spin_unlock_irqrestore(&entity->lock, flags); - - return streaming; -} - -void vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) -{ - unsigned long flags; - - spin_lock_irqsave(&entity->lock, flags); - entity->streaming = streaming; - spin_unlock_irqrestore(&entity->lock, flags); -} - void vsp1_entity_route_setup(struct vsp1_entity *source) { struct vsp1_entity *sink; @@ -198,8 +177,6 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; - spin_lock_init(&entity->lock); - entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index c0d6db82ebfb..203872164f8e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -73,9 +73,6 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_mbus_framefmt *formats; - - spinlock_t lock; /* Protects the streaming field */ - bool streaming; }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -100,9 +97,6 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, void vsp1_entity_init_formats(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); -bool vsp1_entity_is_streaming(struct vsp1_entity *entity); -void vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); - void vsp1_entity_route_setup(struct vsp1_entity *source); void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 62d898c0ad65..ffe097b27a77 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -46,8 +46,6 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) u32 pstride; u32 infmt; - vsp1_entity_set_streaming(&rpf->entity, enable); - if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 810c6b376e14..371b20ec5d1b 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -114,8 +114,6 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) struct v4l2_mbus_framefmt *output; u32 ctrl0; - vsp1_entity_set_streaming(&sru->entity, enable); - if (!enable) return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 28654cffeeca..1013190e440b 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -47,8 +47,6 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) u32 srcrpf = 0; u32 outfmt = 0; - vsp1_entity_set_streaming(&wpf->entity, enable); - if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + From c9e645a534744029d5d465d9b7bfae3de9123031 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 14:01:51 -0200 Subject: [PATCH 052/173] [media] v4l: vsp1: Document calling context of vsp1_pipeline_propagate_alpha() The function can only be called from a s_stream handler as it requires a valid display list context (due to calling vsp1_uds_set_alpha() which writes to module registers). Document the requirement. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_pipe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index cb67b8f80635..a9a754e17e8d 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -318,6 +318,9 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha * value. The UDS then outputs a fixed alpha value which needs to be programmed * from the input RPF alpha. + * + * This function can only be called from a subdev s_stream handler as it + * requires a valid display list context. */ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_entity *input, From 1bd0a1bd3462f2b04f969f649875b28eaa85c97d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 15:18:32 -0200 Subject: [PATCH 053/173] [media] v4l: vsp1: Fix 80 characters per line violations Commit f7234138f14c ("v4l2-subdev: replace v4l2_subdev_fh by v4l2_subdev_pad_config") introduced lots of 80 characters per line violations. Fix them. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 12 ++++++++---- drivers/media/platform/vsp1/vsp1_lif.c | 6 ++++-- drivers/media/platform/vsp1/vsp1_lut.c | 6 ++++-- drivers/media/platform/vsp1/vsp1_rwpf.c | 12 ++++++++---- drivers/media/platform/vsp1/vsp1_rwpf.h | 6 ++++-- drivers/media/platform/vsp1/vsp1_sru.c | 9 ++++++--- drivers/media/platform/vsp1/vsp1_uds.c | 9 ++++++--- 7 files changed, 40 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 74cc4903e858..6a6e9d84f1ca 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -195,7 +195,8 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; format = vsp1_entity_get_pad_format(&bru->entity, cfg, - BRU_PAD_SINK(0), code->which); + BRU_PAD_SINK(0), + code->which); code->code = format->code; } @@ -235,7 +236,8 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, } } -static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int bru_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); @@ -246,7 +248,8 @@ static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con return 0; } -static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg, +static void bru_try_format(struct vsp1_bru *bru, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -274,7 +277,8 @@ static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config * fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int bru_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 433853ce8dbf..be0ea166016c 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -128,7 +128,8 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int lif_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); @@ -139,7 +140,8 @@ static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con return 0; } -static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int lif_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index fc9011b12993..3af849dbee5a 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -139,7 +139,8 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int lut_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); @@ -150,7 +151,8 @@ static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con return 0; } -static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int lut_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 54070ccdc2ff..0924079b920c 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -73,11 +73,13 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, } static struct v4l2_rect * -vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which) +vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, + u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK); + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, + RWPF_PAD_SINK); case V4L2_SUBDEV_FORMAT_ACTIVE: return &rwpf->crop; default: @@ -85,7 +87,8 @@ vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u } } -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); @@ -96,7 +99,8 @@ int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_conf return 0; } -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index bda0416dc7db..57f15d45f8bb 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -86,9 +86,11 @@ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse); -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 371b20ec5d1b..19b7923cb137 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -209,7 +209,8 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int sru_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); @@ -220,7 +221,8 @@ static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con return 0; } -static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg, +static void sru_try_format(struct vsp1_sru *sru, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -271,7 +273,8 @@ static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config * fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int sru_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index c608b06ed677..83ec8942f8e7 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -222,7 +222,8 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int uds_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); @@ -233,7 +234,8 @@ static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con return 0; } -static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *cfg, +static void uds_try_format(struct vsp1_uds *uds, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { @@ -269,7 +271,8 @@ static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config * fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, +static int uds_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); From 1216198935d476e33affd104f0b4210c1fcc2477 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 14 Nov 2015 22:48:27 -0200 Subject: [PATCH 054/173] [media] v4l: vsp1: Add header display list support Display lists can operate in header or headerless mode. The headerless mode is only available on WPF0, to be used with the display engine. All other WPF instances can only use display lists in header mode. Implement support for header mode to prepare for display list usage on WPFs other than 0. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 72 ++++++++++++++++++++++++-- drivers/media/platform/vsp1/vsp1_dl.h | 1 + drivers/media/platform/vsp1/vsp1_wpf.c | 2 +- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index e0f4816925d5..81ea1b2ce00d 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -28,9 +28,23 @@ * - DL swap */ +#define VSP1_DL_HEADER_SIZE 76 #define VSP1_DL_BODY_SIZE (2 * 4 * 256) #define VSP1_DL_NUM_LISTS 3 +#define VSP1_DLH_INT_ENABLE (1 << 1) +#define VSP1_DLH_AUTO_START (1 << 0) + +struct vsp1_dl_header { + u32 num_lists; + struct { + u32 num_bytes; + u32 addr; + } lists[8]; + u32 next_header; + u32 flags; +} __attribute__((__packed__)); + struct vsp1_dl_entry { u32 addr; u32 data; @@ -41,6 +55,7 @@ struct vsp1_dl_list { struct vsp1_dl_manager *dlm; + struct vsp1_dl_header *header; struct vsp1_dl_entry *body; dma_addr_t dma; size_t size; @@ -48,8 +63,15 @@ struct vsp1_dl_list { int reg_count; }; +enum vsp1_dl_mode { + VSP1_DL_MODE_HEADER, + VSP1_DL_MODE_HEADERLESS, +}; + /** * struct vsp1_dl_manager - Display List manager + * @index: index of the related WPF + * @mode: display list operation mode (header or headerless) * @vsp1: the VSP1 device * @lock: protects the active, queued and pending lists * @free: array of all free display lists @@ -58,6 +80,8 @@ struct vsp1_dl_list { * @pending: list waiting to be queued to the hardware */ struct vsp1_dl_manager { + unsigned int index; + enum vsp1_dl_mode mode; struct vsp1_device *vsp1; spinlock_t lock; @@ -74,26 +98,43 @@ struct vsp1_dl_manager { static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; + size_t header_size; + + /* The body needs to be aligned on a 8 bytes boundary, pad the header + * size to allow allocating both in a single operation. + */ + header_size = dlm->mode == VSP1_DL_MODE_HEADER + ? ALIGN(sizeof(struct vsp1_dl_header), 8) + : 0; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) return NULL; dl->dlm = dlm; - dl->size = VSP1_DL_BODY_SIZE; + dl->size = header_size + VSP1_DL_BODY_SIZE; - dl->body = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma, GFP_KERNEL); - if (!dl->body) { + dl->header = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma, + GFP_KERNEL); + if (!dl->header) { kfree(dl); return NULL; } + if (dlm->mode == VSP1_DL_MODE_HEADER) { + memset(dl->header, 0, sizeof(*dl->header)); + dl->header->lists[0].addr = dl->dma + header_size; + dl->header->flags = VSP1_DLH_INT_ENABLE; + } + + dl->body = ((void *)dl->header) + header_size; + return dl; } static void vsp1_dl_list_free(struct vsp1_dl_list *dl) { - dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->body, dl->dma); + dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->header, dl->dma); kfree(dl); } @@ -159,6 +200,18 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) spin_lock_irqsave(&dlm->lock, flags); + if (dl->dlm->mode == VSP1_DL_MODE_HEADER) { + /* Program the hardware with the display list body address and + * size. In header mode the caller guarantees that the hardware + * is idle at this point. + */ + dl->header->lists[0].num_bytes = dl->reg_count * 8; + vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); + + dlm->active = dl; + goto done; + } + /* Once the UPD bit has been set the hardware can start processing the * display list at any time and we can't touch the address and size * registers. In that case mark the update as pending, it will be @@ -214,6 +267,13 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) vsp1_dl_list_put(dlm->active); dlm->active = NULL; + /* Header mode is used for mem-to-mem pipelines only. We don't need to + * perform any operation as there can't be any new display list queued + * in that case. + */ + if (dlm->mode == VSP1_DL_MODE_HEADER) + goto done; + /* The UPD bit set indicates that the commit operation raced with the * interrupt and occurred after the frame end event and UPD clear but * before interrupt processing. The hardware hasn't taken the update @@ -276,6 +336,7 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) } struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, + unsigned int index, unsigned int prealloc) { struct vsp1_dl_manager *dlm; @@ -285,6 +346,9 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, if (!dlm) return NULL; + dlm->index = index; + dlm->mode = index == 0 && !vsp1->info->uapi + ? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER; dlm->vsp1 = vsp1; spin_lock_init(&dlm->lock); diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 46f7ae337374..571ed6d8e7c2 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -22,6 +22,7 @@ struct vsp1_dl_manager; void vsp1_dlm_setup(struct vsp1_device *vsp1); struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, + unsigned int index, unsigned int prealloc); void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm); void vsp1_dlm_reset(struct vsp1_dl_manager *dlm); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 1013190e440b..d1fad9effb9b 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -202,7 +202,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) /* Initialize the display list manager if the WPF is used for display */ if ((vsp1->info->features & VSP1_HAS_LIF) && index == 0) { - wpf->dlm = vsp1_dlm_create(vsp1, 4); + wpf->dlm = vsp1_dlm_create(vsp1, index, 4); if (!wpf->dlm) { ret = -ENOMEM; goto error; From 351bbf99f245f4bada0edec3b0863146d71f06a9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 1 Nov 2015 15:18:56 -0200 Subject: [PATCH 055/173] [media] v4l: vsp1: Use display lists with the userspace API Don't restrict display list usage to the DRM pipeline, use them unconditionally. This prepares the driver to support the request API. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 11 +- drivers/media/platform/vsp1/vsp1_drm.c | 23 ++-- drivers/media/platform/vsp1/vsp1_entity.c | 5 +- drivers/media/platform/vsp1/vsp1_pipe.c | 33 +---- drivers/media/platform/vsp1/vsp1_rpf.c | 9 +- drivers/media/platform/vsp1/vsp1_rwpf.c | 26 ---- drivers/media/platform/vsp1/vsp1_rwpf.h | 18 ++- drivers/media/platform/vsp1/vsp1_video.c | 145 +++++++++++++++------- drivers/media/platform/vsp1/vsp1_wpf.c | 18 ++- 9 files changed, 142 insertions(+), 146 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 81ea1b2ce00d..54f8f4719276 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -311,14 +311,15 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) /* Hardware Setup */ void vsp1_dlm_setup(struct vsp1_device *vsp1) { - u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT); + u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) + | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 + | VI6_DL_CTRL_DLE; - /* The DRM pipeline operates with header-less display lists in - * Continuous Frame Mode. + /* The DRM pipeline operates with display lists in Continuous Frame + * Mode, all other pipelines use manual start. */ if (vsp1->drm) - ctrl |= VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 - | VI6_DL_CTRL_DLE | VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; + ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; vsp1_write(vsp1, VI6_DL_CTRL, ctrl); vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 9193b7b7d183..a73018c9e8b5 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -36,11 +36,6 @@ void vsp1_drm_display_start(struct vsp1_device *vsp1) vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm); } -static void vsp1_drm_frame_end(struct vsp1_pipeline *pipe) -{ - vsp1_dlm_irq_frame_end(pipe->output->dlm); -} - /* ----------------------------------------------------------------------------- * DU Driver API */ @@ -280,7 +275,6 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, const struct vsp1_format_info *fmtinfo; struct v4l2_subdev_selection sel; struct v4l2_subdev_format format; - struct vsp1_rwpf_memory memory; struct vsp1_rwpf *rpf; unsigned long flags; int ret; @@ -420,15 +414,12 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, rpf->location.left = dst->left; rpf->location.top = dst->top; - /* Set the memory buffer address but don't apply the values to the + /* Cache the memory buffer address but don't apply the values to the * hardware as the crop offsets haven't been computed yet. */ - memory.num_planes = fmtinfo->planes; - memory.addr[0] = mem[0]; - memory.addr[1] = mem[1]; - memory.addr[2] = 0; - - vsp1_rwpf_set_memory(rpf, &memory, false); + rpf->mem.addr[0] = mem[0]; + rpf->mem.addr[1] = mem[1]; + rpf->mem.addr[2] = 0; spin_lock_irqsave(&pipe->irqlock, flags); @@ -482,14 +473,17 @@ void vsp1_du_atomic_flush(struct device *dev) entity->subdev.name); return; } + + if (entity->type == VSP1_ENTITY_RPF) + vsp1_rwpf_set_memory(to_rwpf(&entity->subdev)); } vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; + /* Start or stop the pipeline if needed. */ spin_lock_irqsave(&pipe->irqlock, flags); - /* Start or stop the pipeline if needed. */ if (!vsp1->drm->num_inputs && pipe->num_inputs) { vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE); @@ -569,7 +563,6 @@ int vsp1_drm_init(struct vsp1_device *vsp1) pipe = &vsp1->drm->pipe; vsp1_pipeline_init(pipe); - pipe->frame_end = vsp1_drm_frame_end; /* The DRM pipeline is static, add entities manually. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index be67727f6f78..7b2301dbd584 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -27,10 +27,7 @@ void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); - if (pipe->dl) - vsp1_dl_list_write(pipe->dl, reg, data); - else - vsp1_write(e->vsp1, reg, data); + vsp1_dl_list_write(pipe->dl, reg, data); } void vsp1_entity_route_setup(struct vsp1_entity *source) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index a9a754e17e8d..3311db18f40b 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -273,42 +273,13 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { - enum vsp1_pipeline_state state; - unsigned long flags; - if (pipe == NULL) return; - /* Signal frame end to the pipeline handler. */ + vsp1_dlm_irq_frame_end(pipe->output->dlm); + if (pipe->frame_end) pipe->frame_end(pipe); - - spin_lock_irqsave(&pipe->irqlock, flags); - - state = pipe->state; - - /* When using display lists in continuous frame mode the pipeline is - * automatically restarted by the hardware. - */ - if (pipe->lif) - goto done; - - pipe->state = VSP1_PIPELINE_STOPPED; - - /* If a stop has been requested, mark the pipeline as stopped and - * return. - */ - if (state == VSP1_PIPELINE_STOPPING) { - wake_up(&pipe->wq); - goto done; - } - - /* Restart the pipeline if ready. */ - if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); - -done: - spin_unlock_irqrestore(&pipe->irqlock, flags); } /* diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index ffe097b27a77..09919db7e0ea 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -78,9 +78,6 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); - /* Now that the offsets have been computed program the DMA addresses. */ - rpf->ops->set_memory(rpf); - /* Format */ infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -150,11 +147,11 @@ static struct v4l2_subdev_ops rpf_ops = { static void rpf_set_memory(struct vsp1_rwpf *rpf) { vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, - rpf->buf_addr[0] + rpf->offsets[0]); + rpf->mem.addr[0] + rpf->offsets[0]); vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, - rpf->buf_addr[1] + rpf->offsets[1]); + rpf->mem.addr[1] + rpf->offsets[1]); vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, - rpf->buf_addr[2] + rpf->offsets[1]); + rpf->mem.addr[2] + rpf->offsets[1]); } static const struct vsp1_rwpf_operations rpf_vdev_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 0924079b920c..38893ab06cd9 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -269,29 +269,3 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf) return rwpf->ctrls.error; } - -/* ----------------------------------------------------------------------------- - * Buffers - */ - -/** - * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF - * @rwpf: the [RW]PF instance - * @mem: DMA memory addresses - * @apply: whether to apply the configuration to the hardware - * - * This function stores the DMA addresses for all planes in the rwpf instance - * and optionally applies the configuration to hardware registers if the apply - * argument is set to true. - */ -void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, struct vsp1_rwpf_memory *mem, - bool apply) -{ - unsigned int i; - - for (i = 0; i < 3; ++i) - rwpf->buf_addr[i] = mem->addr[i]; - - if (apply) - rwpf->ops->set_memory(rwpf); -} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 57f15d45f8bb..2bbcc331959b 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -29,15 +29,13 @@ struct vsp1_rwpf; struct vsp1_video; struct vsp1_rwpf_memory { - unsigned int num_planes; dma_addr_t addr[3]; - unsigned int length[3]; }; /** * struct vsp1_rwpf_operations - RPF and WPF operations * @set_memory: Setup memory buffer access. This operation applies the settings - * stored in the rwpf buf_addr field to the hardware. + * stored in the rwpf mem field to the hardware. */ struct vsp1_rwpf_operations { void (*set_memory)(struct vsp1_rwpf *rwpf); @@ -65,7 +63,7 @@ struct vsp1_rwpf { unsigned int alpha; unsigned int offsets[2]; - dma_addr_t buf_addr[3]; + struct vsp1_rwpf_memory mem; struct vsp1_dl_manager *dlm; }; @@ -99,7 +97,15 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); -void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, struct vsp1_rwpf_memory *mem, - bool apply); +/** + * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF + * @rwpf: the [RW]PF instance + * + * This function applies the cached memory buffer address to the hardware. + */ +static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf) +{ + rwpf->ops->set_memory(rwpf); +} #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 96b04fcd33ae..7cb270f57f62 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -29,6 +29,7 @@ #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" @@ -424,7 +425,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video) done->buf.vb2_buf.timestamp = ktime_get_ns(); for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) vb2_set_plane_payload(&done->buf.vb2_buf, i, - done->mem.length[i]); + vb2_plane_size(&done->buf.vb2_buf, i)); vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); return next; @@ -443,15 +444,41 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, spin_lock_irqsave(&pipe->irqlock, flags); - vsp1_rwpf_set_memory(video->rwpf, &buf->mem, true); + video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; spin_unlock_irqrestore(&pipe->irqlock, flags); } +static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + unsigned int i; + + if (!pipe->dl) + pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rwpf = pipe->inputs[i]; + + if (rwpf) + vsp1_rwpf_set_memory(rwpf); + } + + if (!pipe->lif) + vsp1_rwpf_set_memory(pipe->output); + + vsp1_dl_list_commit(pipe->dl); + pipe->dl = NULL; + + vsp1_pipeline_run(pipe); +} + static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + enum vsp1_pipeline_state state; + unsigned long flags; unsigned int i; /* Complete buffers on all video nodes. */ @@ -462,8 +489,22 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) vsp1_video_frame_end(pipe, pipe->inputs[i]); } - if (!pipe->lif) - vsp1_video_frame_end(pipe, pipe->output); + vsp1_video_frame_end(pipe, pipe->output); + + spin_lock_irqsave(&pipe->irqlock, flags); + + state = pipe->state; + pipe->state = VSP1_PIPELINE_STOPPED; + + /* If a stop has been requested, mark the pipeline as stopped and + * return. Otherwise restart the pipeline if ready. + */ + if (state == VSP1_PIPELINE_STOPPING) + wake_up(&pipe->wq); + else if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + + spin_unlock_irqrestore(&pipe->irqlock, flags); } /* ----------------------------------------------------------------------------- @@ -512,20 +553,15 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) if (vb->num_planes < format->num_planes) return -EINVAL; - buf->mem.num_planes = vb->num_planes; - for (i = 0; i < vb->num_planes; ++i) { buf->mem.addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); - buf->mem.length[i] = vb2_plane_size(vb, i); - if (buf->mem.length[i] < format->plane_fmt[i].sizeimage) + if (vb2_plane_size(vb, i) < format->plane_fmt[i].sizeimage) return -EINVAL; } - for ( ; i < 3; ++i) { + for ( ; i < 3; ++i) buf->mem.addr[i] = 0; - buf->mem.length[i] = 0; - } return 0; } @@ -549,54 +585,74 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pipe->irqlock, flags); - vsp1_rwpf_set_memory(video->rwpf, &buf->mem, true); + video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; if (vb2_is_streaming(&video->queue) && vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); + vsp1_video_pipeline_run(pipe); spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) +{ + struct vsp1_entity *entity; + int ret; + + /* Prepare the display list. */ + pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + if (!pipe->dl) + return -ENOMEM; + + if (pipe->uds) { + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); + + /* If a BRU is present in the pipeline before the UDS, the alpha + * component doesn't need to be scaled as the BRU output alpha + * value is fixed to 255. Otherwise we need to scale the alpha + * component only when available at the input RPF. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { + uds->scale_alpha = false; + } else { + struct vsp1_rwpf *rpf = + to_rwpf(&pipe->uds_input->subdev); + + uds->scale_alpha = rpf->fmtinfo->alpha; + } + } + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity); + + ret = v4l2_subdev_call(&entity->subdev, video, s_stream, 1); + if (ret < 0) + goto error; + } + + return 0; + +error: + vsp1_dl_list_put(pipe->dl); + pipe->dl = NULL; + + return ret; +} + static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); - struct vsp1_entity *entity; unsigned long flags; int ret; mutex_lock(&pipe->lock); if (pipe->stream_count == pipe->num_inputs) { - if (pipe->uds) { - struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); - - /* If a BRU is present in the pipeline before the UDS, - * the alpha component doesn't need to be scaled as the - * BRU output alpha value is fixed to 255. Otherwise we - * need to scale the alpha component only when available - * at the input RPF. - */ - if (pipe->uds_input->type == VSP1_ENTITY_BRU) { - uds->scale_alpha = false; - } else { - struct vsp1_rwpf *rpf = - to_rwpf(&pipe->uds_input->subdev); - - uds->scale_alpha = rpf->fmtinfo->alpha; - } - } - - list_for_each_entry(entity, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity); - - ret = v4l2_subdev_call(&entity->subdev, video, - s_stream, 1); - if (ret < 0) { - mutex_unlock(&pipe->lock); - return ret; - } + ret = vsp1_video_setup_pipeline(pipe); + if (ret < 0) { + mutex_unlock(&pipe->lock); + return ret; } } @@ -605,7 +661,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) spin_lock_irqsave(&pipe->irqlock, flags); if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); + vsp1_video_pipeline_run(pipe); spin_unlock_irqrestore(&pipe->irqlock, flags); return 0; @@ -625,6 +681,9 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) ret = vsp1_pipeline_stop(pipe); if (ret == -ETIMEDOUT) dev_err(video->vsp1->dev, "pipeline stop timeout\n"); + + vsp1_dl_list_put(pipe->dl); + pipe->dl = NULL; } mutex_unlock(&pipe->lock); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d1fad9effb9b..d889997b7948 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -157,9 +157,9 @@ static struct v4l2_subdev_ops wpf_ops = { static void wpf_set_memory(struct vsp1_rwpf *wpf) { - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, wpf->buf_addr[0]); - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, wpf->buf_addr[1]); - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->buf_addr[2]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); } static const struct vsp1_rwpf_operations wpf_vdev_ops = { @@ -200,13 +200,11 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (ret < 0) return ERR_PTR(ret); - /* Initialize the display list manager if the WPF is used for display */ - if ((vsp1->info->features & VSP1_HAS_LIF) && index == 0) { - wpf->dlm = vsp1_dlm_create(vsp1, index, 4); - if (!wpf->dlm) { - ret = -ENOMEM; - goto error; - } + /* Initialize the display list manager. */ + wpf->dlm = vsp1_dlm_create(vsp1, index, 4); + if (!wpf->dlm) { + ret = -ENOMEM; + goto error; } /* Initialize the V4L2 subdev. */ From 823329dfee7224712569cc4899720bc470a2fe56 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Nov 2015 19:42:01 -0200 Subject: [PATCH 056/173] [media] v4l: vsp1: Move subdev initialization code to vsp1_entity_init() Don't duplicate the code in every module driver, centralize it in a single place. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 18 ++------------ drivers/media/platform/vsp1/vsp1_entity.c | 30 +++++++++++++++++++---- drivers/media/platform/vsp1/vsp1_entity.h | 5 ++-- drivers/media/platform/vsp1/vsp1_hsit.c | 17 ++----------- drivers/media/platform/vsp1/vsp1_lif.c | 16 +----------- drivers/media/platform/vsp1/vsp1_lut.c | 16 +----------- drivers/media/platform/vsp1/vsp1_rpf.c | 18 +++----------- drivers/media/platform/vsp1/vsp1_sru.c | 16 +----------- drivers/media/platform/vsp1/vsp1_uds.c | 18 +++----------- drivers/media/platform/vsp1/vsp1_wpf.c | 18 +++----------- 10 files changed, 43 insertions(+), 129 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 6a6e9d84f1ca..2ca80d3dda1f 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -405,7 +405,6 @@ static struct v4l2_subdev_ops bru_ops = { struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) { - struct v4l2_subdev *subdev; struct vsp1_bru *bru; int ret; @@ -415,24 +414,11 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) bru->entity.type = VSP1_ENTITY_BRU; - ret = vsp1_entity_init(vsp1, &bru->entity, - vsp1->info->num_bru_inputs + 1); + ret = vsp1_entity_init(vsp1, &bru->entity, "bru", + vsp1->info->num_bru_inputs + 1, &bru_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &bru->entity.subdev; - v4l2_subdev_init(subdev, &bru_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s bru", - dev_name(vsp1->dev)); - v4l2_set_subdevdata(subdev, bru); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - /* Initialize the control handler. */ v4l2_ctrl_handler_init(&bru->ctrls, 1); v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 7b2301dbd584..8432c49fbd75 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -70,8 +70,8 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, * formats are initialized on the file handle. Otherwise active formats are * initialized on the device. */ -void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) +static void vsp1_entity_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) { struct v4l2_subdev_format format; unsigned int pad; @@ -159,9 +159,12 @@ static const struct vsp1_route vsp1_routes[] = { }; int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, - unsigned int num_pads) + const char *name, unsigned int num_pads, + const struct v4l2_subdev_ops *ops) { + struct v4l2_subdev *subdev; unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { if (vsp1_routes[i].type == entity->type && @@ -196,8 +199,25 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; /* Initialize the media entity. */ - return media_entity_pads_init(&entity->subdev.entity, num_pads, - entity->pads); + ret = media_entity_pads_init(&entity->subdev.entity, num_pads, + entity->pads); + if (ret < 0) + return ret; + + /* Initialize the V4L2 subdev. */ + subdev = &entity->subdev; + v4l2_subdev_init(subdev, ops); + + subdev->entity.ops = &vsp1->media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + snprintf(subdev->name, sizeof(subdev->name), "%s %s", + dev_name(vsp1->dev), name); + + vsp1_entity_init_formats(subdev, NULL); + + return 0; } void vsp1_entity_destroy(struct vsp1_entity *entity) diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 203872164f8e..d76090059ced 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -81,7 +81,8 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) } int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, - unsigned int num_pads); + const char *name, unsigned int num_pads, + const struct v4l2_subdev_ops *ops); void vsp1_entity_destroy(struct vsp1_entity *entity); extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; @@ -94,8 +95,6 @@ struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which); -void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg); void vsp1_entity_route_setup(struct vsp1_entity *source); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index e820fe0b4f00..49ff74b51e03 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -180,7 +180,6 @@ static struct v4l2_subdev_ops hsit_ops = { struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) { - struct v4l2_subdev *subdev; struct vsp1_hsit *hsit; int ret; @@ -195,22 +194,10 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) else hsit->entity.type = VSP1_ENTITY_HST; - ret = vsp1_entity_init(vsp1, &hsit->entity, 2); + ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2, + &hsit_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &hsit->entity.subdev; - v4l2_subdev_init(subdev, &hsit_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s %s", - dev_name(vsp1->dev), inverse ? "hsi" : "hst"); - v4l2_set_subdevdata(subdev, hsit); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - return hsit; } diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index be0ea166016c..ead6cc3aa3fa 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -207,7 +207,6 @@ static struct v4l2_subdev_ops lif_ops = { struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) { - struct v4l2_subdev *subdev; struct vsp1_lif *lif; int ret; @@ -217,22 +216,9 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) lif->entity.type = VSP1_ENTITY_LIF; - ret = vsp1_entity_init(vsp1, &lif->entity, 2); + ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &lif->entity.subdev; - v4l2_subdev_init(subdev, &lif_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s lif", - dev_name(vsp1->dev)); - v4l2_set_subdevdata(subdev, lif); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - return lif; } diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 3af849dbee5a..6ba6a58fbac6 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -221,7 +221,6 @@ static struct v4l2_subdev_ops lut_ops = { struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) { - struct v4l2_subdev *subdev; struct vsp1_lut *lut; int ret; @@ -231,22 +230,9 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) lut->entity.type = VSP1_ENTITY_LUT; - ret = vsp1_entity_init(vsp1, &lut->entity, 2); + ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &lut->entity.subdev; - v4l2_subdev_init(subdev, &lut_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s lut", - dev_name(vsp1->dev)); - v4l2_set_subdevdata(subdev, lut); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - return lut; } diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 09919db7e0ea..8d9e511fd61a 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -164,8 +164,8 @@ static const struct vsp1_rwpf_operations rpf_vdev_ops = { struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) { - struct v4l2_subdev *subdev; struct vsp1_rwpf *rpf; + char name[6]; int ret; rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); @@ -180,23 +180,11 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.type = VSP1_ENTITY_RPF; rpf->entity.index = index; - ret = vsp1_entity_init(vsp1, &rpf->entity, 2); + sprintf(name, "rpf.%u", index); + ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &rpf->entity.subdev; - v4l2_subdev_init(subdev, &rpf_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", - dev_name(vsp1->dev), index); - v4l2_set_subdevdata(subdev, rpf); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - /* Initialize the control handler. */ ret = vsp1_rwpf_init_ctrls(rpf); if (ret < 0) { diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 19b7923cb137..c8642bf8b1a1 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -324,7 +324,6 @@ static struct v4l2_subdev_ops sru_ops = { struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) { - struct v4l2_subdev *subdev; struct vsp1_sru *sru; int ret; @@ -334,23 +333,10 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) sru->entity.type = VSP1_ENTITY_SRU; - ret = vsp1_entity_init(vsp1, &sru->entity, 2); + ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &sru->entity.subdev; - v4l2_subdev_init(subdev, &sru_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s sru", - dev_name(vsp1->dev)); - v4l2_set_subdevdata(subdev, sru); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - /* Initialize the control handler. */ v4l2_ctrl_handler_init(&sru->ctrls, 1); v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 83ec8942f8e7..34689adda810 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -322,8 +322,8 @@ static struct v4l2_subdev_ops uds_ops = { struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) { - struct v4l2_subdev *subdev; struct vsp1_uds *uds; + char name[6]; int ret; uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL); @@ -333,22 +333,10 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) uds->entity.type = VSP1_ENTITY_UDS; uds->entity.index = index; - ret = vsp1_entity_init(vsp1, &uds->entity, 2); + sprintf(name, "uds.%u", index); + ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops); if (ret < 0) return ERR_PTR(ret); - /* Initialize the V4L2 subdev. */ - subdev = &uds->entity.subdev; - v4l2_subdev_init(subdev, &uds_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u", - dev_name(vsp1->dev), index); - v4l2_set_subdevdata(subdev, uds); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - return uds; } diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d889997b7948..b81595eb51dc 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -179,8 +179,8 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) { - struct v4l2_subdev *subdev; struct vsp1_rwpf *wpf; + char name[6]; int ret; wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); @@ -196,7 +196,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->entity.type = VSP1_ENTITY_WPF; wpf->entity.index = index; - ret = vsp1_entity_init(vsp1, &wpf->entity, 2); + sprintf(name, "wpf.%u", index); + ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops); if (ret < 0) return ERR_PTR(ret); @@ -207,19 +208,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; } - /* Initialize the V4L2 subdev. */ - subdev = &wpf->entity.subdev; - v4l2_subdev_init(subdev, &wpf_ops); - - subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; - snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u", - dev_name(vsp1->dev), index); - v4l2_set_subdevdata(subdev, wpf); - subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - vsp1_entity_init_formats(subdev, NULL); - /* Initialize the control handler. */ ret = vsp1_rwpf_init_ctrls(wpf); if (ret < 0) { From 5243453472e7bce74764ddf9f206450dcc8769c5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Nov 2015 12:23:23 -0200 Subject: [PATCH 057/173] [media] v4l: vsp1: Consolidate entity ops in a struct vsp1_entity_operations Entities have two operations, a destroy operation stored directly in vsp1_entity and a set_memory operation stored in a vsp1_rwpf_operations structure. Move the two to a more generic vsp1_entity_operations structure that will serve to implement additional operations. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_entity.c | 4 ++-- drivers/media/platform/vsp1/vsp1_entity.h | 14 +++++++++++- drivers/media/platform/vsp1/vsp1_rpf.c | 11 ++++----- drivers/media/platform/vsp1/vsp1_rwpf.h | 18 +++++---------- drivers/media/platform/vsp1/vsp1_wpf.c | 27 ++++++++++++----------- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 8432c49fbd75..2676f6723994 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -222,8 +222,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { - if (entity->destroy) - entity->destroy(entity); + if (entity->ops && entity->ops->destroy) + entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index d76090059ced..0fdda82a8d9a 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -53,10 +53,22 @@ struct vsp1_route { unsigned int inputs[VSP1_ENTITY_MAX_INPUTS]; }; +/** + * struct vsp1_entity_operations - Entity operations + * @destroy: Destroy the entity. + * @set_memory: Setup memory buffer access. This operation applies the settings + * stored in the rwpf mem field to the hardware. Valid for RPF and + * WPF only. + */ +struct vsp1_entity_operations { + void (*destroy)(struct vsp1_entity *); + void (*set_memory)(struct vsp1_entity *); +}; + struct vsp1_entity { struct vsp1_device *vsp1; - void (*destroy)(struct vsp1_entity *); + const struct vsp1_entity_operations *ops; enum vsp1_entity_type type; unsigned int index; diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 8d9e511fd61a..a68f26db9b3f 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -141,11 +141,13 @@ static struct v4l2_subdev_ops rpf_ops = { }; /* ----------------------------------------------------------------------------- - * Video Device Operations + * VSP1 Entity Operations */ -static void rpf_set_memory(struct vsp1_rwpf *rpf) +static void rpf_set_memory(struct vsp1_entity *entity) { + struct vsp1_rwpf *rpf = entity_to_rwpf(entity); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, rpf->mem.addr[0] + rpf->offsets[0]); vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, @@ -154,7 +156,7 @@ static void rpf_set_memory(struct vsp1_rwpf *rpf) rpf->mem.addr[2] + rpf->offsets[1]); } -static const struct vsp1_rwpf_operations rpf_vdev_ops = { +static const struct vsp1_entity_operations rpf_entity_ops = { .set_memory = rpf_set_memory, }; @@ -172,11 +174,10 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) if (rpf == NULL) return ERR_PTR(-ENOMEM); - rpf->ops = &rpf_vdev_ops; - rpf->max_width = RPF_MAX_WIDTH; rpf->max_height = RPF_MAX_HEIGHT; + rpf->entity.ops = &rpf_entity_ops; rpf->entity.type = VSP1_ENTITY_RPF; rpf->entity.index = index; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 2bbcc331959b..e8ca9b6ee689 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -32,23 +32,12 @@ struct vsp1_rwpf_memory { dma_addr_t addr[3]; }; -/** - * struct vsp1_rwpf_operations - RPF and WPF operations - * @set_memory: Setup memory buffer access. This operation applies the settings - * stored in the rwpf mem field to the hardware. - */ -struct vsp1_rwpf_operations { - void (*set_memory)(struct vsp1_rwpf *rwpf); -}; - struct vsp1_rwpf { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; struct vsp1_video *video; - const struct vsp1_rwpf_operations *ops; - unsigned int max_width; unsigned int max_height; @@ -73,6 +62,11 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) return container_of(subdev, struct vsp1_rwpf, entity.subdev); } +static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *entity) +{ + return container_of(entity, struct vsp1_rwpf, entity); +} + struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); @@ -105,7 +99,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, */ static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf) { - rwpf->ops->set_memory(rwpf); + rwpf->entity.ops->set_memory(&rwpf->entity); } #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index b81595eb51dc..84772fa258a5 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -152,17 +152,27 @@ static struct v4l2_subdev_ops wpf_ops = { }; /* ----------------------------------------------------------------------------- - * Video Device Operations + * VSP1 Entity Operations */ -static void wpf_set_memory(struct vsp1_rwpf *wpf) +static void vsp1_wpf_destroy(struct vsp1_entity *entity) { + struct vsp1_rwpf *wpf = entity_to_rwpf(entity); + + vsp1_dlm_destroy(wpf->dlm); +} + +static void wpf_set_memory(struct vsp1_entity *entity) +{ + struct vsp1_rwpf *wpf = entity_to_rwpf(entity); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); } -static const struct vsp1_rwpf_operations wpf_vdev_ops = { +static const struct vsp1_entity_operations wpf_entity_ops = { + .destroy = vsp1_wpf_destroy, .set_memory = wpf_set_memory, }; @@ -170,13 +180,6 @@ static const struct vsp1_rwpf_operations wpf_vdev_ops = { * Initialization and Cleanup */ -static void vsp1_wpf_destroy(struct vsp1_entity *entity) -{ - struct vsp1_rwpf *wpf = container_of(entity, struct vsp1_rwpf, entity); - - vsp1_dlm_destroy(wpf->dlm); -} - struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) { struct vsp1_rwpf *wpf; @@ -187,12 +190,10 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (wpf == NULL) return ERR_PTR(-ENOMEM); - wpf->ops = &wpf_vdev_ops; - wpf->max_width = WPF_MAX_WIDTH; wpf->max_height = WPF_MAX_HEIGHT; - wpf->entity.destroy = vsp1_wpf_destroy; + wpf->entity.ops = &wpf_entity_ops; wpf->entity.type = VSP1_ENTITY_WPF; wpf->entity.index = index; From 21b3d7365f4fa7865568823ba844ce056c1fcf9b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Nov 2015 21:51:01 -0200 Subject: [PATCH 058/173] [media] v4l: vsp1: Fix BRU try compose rectangle storage Fix a typo that stored the try compose rectangle in the crop rectangle. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 2ca80d3dda1f..d8b7bdeb5af5 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -228,7 +228,8 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&bru->entity.subdev, cfg, pad); + return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, + pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &bru->inputs[pad].compose; default: From 613721265ab8d3df784488e3073d92fcb466df34 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Nov 2015 10:28:56 -0200 Subject: [PATCH 059/173] [media] v4l: vsp1: Add race condition FIXME comment Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 7cb270f57f62..102977ae1daa 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -813,6 +813,9 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * * Use the VSP1 pipeline object embedded in the first video object that * starts streaming. + * + * FIXME: This is racy, the ioctl is only protected by the video node + * lock. */ pipe = video->video.entity.pipe ? to_vsp1_pipeline(&video->video.entity) : &video->pipe; From 0efdf0f5eaaff6c18d1e645a8e1fdebf73400fe1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Nov 2015 20:09:08 -0200 Subject: [PATCH 060/173] [media] v4l: vsp1: Implement and use the subdev pad::init_cfg configuration Turn the custom formats initialization function into a standard pad::init_cfg handler and use it in subdevs instead of initializing formats in the subdev open handler. This makes the subdev open handler empty, so remove it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 1 + drivers/media/platform/vsp1/vsp1_entity.c | 24 ++++++----------------- drivers/media/platform/vsp1/vsp1_entity.h | 2 ++ drivers/media/platform/vsp1/vsp1_hsit.c | 1 + drivers/media/platform/vsp1/vsp1_lif.c | 1 + drivers/media/platform/vsp1/vsp1_lut.c | 1 + drivers/media/platform/vsp1/vsp1_rpf.c | 1 + drivers/media/platform/vsp1/vsp1_sru.c | 1 + drivers/media/platform/vsp1/vsp1_uds.c | 1 + drivers/media/platform/vsp1/vsp1_wpf.c | 1 + 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index d8b7bdeb5af5..879dc9c9d3f0 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -387,6 +387,7 @@ static struct v4l2_subdev_video_ops bru_video_ops = { }; static struct v4l2_subdev_pad_ops bru_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = bru_enum_mbus_code, .enum_frame_size = bru_enum_frame_size, .get_fmt = bru_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 2676f6723994..5423e29e0d49 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -62,16 +62,15 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, } /* - * vsp1_entity_init_formats - Initialize formats on all pads + * vsp1_entity_init_cfg - Initialize formats on all pads * @subdev: V4L2 subdevice * @cfg: V4L2 subdev pad configuration * - * Initialize all pad formats with default values. If cfg is not NULL, try - * formats are initialized on the file handle. Otherwise active formats are - * initialized on the device. + * Initialize all pad formats with default values in the given pad config. This + * function can be used as a handler for the subdev pad::init_cfg operation. */ -static void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) +int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) { struct v4l2_subdev_format format; unsigned int pad; @@ -85,20 +84,10 @@ static void vsp1_entity_init_formats(struct v4l2_subdev *subdev, v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format); } -} - -static int vsp1_entity_open(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh) -{ - vsp1_entity_init_formats(subdev, fh->pad); return 0; } -const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { - .open = vsp1_entity_open, -}; - /* ----------------------------------------------------------------------------- * Media Operations */ @@ -209,13 +198,12 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, v4l2_subdev_init(subdev, ops); subdev->entity.ops = &vsp1->media_ops; - subdev->internal_ops = &vsp1_subdev_internal_ops; subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), name); - vsp1_entity_init_formats(subdev, NULL); + vsp1_entity_init_cfg(subdev, NULL); return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 0fdda82a8d9a..8e6cf776e990 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -107,6 +107,8 @@ struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which); +int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg); void vsp1_entity_route_setup(struct vsp1_entity *source); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 49ff74b51e03..3e36755c8c2a 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -163,6 +163,7 @@ static struct v4l2_subdev_video_ops hsit_video_ops = { }; static struct v4l2_subdev_pad_ops hsit_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, .get_fmt = hsit_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index ead6cc3aa3fa..5edf6a742c5b 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -190,6 +190,7 @@ static struct v4l2_subdev_video_ops lif_video_ops = { }; static struct v4l2_subdev_pad_ops lif_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, .get_fmt = lif_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 6ba6a58fbac6..f0afc4291387 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -203,6 +203,7 @@ static struct v4l2_subdev_video_ops lut_video_ops = { }; static struct v4l2_subdev_pad_ops lut_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, .get_fmt = lut_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index a68f26db9b3f..69fd76eed0bb 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -127,6 +127,7 @@ static struct v4l2_subdev_video_ops rpf_video_ops = { }; static struct v4l2_subdev_pad_ops rpf_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_rwpf_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index c8642bf8b1a1..043dac6644c1 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -307,6 +307,7 @@ static struct v4l2_subdev_video_ops sru_video_ops = { }; static struct v4l2_subdev_pad_ops sru_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, .get_fmt = sru_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 34689adda810..666fabcd0382 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -305,6 +305,7 @@ static struct v4l2_subdev_video_ops uds_video_ops = { }; static struct v4l2_subdev_pad_ops uds_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, .get_fmt = uds_get_format, diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 84772fa258a5..d46910db7e08 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -138,6 +138,7 @@ static struct v4l2_subdev_video_ops wpf_video_ops = { }; static struct v4l2_subdev_pad_ops wpf_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, .get_fmt = vsp1_rwpf_get_format, From e790c3cb8d904c4bad0d4a37885bece2eb848eeb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Nov 2015 19:14:22 -0200 Subject: [PATCH 061/173] [media] v4l: vsp1: Store active formats in a pad config structure Add a pad config structure field to the vsp1_entity structure and use it to store all active pad formats. This generalizes the code to operate on pad config structures. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 59 ++++++++++++++------- drivers/media/platform/vsp1/vsp1_entity.c | 64 +++++++++++++++++------ drivers/media/platform/vsp1/vsp1_entity.h | 8 ++- drivers/media/platform/vsp1/vsp1_hsit.c | 29 +++++++--- drivers/media/platform/vsp1/vsp1_lif.c | 42 +++++++++++---- drivers/media/platform/vsp1/vsp1_lut.c | 42 +++++++++++---- drivers/media/platform/vsp1/vsp1_rpf.c | 12 ++++- drivers/media/platform/vsp1/vsp1_rwpf.c | 51 +++++++++++++----- drivers/media/platform/vsp1/vsp1_sru.c | 61 ++++++++++++++------- drivers/media/platform/vsp1/vsp1_uds.c | 59 ++++++++++++++------- drivers/media/platform/vsp1/vsp1_wpf.c | 12 ++++- 11 files changed, 313 insertions(+), 126 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 879dc9c9d3f0..a0aa0fb2a5e1 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -70,7 +70,8 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) return 0; - format = &bru->entity.formats[bru->entity.source_pad]; + format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, + bru->entity.source_pad); /* The hardware is extremely flexible but we have no userspace API to * expose all the parameters, nor is it clear whether we would have use @@ -183,7 +184,6 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_AYUV8_1X32, }; struct vsp1_bru *bru = to_bru(subdev); - struct v4l2_mbus_framefmt *format; if (code->pad == BRU_PAD_SINK(0)) { if (code->index >= ARRAY_SIZE(codes)) @@ -191,12 +191,19 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + if (code->index) return -EINVAL; - format = vsp1_entity_get_pad_format(&bru->entity, cfg, - BRU_PAD_SINK(0), + config = vsp1_entity_get_pad_config(&bru->entity, cfg, code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&bru->entity, config, + BRU_PAD_SINK(0)); code->code = format->code; } @@ -242,17 +249,21 @@ static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&bru->entity, config, + fmt->pad); return 0; } static void bru_try_format(struct vsp1_bru *bru, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) + struct v4l2_subdev_pad_config *config, + unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; @@ -266,8 +277,8 @@ static void bru_try_format(struct vsp1_bru *bru, default: /* The BRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&bru->entity, cfg, - BRU_PAD_SINK(0), which); + format = vsp1_entity_get_pad_format(&bru->entity, config, + BRU_PAD_SINK(0)); fmt->code = format->code; break; } @@ -283,12 +294,16 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - bru_try_format(bru, cfg, fmt->pad, &fmt->format, fmt->which); + config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); + if (!config) + return -EINVAL; - format = vsp1_entity_get_pad_format(&bru->entity, cfg, fmt->pad, - fmt->which); + bru_try_format(bru, config, fmt->pad, &fmt->format); + + format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad); *format = fmt->format; /* Reset the compose rectangle */ @@ -307,8 +322,8 @@ static int bru_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= bru->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&bru->entity, cfg, - i, fmt->which); + format = vsp1_entity_get_pad_format(&bru->entity, + config, i); format->code = fmt->format.code; } } @@ -347,6 +362,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; @@ -356,19 +372,22 @@ static int bru_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; + config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which); + if (!config) + return -EINVAL; + /* The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, cfg, - bru->entity.source_pad, sel->which); + format = vsp1_entity_get_pad_format(&bru->entity, config, + bru->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); /* Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&bru->entity, cfg, sel->pad, - sel->which); + format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad); sel->r.width = format->width; sel->r.height = format->height; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 5423e29e0d49..5185a1f5d3b8 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -46,19 +46,46 @@ void vsp1_entity_route_setup(struct vsp1_entity *source) * V4L2 Subdevice Operations */ +/** + * vsp1_entity_get_pad_config - Get the pad configuration for an entity + * @entity: the entity + * @cfg: the TRY pad configuration + * @which: configuration selector (ACTIVE or TRY) + * + * Return the pad configuration requested by the which argument. The TRY + * configuration is passed explicitly to the function through the cfg argument + * and simply returned when requested. The ACTIVE configuration comes from the + * entity structure. + */ +struct v4l2_subdev_pad_config * +vsp1_entity_get_pad_config(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_ACTIVE: + return entity->config; + case V4L2_SUBDEV_FORMAT_TRY: + default: + return cfg; + } +} + +/** + * vsp1_entity_get_pad_format - Get a pad format from storage for an entity + * @entity: the entity + * @cfg: the configuration storage + * @pad: the pad number + * + * Return the format stored in the given configuration for an entity's pad. The + * configuration can be an ACTIVE or TRY configuration. + */ struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, u32 which) + unsigned int pad) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &entity->formats[pad]; - default: - return NULL; - } + return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); } /* @@ -169,19 +196,12 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; - /* Allocate formats and pads. */ - entity->formats = devm_kzalloc(vsp1->dev, - num_pads * sizeof(*entity->formats), - GFP_KERNEL); - if (entity->formats == NULL) - return -ENOMEM; - + /* Allocate and initialize pads. */ entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), GFP_KERNEL); if (entity->pads == NULL) return -ENOMEM; - /* Initialize pads. */ for (i = 0; i < num_pads - 1; ++i) entity->pads[i].flags = MEDIA_PAD_FL_SINK; @@ -205,6 +225,15 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, vsp1_entity_init_cfg(subdev, NULL); + /* Allocate the pad configuration to store formats and selection + * rectangles. + */ + entity->config = v4l2_subdev_alloc_pad_config(&entity->subdev); + if (entity->config == NULL) { + media_entity_cleanup(&entity->subdev.entity); + return -ENOMEM; + } + return 0; } @@ -214,5 +243,6 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); + v4l2_subdev_free_pad_config(entity->config); media_entity_cleanup(&entity->subdev.entity); } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 8e6cf776e990..7b2081aff869 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -84,7 +84,7 @@ struct vsp1_entity { unsigned int sink_pad; struct v4l2_subdev subdev; - struct v4l2_mbus_framefmt *formats; + struct v4l2_subdev_pad_config *config; }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -103,10 +103,14 @@ int vsp1_entity_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags); +struct v4l2_subdev_pad_config * +vsp1_entity_get_pad_config(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + enum v4l2_subdev_format_whence which); struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, u32 which); + unsigned int pad); int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 3e36755c8c2a..4f87f6f8ee38 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -77,10 +77,14 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_hsit *hsit = to_hsit(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fse->pad, - fse->which); + config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&hsit->entity, config, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -108,9 +112,14 @@ static int hsit_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, config, + fmt->pad); return 0; } @@ -120,10 +129,14 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_hsit *hsit = to_hsit(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* The HST and HSI output format code and resolution can't be @@ -145,8 +158,8 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE, - fmt->which); + format = vsp1_entity_get_pad_format(&hsit->entity, config, + HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 5edf6a742c5b..4090a0351263 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -48,7 +48,8 @@ static int lif_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } - format = &lif->entity.formats[LIF_PAD_SOURCE]; + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, + LIF_PAD_SOURCE); obth = min(obth, (format->width + 1) / 2 * format->height - 4); @@ -84,6 +85,7 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; /* The LIF can't perform format conversion, the sink format is @@ -92,8 +94,13 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, if (code->index) return -EINVAL; - format = vsp1_entity_get_pad_format(&lif->entity, cfg, - LIF_PAD_SINK, code->which); + config = vsp1_entity_get_pad_config(&lif->entity, cfg, + code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&lif->entity, config, + LIF_PAD_SINK); code->code = format->code; } @@ -105,10 +112,14 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_lif *lif = to_lif(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SINK, - fse->which); + config = vsp1_entity_get_pad_config(&lif->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&lif->entity, config, LIF_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -133,9 +144,14 @@ static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&lif->entity, config, + fmt->pad); return 0; } @@ -145,15 +161,19 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_lif *lif = to_lif(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, - fmt->which); + format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad); if (fmt->pad == LIF_PAD_SOURCE) { /* The LIF source format is always identical to its sink @@ -174,8 +194,8 @@ static int lif_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE, - fmt->which); + format = vsp1_entity_get_pad_format(&lif->entity, config, + LIF_PAD_SOURCE); *format = fmt->format; return 0; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index f0afc4291387..a5b839b28320 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -86,7 +86,6 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_AYUV8_1X32, }; struct vsp1_lut *lut = to_lut(subdev); - struct v4l2_mbus_framefmt *format; if (code->pad == LUT_PAD_SINK) { if (code->index >= ARRAY_SIZE(codes)) @@ -94,14 +93,22 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + /* The LUT can't perform format conversion, the sink format is * always identical to the source format. */ if (code->index) return -EINVAL; - format = vsp1_entity_get_pad_format(&lut->entity, cfg, - LUT_PAD_SINK, code->which); + config = vsp1_entity_get_pad_config(&lut->entity, cfg, + code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&lut->entity, config, + LUT_PAD_SINK); code->code = format->code; } @@ -113,10 +120,14 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_lut *lut = to_lut(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&lut->entity, cfg, - fse->pad, fse->which); + config = vsp1_entity_get_pad_config(&lut->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&lut->entity, config, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -144,9 +155,14 @@ static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&lut->entity, config, + fmt->pad); return 0; } @@ -156,16 +172,20 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_lut *lut = to_lut(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad, - fmt->which); + format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad); if (fmt->pad == LUT_PAD_SOURCE) { /* The LUT output format can't be modified. */ @@ -183,8 +203,8 @@ static int lut_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE, - fmt->which); + format = vsp1_entity_get_pad_format(&lut->entity, config, + LUT_PAD_SOURCE); *format = fmt->format; return 0; diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 69fd76eed0bb..3b55cd93983f 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -42,6 +42,8 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) struct vsp1_rwpf *rpf = to_rwpf(subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; + const struct v4l2_mbus_framefmt *source_format; + const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; @@ -79,6 +81,13 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ + sink_format = vsp1_entity_get_pad_format(&rpf->entity, + rpf->entity.config, + RWPF_PAD_SINK); + source_format = vsp1_entity_get_pad_format(&rpf->entity, + rpf->entity.config, + RWPF_PAD_SOURCE); + infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -87,8 +96,7 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) if (fmtinfo->swap_uv) infmt |= VI6_RPF_INFMT_SPUVS; - if (rpf->entity.formats[RWPF_PAD_SINK].code != - rpf->entity.formats[RWPF_PAD_SOURCE].code) + if (sink_format->code != source_format->code) infmt |= VI6_RPF_INFMT_CSC; vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 38893ab06cd9..e5216d39723e 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -46,10 +46,14 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fse->pad, - fse->which); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&rwpf->entity, config, fse->pad); if (fse->index || fse->code != format->code) return -EINVAL; @@ -92,9 +96,14 @@ int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, config, + fmt->pad); return 0; } @@ -104,16 +113,20 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad, - fmt->which); + format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* The RWPF performs format conversion but can't scale, only the @@ -142,8 +155,8 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, crop->height = fmt->format.height; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, - fmt->which); + format = vsp1_entity_get_pad_format(&rwpf->entity, config, + RWPF_PAD_SOURCE); *format = fmt->format; return 0; @@ -154,20 +167,25 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; /* Cropping is implemented on the sink pad. */ if (sel->pad != RWPF_PAD_SINK) return -EINVAL; + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); + if (!config) + return -EINVAL; + switch (sel->target) { case V4L2_SEL_TGT_CROP: sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, - RWPF_PAD_SINK, sel->which); + format = vsp1_entity_get_pad_format(&rwpf->entity, config, + RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -186,6 +204,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; @@ -196,11 +215,15 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); + if (!config) + return -EINVAL; + /* Make sure the crop rectangle is entirely contained in the image. The * WPF top and left offsets are limited to 255. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, - sel->which); + format = vsp1_entity_get_pad_format(&rwpf->entity, config, + RWPF_PAD_SINK); /* Restrict the crop rectangle coordinates to multiples of 2 to avoid * shifting the color plane. @@ -227,8 +250,8 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE, - sel->which); + format = vsp1_entity_get_pad_format(&rwpf->entity, config, + RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 043dac6644c1..c9a97ba5a042 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -117,8 +117,10 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) return 0; - input = &sru->entity.formats[SRU_PAD_SINK]; - output = &sru->entity.formats[SRU_PAD_SOURCE]; + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SINK); + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -153,7 +155,6 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_AYUV8_1X32, }; struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_mbus_framefmt *format; if (code->pad == SRU_PAD_SINK) { if (code->index >= ARRAY_SIZE(codes)) @@ -161,14 +162,22 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + /* The SRU can't perform format conversion, the sink format is * always identical to the source format. */ if (code->index) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, cfg, - SRU_PAD_SINK, code->which); + config = vsp1_entity_get_pad_config(&sru->entity, cfg, + code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&sru->entity, config, + SRU_PAD_SINK); code->code = format->code; } @@ -180,10 +189,14 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_sru *sru = to_sru(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&sru->entity, cfg, - SRU_PAD_SINK, fse->which); + config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -214,17 +227,21 @@ static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&sru->entity, config, + fmt->pad); return 0; } static void sru_try_format(struct vsp1_sru *sru, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) + struct v4l2_subdev_pad_config *config, + unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; unsigned int input_area; @@ -243,8 +260,8 @@ static void sru_try_format(struct vsp1_sru *sru, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, cfg, - SRU_PAD_SINK, which); + format = vsp1_entity_get_pad_format(&sru->entity, config, + SRU_PAD_SINK); fmt->code = format->code; /* We can upscale by 2 in both direction, but not independently. @@ -278,21 +295,25 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_sru *sru = to_sru(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which); + config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); + if (!config) + return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad, - fmt->which); + sru_try_format(sru, config, fmt->pad, &fmt->format); + + format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, cfg, - SRU_PAD_SOURCE, fmt->which); + format = vsp1_entity_get_pad_format(&sru->entity, config, + SRU_PAD_SOURCE); *format = fmt->format; - sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which); + sru_try_format(sru, config, SRU_PAD_SOURCE, format); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 666fabcd0382..3ba0f6844d1d 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -120,8 +120,10 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) return 0; - input = &uds->entity.formats[UDS_PAD_SINK]; - output = &uds->entity.formats[UDS_PAD_SOURCE]; + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SINK); + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -178,16 +180,22 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; } else { + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + config = vsp1_entity_get_pad_config(&uds->entity, cfg, + code->which); + if (!config) + return -EINVAL; + /* The UDS can't perform format conversion, the sink format is * always identical to the source format. */ if (code->index) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, cfg, - UDS_PAD_SINK, code->which); + format = vsp1_entity_get_pad_format(&uds->entity, config, + UDS_PAD_SINK); code->code = format->code; } @@ -199,10 +207,15 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_uds *uds = to_uds(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - format = vsp1_entity_get_pad_format(&uds->entity, cfg, - UDS_PAD_SINK, fse->which); + config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(&uds->entity, config, + UDS_PAD_SINK); if (fse->index || fse->code != format->code) return -EINVAL; @@ -227,17 +240,21 @@ static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); + struct v4l2_subdev_pad_config *config; - fmt->format = *vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, - fmt->which); + config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(&uds->entity, config, + fmt->pad); return 0; } static void uds_try_format(struct vsp1_uds *uds, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad, struct v4l2_mbus_framefmt *fmt, - enum v4l2_subdev_format_whence which) + struct v4l2_subdev_pad_config *config, + unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; unsigned int minimum; @@ -256,8 +273,8 @@ static void uds_try_format(struct vsp1_uds *uds, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, cfg, - UDS_PAD_SINK, which); + format = vsp1_entity_get_pad_format(&uds->entity, config, + UDS_PAD_SINK); fmt->code = format->code; uds_output_limits(format->width, &minimum, &maximum); @@ -276,21 +293,25 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_uds *uds = to_uds(subdev); + struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - uds_try_format(uds, cfg, fmt->pad, &fmt->format, fmt->which); + config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); + if (!config) + return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, cfg, fmt->pad, - fmt->which); + uds_try_format(uds, config, fmt->pad, &fmt->format); + + format = vsp1_entity_get_pad_format(&uds->entity, config, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, cfg, - UDS_PAD_SOURCE, fmt->which); + format = vsp1_entity_get_pad_format(&uds->entity, config, + UDS_PAD_SOURCE); *format = fmt->format; - uds_try_format(uds, cfg, UDS_PAD_SOURCE, format, fmt->which); + uds_try_format(uds, config, UDS_PAD_SOURCE, format); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d46910db7e08..c86d31f274bf 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -42,6 +42,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *wpf = to_rwpf(subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; + const struct v4l2_mbus_framefmt *source_format; + const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_rect *crop = &wpf->crop; unsigned int i; u32 srcrpf = 0; @@ -94,6 +96,13 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); /* Format */ + sink_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SINK); + source_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SOURCE); + if (!pipe->lif) { const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -109,8 +118,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); } - if (wpf->entity.formats[RWPF_PAD_SINK].code != - wpf->entity.formats[RWPF_PAD_SOURCE].code) + if (sink_format->code != source_format->code) outfmt |= VI6_WPF_OUTFMT_CSC; outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; From b7e5107eebb73d27affed95c20cedbf4784bf17c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Nov 2015 19:14:22 -0200 Subject: [PATCH 062/173] [media] v4l: vsp1: Store active selection rectangles in a pad config structure Use the pad config structure part of the vsp1_entity to store all active pad selection rectangles. This generalizes the code to operate on pad config structures. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 24 +++++++++++------------ drivers/media/platform/vsp1/vsp1_bru.h | 1 - drivers/media/platform/vsp1/vsp1_drm.c | 5 ++--- drivers/media/platform/vsp1/vsp1_entity.c | 8 ++++++++ drivers/media/platform/vsp1/vsp1_entity.h | 4 ++++ drivers/media/platform/vsp1/vsp1_rpf.c | 20 ++++++++++++++++--- drivers/media/platform/vsp1/vsp1_rwpf.c | 22 +++++++-------------- drivers/media/platform/vsp1/vsp1_rwpf.h | 8 +++----- drivers/media/platform/vsp1/vsp1_video.c | 13 +++--------- drivers/media/platform/vsp1/vsp1_wpf.c | 4 +++- 10 files changed, 58 insertions(+), 51 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index a0aa0fb2a5e1..c1848a3ac010 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -231,17 +231,9 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev, static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, u32 which) + unsigned int pad) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, - pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &bru->inputs[pad].compose; - default: - return NULL; - } + return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad); } static int bru_get_format(struct v4l2_subdev *subdev, @@ -310,7 +302,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, if (fmt->pad != bru->entity.source_pad) { struct v4l2_rect *compose; - compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which); + compose = bru_get_compose(bru, config, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -336,6 +328,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_selection *sel) { struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_subdev_pad_config *config; if (sel->pad == bru->entity.source_pad) return -EINVAL; @@ -349,7 +342,12 @@ static int bru_get_selection(struct v4l2_subdev *subdev, return 0; case V4L2_SEL_TGT_COMPOSE: - sel->r = *bru_get_compose(bru, cfg, sel->pad, sel->which); + config = vsp1_entity_get_pad_config(&bru->entity, cfg, + sel->which); + if (!config) + return -EINVAL; + + sel->r = *bru_get_compose(bru, config, sel->pad); return 0; default: @@ -391,7 +389,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, sel->r.width = format->width; sel->r.height = format->height; - compose = bru_get_compose(bru, cfg, sel->pad, sel->which); + compose = bru_get_compose(bru, config, sel->pad); *compose = sel->r; return 0; diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index 4e7d2e79b940..828a3fcadea8 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -31,7 +31,6 @@ struct vsp1_bru { struct { struct vsp1_rwpf *rpf; - struct v4l2_rect compose; } inputs[VSP1_MAX_RPF]; u32 bgcolor; diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a73018c9e8b5..acbf36d315b9 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -410,9 +410,8 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, sel.pad); - /* Store the compose rectangle coordinates in the RPF. */ - rpf->location.left = dst->left; - rpf->location.top = dst->top; + /* Store the BRU input pad number in the RPF. */ + rpf->bru_input = rpf->entity.index; /* Cache the memory buffer address but don't apply the values to the * hardware as the crop offsets haven't been computed yet. diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 5185a1f5d3b8..09c9a1b86e3a 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -88,6 +88,14 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); } +struct v4l2_rect * +vsp1_entity_get_pad_compose(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) +{ + return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); +} + /* * vsp1_entity_init_cfg - Initialize formats on all pads * @subdev: V4L2 subdevice diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 7b2081aff869..f7a360823373 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -111,6 +111,10 @@ struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, unsigned int pad); +struct v4l2_rect * +vsp1_entity_get_pad_compose(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad); int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 3b55cd93983f..cb3d5ed148cc 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -44,7 +44,9 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop = &rpf->crop; + const struct v4l2_rect *crop; + unsigned int left = 0; + unsigned int top = 0; u32 pstride; u32 infmt; @@ -57,6 +59,8 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) * left corner in the plane buffer. Only two offsets are needed, as * planes 2 and 3 always have identical strides. */ + crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); + vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); @@ -103,9 +107,19 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); /* Output location */ + if (pipe->bru) { + const struct v4l2_rect *compose; + + compose = vsp1_entity_get_pad_compose(pipe->bru, + pipe->bru->config, + rpf->bru_input); + left = compose->left; + top = compose->top; + } + vsp1_rpf_write(rpf, VI6_RPF_LOC, - (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | - (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); + (left << VI6_RPF_LOC_HCOORD_SHIFT) | + (top << VI6_RPF_LOC_VCOORD_SHIFT)); /* Use the alpha channel (extended to 8 bits) when available or an * alpha value set through the V4L2_CID_ALPHA_COMPONENT control diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index e5216d39723e..0c5ad023adfb 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -76,19 +76,11 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect * -vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, - u32 which) +struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, + struct v4l2_subdev_pad_config *config) { - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, - RWPF_PAD_SINK); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &rwpf->crop; - default: - return NULL; - } + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, + RWPF_PAD_SINK); } int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, @@ -148,7 +140,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which); + crop = vsp1_rwpf_get_crop(rwpf, config); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -180,7 +172,7 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which); + sel->r = *vsp1_rwpf_get_crop(rwpf, config); break; case V4L2_SEL_TGT_CROP_BOUNDS: @@ -246,7 +238,7 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which); + crop = vsp1_rwpf_get_crop(rwpf, config); *crop = sel->r; /* Propagate the format to the source pad. */ diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index e8ca9b6ee689..4ebfab61e0ef 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -43,11 +43,7 @@ struct vsp1_rwpf { struct v4l2_pix_format_mplane format; const struct vsp1_format_info *fmtinfo; - struct { - unsigned int left; - unsigned int top; - } location; - struct v4l2_rect crop; + unsigned int bru_input; unsigned int alpha; @@ -91,6 +87,8 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel); +struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, + struct v4l2_subdev_pad_config *config); /** * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF * @rwpf: the [RW]PF instance diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 102977ae1daa..d4a092c8ece3 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -182,9 +182,6 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, bool bru_found = false; int ret; - input->location.left = 0; - input->location.top = 0; - ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev); if (ret < 0) return ret; @@ -206,18 +203,14 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, entity = to_vsp1_entity( media_entity_to_v4l2_subdev(pad->entity)); - /* A BRU is present in the pipeline, store the compose rectangle - * location in the input RPF for use when configuring the RPF. + /* A BRU is present in the pipeline, store the BRU input pad + * number in the input RPF for use when configuring the RPF. */ if (entity->type == VSP1_ENTITY_BRU) { struct vsp1_bru *bru = to_bru(&entity->subdev); - struct v4l2_rect *rect = - &bru->inputs[pad->index].compose; bru->inputs[pad->index].rpf = input; - - input->location.left = rect->left; - input->location.top = rect->top; + input->bru_input = pad->index; bru_found = true; } diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index c86d31f274bf..0797927d14cf 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -44,7 +44,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop = &wpf->crop; + const struct v4l2_rect *crop; unsigned int i; u32 srcrpf = 0; u32 outfmt = 0; @@ -88,6 +88,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) format->plane_fmt[1].bytesperline); } + crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); + vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); From 7b905f0583b2e6fe1494a85303a89aa0cd30b0b3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Nov 2015 13:10:26 -0200 Subject: [PATCH 063/173] [media] v4l: vsp1: Create a new configure operation to setup modules The subdev s_stream operation is abused as a generic way to setup modules at every frame. Move the code out to a new VSP1 entity configure operation. Most modules now have an empty s_stream operation that can be removed. The only exception is the WPF module that needs to perform hardware configuration when stopping the stream. The code can be simplified accordingly as we know that that operation never fails. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 231 +++++++++++----------- drivers/media/platform/vsp1/vsp1_drm.c | 14 +- drivers/media/platform/vsp1/vsp1_entity.h | 3 + drivers/media/platform/vsp1/vsp1_hsit.c | 50 ++--- drivers/media/platform/vsp1/vsp1_lif.c | 77 ++++---- drivers/media/platform/vsp1/vsp1_lut.c | 41 ++-- drivers/media/platform/vsp1/vsp1_pipe.c | 4 +- drivers/media/platform/vsp1/vsp1_rpf.c | 83 ++++---- drivers/media/platform/vsp1/vsp1_sru.c | 91 ++++----- drivers/media/platform/vsp1/vsp1_uds.c | 117 ++++++----- drivers/media/platform/vsp1/vsp1_video.c | 15 +- drivers/media/platform/vsp1/vsp1_wpf.c | 188 +++++++++--------- 12 files changed, 428 insertions(+), 486 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index c1848a3ac010..4ab0a805d4b2 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -56,117 +56,7 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = { }; /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - -static int bru_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); - struct vsp1_bru *bru = to_bru(subdev); - struct v4l2_mbus_framefmt *format; - unsigned int flags; - unsigned int i; - - if (!enable) - return 0; - - format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, - bru->entity.source_pad); - - /* The hardware is extremely flexible but we have no userspace API to - * expose all the parameters, nor is it clear whether we would have use - * cases for all the supported modes. Let's just harcode the parameters - * to sane default values for now. - */ - - /* Disable dithering and enable color data normalization unless the - * format at the pipeline output is premultiplied. - */ - flags = pipe->output ? pipe->output->format.flags : 0; - vsp1_bru_write(bru, VI6_BRU_INCTRL, - flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? - 0 : VI6_BRU_INCTRL_NRM); - - /* Set the background position to cover the whole output image and - * configure its color. - */ - vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, - (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | - (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); - vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); - - vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, bru->bgcolor | - (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); - - /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP - * unit with a NOP operation to make BRU input 1 available as the - * Blend/ROP unit B SRC input. - */ - vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | - VI6_BRU_ROP_CROP(VI6_ROP_NOP) | - VI6_BRU_ROP_AROP(VI6_ROP_NOP)); - - for (i = 0; i < bru->entity.source_pad; ++i) { - bool premultiplied = false; - u32 ctrl = 0; - - /* Configure all Blend/ROP units corresponding to an enabled BRU - * input for alpha blending. Blend/ROP units corresponding to - * disabled BRU inputs are used in ROP NOP mode to ignore the - * SRC input. - */ - if (bru->inputs[i].rpf) { - ctrl |= VI6_BRU_CTRL_RBC; - - premultiplied = bru->inputs[i].rpf->format.flags - & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; - } else { - ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) - | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); - } - - /* Select the virtual RPF as the Blend/ROP unit A DST input to - * serve as a background color. - */ - if (i == 0) - ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; - - /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to - * D in that order. The Blend/ROP unit B SRC is hardwired to the - * ROP unit output, the corresponding register bits must be set - * to 0. - */ - if (i != 1) - ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); - - vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl); - - /* Harcode the blending formula to - * - * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa - * DSTa = DSTa * (1 - SRCa) + SRCa - * - * when the SRC input isn't premultiplied, and to - * - * DSTc = DSTc * (1 - SRCa) + SRCc - * DSTa = DSTa * (1 - SRCa) + SRCa - * - * otherwise. - */ - vsp1_bru_write(bru, VI6_BRU_BLD(i), - VI6_BRU_BLD_CCMDX_255_SRC_A | - (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : - VI6_BRU_BLD_CCMDY_SRC_A) | - VI6_BRU_BLD_ACMDX_255_SRC_A | - VI6_BRU_BLD_ACMDY_COEFY | - (0xff << VI6_BRU_BLD_COEFY_SHIFT)); - } - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ /* @@ -395,14 +285,6 @@ static int bru_set_selection(struct v4l2_subdev *subdev, return 0; } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - -static struct v4l2_subdev_video_ops bru_video_ops = { - .s_stream = bru_s_stream, -}; - static struct v4l2_subdev_pad_ops bru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = bru_enum_mbus_code, @@ -414,10 +296,118 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = { }; static struct v4l2_subdev_ops bru_ops = { - .video = &bru_video_ops, .pad = &bru_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void bru_configure(struct vsp1_entity *entity) +{ + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); + struct vsp1_bru *bru = to_bru(&entity->subdev); + struct v4l2_mbus_framefmt *format; + unsigned int flags; + unsigned int i; + + format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, + bru->entity.source_pad); + + /* The hardware is extremely flexible but we have no userspace API to + * expose all the parameters, nor is it clear whether we would have use + * cases for all the supported modes. Let's just harcode the parameters + * to sane default values for now. + */ + + /* Disable dithering and enable color data normalization unless the + * format at the pipeline output is premultiplied. + */ + flags = pipe->output ? pipe->output->format.flags : 0; + vsp1_bru_write(bru, VI6_BRU_INCTRL, + flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? + 0 : VI6_BRU_INCTRL_NRM); + + /* Set the background position to cover the whole output image and + * configure its color. + */ + vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, + (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | + (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); + vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); + + vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, bru->bgcolor | + (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); + + /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP + * unit with a NOP operation to make BRU input 1 available as the + * Blend/ROP unit B SRC input. + */ + vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | + VI6_BRU_ROP_CROP(VI6_ROP_NOP) | + VI6_BRU_ROP_AROP(VI6_ROP_NOP)); + + for (i = 0; i < bru->entity.source_pad; ++i) { + bool premultiplied = false; + u32 ctrl = 0; + + /* Configure all Blend/ROP units corresponding to an enabled BRU + * input for alpha blending. Blend/ROP units corresponding to + * disabled BRU inputs are used in ROP NOP mode to ignore the + * SRC input. + */ + if (bru->inputs[i].rpf) { + ctrl |= VI6_BRU_CTRL_RBC; + + premultiplied = bru->inputs[i].rpf->format.flags + & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; + } else { + ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) + | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); + } + + /* Select the virtual RPF as the Blend/ROP unit A DST input to + * serve as a background color. + */ + if (i == 0) + ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; + + /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to + * D in that order. The Blend/ROP unit B SRC is hardwired to the + * ROP unit output, the corresponding register bits must be set + * to 0. + */ + if (i != 1) + ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); + + vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl); + + /* Harcode the blending formula to + * + * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa + * DSTa = DSTa * (1 - SRCa) + SRCa + * + * when the SRC input isn't premultiplied, and to + * + * DSTc = DSTc * (1 - SRCa) + SRCc + * DSTa = DSTa * (1 - SRCa) + SRCa + * + * otherwise. + */ + vsp1_bru_write(bru, VI6_BRU_BLD(i), + VI6_BRU_BLD_CCMDX_255_SRC_A | + (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : + VI6_BRU_BLD_CCMDY_SRC_A) | + VI6_BRU_BLD_ACMDX_255_SRC_A | + VI6_BRU_BLD_ACMDY_COEFY | + (0xff << VI6_BRU_BLD_COEFY_SHIFT)); + } +} + +static const struct vsp1_entity_operations bru_entity_ops = { + .configure = bru_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -431,6 +421,7 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) if (bru == NULL) return ERR_PTR(-ENOMEM); + bru->entity.ops = &bru_entity_ops; bru->entity.type = VSP1_ENTITY_BRU; ret = vsp1_entity_init(vsp1, &bru->entity, "bru", diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index acbf36d315b9..bec7a651d152 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -448,7 +448,6 @@ void vsp1_du_atomic_flush(struct device *dev) struct vsp1_entity *entity; unsigned long flags; bool stop = false; - int ret; list_for_each_entry(entity, &pipe->entities, list_pipe) { /* Disconnect unused RPFs from the pipeline. */ @@ -464,19 +463,16 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity); - ret = v4l2_subdev_call(&entity->subdev, video, - s_stream, 1); - if (ret < 0) { - dev_err(vsp1->dev, - "DRM pipeline start failure on entity %s\n", - entity->subdev.name); - return; - } + if (entity->ops->configure) + entity->ops->configure(entity); if (entity->type == VSP1_ENTITY_RPF) vsp1_rwpf_set_memory(to_rwpf(&entity->subdev)); } + /* We know that the WPF s_stream operation never fails. */ + v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 1); + vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index f7a360823373..fe164f3163bc 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -59,10 +59,13 @@ struct vsp1_route { * @set_memory: Setup memory buffer access. This operation applies the settings * stored in the rwpf mem field to the hardware. Valid for RPF and * WPF only. + * @configure: Setup the hardware based on the entity state (pipeline, formats, + * selection rectangles, ...) */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); void (*set_memory)(struct vsp1_entity *); + void (*configure)(struct vsp1_entity *); }; struct vsp1_entity { diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 4f87f6f8ee38..7360586c902a 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -32,26 +32,7 @@ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - -static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct vsp1_hsit *hsit = to_hsit(subdev); - - if (!enable) - return 0; - - if (hsit->inverse) - vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); - else - vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, @@ -167,14 +148,6 @@ static int hsit_set_format(struct v4l2_subdev *subdev, return 0; } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - -static struct v4l2_subdev_video_ops hsit_video_ops = { - .s_stream = hsit_s_stream, -}; - static struct v4l2_subdev_pad_ops hsit_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, @@ -184,10 +157,27 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = { }; static struct v4l2_subdev_ops hsit_ops = { - .video = &hsit_video_ops, .pad = &hsit_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void hsit_configure(struct vsp1_entity *entity) +{ + struct vsp1_hsit *hsit = to_hsit(&entity->subdev); + + if (hsit->inverse) + vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); + else + vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); +} + +static const struct vsp1_entity_operations hsit_entity_ops = { + .configure = hsit_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -203,6 +193,8 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) hsit->inverse = inverse; + hsit->entity.ops = &hsit_entity_ops; + if (inverse) hsit->entity.type = VSP1_ENTITY_HSI; else diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 4090a0351263..d8d8d3b6c129 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -32,41 +32,7 @@ static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - -static int lif_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_lif *lif = to_lif(subdev); - unsigned int hbth = 1300; - unsigned int obth = 400; - unsigned int lbth = 200; - - if (!enable) { - vsp1_write(lif->entity.vsp1, VI6_LIF_CTRL, 0); - return 0; - } - - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, - LIF_PAD_SOURCE); - - obth = min(obth, (format->width + 1) / 2 * format->height - 4); - - vsp1_lif_write(lif, VI6_LIF_CSBTH, - (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | - (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); - - vsp1_lif_write(lif, VI6_LIF_CTRL, - (obth << VI6_LIF_CTRL_OBTH_SHIFT) | - (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | - VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, @@ -201,14 +167,6 @@ static int lif_set_format(struct v4l2_subdev *subdev, return 0; } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - -static struct v4l2_subdev_video_ops lif_video_ops = { - .s_stream = lif_s_stream, -}; - static struct v4l2_subdev_pad_ops lif_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, @@ -218,10 +176,40 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = { }; static struct v4l2_subdev_ops lif_ops = { - .video = &lif_video_ops, .pad = &lif_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void lif_configure(struct vsp1_entity *entity) +{ + const struct v4l2_mbus_framefmt *format; + struct vsp1_lif *lif = to_lif(&entity->subdev); + unsigned int hbth = 1300; + unsigned int obth = 400; + unsigned int lbth = 200; + + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, + LIF_PAD_SOURCE); + + obth = min(obth, (format->width + 1) / 2 * format->height - 4); + + vsp1_lif_write(lif, VI6_LIF_CSBTH, + (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | + (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); + + vsp1_lif_write(lif, VI6_LIF_CTRL, + (obth << VI6_LIF_CTRL_OBTH_SHIFT) | + (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | + VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); +} + +static const struct vsp1_entity_operations lif_entity_ops = { + .configure = lif_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -235,6 +223,7 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) if (lif == NULL) return ERR_PTR(-ENOMEM); + lif->entity.ops = &lif_entity_ops; lif->entity.type = VSP1_ENTITY_LIF; ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index a5b839b28320..d5d32ce10f41 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -36,7 +36,7 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) * V4L2 Subdevice Core Operations */ -static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config) +static void lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config) { memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut, sizeof(config->lut)); @@ -48,7 +48,7 @@ static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) switch (cmd) { case VIDIOC_VSP1_LUT_CONFIG: - lut_configure(lut, arg); + lut_set_table(lut, arg); return 0; default: @@ -56,22 +56,6 @@ static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) } } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Video Operations - */ - -static int lut_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct vsp1_lut *lut = to_lut(subdev); - - if (!enable) - return 0; - - vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - - return 0; -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ @@ -218,10 +202,6 @@ static struct v4l2_subdev_core_ops lut_core_ops = { .ioctl = lut_ioctl, }; -static struct v4l2_subdev_video_ops lut_video_ops = { - .s_stream = lut_s_stream, -}; - static struct v4l2_subdev_pad_ops lut_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, @@ -232,10 +212,24 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = { static struct v4l2_subdev_ops lut_ops = { .core = &lut_core_ops, - .video = &lut_video_ops, .pad = &lut_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void lut_configure(struct vsp1_entity *entity) +{ + struct vsp1_lut *lut = to_lut(&entity->subdev); + + vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); +} + +static const struct vsp1_entity_operations lut_entity_ops = { + .configure = lut_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -249,6 +243,7 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (lut == NULL) return ERR_PTR(-ENOMEM); + lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops); diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 3311db18f40b..fe2538d5bed1 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -253,10 +253,10 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) if (entity->route && entity->route->reg) vsp1_write(entity->vsp1, entity->route->reg, VI6_DPR_NODE_UNUSED); - - v4l2_subdev_call(&entity->subdev, video, s_stream, 0); } + v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 0); + return ret; } diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index cb3d5ed148cc..eb17fa134750 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -33,13 +33,43 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations + * V4L2 Subdevice Operations */ -static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) +static struct v4l2_subdev_pad_ops rpf_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, + .get_selection = vsp1_rwpf_get_selection, + .set_selection = vsp1_rwpf_set_selection, +}; + +static struct v4l2_subdev_ops rpf_ops = { + .pad = &rpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void rpf_set_memory(struct vsp1_entity *entity) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); - struct vsp1_rwpf *rpf = to_rwpf(subdev); + struct vsp1_rwpf *rpf = entity_to_rwpf(entity); + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, + rpf->mem.addr[0] + rpf->offsets[0]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + rpf->mem.addr[1] + rpf->offsets[1]); + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + rpf->mem.addr[2] + rpf->offsets[1]); +} + +static void rpf_configure(struct vsp1_entity *entity) +{ + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_mbus_framefmt *source_format; @@ -50,9 +80,6 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) u32 pstride; u32 infmt; - if (!enable) - return 0; - /* Source size, stride and crop offsets. * * The crop offsets correspond to the location of the crop rectangle top @@ -136,51 +163,11 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - -static struct v4l2_subdev_video_ops rpf_video_ops = { - .s_stream = rpf_s_stream, -}; - -static struct v4l2_subdev_pad_ops rpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, - .enum_mbus_code = vsp1_rwpf_enum_mbus_code, - .enum_frame_size = vsp1_rwpf_enum_frame_size, - .get_fmt = vsp1_rwpf_get_format, - .set_fmt = vsp1_rwpf_set_format, - .get_selection = vsp1_rwpf_get_selection, - .set_selection = vsp1_rwpf_set_selection, -}; - -static struct v4l2_subdev_ops rpf_ops = { - .video = &rpf_video_ops, - .pad = &rpf_pad_ops, -}; - -/* ----------------------------------------------------------------------------- - * VSP1 Entity Operations - */ - -static void rpf_set_memory(struct vsp1_entity *entity) -{ - struct vsp1_rwpf *rpf = entity_to_rwpf(entity); - - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, - rpf->mem.addr[0] + rpf->offsets[0]); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, - rpf->mem.addr[1] + rpf->offsets[1]); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, - rpf->mem.addr[2] + rpf->offsets[1]); } static const struct vsp1_entity_operations rpf_entity_ops = { .set_memory = rpf_set_memory, + .configure = rpf_configure, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index c9a97ba5a042..e05149eabde9 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -103,47 +103,7 @@ static const struct v4l2_ctrl_config sru_intensity_control = { }; /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - -static int sru_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const struct vsp1_sru_param *param; - struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_mbus_framefmt *input; - struct v4l2_mbus_framefmt *output; - u32 ctrl0; - - if (!enable) - return 0; - - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, - SRU_PAD_SOURCE); - - if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) - ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 - | VI6_SRU_CTRL0_PARAM4; - else - ctrl0 = VI6_SRU_CTRL0_PARAM3; - - if (input->width != output->width) - ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; - - param = &vsp1_sru_params[sru->intensity - 1]; - - ctrl0 |= param->ctrl0; - - vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); - vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Pad Operations + * V4L2 Subdevice Operations */ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, @@ -319,14 +279,6 @@ static int sru_set_format(struct v4l2_subdev *subdev, return 0; } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Operations - */ - -static struct v4l2_subdev_video_ops sru_video_ops = { - .s_stream = sru_s_stream, -}; - static struct v4l2_subdev_pad_ops sru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, @@ -336,10 +288,48 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = { }; static struct v4l2_subdev_ops sru_ops = { - .video = &sru_video_ops, .pad = &sru_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void sru_configure(struct vsp1_entity *entity) +{ + const struct vsp1_sru_param *param; + struct vsp1_sru *sru = to_sru(&entity->subdev); + struct v4l2_mbus_framefmt *input; + struct v4l2_mbus_framefmt *output; + u32 ctrl0; + + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SINK); + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SOURCE); + + if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) + ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 + | VI6_SRU_CTRL0_PARAM4; + else + ctrl0 = VI6_SRU_CTRL0_PARAM3; + + if (input->width != output->width) + ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; + + param = &vsp1_sru_params[sru->intensity - 1]; + + ctrl0 |= param->ctrl0; + + vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); + vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); +} + +static const struct vsp1_entity_operations sru_entity_ops = { + .configure = sru_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -353,6 +343,7 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) if (sru == NULL) return ERR_PTR(-ENOMEM); + sru->entity.ops = &sru_entity_ops; sru->entity.type = VSP1_ENTITY_SRU; ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 3ba0f6844d1d..1acbdd6d537f 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -104,62 +104,6 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) return (input - 1) * 4096 / (output - 1); } -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - -static int uds_s_stream(struct v4l2_subdev *subdev, int enable) -{ - struct vsp1_uds *uds = to_uds(subdev); - const struct v4l2_mbus_framefmt *output; - const struct v4l2_mbus_framefmt *input; - unsigned int hscale; - unsigned int vscale; - bool multitap; - - if (!enable) - return 0; - - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, - UDS_PAD_SOURCE); - - hscale = uds_compute_ratio(input->width, output->width); - vscale = uds_compute_ratio(input->height, output->height); - - dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale); - - /* Multi-tap scaling can't be enabled along with alpha scaling when - * scaling down with a factor lower than or equal to 1/2 in either - * direction. - */ - if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192)) - multitap = false; - else - multitap = true; - - vsp1_uds_write(uds, VI6_UDS_CTRL, - (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | - (multitap ? VI6_UDS_CTRL_BC : 0)); - - vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, - (uds_passband_width(hscale) - << VI6_UDS_PASS_BWIDTH_H_SHIFT) | - (uds_passband_width(vscale) - << VI6_UDS_PASS_BWIDTH_V_SHIFT)); - - /* Set the scaling ratios and the output size. */ - vsp1_uds_write(uds, VI6_UDS_SCALE, - (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | - (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); - vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, - (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); - - return 0; -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ @@ -321,10 +265,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_video_ops uds_video_ops = { - .s_stream = uds_s_stream, -}; - static struct v4l2_subdev_pad_ops uds_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, @@ -334,10 +274,64 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = { }; static struct v4l2_subdev_ops uds_ops = { - .video = &uds_video_ops, .pad = &uds_pad_ops, }; +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void uds_configure(struct vsp1_entity *entity) +{ + struct vsp1_uds *uds = to_uds(&entity->subdev); + const struct v4l2_mbus_framefmt *output; + const struct v4l2_mbus_framefmt *input; + unsigned int hscale; + unsigned int vscale; + bool multitap; + + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SINK); + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SOURCE); + + hscale = uds_compute_ratio(input->width, output->width); + vscale = uds_compute_ratio(input->height, output->height); + + dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale); + + /* Multi-tap scaling can't be enabled along with alpha scaling when + * scaling down with a factor lower than or equal to 1/2 in either + * direction. + */ + if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192)) + multitap = false; + else + multitap = true; + + vsp1_uds_write(uds, VI6_UDS_CTRL, + (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | + (multitap ? VI6_UDS_CTRL_BC : 0)); + + vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, + (uds_passband_width(hscale) + << VI6_UDS_PASS_BWIDTH_H_SHIFT) | + (uds_passband_width(vscale) + << VI6_UDS_PASS_BWIDTH_V_SHIFT)); + + /* Set the scaling ratios and the output size. */ + vsp1_uds_write(uds, VI6_UDS_SCALE, + (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | + (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); + vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, + (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); +} + +static const struct vsp1_entity_operations uds_entity_ops = { + .configure = uds_configure, +}; + /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ @@ -352,6 +346,7 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) if (uds == NULL) return ERR_PTR(-ENOMEM); + uds->entity.ops = &uds_entity_ops; uds->entity.type = VSP1_ENTITY_UDS; uds->entity.index = index; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index d4a092c8ece3..a3f1145c8a79 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -591,7 +591,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; - int ret; /* Prepare the display list. */ pipe->dl = vsp1_dl_list_get(pipe->output->dlm); @@ -619,18 +618,14 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity); - ret = v4l2_subdev_call(&entity->subdev, video, s_stream, 1); - if (ret < 0) - goto error; + if (entity->ops->configure) + entity->ops->configure(entity); } + /* We know that the WPF s_stream operation never fails. */ + v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 1); + return 0; - -error: - vsp1_dl_list_put(pipe->dl); - pipe->dl = NULL; - - return ret; } static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 0797927d14cf..ccf1f960c46a 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -39,102 +39,18 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *wpf = to_rwpf(subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; - const struct v4l2_mbus_framefmt *source_format; - const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop; - unsigned int i; - u32 srcrpf = 0; - u32 outfmt = 0; - if (!enable) { - vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); - vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + - VI6_WPF_SRCRPF, 0); + if (enable) return 0; - } - /* Sources. If the pipeline has a single input and BRU is not used, - * configure it as the master layer. Otherwise configure all - * inputs as sub-layers and select the virtual RPF as the master - * layer. + /* Write to registers directly when stopping the stream as there will be + * no pipeline run to apply the display list. */ - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *input = pipe->inputs[i]; - - if (!input) - continue; - - srcrpf |= (!pipe->bru && pipe->num_inputs == 1) - ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) - : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); - } - - if (pipe->bru || pipe->num_inputs > 1) - srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; - - vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); - - /* Destination stride. */ - if (!pipe->lif) { - struct v4l2_pix_format_mplane *format = &wpf->format; - - vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, - format->plane_fmt[0].bytesperline); - if (format->num_planes > 1) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, - format->plane_fmt[1].bytesperline); - } - - crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); - - vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | - (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | - (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); - vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | - (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | - (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - - /* Format */ - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.config, - RWPF_PAD_SOURCE); - - if (!pipe->lif) { - const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; - - outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; - - if (fmtinfo->alpha) - outfmt |= VI6_WPF_OUTFMT_PXA; - if (fmtinfo->swap_yc) - outfmt |= VI6_WPF_OUTFMT_SPYCS; - if (fmtinfo->swap_uv) - outfmt |= VI6_WPF_OUTFMT_SPUVS; - - vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); - } - - if (sink_format->code != source_format->code) - outfmt |= VI6_WPF_OUTFMT_CSC; - - outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; - vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); - - vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index), - VI6_DPR_WPF_FPORCH_FP_WPFN); - - vsp1_mod_write(&wpf->entity, VI6_WPF_WRBCK_CTRL, 0); - - /* Enable interrupts */ - vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0); - vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), - VI6_WFP_IRQ_ENB_FREE); + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); + vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + + VI6_WPF_SRCRPF, 0); return 0; } @@ -182,9 +98,101 @@ static void wpf_set_memory(struct vsp1_entity *entity) vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); } +static void wpf_configure(struct vsp1_entity *entity) +{ + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + struct vsp1_device *vsp1 = wpf->entity.vsp1; + const struct v4l2_mbus_framefmt *source_format; + const struct v4l2_mbus_framefmt *sink_format; + const struct v4l2_rect *crop; + unsigned int i; + u32 outfmt = 0; + u32 srcrpf = 0; + + /* Cropping */ + crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); + + vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | + (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | + (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); + + /* Format */ + sink_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SINK); + source_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SOURCE); + + if (!pipe->lif) { + const struct v4l2_pix_format_mplane *format = &wpf->format; + const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; + + outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; + + if (fmtinfo->alpha) + outfmt |= VI6_WPF_OUTFMT_PXA; + if (fmtinfo->swap_yc) + outfmt |= VI6_WPF_OUTFMT_SPYCS; + if (fmtinfo->swap_uv) + outfmt |= VI6_WPF_OUTFMT_SPUVS; + + /* Destination stride and byte swapping. */ + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, + format->plane_fmt[0].bytesperline); + if (format->num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, + format->plane_fmt[1].bytesperline); + + vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); + } + + if (sink_format->code != source_format->code) + outfmt |= VI6_WPF_OUTFMT_CSC; + + outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; + vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + + vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index), + VI6_DPR_WPF_FPORCH_FP_WPFN); + + vsp1_mod_write(&wpf->entity, VI6_WPF_WRBCK_CTRL, 0); + + /* Sources. If the pipeline has a single input and BRU is not used, + * configure it as the master layer. Otherwise configure all + * inputs as sub-layers and select the virtual RPF as the master + * layer. + */ + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *input = pipe->inputs[i]; + + if (!input) + continue; + + srcrpf |= (!pipe->bru && pipe->num_inputs == 1) + ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) + : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); + } + + if (pipe->bru || pipe->num_inputs > 1) + srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; + + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); + + /* Enable interrupts */ + vsp1_mod_write(&wpf->entity, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_mod_write(&wpf->entity, VI6_WPF_IRQ_ENB(wpf->entity.index), + VI6_WFP_IRQ_ENB_FREE); +} + static const struct vsp1_entity_operations wpf_entity_ops = { .destroy = vsp1_wpf_destroy, .set_memory = wpf_set_memory, + .configure = wpf_configure, }; /* ----------------------------------------------------------------------------- From c6c8efb656ff213a4d32776c12454b9c9f0c14e4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 22 Nov 2015 13:37:45 -0200 Subject: [PATCH 064/173] [media] v4l: vsp1: Merge RPF and WPF pad ops structures The two structures are identical, merge them and move the result to vsp1_rwpf.c. All rwpf pad operations can now be declared static. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_rpf.c | 12 +---- drivers/media/platform/vsp1/vsp1_rwpf.c | 60 ++++++++++++++----------- drivers/media/platform/vsp1/vsp1_rwpf.h | 19 +------- drivers/media/platform/vsp1/vsp1_wpf.c | 12 +---- 4 files changed, 38 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index eb17fa134750..84a3aedae768 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -36,18 +36,8 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) * V4L2 Subdevice Operations */ -static struct v4l2_subdev_pad_ops rpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, - .enum_mbus_code = vsp1_rwpf_enum_mbus_code, - .enum_frame_size = vsp1_rwpf_enum_frame_size, - .get_fmt = vsp1_rwpf_get_format, - .set_fmt = vsp1_rwpf_set_format, - .get_selection = vsp1_rwpf_get_selection, - .set_selection = vsp1_rwpf_set_selection, -}; - static struct v4l2_subdev_ops rpf_ops = { - .pad = &rpf_pad_ops, + .pad = &vsp1_rwpf_pad_ops, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 0c5ad023adfb..4d302f5cccb2 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -20,13 +20,20 @@ #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 +struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, + struct v4l2_subdev_pad_config *config) +{ + return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, + RWPF_PAD_SINK); +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ -int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) +static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { static const unsigned int codes[] = { MEDIA_BUS_FMT_ARGB8888_1X32, @@ -41,9 +48,9 @@ int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, return 0; } -int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) +static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; @@ -76,16 +83,9 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_pad_config *config) -{ - return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, - RWPF_PAD_SINK); -} - -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; @@ -100,9 +100,9 @@ int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, return 0; } -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; @@ -154,9 +154,9 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, return 0; } -int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) +static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; @@ -191,9 +191,9 @@ int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, return 0; } -int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) +static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; @@ -250,6 +250,16 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, return 0; } +const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, + .get_selection = vsp1_rwpf_get_selection, + .set_selection = vsp1_rwpf_set_selection, +}; + /* ----------------------------------------------------------------------------- * Controls */ diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 4ebfab61e0ef..9502710977e8 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -68,24 +68,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf); -int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code); -int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse); -int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt); -int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt); -int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel); -int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel); +extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *config); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index ccf1f960c46a..80a87f39f06c 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -63,19 +63,9 @@ static struct v4l2_subdev_video_ops wpf_video_ops = { .s_stream = wpf_s_stream, }; -static struct v4l2_subdev_pad_ops wpf_pad_ops = { - .init_cfg = vsp1_entity_init_cfg, - .enum_mbus_code = vsp1_rwpf_enum_mbus_code, - .enum_frame_size = vsp1_rwpf_enum_frame_size, - .get_fmt = vsp1_rwpf_get_format, - .set_fmt = vsp1_rwpf_set_format, - .get_selection = vsp1_rwpf_get_selection, - .set_selection = vsp1_rwpf_set_selection, -}; - static struct v4l2_subdev_ops wpf_ops = { .video = &wpf_video_ops, - .pad = &wpf_pad_ops, + .pad = &vsp1_rwpf_pad_ops, }; /* ----------------------------------------------------------------------------- From b911605dcce9f7ebfea2e8f8833fb73782f55c22 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 22 Nov 2015 14:08:18 -0200 Subject: [PATCH 065/173] [media] v4l: vsp1: Use __vsp1_video_try_format to initialize format at init time Reuse the runtime logic to initialize the default format instead of open-coding it. This ensures coherency between intialization and runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a3f1145c8a79..4dcc892977df 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -958,17 +958,10 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, return ERR_PTR(ret); /* ... and the format ... */ - rwpf->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); - rwpf->format.pixelformat = rwpf->fmtinfo->fourcc; - rwpf->format.colorspace = V4L2_COLORSPACE_SRGB; - rwpf->format.field = V4L2_FIELD_NONE; + rwpf->format.pixelformat = VSP1_VIDEO_DEF_FORMAT; rwpf->format.width = VSP1_VIDEO_DEF_WIDTH; rwpf->format.height = VSP1_VIDEO_DEF_HEIGHT; - rwpf->format.num_planes = 1; - rwpf->format.plane_fmt[0].bytesperline = - rwpf->format.width * rwpf->fmtinfo->bpp[0] / 8; - rwpf->format.plane_fmt[0].sizeimage = - rwpf->format.plane_fmt[0].bytesperline * rwpf->format.height; + __vsp1_video_try_format(video, &rwpf->format, &rwpf->fmtinfo); /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; From 5e8dbbf372fc187de564a8aab635e2da2f7c2153 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 22 Nov 2015 20:29:25 -0200 Subject: [PATCH 066/173] [media] v4l: vsp1: Pass display list explicitly to configure functions Modules write register values to the active display list pointed to by the pipeline. In order to support preparing display lists ahead of time, pass them explicitly to all configuration functions. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 22 ++++++------ drivers/media/platform/vsp1/vsp1_drm.c | 14 ++++---- drivers/media/platform/vsp1/vsp1_entity.c | 15 +++----- drivers/media/platform/vsp1/vsp1_entity.h | 14 ++++---- drivers/media/platform/vsp1/vsp1_hsit.c | 12 ++++--- drivers/media/platform/vsp1/vsp1_lif.c | 12 ++++--- drivers/media/platform/vsp1/vsp1_lut.c | 10 +++--- drivers/media/platform/vsp1/vsp1_pipe.c | 3 +- drivers/media/platform/vsp1/vsp1_pipe.h | 1 + drivers/media/platform/vsp1/vsp1_rpf.c | 39 +++++++++++---------- drivers/media/platform/vsp1/vsp1_rwpf.h | 8 +++-- drivers/media/platform/vsp1/vsp1_sru.c | 14 ++++---- drivers/media/platform/vsp1/vsp1_uds.c | 23 +++++++------ drivers/media/platform/vsp1/vsp1_uds.h | 3 +- drivers/media/platform/vsp1/vsp1_video.c | 11 +++--- drivers/media/platform/vsp1/vsp1_wpf.c | 42 +++++++++++------------ 16 files changed, 125 insertions(+), 118 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 4ab0a805d4b2..9795e621eb8e 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -18,6 +18,7 @@ #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_dl.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -28,9 +29,10 @@ * Device Access */ -static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) +static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl, + u32 reg, u32 data) { - vsp1_mod_write(&bru->entity, reg, data); + vsp1_dl_list_write(dl, reg, data); } /* ----------------------------------------------------------------------------- @@ -303,7 +305,7 @@ static struct v4l2_subdev_ops bru_ops = { * VSP1 Entity Operations */ -static void bru_configure(struct vsp1_entity *entity) +static void bru_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_bru *bru = to_bru(&entity->subdev); @@ -324,26 +326,26 @@ static void bru_configure(struct vsp1_entity *entity) * format at the pipeline output is premultiplied. */ flags = pipe->output ? pipe->output->format.flags : 0; - vsp1_bru_write(bru, VI6_BRU_INCTRL, + vsp1_bru_write(bru, dl, VI6_BRU_INCTRL, flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); /* Set the background position to cover the whole output image and * configure its color. */ - vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, + vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); - vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); + vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0); - vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, bru->bgcolor | + vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor | (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP * unit with a NOP operation to make BRU input 1 available as the * Blend/ROP unit B SRC input. */ - vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | + vsp1_bru_write(bru, dl, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | VI6_BRU_ROP_CROP(VI6_ROP_NOP) | VI6_BRU_ROP_AROP(VI6_ROP_NOP)); @@ -380,7 +382,7 @@ static void bru_configure(struct vsp1_entity *entity) if (i != 1) ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); - vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl); + vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl); /* Harcode the blending formula to * @@ -394,7 +396,7 @@ static void bru_configure(struct vsp1_entity *entity) * * otherwise. */ - vsp1_bru_write(bru, VI6_BRU_BLD(i), + vsp1_bru_write(bru, dl, VI6_BRU_BLD(i), VI6_BRU_BLD_CCMDX_255_SRC_A | (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : VI6_BRU_BLD_CCMDY_SRC_A) | diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index bec7a651d152..a9735b199a4b 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -455,24 +455,22 @@ void vsp1_du_atomic_flush(struct device *dev) struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); if (!pipe->inputs[rpf->entity.index]) { - vsp1_mod_write(entity, entity->route->reg, - VI6_DPR_NODE_UNUSED); + vsp1_dl_list_write(pipe->dl, entity->route->reg, + VI6_DPR_NODE_UNUSED); continue; } } - vsp1_entity_route_setup(entity); + vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity); + entity->ops->configure(entity, pipe->dl); if (entity->type == VSP1_ENTITY_RPF) - vsp1_rwpf_set_memory(to_rwpf(&entity->subdev)); + vsp1_rwpf_set_memory(to_rwpf(&entity->subdev), + pipe->dl); } - /* We know that the WPF s_stream operation never fails. */ - v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 1); - vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 09c9a1b86e3a..e9dd4dbda2dc 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -21,16 +21,9 @@ #include "vsp1.h" #include "vsp1_dl.h" #include "vsp1_entity.h" -#include "vsp1_pipe.h" -void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) -{ - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); - - vsp1_dl_list_write(pipe->dl, reg, data); -} - -void vsp1_entity_route_setup(struct vsp1_entity *source) +void vsp1_entity_route_setup(struct vsp1_entity *source, + struct vsp1_dl_list *dl) { struct vsp1_entity *sink; @@ -38,8 +31,8 @@ void vsp1_entity_route_setup(struct vsp1_entity *source) return; sink = container_of(source->sink, struct vsp1_entity, subdev.entity); - vsp1_mod_write(source, source->route->reg, - sink->route->inputs[source->sink_pad]); + vsp1_dl_list_write(dl, source->route->reg, + sink->route->inputs[source->sink_pad]); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index fe164f3163bc..f0a718ceaefe 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -19,6 +19,7 @@ #include struct vsp1_device; +struct vsp1_dl_list; enum vsp1_entity_type { VSP1_ENTITY_BRU, @@ -57,15 +58,15 @@ struct vsp1_route { * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. * @set_memory: Setup memory buffer access. This operation applies the settings - * stored in the rwpf mem field to the hardware. Valid for RPF and - * WPF only. + * stored in the rwpf mem field to the display list. Valid for RPF + * and WPF only. * @configure: Setup the hardware based on the entity state (pipeline, formats, * selection rectangles, ...) */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); - void (*set_memory)(struct vsp1_entity *); - void (*configure)(struct vsp1_entity *); + void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl); + void (*configure)(struct vsp1_entity *, struct vsp1_dl_list *dl); }; struct vsp1_entity { @@ -121,8 +122,7 @@ vsp1_entity_get_pad_compose(struct vsp1_entity *entity, int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg); -void vsp1_entity_route_setup(struct vsp1_entity *source); - -void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data); +void vsp1_entity_route_setup(struct vsp1_entity *source, + struct vsp1_dl_list *dl); #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 7360586c902a..4fcdb30d08c4 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -17,6 +17,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_hsit.h" #define HSIT_MIN_SIZE 4U @@ -26,9 +27,10 @@ * Device Access */ -static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) +static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, + struct vsp1_dl_list *dl, u32 reg, u32 data) { - vsp1_mod_write(&hsit->entity, reg, data); + vsp1_dl_list_write(dl, reg, data); } /* ----------------------------------------------------------------------------- @@ -164,14 +166,14 @@ static struct v4l2_subdev_ops hsit_ops = { * VSP1 Entity Operations */ -static void hsit_configure(struct vsp1_entity *entity) +static void hsit_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); if (hsit->inverse) - vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); + vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); else - vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); + vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN); } static const struct vsp1_entity_operations hsit_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index d8d8d3b6c129..bed1784cccb9 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -17,6 +17,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_lif.h" #define LIF_MIN_SIZE 2U @@ -26,9 +27,10 @@ * Device Access */ -static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) +static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl, + u32 reg, u32 data) { - vsp1_mod_write(&lif->entity, reg, data); + vsp1_dl_list_write(dl, reg, data); } /* ----------------------------------------------------------------------------- @@ -183,7 +185,7 @@ static struct v4l2_subdev_ops lif_ops = { * VSP1 Entity Operations */ -static void lif_configure(struct vsp1_entity *entity) +static void lif_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -196,11 +198,11 @@ static void lif_configure(struct vsp1_entity *entity) obth = min(obth, (format->width + 1) / 2 * format->height - 4); - vsp1_lif_write(lif, VI6_LIF_CSBTH, + vsp1_lif_write(lif, dl, VI6_LIF_CSBTH, (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); - vsp1_lif_write(lif, VI6_LIF_CTRL, + vsp1_lif_write(lif, dl, VI6_LIF_CTRL, (obth << VI6_LIF_CTRL_OBTH_SHIFT) | (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index d5d32ce10f41..ec0b9bf53d1f 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -18,6 +18,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_lut.h" #define LUT_MIN_SIZE 4U @@ -27,9 +28,10 @@ * Device Access */ -static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) +static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, + u32 reg, u32 data) { - vsp1_mod_write(&lut->entity, reg, data); + vsp1_dl_list_write(dl, reg, data); } /* ----------------------------------------------------------------------------- @@ -219,11 +221,11 @@ static struct v4l2_subdev_ops lut_ops = { * VSP1 Entity Operations */ -static void lut_configure(struct vsp1_entity *entity) +static void lut_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_lut *lut = to_lut(&entity->subdev); - vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); + vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); } static const struct vsp1_entity_operations lut_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index fe2538d5bed1..4d06519f717d 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -295,6 +295,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) */ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_entity *input, + struct vsp1_dl_list *dl, unsigned int alpha) { struct vsp1_entity *entity; @@ -317,7 +318,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, if (entity->type == VSP1_ENTITY_UDS) { struct vsp1_uds *uds = to_uds(&entity->subdev); - vsp1_uds_set_alpha(uds, alpha); + vsp1_uds_set_alpha(uds, dl, alpha); break; } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index f4bdfc943add..1100229a1ed2 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -123,6 +123,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_entity *input, + struct vsp1_dl_list *dl, unsigned int alpha); void vsp1_pipelines_suspend(struct vsp1_device *vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 84a3aedae768..ce408e4467af 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -16,6 +16,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -26,10 +27,10 @@ * Device Access */ -static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) +static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, + struct vsp1_dl_list *dl, u32 reg, u32 data) { - vsp1_mod_write(&rpf->entity, reg + rpf->entity.index * VI6_RPF_OFFSET, - data); + vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data); } /* ----------------------------------------------------------------------------- @@ -44,19 +45,19 @@ static struct v4l2_subdev_ops rpf_ops = { * VSP1 Entity Operations */ -static void rpf_set_memory(struct vsp1_entity *entity) +static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_rwpf *rpf = entity_to_rwpf(entity); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, rpf->mem.addr[0] + rpf->offsets[0]); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, rpf->mem.addr[1] + rpf->offsets[1]); - vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, rpf->mem.addr[2] + rpf->offsets[1]); } -static void rpf_configure(struct vsp1_entity *entity) +static void rpf_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); @@ -78,10 +79,10 @@ static void rpf_configure(struct vsp1_entity *entity) */ crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); - vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); - vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); @@ -99,7 +100,7 @@ static void rpf_configure(struct vsp1_entity *entity) rpf->offsets[1] = 0; } - vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ sink_format = vsp1_entity_get_pad_format(&rpf->entity, @@ -120,8 +121,8 @@ static void rpf_configure(struct vsp1_entity *entity) if (sink_format->code != source_format->code) infmt |= VI6_RPF_INFMT_CSC; - vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); - vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); + vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt); + vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap); /* Output location */ if (pipe->bru) { @@ -134,7 +135,7 @@ static void rpf_configure(struct vsp1_entity *entity) top = compose->top; } - vsp1_rpf_write(rpf, VI6_RPF_LOC, + vsp1_rpf_write(rpf, dl, VI6_RPF_LOC, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); @@ -142,17 +143,17 @@ static void rpf_configure(struct vsp1_entity *entity) * alpha value set through the V4L2_CID_ALPHA_COMPONENT control * otherwise. Disable color keying. */ - vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | + vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); - vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, + vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha); + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha); - vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); - vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); + vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); + vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 9502710977e8..38c8c902db52 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -75,12 +75,14 @@ struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, /** * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF * @rwpf: the [RW]PF instance + * @dl: the display list * - * This function applies the cached memory buffer address to the hardware. + * This function applies the cached memory buffer address to the display list. */ -static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf) +static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, + struct vsp1_dl_list *dl) { - rwpf->entity.ops->set_memory(&rwpf->entity); + rwpf->entity.ops->set_memory(&rwpf->entity, dl); } #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index e05149eabde9..6c1cb4ccba22 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -17,6 +17,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_sru.h" #define SRU_MIN_SIZE 4U @@ -26,9 +27,10 @@ * Device Access */ -static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) +static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl, + u32 reg, u32 data) { - vsp1_mod_write(&sru->entity, reg, data); + vsp1_dl_list_write(dl, reg, data); } /* ----------------------------------------------------------------------------- @@ -295,7 +297,7 @@ static struct v4l2_subdev_ops sru_ops = { * VSP1 Entity Operations */ -static void sru_configure(struct vsp1_entity *entity) +static void sru_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -321,9 +323,9 @@ static void sru_configure(struct vsp1_entity *entity) ctrl0 |= param->ctrl0; - vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); - vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0); + vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2); } static const struct vsp1_entity_operations sru_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 1acbdd6d537f..90e7d7141160 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -17,6 +17,7 @@ #include #include "vsp1.h" +#include "vsp1_dl.h" #include "vsp1_uds.h" #define UDS_MIN_SIZE 4U @@ -29,19 +30,21 @@ * Device Access */ -static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) +static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl, + u32 reg, u32 data) { - vsp1_mod_write(&uds->entity, reg + uds->entity.index * VI6_UDS_OFFSET, - data); + vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data); } /* ----------------------------------------------------------------------------- * Scaling Computation */ -void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha) +void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl, + unsigned int alpha) { - vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); + vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL, + alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); } /* @@ -281,7 +284,7 @@ static struct v4l2_subdev_ops uds_ops = { * VSP1 Entity Operations */ -static void uds_configure(struct vsp1_entity *entity) +static void uds_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -309,21 +312,21 @@ static void uds_configure(struct vsp1_entity *entity) else multitap = true; - vsp1_uds_write(uds, VI6_UDS_CTRL, + vsp1_uds_write(uds, dl, VI6_UDS_CTRL, (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | (multitap ? VI6_UDS_CTRL_BC : 0)); - vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, + vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH, (uds_passband_width(hscale) << VI6_UDS_PASS_BWIDTH_H_SHIFT) | (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); /* Set the scaling ratios and the output size. */ - vsp1_uds_write(uds, VI6_UDS_SCALE, + vsp1_uds_write(uds, dl, VI6_UDS_SCALE, (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); - vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, + vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 031ac0da1b66..5c8cbfcad4cc 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -35,6 +35,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); -void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha); +void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl, + unsigned int alpha); #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 4dcc892977df..a45bf68e0ba1 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -455,11 +455,11 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) struct vsp1_rwpf *rwpf = pipe->inputs[i]; if (rwpf) - vsp1_rwpf_set_memory(rwpf); + vsp1_rwpf_set_memory(rwpf, pipe->dl); } if (!pipe->lif) - vsp1_rwpf_set_memory(pipe->output); + vsp1_rwpf_set_memory(pipe->output, pipe->dl); vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; @@ -616,15 +616,12 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) } list_for_each_entry(entity, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity); + vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity); + entity->ops->configure(entity, pipe->dl); } - /* We know that the WPF s_stream operation never fails. */ - v4l2_subdev_call(&pipe->output->entity.subdev, video, s_stream, 1); - return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 80a87f39f06c..e2c558dbac13 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -27,10 +27,10 @@ * Device Access */ -static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) +static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, + struct vsp1_dl_list *dl, u32 reg, u32 data) { - vsp1_mod_write(&wpf->entity, - reg + wpf->entity.index * VI6_WPF_OFFSET, data); + vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data); } /* ----------------------------------------------------------------------------- @@ -79,16 +79,16 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } -static void wpf_set_memory(struct vsp1_entity *entity) +static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_rwpf *wpf = entity_to_rwpf(entity); - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); } -static void wpf_configure(struct vsp1_entity *entity) +static void wpf_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -103,10 +103,10 @@ static void wpf_configure(struct vsp1_entity *entity) /* Cropping */ crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); - vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); - vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); @@ -132,25 +132,25 @@ static void wpf_configure(struct vsp1_entity *entity) outfmt |= VI6_WPF_OUTFMT_SPUVS; /* Destination stride and byte swapping. */ - vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y, format->plane_fmt[0].bytesperline); if (format->num_planes > 1) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C, format->plane_fmt[1].bytesperline); - vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); } if (sink_format->code != source_format->code) outfmt |= VI6_WPF_OUTFMT_CSC; outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; - vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); - vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index), - VI6_DPR_WPF_FPORCH_FP_WPFN); + vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), + VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_mod_write(&wpf->entity, VI6_WPF_WRBCK_CTRL, 0); + vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); /* Sources. If the pipeline has a single input and BRU is not used, * configure it as the master layer. Otherwise configure all @@ -171,12 +171,12 @@ static void wpf_configure(struct vsp1_entity *entity) if (pipe->bru || pipe->num_inputs > 1) srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; - vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); + vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf); /* Enable interrupts */ - vsp1_mod_write(&wpf->entity, VI6_WPF_IRQ_STA(wpf->entity.index), 0); - vsp1_mod_write(&wpf->entity, VI6_WPF_IRQ_ENB(wpf->entity.index), - VI6_WFP_IRQ_ENB_FREE); + vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), + VI6_WFP_IRQ_ENB_FREE); } static const struct vsp1_entity_operations wpf_entity_ops = { From d2219824cb4a41013292590c1b00a047f356afa4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 19 Jan 2016 19:42:56 -0200 Subject: [PATCH 067/173] [media] v4l: vsp1: Rename pipeline validate functions to pipeline build The primary purpose of those functions is to build the pipeline, rename them to make this clearer. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a45bf68e0ba1..ddf440dc9a9c 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -172,9 +172,9 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * Pipeline Management */ -static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, - struct vsp1_rwpf *input, - struct vsp1_rwpf *output) +static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *input, + struct vsp1_rwpf *output) { struct media_entity_enum ent_enum; struct vsp1_entity *entity; @@ -257,8 +257,8 @@ static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, return ret; } -static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe, - struct vsp1_video *video) +static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, + struct vsp1_video *video) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; @@ -321,8 +321,8 @@ static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe, if (!pipe->inputs[i]) continue; - ret = vsp1_video_pipeline_validate_branch(pipe, pipe->inputs[i], - pipe->output); + ret = vsp1_video_pipeline_build_branch(pipe, pipe->inputs[i], + pipe->output); if (ret < 0) goto error; } @@ -341,9 +341,9 @@ static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, mutex_lock(&pipe->lock); - /* If we're the first user validate and initialize the pipeline. */ + /* If we're the first user build and validate the pipeline. */ if (pipe->use_count == 0) { - ret = vsp1_video_pipeline_validate(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); if (ret < 0) goto done; } From 83dd019d308d3c1529df1c7da96c3bdb895947e4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 14 Jan 2016 14:17:32 -0200 Subject: [PATCH 068/173] [media] v4l: vsp1: Pass pipe pointer to entity configure functions Pass the pipe explicitly instead of retrieving it through media entities. This decouples device state stored in the pipeline from the active state stored in entities, preparing for dynamic pipeline creation. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 5 +++-- drivers/media/platform/vsp1/vsp1_drm.c | 2 +- drivers/media/platform/vsp1/vsp1_entity.h | 4 +++- drivers/media/platform/vsp1/vsp1_hsit.c | 4 +++- drivers/media/platform/vsp1/vsp1_lif.c | 4 +++- drivers/media/platform/vsp1/vsp1_lut.c | 4 +++- drivers/media/platform/vsp1/vsp1_rpf.c | 5 +++-- drivers/media/platform/vsp1/vsp1_sru.c | 4 +++- drivers/media/platform/vsp1/vsp1_uds.c | 4 +++- drivers/media/platform/vsp1/vsp1_video.c | 2 +- drivers/media/platform/vsp1/vsp1_wpf.c | 5 +++-- 11 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 9795e621eb8e..3ece40245396 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -305,9 +305,10 @@ static struct v4l2_subdev_ops bru_ops = { * VSP1 Entity Operations */ -static void bru_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void bru_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_bru *bru = to_bru(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a9735b199a4b..7cde2d970dba 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -464,7 +464,7 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity, pipe->dl); + entity->ops->configure(entity, pipe, pipe->dl); if (entity->type == VSP1_ENTITY_RPF) vsp1_rwpf_set_memory(to_rwpf(&entity->subdev), diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index f0a718ceaefe..bbf378437c3b 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -20,6 +20,7 @@ struct vsp1_device; struct vsp1_dl_list; +struct vsp1_pipeline; enum vsp1_entity_type { VSP1_ENTITY_BRU, @@ -66,7 +67,8 @@ struct vsp1_route { struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl); - void (*configure)(struct vsp1_entity *, struct vsp1_dl_list *dl); + void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, + struct vsp1_dl_list *); }; struct vsp1_entity { diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 4fcdb30d08c4..f02e4ca77a7c 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -166,7 +166,9 @@ static struct v4l2_subdev_ops hsit_ops = { * VSP1 Entity Operations */ -static void hsit_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void hsit_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index bed1784cccb9..42ed8f80cc88 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -185,7 +185,9 @@ static struct v4l2_subdev_ops lif_ops = { * VSP1 Entity Operations */ -static void lif_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void lif_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index ec0b9bf53d1f..596537a95210 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -221,7 +221,9 @@ static struct v4l2_subdev_ops lut_ops = { * VSP1 Entity Operations */ -static void lut_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void lut_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { struct vsp1_lut *lut = to_lut(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index ce408e4467af..e7b6abbb0024 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -57,9 +57,10 @@ static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) rpf->mem.addr[2] + rpf->offsets[1]); } -static void rpf_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void rpf_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 6c1cb4ccba22..00edd95093e3 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -297,7 +297,9 @@ static struct v4l2_subdev_ops sru_ops = { * VSP1 Entity Operations */ -static void sru_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void sru_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 90e7d7141160..42f2d0465bd6 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -284,7 +284,9 @@ static struct v4l2_subdev_ops uds_ops = { * VSP1 Entity Operations */ -static void uds_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void uds_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index ddf440dc9a9c..a16a661e5b69 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -619,7 +619,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity, pipe->dl); + entity->ops->configure(entity, pipe, pipe->dl); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index e2c558dbac13..d1d5c08ca35e 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -88,9 +88,10 @@ static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); } -static void wpf_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl) +static void wpf_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&entity->subdev.entity); struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_mbus_framefmt *source_format; From ff7e97c94d9f7f370fe3ce2a72e85361ca22a605 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 19 Jan 2016 19:16:36 -0200 Subject: [PATCH 069/173] [media] v4l: vsp1: Store pipeline pointer in rwpf This prepares for dynamic pipeline allocation by providing a field that can be used to store the pipeline pointer atomically under driver control. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drv.c | 4 +--- drivers/media/platform/vsp1/vsp1_pipe.c | 14 +++++++++----- drivers/media/platform/vsp1/vsp1_pipe.h | 8 -------- drivers/media/platform/vsp1/vsp1_rwpf.h | 2 ++ drivers/media/platform/vsp1/vsp1_video.c | 13 +++++++------ 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index bfdc01c9172d..f1be2680013d 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -49,17 +49,15 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; if (wpf == NULL) continue; - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i)); vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); if (status & VI6_WFP_IRQ_STA_FRE) { - vsp1_pipeline_frame_end(pipe); + vsp1_pipeline_frame_end(wpf->pipe); ret = IRQ_HANDLED; } } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 4d06519f717d..8ac080f87b08 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -172,14 +172,18 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) bru->inputs[i].rpf = NULL; } - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) + for (i = 0; i < pipe->num_inputs; ++i) { + pipe->inputs[i]->pipe = NULL; pipe->inputs[i] = NULL; + } + + pipe->output->pipe = NULL; + pipe->output = NULL; INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; pipe->buffers_ready = 0; pipe->num_inputs = 0; - pipe->output = NULL; pipe->bru = NULL; pipe->lif = NULL; pipe->uds = NULL; @@ -344,7 +348,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1) if (wpf == NULL) continue; - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + pipe = wpf->pipe; if (pipe == NULL) continue; @@ -361,7 +365,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1) if (wpf == NULL) continue; - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + pipe = wpf->pipe; if (pipe == NULL) continue; @@ -385,7 +389,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1) if (wpf == NULL) continue; - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + pipe = wpf->pipe; if (pipe == NULL) continue; diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 1100229a1ed2..9fd688bfe638 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -103,14 +103,6 @@ struct vsp1_pipeline { struct vsp1_dl_list *dl; }; -static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) -{ - if (likely(e->pipe)) - return container_of(e->pipe, struct vsp1_pipeline, pipe); - else - return NULL; -} - void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 38c8c902db52..9ff7c78f239e 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -25,6 +25,7 @@ struct v4l2_ctrl; struct vsp1_dl_manager; +struct vsp1_pipeline; struct vsp1_rwpf; struct vsp1_video; @@ -36,6 +37,7 @@ struct vsp1_rwpf { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; + struct vsp1_pipeline *pipe; struct vsp1_video *video; unsigned int max_width; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a16a661e5b69..2c642726a259 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -293,10 +293,12 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, rwpf = to_rwpf(subdev); pipe->inputs[rwpf->entity.index] = rwpf; rwpf->video->pipe_index = ++pipe->num_inputs; + rwpf->pipe = pipe; } else if (e->type == VSP1_ENTITY_WPF) { rwpf = to_rwpf(subdev); pipe->output = rwpf; rwpf->video->pipe_index = 0; + rwpf->pipe = pipe; } else if (e->type == VSP1_ENTITY_LIF) { pipe->lif = e; } else if (e->type == VSP1_ENTITY_BRU) { @@ -384,7 +386,7 @@ static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe) static struct vsp1_vb2_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_pipeline *pipe = video->rwpf->pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; unsigned long flags; @@ -563,7 +565,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_pipeline *pipe = video->rwpf->pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); unsigned long flags; bool empty; @@ -628,7 +630,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_pipeline *pipe = video->rwpf->pipe; unsigned long flags; int ret; @@ -655,7 +657,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_pipeline *pipe = video->rwpf->pipe; struct vsp1_vb2_buffer *buffer; unsigned long flags; int ret; @@ -802,8 +804,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * FIXME: This is racy, the ioctl is only protected by the video node * lock. */ - pipe = video->video.entity.pipe - ? to_vsp1_pipeline(&video->video.entity) : &video->pipe; + pipe = video->rwpf->pipe ? video->rwpf->pipe : &video->pipe; ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); if (ret < 0) From 76c29755960c911b4e1bec3da90d4d5f6b44d3f3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 17 Jan 2016 19:55:18 -0200 Subject: [PATCH 070/173] [media] v4l: vsp1: video: Reorder functions Move the pipeline initialization and cleanup functions to prepare for the next commit. No functional code change is performed here. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 266 +++++++++++------------ 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 2c642726a259..4396018d1408 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -172,6 +172,139 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * Pipeline Management */ +/* + * vsp1_video_complete_buffer - Complete the current buffer + * @video: the video node + * + * This function completes the current buffer by filling its sequence number, + * time stamp and payload size, and hands it back to the videobuf core. + * + * When operating in DU output mode (deep pipeline to the DU through the LIF), + * the VSP1 needs to constantly supply frames to the display. In that case, if + * no other buffer is queued, reuse the one that has just been processed instead + * of handing it back to the videobuf core. + * + * Return the next queued buffer or NULL if the queue is empty. + */ +static struct vsp1_vb2_buffer * +vsp1_video_complete_buffer(struct vsp1_video *video) +{ + struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_vb2_buffer *next = NULL; + struct vsp1_vb2_buffer *done; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&video->irqlock, flags); + + if (list_empty(&video->irqqueue)) { + spin_unlock_irqrestore(&video->irqlock, flags); + return NULL; + } + + done = list_first_entry(&video->irqqueue, + struct vsp1_vb2_buffer, queue); + + /* In DU output mode reuse the buffer if the list is singular. */ + if (pipe->lif && list_is_singular(&video->irqqueue)) { + spin_unlock_irqrestore(&video->irqlock, flags); + return done; + } + + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_vb2_buffer, queue); + + spin_unlock_irqrestore(&video->irqlock, flags); + + done->buf.sequence = video->sequence++; + done->buf.vb2_buf.timestamp = ktime_get_ns(); + for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) + vb2_set_plane_payload(&done->buf.vb2_buf, i, + vb2_plane_size(&done->buf.vb2_buf, i)); + vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); + + return next; +} + +static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *rwpf) +{ + struct vsp1_video *video = rwpf->video; + struct vsp1_vb2_buffer *buf; + unsigned long flags; + + buf = vsp1_video_complete_buffer(video); + if (buf == NULL) + return; + + spin_lock_irqsave(&pipe->irqlock, flags); + + video->rwpf->mem = buf->mem; + pipe->buffers_ready |= 1 << video->pipe_index; + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + unsigned int i; + + if (!pipe->dl) + pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rwpf = pipe->inputs[i]; + + if (rwpf) + vsp1_rwpf_set_memory(rwpf, pipe->dl); + } + + if (!pipe->lif) + vsp1_rwpf_set_memory(pipe->output, pipe->dl); + + vsp1_dl_list_commit(pipe->dl); + pipe->dl = NULL; + + vsp1_pipeline_run(pipe); +} + +static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + enum vsp1_pipeline_state state; + unsigned long flags; + unsigned int i; + + /* Complete buffers on all video nodes. */ + for (i = 0; i < vsp1->info->rpf_count; ++i) { + if (!pipe->inputs[i]) + continue; + + vsp1_video_frame_end(pipe, pipe->inputs[i]); + } + + vsp1_video_frame_end(pipe, pipe->output); + + spin_lock_irqsave(&pipe->irqlock, flags); + + state = pipe->state; + pipe->state = VSP1_PIPELINE_STOPPED; + + /* If a stop has been requested, mark the pipeline as stopped and + * return. Otherwise restart the pipeline if ready. + */ + if (state == VSP1_PIPELINE_STOPPING) + wake_up(&pipe->wq); + else if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, struct vsp1_rwpf *input, struct vsp1_rwpf *output) @@ -369,139 +502,6 @@ static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe) mutex_unlock(&pipe->lock); } -/* - * vsp1_video_complete_buffer - Complete the current buffer - * @video: the video node - * - * This function completes the current buffer by filling its sequence number, - * time stamp and payload size, and hands it back to the videobuf core. - * - * When operating in DU output mode (deep pipeline to the DU through the LIF), - * the VSP1 needs to constantly supply frames to the display. In that case, if - * no other buffer is queued, reuse the one that has just been processed instead - * of handing it back to the videobuf core. - * - * Return the next queued buffer or NULL if the queue is empty. - */ -static struct vsp1_vb2_buffer * -vsp1_video_complete_buffer(struct vsp1_video *video) -{ - struct vsp1_pipeline *pipe = video->rwpf->pipe; - struct vsp1_vb2_buffer *next = NULL; - struct vsp1_vb2_buffer *done; - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&video->irqlock, flags); - - if (list_empty(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return NULL; - } - - done = list_first_entry(&video->irqqueue, - struct vsp1_vb2_buffer, queue); - - /* In DU output mode reuse the buffer if the list is singular. */ - if (pipe->lif && list_is_singular(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return done; - } - - list_del(&done->queue); - - if (!list_empty(&video->irqqueue)) - next = list_first_entry(&video->irqqueue, - struct vsp1_vb2_buffer, queue); - - spin_unlock_irqrestore(&video->irqlock, flags); - - done->buf.sequence = video->sequence++; - done->buf.vb2_buf.timestamp = ktime_get_ns(); - for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) - vb2_set_plane_payload(&done->buf.vb2_buf, i, - vb2_plane_size(&done->buf.vb2_buf, i)); - vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); - - return next; -} - -static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, - struct vsp1_rwpf *rwpf) -{ - struct vsp1_video *video = rwpf->video; - struct vsp1_vb2_buffer *buf; - unsigned long flags; - - buf = vsp1_video_complete_buffer(video); - if (buf == NULL) - return; - - spin_lock_irqsave(&pipe->irqlock, flags); - - video->rwpf->mem = buf->mem; - pipe->buffers_ready |= 1 << video->pipe_index; - - spin_unlock_irqrestore(&pipe->irqlock, flags); -} - -static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - unsigned int i; - - if (!pipe->dl) - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); - - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *rwpf = pipe->inputs[i]; - - if (rwpf) - vsp1_rwpf_set_memory(rwpf, pipe->dl); - } - - if (!pipe->lif) - vsp1_rwpf_set_memory(pipe->output, pipe->dl); - - vsp1_dl_list_commit(pipe->dl); - pipe->dl = NULL; - - vsp1_pipeline_run(pipe); -} - -static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - enum vsp1_pipeline_state state; - unsigned long flags; - unsigned int i; - - /* Complete buffers on all video nodes. */ - for (i = 0; i < vsp1->info->rpf_count; ++i) { - if (!pipe->inputs[i]) - continue; - - vsp1_video_frame_end(pipe, pipe->inputs[i]); - } - - vsp1_video_frame_end(pipe, pipe->output); - - spin_lock_irqsave(&pipe->irqlock, flags); - - state = pipe->state; - pipe->state = VSP1_PIPELINE_STOPPED; - - /* If a stop has been requested, mark the pipeline as stopped and - * return. Otherwise restart the pipeline if ready. - */ - if (state == VSP1_PIPELINE_STOPPING) - wake_up(&pipe->wq); - else if (vsp1_pipeline_ready(pipe)) - vsp1_video_pipeline_run(pipe); - - spin_unlock_irqrestore(&pipe->irqlock, flags); -} - /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ From a0cdac5610ea900dcf6a78d4d0216aef2bca7b80 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 17 Jan 2016 19:53:56 -0200 Subject: [PATCH 071/173] [media] v4l: vsp1: Allocate pipelines on demand Instead of embedding pipelines in the vsp1_video objects allocate them on demand when they are needed. This fixes the streamon race condition where pipelines objects from different video nodes could be used for the same pipeline. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 1 + drivers/media/platform/vsp1/vsp1_drv.c | 1 + drivers/media/platform/vsp1/vsp1_pipe.c | 1 + drivers/media/platform/vsp1/vsp1_pipe.h | 5 +- drivers/media/platform/vsp1/vsp1_rpf.c | 1 + drivers/media/platform/vsp1/vsp1_video.c | 130 ++++++++++++----------- drivers/media/platform/vsp1/vsp1_video.h | 2 - drivers/media/platform/vsp1/vsp1_wpf.c | 1 + 8 files changed, 78 insertions(+), 64 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 3ece40245396..d27de5363c5a 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -19,6 +19,7 @@ #include "vsp1.h" #include "vsp1_bru.h" #include "vsp1_dl.h" +#include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index f1be2680013d..596f26d81494 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -30,6 +30,7 @@ #include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_lut.h" +#include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 8ac080f87b08..4913b933562c 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -194,6 +194,7 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) mutex_init(&pipe->lock); spin_lock_init(&pipe->irqlock); init_waitqueue_head(&pipe->wq); + kref_init(&pipe->kref); INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 9fd688bfe638..7b56113511dd 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -13,6 +13,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include #include #include #include @@ -63,7 +64,7 @@ enum vsp1_pipeline_state { * @wq: work queue to wait for state change completion * @frame_end: frame end interrupt handler * @lock: protects the pipeline use count and stream count - * @use_count: number of video nodes using the pipeline + * @kref: pipeline reference count * @stream_count: number of streaming video nodes * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available * @num_inputs: number of RPFs @@ -86,7 +87,7 @@ struct vsp1_pipeline { void (*frame_end)(struct vsp1_pipeline *pipe); struct mutex lock; - unsigned int use_count; + struct kref kref; unsigned int stream_count; unsigned int buffers_ready; diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index e7b6abbb0024..5486ff54a2b3 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -17,6 +17,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 4396018d1408..a9aec5c0bec6 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -399,14 +399,10 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, unsigned int i; int ret; - mutex_lock(&mdev->graph_mutex); - /* Walk the graph to locate the entities and video nodes. */ ret = media_entity_graph_walk_init(&graph, mdev); - if (ret) { - mutex_unlock(&mdev->graph_mutex); + if (ret) return ret; - } media_entity_graph_walk_start(&graph, entity); @@ -439,15 +435,11 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, } } - mutex_unlock(&mdev->graph_mutex); - media_entity_graph_walk_cleanup(&graph); /* We need one output and at least one input. */ - if (pipe->num_inputs == 0 || !pipe->output) { - ret = -EPIPE; - goto error; - } + if (pipe->num_inputs == 0 || !pipe->output) + return -EPIPE; /* Follow links downstream for each input and make sure the graph * contains no loop and that all branches end at the output WPF. @@ -459,47 +451,66 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, ret = vsp1_video_pipeline_build_branch(pipe, pipe->inputs[i], pipe->output); if (ret < 0) - goto error; + return ret; } return 0; - -error: - vsp1_pipeline_reset(pipe); - return ret; } static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { - int ret; + vsp1_pipeline_init(pipe); - mutex_lock(&pipe->lock); + pipe->frame_end = vsp1_video_pipeline_frame_end; - /* If we're the first user build and validate the pipeline. */ - if (pipe->use_count == 0) { - ret = vsp1_video_pipeline_build(pipe, video); - if (ret < 0) - goto done; - } - - pipe->use_count++; - ret = 0; - -done: - mutex_unlock(&pipe->lock); - return ret; + return vsp1_video_pipeline_build(pipe, video); } -static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe) +static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) { - mutex_lock(&pipe->lock); + struct vsp1_pipeline *pipe; + int ret; - /* If we're the last user clean up the pipeline. */ - if (--pipe->use_count == 0) - vsp1_pipeline_reset(pipe); + /* Get a pipeline object for the video node. If a pipeline has already + * been allocated just increment its reference count and return it. + * Otherwise allocate a new pipeline and initialize it, it will be freed + * when the last reference is released. + */ + if (!video->rwpf->pipe) { + pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return ERR_PTR(-ENOMEM); - mutex_unlock(&pipe->lock); + ret = vsp1_video_pipeline_init(pipe, video); + if (ret < 0) { + vsp1_pipeline_reset(pipe); + kfree(pipe); + return ERR_PTR(ret); + } + } else { + pipe = video->rwpf->pipe; + kref_get(&pipe->kref); + } + + return pipe; +} + +static void vsp1_video_pipeline_release(struct kref *kref) +{ + struct vsp1_pipeline *pipe = container_of(kref, typeof(*pipe), kref); + + vsp1_pipeline_reset(pipe); + kfree(pipe); +} + +static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe) +{ + struct media_device *mdev = &pipe->output->entity.vsp1->media_dev; + + mutex_lock(&mdev->graph_mutex); + kref_put(&pipe->kref, vsp1_video_pipeline_release); + mutex_unlock(&mdev->graph_mutex); } /* ----------------------------------------------------------------------------- @@ -674,8 +685,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) } mutex_unlock(&pipe->lock); - vsp1_video_pipeline_cleanup(pipe); media_entity_pipeline_stop(&video->video.entity); + vsp1_video_pipeline_put(pipe); /* Remove all buffers from the IRQ queue. */ spin_lock_irqsave(&video->irqlock, flags); @@ -787,6 +798,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct v4l2_fh *vfh = file->private_data; struct vsp1_video *video = to_vsp1_video(vfh->vdev); + struct media_device *mdev = &video->vsp1->media_dev; struct vsp1_pipeline *pipe; int ret; @@ -795,20 +807,25 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) video->sequence = 0; - /* Start streaming on the pipeline. No link touching an entity in the - * pipeline can be activated or deactivated once streaming is started. - * - * Use the VSP1 pipeline object embedded in the first video object that - * starts streaming. - * - * FIXME: This is racy, the ioctl is only protected by the video node - * lock. + /* Get a pipeline for the video node and start streaming on it. No link + * touching an entity in the pipeline can be activated or deactivated + * once streaming is started. */ - pipe = video->rwpf->pipe ? video->rwpf->pipe : &video->pipe; + mutex_lock(&mdev->graph_mutex); - ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); - if (ret < 0) - return ret; + pipe = vsp1_video_pipeline_get(video); + if (IS_ERR(pipe)) { + mutex_unlock(&mdev->graph_mutex); + return PTR_ERR(pipe); + } + + ret = __media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) { + mutex_unlock(&mdev->graph_mutex); + goto err_pipe; + } + + mutex_unlock(&mdev->graph_mutex); /* Verify that the configured format matches the output of the connected * subdev. @@ -817,21 +834,17 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_stop; - ret = vsp1_video_pipeline_init(pipe, video); - if (ret < 0) - goto err_stop; - /* Start the queue. */ ret = vb2_streamon(&video->queue, type); if (ret < 0) - goto err_cleanup; + goto err_stop; return 0; -err_cleanup: - vsp1_video_pipeline_cleanup(pipe); err_stop: media_entity_pipeline_stop(&video->video.entity); +err_pipe: + vsp1_video_pipeline_put(pipe); return ret; } @@ -947,9 +960,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, spin_lock_init(&video->irqlock); INIT_LIST_HEAD(&video->irqqueue); - vsp1_pipeline_init(&video->pipe); - video->pipe.frame_end = vsp1_video_pipeline_frame_end; - /* Initialize the media entity... */ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 64abd39ee1e7..867b00807c46 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -18,7 +18,6 @@ #include -#include "vsp1_pipe.h" #include "vsp1_rwpf.h" struct vsp1_vb2_buffer { @@ -44,7 +43,6 @@ struct vsp1_video { struct mutex lock; - struct vsp1_pipeline pipe; unsigned int pipe_index; struct vb2_queue queue; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d1d5c08ca35e..ce1d0b4094db 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -17,6 +17,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" From 5b22a11e0b21e7da8fcefd913688c4cfcdf08825 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 24 Feb 2016 18:40:12 -0300 Subject: [PATCH 072/173] [media] v4l: vsp1: RPF entities can't be target nodes The RPF entities are located at the very beginning of pipelines, they can't be target nodes in the Data Path Router matrix. Remove their input ID from the routing table. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_entity.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index e9dd4dbda2dc..dfecddffbc81 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -160,11 +160,11 @@ static const struct vsp1_route vsp1_routes[] = { { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } }, - { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } }, - { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } }, - { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } }, - { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } }, - { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } }, + { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { 0, } }, + { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { 0, } }, + { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { 0, } }, + { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { 0, } }, + { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { 0, } }, { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } }, { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } }, { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } }, From 3f557220cc29d1961ef9efa2a8db04c7c5f6e6d4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 24 Feb 2016 21:10:13 -0300 Subject: [PATCH 073/173] [media] v4l: vsp1: Factorize get pad format code All entities implement the same get pad format handler, factorize it into a common function. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_entity.c | 25 +++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_entity.h | 4 ++++ drivers/media/platform/vsp1/vsp1_hsit.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_lif.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_lut.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_rwpf.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_sru.c | 19 +---------------- drivers/media/platform/vsp1/vsp1_uds.c | 19 +---------------- 9 files changed, 36 insertions(+), 126 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index d27de5363c5a..fb32f87d4625 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -129,23 +129,6 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad); } -static int bru_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_bru *bru = to_bru(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&bru->entity, config, - fmt->pad); - - return 0; -} - static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_pad_config *config, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -292,7 +275,7 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = bru_enum_mbus_code, .enum_frame_size = bru_enum_frame_size, - .get_fmt = bru_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = bru_set_format, .get_selection = bru_get_selection, .set_selection = bru_set_selection, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index dfecddffbc81..e4d6c7a1ed77 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -116,6 +116,31 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } +/* + * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: V4L2 subdev format + * + * This function implements the subdev get_fmt pad operation. It can be used as + * a direct drop-in for the operation handler. + */ +int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + + config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); + + return 0; +} + /* ----------------------------------------------------------------------------- * Media Operations */ diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index bbf378437c3b..d2bb970e72b2 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -127,4 +127,8 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, void vsp1_entity_route_setup(struct vsp1_entity *source, struct vsp1_dl_list *dl); +int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt); + #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index f02e4ca77a7c..7cf2add51bf9 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -90,23 +90,6 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int hsit_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_hsit *hsit = to_hsit(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, config, - fmt->pad); - - return 0; -} - static int hsit_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -154,7 +137,7 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = hsit_enum_mbus_code, .enum_frame_size = hsit_enum_frame_size, - .get_fmt = hsit_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = hsit_set_format, }; diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 42ed8f80cc88..730db64bd4d3 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -107,23 +107,6 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lif_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_lif *lif = to_lif(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&lif->entity, config, - fmt->pad); - - return 0; -} - static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -173,7 +156,7 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lif_enum_mbus_code, .enum_frame_size = lif_enum_frame_size, - .get_fmt = lif_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = lif_set_format, }; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 596537a95210..f84ee8878858 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -136,23 +136,6 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int lut_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_lut *lut = to_lut(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&lut->entity, config, - fmt->pad); - - return 0; -} - static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -208,7 +191,7 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, .enum_frame_size = lut_enum_frame_size, - .get_fmt = lut_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = lut_set_format, }; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 4d302f5cccb2..64d649a1bcf5 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -83,23 +83,6 @@ static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, config, - fmt->pad); - - return 0; -} - static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -254,7 +237,7 @@ const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = vsp1_rwpf_enum_mbus_code, .enum_frame_size = vsp1_rwpf_enum_frame_size, - .get_fmt = vsp1_rwpf_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = vsp1_rwpf_set_format, .get_selection = vsp1_rwpf_get_selection, .set_selection = vsp1_rwpf_set_selection, diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 00edd95093e3..51b017d841f5 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -184,23 +184,6 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int sru_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_sru *sru = to_sru(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&sru->entity, config, - fmt->pad); - - return 0; -} - static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *config, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -285,7 +268,7 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = sru_enum_mbus_code, .enum_frame_size = sru_enum_frame_size, - .get_fmt = sru_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = sru_set_format, }; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 42f2d0465bd6..59dd53c0d2be 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -182,23 +182,6 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static int uds_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_uds *uds = to_uds(subdev); - struct v4l2_subdev_pad_config *config; - - config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); - if (!config) - return -EINVAL; - - fmt->format = *vsp1_entity_get_pad_format(&uds->entity, config, - fmt->pad); - - return 0; -} - static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_pad_config *config, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -272,7 +255,7 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = uds_enum_mbus_code, .enum_frame_size = uds_enum_frame_size, - .get_fmt = uds_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = uds_set_format, }; From 6ad9ba9c14fad546b91d654c5b4e870d009ace28 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 24 Feb 2016 20:25:42 -0300 Subject: [PATCH 074/173] [media] v4l: vsp1: Factorize media bus codes enumeration code Most of the entities can't perform format conversion and implement the same media bus enumeration function. Factorize the code into a single implementation. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_bru.c | 26 +------------ drivers/media/platform/vsp1/vsp1_entity.c | 46 +++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_entity.h | 4 ++ drivers/media/platform/vsp1/vsp1_lif.c | 29 +------------- drivers/media/platform/vsp1/vsp1_lut.c | 29 +------------- drivers/media/platform/vsp1/vsp1_sru.c | 29 +------------- drivers/media/platform/vsp1/vsp1_uds.c | 29 +------------- 7 files changed, 60 insertions(+), 132 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index fb32f87d4625..b1068c018011 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -76,31 +76,9 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; - struct vsp1_bru *bru = to_bru(subdev); - if (code->pad == BRU_PAD_SINK(0)) { - if (code->index >= ARRAY_SIZE(codes)) - return -EINVAL; - - code->code = codes[code->index]; - } else { - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - if (code->index) - return -EINVAL; - - config = vsp1_entity_get_pad_config(&bru->entity, cfg, - code->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&bru->entity, config, - BRU_PAD_SINK(0)); - code->code = format->code; - } - - return 0; + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); } static int bru_enum_frame_size(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index e4d6c7a1ed77..9e848260d85e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -141,6 +141,52 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return 0; } +/* + * vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: Media bus code enumeration + * @codes: Array of supported media bus codes + * @ncodes: Number of supported media bus codes + * + * This function implements the subdev enum_mbus_code pad operation for entities + * that do not support format conversion. It enumerates the given supported + * media bus codes on the sink pad and reports a source pad format identical to + * the sink pad. + */ +int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code, + const unsigned int *codes, unsigned int ncodes) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + + if (code->pad == 0) { + if (code->index >= ncodes) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + /* The entity can't perform format conversion, the sink format + * is always identical to the source format. + */ + if (code->index) + return -EINVAL; + + config = vsp1_entity_get_pad_config(entity, cfg, code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(entity, config, 0); + code->code = format->code; + } + + return 0; +} + /* ----------------------------------------------------------------------------- * Media Operations */ diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index d2bb970e72b2..ab7cc498d139 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -130,5 +130,9 @@ void vsp1_entity_route_setup(struct vsp1_entity *source, int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); +int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code, + const unsigned int *codes, unsigned int ncodes); #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 730db64bd4d3..59a8017f39af 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -45,34 +45,9 @@ static int lif_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; - struct vsp1_lif *lif = to_lif(subdev); - if (code->pad == LIF_PAD_SINK) { - if (code->index >= ARRAY_SIZE(codes)) - return -EINVAL; - - code->code = codes[code->index]; - } else { - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - /* The LIF can't perform format conversion, the sink format is - * always identical to the source format. - */ - if (code->index) - return -EINVAL; - - config = vsp1_entity_get_pad_config(&lif->entity, cfg, - code->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&lif->entity, config, - LIF_PAD_SINK); - code->code = format->code; - } - - return 0; + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); } static int lif_enum_frame_size(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index f84ee8878858..12a069adf567 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -71,34 +71,9 @@ static int lut_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_AHSV8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; - struct vsp1_lut *lut = to_lut(subdev); - if (code->pad == LUT_PAD_SINK) { - if (code->index >= ARRAY_SIZE(codes)) - return -EINVAL; - - code->code = codes[code->index]; - } else { - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - /* The LUT can't perform format conversion, the sink format is - * always identical to the source format. - */ - if (code->index) - return -EINVAL; - - config = vsp1_entity_get_pad_config(&lut->entity, cfg, - code->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&lut->entity, config, - LUT_PAD_SINK); - code->code = format->code; - } - - return 0; + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); } static int lut_enum_frame_size(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 51b017d841f5..97ef997ae735 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -116,34 +116,9 @@ static int sru_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; - struct vsp1_sru *sru = to_sru(subdev); - if (code->pad == SRU_PAD_SINK) { - if (code->index >= ARRAY_SIZE(codes)) - return -EINVAL; - - code->code = codes[code->index]; - } else { - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - /* The SRU can't perform format conversion, the sink format is - * always identical to the source format. - */ - if (code->index) - return -EINVAL; - - config = vsp1_entity_get_pad_config(&sru->entity, cfg, - code->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&sru->entity, config, - SRU_PAD_SINK); - code->code = format->code; - } - - return 0; + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); } static int sru_enum_frame_size(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 59dd53c0d2be..1875e29da184 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -119,34 +119,9 @@ static int uds_enum_mbus_code(struct v4l2_subdev *subdev, MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_AYUV8_1X32, }; - struct vsp1_uds *uds = to_uds(subdev); - if (code->pad == UDS_PAD_SINK) { - if (code->index >= ARRAY_SIZE(codes)) - return -EINVAL; - - code->code = codes[code->index]; - } else { - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - config = vsp1_entity_get_pad_config(&uds->entity, cfg, - code->which); - if (!config) - return -EINVAL; - - /* The UDS can't perform format conversion, the sink format is - * always identical to the source format. - */ - if (code->index) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&uds->entity, config, - UDS_PAD_SINK); - code->code = format->code; - } - - return 0; + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); } static int uds_enum_frame_size(struct v4l2_subdev *subdev, From c431cbbb446851ecb17095812e049119b2be17ed Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 13 Apr 2016 19:08:55 -0300 Subject: [PATCH 075/173] Revert "[media] v4l2-ioctl: simplify code" There are some issues rised on this patch during patch review. I ended by merging this one by mistake. So, let's revert it. This reverts commit 54ace1cfd4358fd11112f17cc711eea234d5ab9e. Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 57 ++++++++++++---------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 3cf8d3adab03..6bf5a3ecd126 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2160,41 +2160,34 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_cropcap *p = arg; - struct v4l2_selection s = { .type = p->type }; - int ret; - if (ops->vidioc_g_selection == NULL) { - /* - * The determine_valid_ioctls() call already should ensure - * that ops->vidioc_cropcap != NULL, but just in case... - */ - if (ops->vidioc_cropcap) - return ops->vidioc_cropcap(file, fh, p); - return -ENOTTY; + if (ops->vidioc_g_selection) { + struct v4l2_selection s = { .type = p->type }; + int ret; + + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; + + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; } - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; - - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; - /* setting trivial pixelaspect */ p->pixelaspect.numerator = 1; p->pixelaspect.denominator = 1; From 076e834fee91db7e9df4fe2d3ecf3ed67eadbe88 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 24 Feb 2016 20:25:42 -0300 Subject: [PATCH 076/173] [media] v4l: vsp1: Factorize frame size enumeration code Most of the entities can't perform scaling and implement the same frame size enumeration function. Factorize the code into a single implementation. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_entity.c | 52 +++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_entity.h | 5 +++ drivers/media/platform/vsp1/vsp1_hsit.c | 32 ++------------ drivers/media/platform/vsp1/vsp1_lif.c | 29 ++----------- drivers/media/platform/vsp1/vsp1_lut.c | 32 ++------------ drivers/media/platform/vsp1/vsp1_rwpf.c | 30 ++----------- 6 files changed, 69 insertions(+), 111 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 9e848260d85e..3d070bcc6053 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -187,6 +187,58 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return 0; } +/* + * vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: Frame size enumeration + * @min_width: Minimum image width + * @min_height: Minimum image height + * @max_width: Maximum image width + * @max_height: Maximum image height + * + * This function implements the subdev enum_frame_size pad operation for + * entities that do not support scaling or cropping. It reports the given + * minimum and maximum frame width and height on the sink pad, and a fixed + * source pad size identical to the sink pad. + */ +int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + config = vsp1_entity_get_pad_config(entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(entity, config, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == 0) { + fse->min_width = min_width; + fse->max_width = max_width; + fse->min_height = min_height; + fse->max_height = max_height; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + /* ----------------------------------------------------------------------------- * Media Operations */ diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index ab7cc498d139..69eff4e17350 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -134,5 +134,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code, const unsigned int *codes, unsigned int ncodes); +int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse, + unsigned int min_w, unsigned int min_h, + unsigned int max_w, unsigned int max_h); #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 7cf2add51bf9..68b8567b374d 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -59,35 +59,9 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - struct vsp1_hsit *hsit = to_hsit(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fse->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&hsit->entity, config, fse->pad); - - if (fse->index || fse->code != format->code) - return -EINVAL; - - if (fse->pad == HSIT_PAD_SINK) { - fse->min_width = HSIT_MIN_SIZE; - fse->max_width = HSIT_MAX_SIZE; - fse->min_height = HSIT_MIN_SIZE; - fse->max_height = HSIT_MAX_SIZE; - } else { - /* The size on the source pad are fixed and always identical to - * the size on the sink pad. - */ - fse->min_width = format->width; - fse->max_width = format->width; - fse->min_height = format->height; - fse->max_height = format->height; - } - - return 0; + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HSIT_MIN_SIZE, + HSIT_MIN_SIZE, HSIT_MAX_SIZE, + HSIT_MAX_SIZE); } static int hsit_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 59a8017f39af..579e142b4e94 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -54,32 +54,9 @@ static int lif_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - struct vsp1_lif *lif = to_lif(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - config = vsp1_entity_get_pad_config(&lif->entity, cfg, fse->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&lif->entity, config, LIF_PAD_SINK); - - if (fse->index || fse->code != format->code) - return -EINVAL; - - if (fse->pad == LIF_PAD_SINK) { - fse->min_width = LIF_MIN_SIZE; - fse->max_width = LIF_MAX_SIZE; - fse->min_height = LIF_MIN_SIZE; - fse->max_height = LIF_MAX_SIZE; - } else { - fse->min_width = format->width; - fse->max_width = format->width; - fse->min_height = format->height; - fse->max_height = format->height; - } - - return 0; + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LIF_MIN_SIZE, + LIF_MIN_SIZE, LIF_MAX_SIZE, + LIF_MAX_SIZE); } static int lif_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 12a069adf567..c779648882ab 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -80,35 +80,9 @@ static int lut_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - struct vsp1_lut *lut = to_lut(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - - config = vsp1_entity_get_pad_config(&lut->entity, cfg, fse->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&lut->entity, config, fse->pad); - - if (fse->index || fse->code != format->code) - return -EINVAL; - - if (fse->pad == LUT_PAD_SINK) { - fse->min_width = LUT_MIN_SIZE; - fse->max_width = LUT_MAX_SIZE; - fse->min_height = LUT_MIN_SIZE; - fse->max_height = LUT_MAX_SIZE; - } else { - /* The size on the source pad are fixed and always identical to - * the size on the sink pad. - */ - fse->min_width = format->width; - fse->max_width = format->width; - fse->min_height = format->height; - fse->max_height = format->height; - } - - return 0; + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LUT_MIN_SIZE, + LUT_MIN_SIZE, LUT_MAX_SIZE, + LUT_MAX_SIZE); } static int lut_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 64d649a1bcf5..3b6e032e7806 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -53,34 +53,10 @@ static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct vsp1_rwpf *rwpf = to_rwpf(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fse->which); - if (!config) - return -EINVAL; - - format = vsp1_entity_get_pad_format(&rwpf->entity, config, fse->pad); - - if (fse->index || fse->code != format->code) - return -EINVAL; - - if (fse->pad == RWPF_PAD_SINK) { - fse->min_width = RWPF_MIN_WIDTH; - fse->max_width = rwpf->max_width; - fse->min_height = RWPF_MIN_HEIGHT; - fse->max_height = rwpf->max_height; - } else { - /* The size on the source pad are fixed and always identical to - * the size on the sink pad. - */ - fse->min_width = format->width; - fse->max_width = format->width; - fse->min_height = format->height; - fse->max_height = format->height; - } - - return 0; + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH, + RWPF_MIN_HEIGHT, rwpf->max_width, + rwpf->max_height); } static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, From e2b6d7b38c8d2082bacb72a322db9e6a6216ae15 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 16 Nov 2015 02:07:58 -0200 Subject: [PATCH 077/173] [media] v4l: vsp1: Fix LUT format setting The LUT set format handler overrides the requested format by mistake. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_lut.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index c779648882ab..33e3b5cc9c9b 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -111,6 +111,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, return 0; } + format->code = fmt->format.code; format->width = clamp_t(unsigned int, fmt->format.width, LUT_MIN_SIZE, LUT_MAX_SIZE); format->height = clamp_t(unsigned int, fmt->format.height, From b25854e134e9a28c600937ac2320d65c1530283e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 09:25:47 -0300 Subject: [PATCH 078/173] [media] v4l: vsp1: dl: Make reg_count field unsigned The field takes positive values only, make it unsigned. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 54f8f4719276..51d14c4a4231 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -60,7 +60,7 @@ struct vsp1_dl_list { dma_addr_t dma; size_t size; - int reg_count; + unsigned int reg_count; }; enum vsp1_dl_mode { From d2c1b028db2e0b153f1aff28e3010a494c8aadc1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 09:26:47 -0300 Subject: [PATCH 079/173] [media] v4l: vsp1: dl: Fix race conditions The vsp1_dl_list_put() function expects to be called with the display list manager lock held. This assumption is correct for calls from within the vsp1_dl.c file, but not for the external calls. Fix it by taking the lock inside the function and providing an unlocked version for the internal callers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 55 ++++++++++++++++++--------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 51d14c4a4231..a931cced9a57 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -163,18 +163,8 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) return dl; } -/** - * vsp1_dl_list_put - Release a display list - * @dl: The display list - * - * Release the display list and return it to the pool of free lists. - * - * This function must be called with the display list manager lock held. - * - * Passing a NULL pointer to this function is safe, in that case no operation - * will be performed. - */ -void vsp1_dl_list_put(struct vsp1_dl_list *dl) +/* This function must be called with the display list manager lock held.*/ +static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) { if (!dl) return; @@ -184,6 +174,27 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl) list_add_tail(&dl->list, &dl->dlm->free); } +/** + * vsp1_dl_list_put - Release a display list + * @dl: The display list + * + * Release the display list and return it to the pool of free lists. + * + * Passing a NULL pointer to this function is safe, in that case no operation + * will be performed. + */ +void vsp1_dl_list_put(struct vsp1_dl_list *dl) +{ + unsigned long flags; + + if (!dl) + return; + + spin_lock_irqsave(&dl->dlm->lock, flags); + __vsp1_dl_list_put(dl); + spin_unlock_irqrestore(&dl->dlm->lock, flags); +} + void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data) { dl->body[dl->reg_count].addr = reg; @@ -219,7 +230,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) */ update = !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD); if (update) { - vsp1_dl_list_put(dlm->pending); + __vsp1_dl_list_put(dlm->pending); dlm->pending = dl; goto done; } @@ -232,7 +243,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | (dl->reg_count * 8)); - vsp1_dl_list_put(dlm->queued); + __vsp1_dl_list_put(dlm->queued); dlm->queued = dl; done: @@ -252,7 +263,7 @@ void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm) * processing by the device. The active display list, if any, won't be * accessed anymore and can be reused. */ - vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->active); dlm->active = NULL; spin_unlock(&dlm->lock); @@ -264,7 +275,7 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) spin_lock(&dlm->lock); - vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->active); dlm->active = NULL; /* Header mode is used for mem-to-mem pipelines only. We don't need to @@ -327,9 +338,15 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) { - vsp1_dl_list_put(dlm->active); - vsp1_dl_list_put(dlm->queued); - vsp1_dl_list_put(dlm->pending); + unsigned long flags; + + spin_lock_irqsave(&dlm->lock, flags); + + __vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->queued); + __vsp1_dl_list_put(dlm->pending); + + spin_unlock_irqrestore(&dlm->lock, flags); dlm->active = NULL; dlm->queued = NULL; From f81e83c418b0d59c036e071e11a7c143bc507781 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 13:36:34 -0300 Subject: [PATCH 080/173] [media] v4l: vsp1: dl: Add support for multi-body display lists Display lists support up to 8 bodies but we currently use a single one. To support preparing display lists for large look-up tables, add support for multi-body display lists. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 293 +++++++++++++++++++++----- drivers/media/platform/vsp1/vsp1_dl.h | 8 + 2 files changed, 253 insertions(+), 48 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index a931cced9a57..e238d9b9376b 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -19,28 +19,20 @@ #include "vsp1.h" #include "vsp1_dl.h" -/* - * Global resources - * - * - Display-related interrupts (can be used for vblank evasion ?) - * - Display-list enable - * - Header-less for WPF0 - * - DL swap - */ - -#define VSP1_DL_HEADER_SIZE 76 -#define VSP1_DL_BODY_SIZE (2 * 4 * 256) +#define VSP1_DL_NUM_ENTRIES 256 #define VSP1_DL_NUM_LISTS 3 #define VSP1_DLH_INT_ENABLE (1 << 1) #define VSP1_DLH_AUTO_START (1 << 0) +struct vsp1_dl_header_list { + u32 num_bytes; + u32 addr; +} __attribute__((__packed__)); + struct vsp1_dl_header { u32 num_lists; - struct { - u32 num_bytes; - u32 addr; - } lists[8]; + struct vsp1_dl_header_list lists[8]; u32 next_header; u32 flags; } __attribute__((__packed__)); @@ -50,17 +42,44 @@ struct vsp1_dl_entry { u32 data; } __attribute__((__packed__)); -struct vsp1_dl_list { +/** + * struct vsp1_dl_body - Display list body + * @list: entry in the display list list of bodies + * @vsp1: the VSP1 device + * @entries: array of entries + * @dma: DMA address of the entries + * @size: size of the DMA memory in bytes + * @num_entries: number of stored entries + */ +struct vsp1_dl_body { struct list_head list; + struct vsp1_device *vsp1; - struct vsp1_dl_manager *dlm; - - struct vsp1_dl_header *header; - struct vsp1_dl_entry *body; + struct vsp1_dl_entry *entries; dma_addr_t dma; size_t size; - unsigned int reg_count; + unsigned int num_entries; +}; + +/** + * struct vsp1_dl_list - Display list + * @list: entry in the display list manager lists + * @dlm: the display list manager + * @header: display list header, NULL for headerless lists + * @dma: DMA address for the header + * @body0: first display list body + * @fragments: list of extra display list bodies + */ +struct vsp1_dl_list { + struct list_head list; + struct vsp1_dl_manager *dlm; + + struct vsp1_dl_header *header; + dma_addr_t dma; + + struct vsp1_dl_body body0; + struct list_head fragments; }; enum vsp1_dl_mode { @@ -91,6 +110,110 @@ struct vsp1_dl_manager { struct vsp1_dl_list *pending; }; +/* ----------------------------------------------------------------------------- + * Display List Body Management + */ + +/* + * Initialize a display list body object and allocate DMA memory for the body + * data. The display list body object is expected to have been initialized to + * 0 when allocated. + */ +static int vsp1_dl_body_init(struct vsp1_device *vsp1, + struct vsp1_dl_body *dlb, unsigned int num_entries, + size_t extra_size) +{ + size_t size = num_entries * sizeof(*dlb->entries) + extra_size; + + dlb->vsp1 = vsp1; + dlb->size = size; + + dlb->entries = dma_alloc_wc(vsp1->dev, dlb->size, &dlb->dma, + GFP_KERNEL); + if (!dlb->entries) + return -ENOMEM; + + return 0; +} + +/* + * Cleanup a display list body and free allocated DMA memory allocated. + */ +static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb) +{ + dma_free_wc(dlb->vsp1->dev, dlb->size, dlb->entries, dlb->dma); +} + +/** + * vsp1_dl_fragment_alloc - Allocate a display list fragment + * @vsp1: The VSP1 device + * @num_entries: The maximum number of entries that the fragment can contain + * + * Allocate a display list fragment with enough memory to contain the requested + * number of entries. + * + * Return a pointer to a fragment on success or NULL if memory can't be + * allocated. + */ +struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, + unsigned int num_entries) +{ + struct vsp1_dl_body *dlb; + int ret; + + dlb = kzalloc(sizeof(*dlb), GFP_KERNEL); + if (!dlb) + return NULL; + + ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0); + if (ret < 0) { + kfree(dlb); + return NULL; + } + + return dlb; +} + +/** + * vsp1_dl_fragment_free - Free a display list fragment + * @dlb: The fragment + * + * Free the given display list fragment and the associated DMA memory. + * + * Fragments must only be freed explicitly if they are not added to a display + * list, as the display list will take ownership of them and free them + * otherwise. Manual free typically happens at cleanup time for fragments that + * have been allocated but not used. + * + * Passing a NULL pointer to this function is safe, in that case no operation + * will be performed. + */ +void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb) +{ + if (!dlb) + return; + + vsp1_dl_body_cleanup(dlb); + kfree(dlb); +} + +/** + * vsp1_dl_fragment_write - Write a register to a display list fragment + * @dlb: The fragment + * @reg: The register address + * @data: The register value + * + * Write the given register and value to the display list fragment. The maximum + * number of entries that can be written in a fragment is specified when the + * fragment is allocated by vsp1_dl_fragment_alloc(). + */ +void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) +{ + dlb->entries[dlb->num_entries].addr = reg; + dlb->entries[dlb->num_entries].data = data; + dlb->num_entries++; +} + /* ----------------------------------------------------------------------------- * Display List Transaction Management */ @@ -99,42 +222,61 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; size_t header_size; - - /* The body needs to be aligned on a 8 bytes boundary, pad the header - * size to allow allocating both in a single operation. - */ - header_size = dlm->mode == VSP1_DL_MODE_HEADER - ? ALIGN(sizeof(struct vsp1_dl_header), 8) - : 0; + int ret; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) return NULL; + INIT_LIST_HEAD(&dl->fragments); dl->dlm = dlm; - dl->size = header_size + VSP1_DL_BODY_SIZE; - dl->header = dma_alloc_wc(dlm->vsp1->dev, dl->size, &dl->dma, - GFP_KERNEL); - if (!dl->header) { + /* Initialize the display list body and allocate DMA memory for the body + * and the optional header. Both are allocated together to avoid memory + * fragmentation, with the header located right after the body in + * memory. + */ + header_size = dlm->mode == VSP1_DL_MODE_HEADER + ? ALIGN(sizeof(struct vsp1_dl_header), 8) + : 0; + + ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES, + header_size); + if (ret < 0) { kfree(dl); return NULL; } if (dlm->mode == VSP1_DL_MODE_HEADER) { + size_t header_offset = VSP1_DL_NUM_ENTRIES + * sizeof(*dl->body0.entries); + + dl->header = ((void *)dl->body0.entries) + header_offset; + dl->dma = dl->body0.dma + header_offset; + memset(dl->header, 0, sizeof(*dl->header)); - dl->header->lists[0].addr = dl->dma + header_size; + dl->header->lists[0].addr = dl->body0.dma; dl->header->flags = VSP1_DLH_INT_ENABLE; } - dl->body = ((void *)dl->header) + header_size; - return dl; } +static void vsp1_dl_list_free_fragments(struct vsp1_dl_list *dl) +{ + struct vsp1_dl_body *dlb, *next; + + list_for_each_entry_safe(dlb, next, &dl->fragments, list) { + list_del(&dlb->list); + vsp1_dl_body_cleanup(dlb); + kfree(dlb); + } +} + static void vsp1_dl_list_free(struct vsp1_dl_list *dl) { - dma_free_wc(dl->dlm->vsp1->dev, dl->size, dl->header, dl->dma); + vsp1_dl_body_cleanup(&dl->body0); + vsp1_dl_list_free_fragments(dl); kfree(dl); } @@ -169,7 +311,8 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) if (!dl) return; - dl->reg_count = 0; + vsp1_dl_list_free_fragments(dl); + dl->body0.num_entries = 0; list_add_tail(&dl->list, &dl->dlm->free); } @@ -195,11 +338,45 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl) spin_unlock_irqrestore(&dl->dlm->lock, flags); } +/** + * vsp1_dl_list_write - Write a register to the display list + * @dl: The display list + * @reg: The register address + * @data: The register value + * + * Write the given register and value to the display list. Up to 256 registers + * can be written per display list. + */ void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data) { - dl->body[dl->reg_count].addr = reg; - dl->body[dl->reg_count].data = data; - dl->reg_count++; + vsp1_dl_fragment_write(&dl->body0, reg, data); +} + +/** + * vsp1_dl_list_add_fragment - Add a fragment to the display list + * @dl: The display list + * @dlb: The fragment + * + * Add a display list body as a fragment to a display list. Registers contained + * in fragments are processed after registers contained in the main display + * list, in the order in which fragments are added. + * + * Adding a fragment to a display list passes ownership of the fragment to the + * list. The caller must not touch the fragment after this call, and must not + * free it explicitly with vsp1_dl_fragment_free(). + * + * Fragments are only usable for display lists in header mode. Attempt to + * add a fragment to a header-less display list will return an error. + */ +int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + /* Multi-body lists are only available in header mode. */ + if (dl->dlm->mode != VSP1_DL_MODE_HEADER) + return -EINVAL; + + list_add_tail(&dlb->list, &dl->fragments); + return 0; } void vsp1_dl_list_commit(struct vsp1_dl_list *dl) @@ -212,11 +389,30 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) spin_lock_irqsave(&dlm->lock, flags); if (dl->dlm->mode == VSP1_DL_MODE_HEADER) { - /* Program the hardware with the display list body address and - * size. In header mode the caller guarantees that the hardware - * is idle at this point. + struct vsp1_dl_header_list *hdr = dl->header->lists; + struct vsp1_dl_body *dlb; + unsigned int num_lists = 0; + + /* Fill the header with the display list bodies addresses and + * sizes. The address of the first body has already been filled + * when the display list was allocated. + * + * In header mode the caller guarantees that the hardware is + * idle at this point. */ - dl->header->lists[0].num_bytes = dl->reg_count * 8; + hdr->num_bytes = dl->body0.num_entries + * sizeof(*dl->header->lists); + + list_for_each_entry(dlb, &dl->fragments, list) { + num_lists++; + hdr++; + + hdr->addr = dlb->dma; + hdr->num_bytes = dlb->num_entries + * sizeof(*dl->header->lists); + } + + dl->header->num_lists = num_lists; vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); dlm->active = dl; @@ -239,9 +435,9 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) * The UPD bit will be cleared by the device when the display list is * processed. */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->reg_count * 8)); + (dl->body0.num_entries * sizeof(*dl->header->lists))); __vsp1_dl_list_put(dlm->queued); dlm->queued = dl; @@ -307,9 +503,10 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) if (dlm->pending) { struct vsp1_dl_list *dl = dlm->pending; - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->reg_count * 8)); + (dl->body0.num_entries * + sizeof(*dl->header->lists))); dlm->queued = dl; dlm->pending = NULL; diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 571ed6d8e7c2..de387cd4d745 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -16,6 +16,7 @@ #include struct vsp1_device; +struct vsp1_dl_fragment; struct vsp1_dl_list; struct vsp1_dl_manager; @@ -34,4 +35,11 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl); void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data); void vsp1_dl_list_commit(struct vsp1_dl_list *dl); +struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, + unsigned int num_entries); +void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb); +void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); +int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + #endif /* __VSP1_DL_H__ */ From 0e6b9c565f33cd8b22879947647abd6c076cd73e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 14:17:11 -0300 Subject: [PATCH 081/173] [media] v4l: vsp1: lut: Use display list fragments to fill LUT Synchronize the userspace LUT setup with the pipeline operation by using a display list fragment to store LUT data. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_lut.c | 31 +++++++++++++++++++++----- drivers/media/platform/vsp1/vsp1_lut.h | 6 ++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 33e3b5cc9c9b..aa09e59f0ab8 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -38,10 +38,25 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, * V4L2 Subdevice Core Operations */ -static void lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config) +static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config) { - memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut, - sizeof(config->lut)); + struct vsp1_dl_body *dlb; + unsigned int i; + + dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut)); + if (!dlb) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(config->lut); ++i) + vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, + config->lut[i]); + + mutex_lock(&lut->lock); + swap(lut->lut, dlb); + mutex_unlock(&lut->lock); + + vsp1_dl_fragment_free(dlb); + return 0; } static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) @@ -50,8 +65,7 @@ static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) switch (cmd) { case VIDIOC_VSP1_LUT_CONFIG: - lut_set_table(lut, arg); - return 0; + return lut_set_table(lut, arg); default: return -ENOIOCTLCMD; @@ -161,6 +175,13 @@ static void lut_configure(struct vsp1_entity *entity, struct vsp1_lut *lut = to_lut(&entity->subdev); vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); + + mutex_lock(&lut->lock); + if (lut->lut) { + vsp1_dl_list_add_fragment(dl, lut->lut); + lut->lut = NULL; + } + mutex_unlock(&lut->lock); } static const struct vsp1_entity_operations lut_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index f92ffb867350..cef874f22b6a 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -13,6 +13,8 @@ #ifndef __VSP1_LUT_H__ #define __VSP1_LUT_H__ +#include + #include #include @@ -25,7 +27,9 @@ struct vsp1_device; struct vsp1_lut { struct vsp1_entity entity; - u32 lut[256]; + + struct mutex lock; + struct vsp1_dl_body *lut; }; static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev) From 30276a731a9c14123c95070197a08bafc148f7bc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Mar 2016 11:10:27 -0300 Subject: [PATCH 082/173] [media] v4l: vsp1: Add support for the RPF alpha multiplier on Gen3 The Gen3 RPF includes an alpha multiplier that can both multiply the alpha channel by a fixed global alpha value, and multiply the pixel components to convert the input to premultiplied alpha. As alpha premultiplication is available in the BRU for both Gen2 and Gen3 we handle it there and use the Gen3 alpha multiplier for global alpha multiplication only. This prevents conversion to premultiplied alpha if no BRU is present in the pipeline, that use case will be implemented later if needed. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 1 + drivers/media/platform/vsp1/vsp1_drv.c | 8 ++++ drivers/media/platform/vsp1/vsp1_regs.h | 10 +++++ drivers/media/platform/vsp1/vsp1_rpf.c | 57 +++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index dae987a11a70..46738b6c5f72 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -48,6 +48,7 @@ struct vsp1_uds; struct vsp1_device_info { u32 version; + unsigned int gen; unsigned int features; unsigned int rpf_count; unsigned int uds_count; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 596f26d81494..e2d779fac0eb 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -558,6 +558,7 @@ static const struct dev_pm_ops vsp1_pm_ops = { static const struct vsp1_device_info vsp1_device_infos[] = { { .version = VI6_IP_VERSION_MODEL_VSPS_H2, + .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, .rpf_count = 5, .uds_count = 3, @@ -566,6 +567,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPR_H2, + .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_SRU, .rpf_count = 5, .uds_count = 1, @@ -574,6 +576,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, + .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, .rpf_count = 4, .uds_count = 1, @@ -582,6 +585,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPS_M2, + .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, .rpf_count = 5, .uds_count = 3, @@ -590,6 +594,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, + .gen = 3, .features = VSP1_HAS_LUT | VSP1_HAS_SRU, .rpf_count = 1, .uds_count = 1, @@ -597,6 +602,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, + .gen = 3, .features = VSP1_HAS_BRU, .rpf_count = 5, .wpf_count = 1, @@ -604,6 +610,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, + .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_LUT, .rpf_count = 5, .wpf_count = 1, @@ -611,6 +618,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, + .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_LIF, .rpf_count = 5, .wpf_count = 2, diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 069216f0eb44..927b5fb94c48 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -217,6 +217,16 @@ #define VI6_RPF_SRCM_ADDR_C1 0x0344 #define VI6_RPF_SRCM_ADDR_AI 0x0348 +#define VI6_RPF_MULT_ALPHA 0x036c +#define VI6_RPF_MULT_ALPHA_A_MMD_NONE (0 << 12) +#define VI6_RPF_MULT_ALPHA_A_MMD_RATIO (1 << 12) +#define VI6_RPF_MULT_ALPHA_P_MMD_NONE (0 << 8) +#define VI6_RPF_MULT_ALPHA_P_MMD_RATIO (1 << 8) +#define VI6_RPF_MULT_ALPHA_P_MMD_IMAGE (2 << 8) +#define VI6_RPF_MULT_ALPHA_P_MMD_BOTH (3 << 8) +#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff < 0) +#define VI6_RPF_MULT_ALPHA_RATIO_SHIFT 0 + /* ----------------------------------------------------------------------------- * WPF Control Registers */ diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 5486ff54a2b3..49168db3f529 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -141,9 +141,27 @@ static void rpf_configure(struct vsp1_entity *entity, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); - /* Use the alpha channel (extended to 8 bits) when available or an - * alpha value set through the V4L2_CID_ALPHA_COMPONENT control - * otherwise. Disable color keying. + /* On Gen2 use the alpha channel (extended to 8 bits) when available or + * a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control + * otherwise. + * + * The Gen3 RPF has extended alpha capability and can both multiply the + * alpha channel by a fixed global alpha value, and multiply the pixel + * components to convert the input to premultiplied alpha. + * + * As alpha premultiplication is available in the BRU for both Gen2 and + * Gen3 we handle it there and use the Gen3 alpha multiplier for global + * alpha multiplication only. This however prevents conversion to + * premultiplied alpha if no BRU is present in the pipeline. If that use + * case turns out to be useful we will revisit the implementation (for + * Gen3 only). + * + * We enable alpha multiplication on Gen3 using the fixed alpha value + * set through the V4L2_CID_ALPHA_COMPONENT control when the input + * contains an alpha channel. On Gen2 the global alpha is ignored in + * that case. + * + * In all cases, disable color keying. */ vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED @@ -152,10 +170,43 @@ static void rpf_configure(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + if (entity->vsp1->info->gen == 3) { + u32 mult; + + if (fmtinfo->alpha) { + /* When the input contains an alpha channel enable the + * alpha multiplier. If the input is premultiplied we + * need to multiply both the alpha channel and the pixel + * components by the global alpha value to keep them + * premultiplied. Otherwise multiply the alpha channel + * only. + */ + bool premultiplied = format->flags + & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; + + mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO + | (premultiplied ? + VI6_RPF_MULT_ALPHA_P_MMD_RATIO : + VI6_RPF_MULT_ALPHA_P_MMD_NONE) + | (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT); + } else { + /* When the input doesn't contain an alpha channel the + * global alpha value is applied in the unpacking unit, + * the alpha multiplier isn't needed and must be + * disabled. + */ + mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE + | VI6_RPF_MULT_ALPHA_P_MMD_NONE; + } + + vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult); + } + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha); vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); + } static const struct vsp1_entity_operations rpf_entity_ops = { From f5e04e7ea7bebbed77c6438c7f007c354a40ce22 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 24 Mar 2016 05:15:59 -0300 Subject: [PATCH 083/173] [media] v4l: vsp1: Add Z-order support for DRM pipeline Make the Z-order of planes configurable by assigning RPFs to BRU inputs dynamically based on the Z-order position. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 180 ++++++++++++++---------- drivers/media/platform/vsp1/vsp1_drm.h | 12 +- drivers/media/platform/vsp1/vsp1_pipe.c | 1 + include/media/vsp1.h | 18 ++- 4 files changed, 130 insertions(+), 81 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 7cde2d970dba..d85cb0e258c9 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -93,6 +93,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width, media_entity_pipeline_stop(&pipe->output->entity.subdev.entity); for (i = 0; i < bru->entity.source_pad; ++i) { + vsp1->drm->inputs[i].enabled = false; bru->inputs[i].rpf = NULL; pipe->inputs[i] = NULL; } @@ -217,14 +218,9 @@ void vsp1_du_atomic_begin(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_pipeline *pipe = &vsp1->drm->pipe; - unsigned long flags; - - spin_lock_irqsave(&pipe->irqlock, flags); vsp1->drm->num_inputs = pipe->num_inputs; - spin_unlock_irqrestore(&pipe->irqlock, flags); - /* Prepare the display list. */ pipe->dl = vsp1_dl_list_get(pipe->output->dlm); } @@ -239,10 +235,12 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * @mem: DMA addresses of the memory buffers (one per plane) * @src: the source crop rectangle for the RPF * @dst: the destination compose rectangle for the BRU input + * @zpos: the Z-order position of the input * * Configure the VSP to perform composition of the image referenced by @mem * through RPF @rpf_index, using the @src crop rectangle and the @dst - * composition rectangle. The Z-order is fixed with RPF 0 at the bottom. + * composition rectangle. The Z-order is configurable with higher @zpos values + * displayed on top. * * Image format as stored in memory is expressed as a V4L2 @pixelformat value. * As a special case, setting the pixel format to 0 will disable the RPF. The @@ -260,24 +258,16 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * * This function isn't reentrant, the caller needs to serialize calls. * - * TODO: Implement Z-order control by decoupling the RPF index from the BRU - * input index. - * * Return 0 on success or a negative error code on failure. */ -int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, - u32 pixelformat, unsigned int pitch, - dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst) +int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, + u32 pixelformat, unsigned int pitch, + dma_addr_t mem[2], const struct v4l2_rect *src, + const struct v4l2_rect *dst, unsigned int zpos) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - struct vsp1_pipeline *pipe = &vsp1->drm->pipe; const struct vsp1_format_info *fmtinfo; - struct v4l2_subdev_selection sel; - struct v4l2_subdev_format format; struct vsp1_rwpf *rpf; - unsigned long flags; - int ret; if (rpf_index >= vsp1->info->rpf_count) return -EINVAL; @@ -288,31 +278,20 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__, rpf_index); - spin_lock_irqsave(&pipe->irqlock, flags); - - if (pipe->inputs[rpf_index]) { - /* Remove the RPF from the pipeline if it was previously - * enabled. - */ - vsp1->bru->inputs[rpf_index].rpf = NULL; - pipe->inputs[rpf_index] = NULL; - - pipe->num_inputs--; - } - - spin_unlock_irqrestore(&pipe->irqlock, flags); - + vsp1->drm->inputs[rpf_index].enabled = false; return 0; } dev_dbg(vsp1->dev, - "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad }\n", + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n", __func__, rpf_index, src->left, src->top, src->width, src->height, dst->left, dst->top, dst->width, dst->height, - pixelformat, pitch, &mem[0], &mem[1]); + pixelformat, pitch, &mem[0], &mem[1], zpos); - /* Set the stride at the RPF input. */ + /* Store the format, stride, memory buffer address, crop and compose + * rectangles and Z-order position and for the input. + */ fmtinfo = vsp1_get_format_info(pixelformat); if (!fmtinfo) { dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", @@ -325,15 +304,38 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, rpf->format.plane_fmt[0].bytesperline = pitch; rpf->format.plane_fmt[1].bytesperline = pitch; + rpf->mem.addr[0] = mem[0]; + rpf->mem.addr[1] = mem[1]; + rpf->mem.addr[2] = 0; + + vsp1->drm->inputs[rpf_index].crop = *src; + vsp1->drm->inputs[rpf_index].compose = *dst; + vsp1->drm->inputs[rpf_index].zpos = zpos; + vsp1->drm->inputs[rpf_index].enabled = true; + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext); + +static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, + struct vsp1_rwpf *rpf, unsigned int bru_input) +{ + struct v4l2_subdev_selection sel; + struct v4l2_subdev_format format; + const struct v4l2_rect *crop; + int ret; + /* Configure the format on the RPF sink pad and propagate it up to the * BRU sink pad. */ + crop = &vsp1->drm->inputs[rpf->entity.index].crop; + memset(&format, 0, sizeof(format)); format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = RWPF_PAD_SINK; - format.format.width = src->width + src->left; - format.format.height = src->height + src->top; - format.format.code = fmtinfo->mbus; + format.format.width = crop->width + crop->left; + format.format.height = crop->height + crop->top; + format.format.code = rpf->fmtinfo->mbus; format.format.field = V4L2_FIELD_NONE; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, @@ -350,7 +352,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.pad = RWPF_PAD_SINK; sel.target = V4L2_SEL_TGT_CROP; - sel.r = *src; + sel.r = *crop; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, &sel); @@ -385,7 +387,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, return ret; /* BRU sink, propagate the format from the RPF source. */ - format.pad = rpf->entity.index; + format.pad = bru_input; ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL, &format); @@ -396,9 +398,9 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, __func__, format.format.width, format.format.height, format.format.code, format.pad); - sel.pad = rpf->entity.index; + sel.pad = bru_input; sel.target = V4L2_SEL_TGT_COMPOSE; - sel.r = *dst; + sel.r = vsp1->drm->inputs[rpf->entity.index].compose; ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection, NULL, &sel); @@ -410,32 +412,13 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, sel.pad); - /* Store the BRU input pad number in the RPF. */ - rpf->bru_input = rpf->entity.index; - - /* Cache the memory buffer address but don't apply the values to the - * hardware as the crop offsets haven't been computed yet. - */ - rpf->mem.addr[0] = mem[0]; - rpf->mem.addr[1] = mem[1]; - rpf->mem.addr[2] = 0; - - spin_lock_irqsave(&pipe->irqlock, flags); - - /* If the RPF was previously stopped set the BRU input to the RPF and - * store the RPF in the pipeline inputs array. - */ - if (!pipe->inputs[rpf->entity.index]) { - vsp1->bru->inputs[rpf_index].rpf = rpf; - pipe->inputs[rpf->entity.index] = rpf; - pipe->num_inputs++; - } - - spin_unlock_irqrestore(&pipe->irqlock, flags); - return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); + +static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf) +{ + return vsp1->drm->inputs[rpf->entity.index].zpos; +} /** * vsp1_du_atomic_flush - Commit an atomic update @@ -445,10 +428,60 @@ void vsp1_du_atomic_flush(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_pipeline *pipe = &vsp1->drm->pipe; + struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, }; struct vsp1_entity *entity; unsigned long flags; - bool stop = false; + unsigned int i; + int ret; + /* Count the number of enabled inputs and sort them by Z-order. */ + pipe->num_inputs = 0; + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rpf = vsp1->rpf[i]; + unsigned int j; + + if (!vsp1->drm->inputs[i].enabled) { + pipe->inputs[i] = NULL; + continue; + } + + pipe->inputs[i] = rpf; + + /* Insert the RPF in the sorted RPFs array. */ + for (j = pipe->num_inputs++; j > 0; --j) { + if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf)) + break; + inputs[j] = inputs[j-1]; + } + + inputs[j] = rpf; + } + + /* Setup the RPF input pipeline for every enabled input. */ + for (i = 0; i < vsp1->info->num_bru_inputs; ++i) { + struct vsp1_rwpf *rpf = inputs[i]; + + if (!rpf) { + vsp1->bru->inputs[i].rpf = NULL; + continue; + } + + vsp1->bru->inputs[i].rpf = rpf; + rpf->bru_input = i; + rpf->entity.sink_pad = i; + + dev_dbg(vsp1->dev, "%s: connecting RPF.%u to BRU:%u\n", + __func__, rpf->entity.index, i); + + ret = vsp1_du_setup_rpf_pipe(vsp1, rpf, i); + if (ret < 0) + dev_err(vsp1->dev, + "%s: failed to setup RPF.%u\n", + __func__, rpf->entity.index); + } + + /* Configure all entities in the pipeline. */ list_for_each_entry(entity, &pipe->entities, list_pipe) { /* Disconnect unused RPFs from the pipeline. */ if (entity->type == VSP1_ENTITY_RPF) { @@ -466,6 +499,9 @@ void vsp1_du_atomic_flush(struct device *dev) if (entity->ops->configure) entity->ops->configure(entity, pipe, pipe->dl); + /* The memory buffer address must be applied after configuring + * the RPF to make sure the crop offset are computed. + */ if (entity->type == VSP1_ENTITY_RPF) vsp1_rwpf_set_memory(to_rwpf(&entity->subdev), pipe->dl); @@ -475,19 +511,13 @@ void vsp1_du_atomic_flush(struct device *dev) pipe->dl = NULL; /* Start or stop the pipeline if needed. */ - spin_lock_irqsave(&pipe->irqlock, flags); - if (!vsp1->drm->num_inputs && pipe->num_inputs) { vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE); + spin_lock_irqsave(&pipe->irqlock, flags); vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); } else if (vsp1->drm->num_inputs && !pipe->num_inputs) { - stop = true; - } - - spin_unlock_irqrestore(&pipe->irqlock, flags); - - if (stop) { vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0); vsp1_pipeline_stop(pipe); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index e9242f2c870e..9e28ab9254ba 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -13,18 +13,26 @@ #ifndef __VSP1_DRM_H__ #define __VSP1_DRM_H__ +#include + #include "vsp1_pipe.h" /** * vsp1_drm - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display * @num_inputs: number of active pipeline inputs at the beginning of an update - * @update: the pipeline configuration has been updated + * @planes: source crop rectangle, destination compose rectangle and z-order + * position for every input */ struct vsp1_drm { struct vsp1_pipeline pipe; unsigned int num_inputs; - bool update; + struct { + bool enabled; + struct v4l2_rect crop; + struct v4l2_rect compose; + unsigned int zpos; + } inputs[VSP1_MAX_RPF]; }; int vsp1_drm_init(struct vsp1_device *vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 4913b933562c..15e028321fa1 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -200,6 +200,7 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +/* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d01f7cb8f691..e54a493bd3ff 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -24,10 +24,20 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width, unsigned int height); void vsp1_du_atomic_begin(struct device *dev); -int vsp1_du_atomic_update(struct device *dev, unsigned int rpf, u32 pixelformat, - unsigned int pitch, dma_addr_t mem[2], - const struct v4l2_rect *src, - const struct v4l2_rect *dst); +int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf, + u32 pixelformat, unsigned int pitch, + dma_addr_t mem[2], const struct v4l2_rect *src, + const struct v4l2_rect *dst, unsigned int zpos); void vsp1_du_atomic_flush(struct device *dev); +static inline int vsp1_du_atomic_update(struct device *dev, + unsigned int rpf_index, u32 pixelformat, + unsigned int pitch, dma_addr_t mem[2], + const struct v4l2_rect *src, + const struct v4l2_rect *dst) +{ + return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch, + mem, src, dst, 0); +} + #endif /* __MEDIA_VSP1_H__ */ From 04738e7f336376f28adb6c0cad2a5788dcbc8e1d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 24 Mar 2016 05:15:59 -0300 Subject: [PATCH 084/173] [media] v4l: vsp1: Add global alpha support for DRM pipeline Make the global alpha multiplier of DRM planes configurable. All the necessary infrastructure is there, we just need to store the alpha value passed through the DRM API. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 5 ++++- include/media/vsp1.h | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index d85cb0e258c9..fc4bbc401e67 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -235,6 +235,7 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * @mem: DMA addresses of the memory buffers (one per plane) * @src: the source crop rectangle for the RPF * @dst: the destination compose rectangle for the BRU input + * @alpha: global alpha value for the input * @zpos: the Z-order position of the input * * Configure the VSP to perform composition of the image referenced by @mem @@ -263,7 +264,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, u32 pixelformat, unsigned int pitch, dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst, unsigned int zpos) + const struct v4l2_rect *dst, unsigned int alpha, + unsigned int zpos) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); const struct vsp1_format_info *fmtinfo; @@ -303,6 +305,7 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, rpf->format.num_planes = fmtinfo->planes; rpf->format.plane_fmt[0].bytesperline = pitch; rpf->format.plane_fmt[1].bytesperline = pitch; + rpf->alpha = alpha; rpf->mem.addr[0] = mem[0]; rpf->mem.addr[1] = mem[1]; diff --git a/include/media/vsp1.h b/include/media/vsp1.h index e54a493bd3ff..3e654a0455bd 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -27,7 +27,8 @@ void vsp1_du_atomic_begin(struct device *dev); int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf, u32 pixelformat, unsigned int pitch, dma_addr_t mem[2], const struct v4l2_rect *src, - const struct v4l2_rect *dst, unsigned int zpos); + const struct v4l2_rect *dst, unsigned int alpha, + unsigned int zpos); void vsp1_du_atomic_flush(struct device *dev); static inline int vsp1_du_atomic_update(struct device *dev, @@ -37,7 +38,7 @@ static inline int vsp1_du_atomic_update(struct device *dev, const struct v4l2_rect *dst) { return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch, - mem, src, dst, 0); + mem, src, dst, 255, 0); } #endif /* __MEDIA_VSP1_H__ */ From 2d2f99451d4b32f69abd6ff59e229974e2fbe386 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 25 Mar 2016 05:50:02 -0300 Subject: [PATCH 085/173] [media] v4l: vsp1: Fix V4L2_PIX_FMT_XRGB444 format definition The format is erroneously defined with an alpha channel. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 15e028321fa1..4f3b4a1d028a 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -43,7 +43,7 @@ static const struct vsp1_format_info vsp1_video_formats[] = { { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, true }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, From 0d268dcc693a56efb009316d83e0d732cafb9f9c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 25 Mar 2016 06:51:06 -0300 Subject: [PATCH 086/173] [media] v4l: vsp1: Update WPF and LIF maximum sizes for Gen3 The maximum image size supported by the WPF is 2048x2048 on Gen2 and 8190x8190 on Gen3. Update the code accordingly, and fix the maximum LIF size for both Gen2 and Gen3. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_lif.c | 2 +- drivers/media/platform/vsp1/vsp1_wpf.c | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 579e142b4e94..0217393f22df 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -21,7 +21,7 @@ #include "vsp1_lif.h" #define LIF_MIN_SIZE 2U -#define LIF_MAX_SIZE 2048U +#define LIF_MAX_SIZE 8190U /* ----------------------------------------------------------------------------- * Device Access diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index ce1d0b4094db..6c91eaa35e75 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -21,8 +21,10 @@ #include "vsp1_rwpf.h" #include "vsp1_video.h" -#define WPF_MAX_WIDTH 2048 -#define WPF_MAX_HEIGHT 2048 +#define WPF_GEN2_MAX_WIDTH 2048U +#define WPF_GEN2_MAX_HEIGHT 2048U +#define WPF_GEN3_MAX_WIDTH 8190U +#define WPF_GEN3_MAX_HEIGHT 8190U /* ----------------------------------------------------------------------------- * Device Access @@ -201,8 +203,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (wpf == NULL) return ERR_PTR(-ENOMEM); - wpf->max_width = WPF_MAX_WIDTH; - wpf->max_height = WPF_MAX_HEIGHT; + if (vsp1->info->gen == 2) { + wpf->max_width = WPF_GEN2_MAX_WIDTH; + wpf->max_height = WPF_GEN2_MAX_HEIGHT; + } else { + wpf->max_width = WPF_GEN3_MAX_WIDTH; + wpf->max_height = WPF_GEN3_MAX_HEIGHT; + } wpf->entity.ops = &wpf_entity_ops; wpf->entity.type = VSP1_ENTITY_WPF; From 71c5daba0546c456c5589bcf52eb2641abf42a85 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 24 Mar 2016 22:46:45 -0300 Subject: [PATCH 087/173] [media] media: platform: rcar_jpu, vsp1: Use ARCH_RENESAS Make use of ARCH_RENESAS in place of ARCH_SHMOBILE. This is part of an ongoing process to migrate from ARCH_SHMOBILE to ARCH_RENESAS the motivation for which being that RENESAS seems to be a more appropriate name than SHMOBILE for the majority of Renesas ARM based SoCs. Acked-by: Geert Uytterhoeven Acked-by: Hans Verkuil Signed-off-by: Simon Horman Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 201f5c296a95..84e041c0a70e 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -238,7 +238,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_JPU tristate "Renesas JPEG Processing Unit" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -250,7 +250,7 @@ config VIDEO_RENESAS_JPU config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on (ARCH_SHMOBILE && OF) || COMPILE_TEST + depends on (ARCH_RENESAS && OF) || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. From ecb7b0183a89613c154d1bea48b494907efbf8f9 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 16 Mar 2016 09:14:13 -0300 Subject: [PATCH 088/173] [media] m88ds3103: fix undefined division s32tmp in the below code may be negative, and dev->mclk_khz is an unsigned type. s32tmp = 0x10000 * (tuner_frequency - c->frequency); s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz); This is undefined, as DIV_ROUND_CLOSEST is undefined for negative dividends when the divisor is of unsigned type. So, change mclk_khz to be signed (s32). Signed-off-by: Peter Rosin Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88ds3103_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index eee8c22c51ec..651e005146b2 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -46,7 +46,7 @@ struct m88ds3103_dev { /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ - u32 mclk_khz; + s32 mclk_khz; u64 post_bit_error; u64 post_bit_count; }; From e2c91d4d78ee3a69ad634bc7ef90688704baab9d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 6 Apr 2016 10:55:24 -0300 Subject: [PATCH 089/173] [media] media-device: get rid of the spinlock Right now, the lock schema for media_device struct is messy, since sometimes, it is protected via a spin lock, while, for media graph traversal, it is protected by a mutex. Solve this conflict by always using a mutex. As a side effect, this prevents a bug when the media notifiers is called at atomic context, while running the notifier callback: BUG: sleeping function called from invalid context at mm/slub.c:1289 in_atomic(): 1, irqs_disabled(): 0, pid: 3479, name: modprobe 4 locks held by modprobe/3479: #0: (&dev->mutex){......}, at: [] __driver_attach+0xa3/0x160 #1: (&dev->mutex){......}, at: [] __driver_attach+0xb1/0x160 #2: (register_mutex#5){+.+.+.}, at: [] usb_audio_probe+0x257/0x1c90 [snd_usb_audio] #3: (&(&mdev->lock)->rlock){+.+.+.}, at: [] media_device_register_entity+0x1cb/0x700 [media] CPU: 2 PID: 3479 Comm: modprobe Not tainted 4.5.0-rc3+ #49 Hardware name: /NUC5i7RYB, BIOS RYBDWi35.86A.0350.2015.0812.1722 08/12/2015 0000000000000000 ffff8803b3f6f288 ffffffff81933901 ffff8803c4bae000 ffff8803c4bae5c8 ffff8803b3f6f2b0 ffffffff811c6af5 ffff8803c4bae000 ffffffff8285d7f6 0000000000000509 ffff8803b3f6f2f0 ffffffff811c6ce5 Call Trace: [] dump_stack+0x85/0xc4 [] ___might_sleep+0x245/0x3a0 [] __might_sleep+0x95/0x1a0 [] kmem_cache_alloc_trace+0x20e/0x300 [] ? media_add_link+0x4d/0x140 [media] [] media_add_link+0x4d/0x140 [media] [] media_create_pad_link+0xa1/0x600 [media] [] au0828_media_graph_notify+0x173/0x360 [au0828] [] ? media_gobj_create+0x1ba/0x480 [media] [] media_device_register_entity+0x3ab/0x700 [media] Reviewed-by: Javier Martinez Canillas Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 39 ++++++++++++------------------------ drivers/media/media-entity.c | 16 +++++++-------- include/media/media-device.h | 6 +----- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 6e43c95629ea..898a3cf814ba 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -90,18 +90,13 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) id &= ~MEDIA_ENT_ID_FLAG_NEXT; - spin_lock(&mdev->lock); - media_device_for_each_entity(entity, mdev) { if (((media_entity_id(entity) == id) && !next) || ((media_entity_id(entity) > id) && next)) { - spin_unlock(&mdev->lock); return entity; } } - spin_unlock(&mdev->lock); - return NULL; } @@ -431,6 +426,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, struct media_device *dev = to_media_device(devnode); long ret; + mutex_lock(&dev->graph_mutex); switch (cmd) { case MEDIA_IOC_DEVICE_INFO: ret = media_device_get_info(dev, @@ -443,29 +439,24 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, break; case MEDIA_IOC_ENUM_LINKS: - mutex_lock(&dev->graph_mutex); ret = media_device_enum_links(dev, (struct media_links_enum __user *)arg); - mutex_unlock(&dev->graph_mutex); break; case MEDIA_IOC_SETUP_LINK: - mutex_lock(&dev->graph_mutex); ret = media_device_setup_link(dev, (struct media_link_desc __user *)arg); - mutex_unlock(&dev->graph_mutex); break; case MEDIA_IOC_G_TOPOLOGY: - mutex_lock(&dev->graph_mutex); ret = media_device_get_topology(dev, (struct media_v2_topology __user *)arg); - mutex_unlock(&dev->graph_mutex); break; default: ret = -ENOIOCTLCMD; } + mutex_unlock(&dev->graph_mutex); return ret; } @@ -590,12 +581,12 @@ int __must_check media_device_register_entity(struct media_device *mdev, if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL)) return -ENOMEM; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); ret = ida_get_new_above(&mdev->entity_internal_idx, 1, &entity->internal_idx); if (ret < 0) { - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); return ret; } @@ -615,9 +606,6 @@ int __must_check media_device_register_entity(struct media_device *mdev, (notify)->notify(entity, notify->notify_data); } - spin_unlock(&mdev->lock); - - mutex_lock(&mdev->graph_mutex); if (mdev->entity_internal_idx_max >= mdev->pm_count_walk.ent_enum.idx_max) { struct media_entity_graph new = { .top = 0 }; @@ -680,9 +668,9 @@ void media_device_unregister_entity(struct media_entity *entity) if (mdev == NULL) return; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); __media_device_unregister_entity(entity); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_device_unregister_entity); @@ -703,7 +691,6 @@ void media_device_init(struct media_device *mdev) INIT_LIST_HEAD(&mdev->pads); INIT_LIST_HEAD(&mdev->links); INIT_LIST_HEAD(&mdev->entity_notify); - spin_lock_init(&mdev->lock); mutex_init(&mdev->graph_mutex); ida_init(&mdev->entity_internal_idx); @@ -752,9 +739,9 @@ EXPORT_SYMBOL_GPL(__media_device_register); int __must_check media_device_register_entity_notify(struct media_device *mdev, struct media_entity_notify *nptr) { - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); list_add_tail(&nptr->list, &mdev->entity_notify); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); return 0; } EXPORT_SYMBOL_GPL(media_device_register_entity_notify); @@ -771,9 +758,9 @@ static void __media_device_unregister_entity_notify(struct media_device *mdev, void media_device_unregister_entity_notify(struct media_device *mdev, struct media_entity_notify *nptr) { - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); __media_device_unregister_entity_notify(mdev, nptr); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); @@ -787,11 +774,11 @@ void media_device_unregister(struct media_device *mdev) if (mdev == NULL) return; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); /* Check if mdev was ever registered at all */ if (!media_devnode_is_registered(&mdev->devnode)) { - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); return; } @@ -811,7 +798,7 @@ void media_device_unregister(struct media_device *mdev) kfree(intf); } - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); device_remove_file(&mdev->devnode.dev, &dev_attr_model); media_devnode_unregister(&mdev->devnode); diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index e95070b3a3d4..c53c1d5589a0 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -219,7 +219,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, entity->pads = pads; if (mdev) - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); for (i = 0; i < num_pads; i++) { pads[i].entity = entity; @@ -230,7 +230,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, } if (mdev) - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); return 0; } @@ -747,9 +747,9 @@ void media_entity_remove_links(struct media_entity *entity) if (mdev == NULL) return; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); __media_entity_remove_links(entity); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_entity_remove_links); @@ -951,9 +951,9 @@ void media_remove_intf_link(struct media_link *link) if (mdev == NULL) return; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); __media_remove_intf_link(link); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_remove_intf_link); @@ -975,8 +975,8 @@ void media_remove_intf_links(struct media_interface *intf) if (mdev == NULL) return; - spin_lock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); __media_remove_intf_links(intf); - spin_unlock(&mdev->lock); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_remove_intf_links); diff --git a/include/media/media-device.h b/include/media/media-device.h index 07809f698464..b21ef244ad3e 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -304,8 +303,7 @@ struct media_entity_notify { * @pads: List of registered pads * @links: List of registered links * @entity_notify: List of registered entity_notify callbacks - * @lock: Entities list lock - * @graph_mutex: Entities graph operation lock + * @graph_mutex: Protects access to struct media_device data * @pm_count_walk: Graph walk for power state walk. Access serialised using * graph_mutex. * @@ -371,8 +369,6 @@ struct media_device { /* notify callback list invoked when a new entity is registered */ struct list_head entity_notify; - /* Protects the graph objects creation/removal */ - spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; struct media_entity_graph pm_count_walk; From 5ed470feb9a121582dbd455c72b133dc1a856a0a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 6 Apr 2016 10:55:25 -0300 Subject: [PATCH 090/173] [media] media: Improve documentation for link_setup/link_modify Those callbacks are called with the media_device.graph_mutex held. Add a note about that, as the code called by those notifiers should not be touching in the mutex. Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/media-device.h | 3 ++- include/media/media-entity.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/media/media-device.h b/include/media/media-device.h index b21ef244ad3e..a9b33c47310d 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -311,7 +311,8 @@ struct media_entity_notify { * @enable_source: Enable Source Handler function pointer * @disable_source: Disable Source Handler function pointer * - * @link_notify: Link state change notification callback + * @link_notify: Link state change notification callback. This callback is + * called with the graph_mutex held. * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The diff --git a/include/media/media-entity.h b/include/media/media-entity.h index e0295eefd702..cbb266f7f2b5 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -179,6 +179,9 @@ struct media_pad { * @link_validate: Return whether a link is valid from the entity point of * view. The media_entity_pipeline_start() function * validates all links by calling this operation. Optional. + * + * Note: Those these callbacks are called with struct media_device.@graph_mutex + * mutex held. */ struct media_entity_operations { int (*link_setup)(struct media_entity *entity, From 0ff59f3190ba7b380ca4387dab5eb69d2b8f4d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Ha=C5=82asa?= Date: Fri, 11 Mar 2016 09:23:16 -0300 Subject: [PATCH 091/173] [media] TW686x frame grabber driver A driver for Intersil/Techwell TW686x-based PCIe frame grabbers. [hans.verkuil@cisco.com: renamed staging tw686x to tw686x-kh to prevent naming conflicts] [hans.verkuil@cisco.com: don't build tw686x-kh if tw686x is already selected to prevent conflicts] [mchehab@osg.samsung.com: use "unsigned int" instead of just "unsigned" and add some whitespaces to make checkpatch happier] Signed-off-by: Krzysztof Halasa Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/tw686x-kh/Kconfig | 17 + drivers/staging/media/tw686x-kh/Makefile | 3 + drivers/staging/media/tw686x-kh/TODO | 3 + .../staging/media/tw686x-kh/tw686x-kh-core.c | 140 +++ .../staging/media/tw686x-kh/tw686x-kh-regs.h | 103 +++ .../staging/media/tw686x-kh/tw686x-kh-video.c | 820 ++++++++++++++++++ drivers/staging/media/tw686x-kh/tw686x-kh.h | 118 +++ 9 files changed, 1207 insertions(+) create mode 100644 drivers/staging/media/tw686x-kh/Kconfig create mode 100644 drivers/staging/media/tw686x-kh/Makefile create mode 100644 drivers/staging/media/tw686x-kh/TODO create mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-core.c create mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-regs.h create mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-video.c create mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh.h diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 0078b6a92f0b..de7e9f52e7eb 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -37,6 +37,8 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/timb/Kconfig" +source "drivers/staging/media/tw686x-kh/Kconfig" + # Keep LIRC at the end, as it has sub-menus source "drivers/staging/media/lirc/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 91495882a36c..60a35b3a47e7 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_DVB_MN88472) += mn88472/ obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/ +obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh/ diff --git a/drivers/staging/media/tw686x-kh/Kconfig b/drivers/staging/media/tw686x-kh/Kconfig new file mode 100644 index 000000000000..6264d30edf5a --- /dev/null +++ b/drivers/staging/media/tw686x-kh/Kconfig @@ -0,0 +1,17 @@ +config VIDEO_TW686X_KH + tristate "Intersil/Techwell TW686x Video For Linux" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + depends on !(VIDEO_TW686X=y || VIDEO_TW686X=m) || COMPILE_TEST + select VIDEOBUF2_DMA_SG + help + Support for Intersil/Techwell TW686x-based frame grabber cards. + + Currently supported chips: + - TW6864 (4 video channels), + - TW6865 (4 video channels, not tested, second generation chip), + - TW6868 (8 video channels but only 4 first channels using + built-in video decoder are supported, not tested), + - TW6869 (8 video channels, second generation chip). + + To compile this driver as a module, choose M here: the module + will be named tw686x-kh. diff --git a/drivers/staging/media/tw686x-kh/Makefile b/drivers/staging/media/tw686x-kh/Makefile new file mode 100644 index 000000000000..2a36a38cf30e --- /dev/null +++ b/drivers/staging/media/tw686x-kh/Makefile @@ -0,0 +1,3 @@ +tw686x-kh-objs := tw686x-kh-core.o tw686x-kh-video.o + +obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh.o diff --git a/drivers/staging/media/tw686x-kh/TODO b/drivers/staging/media/tw686x-kh/TODO new file mode 100644 index 000000000000..f226e808390e --- /dev/null +++ b/drivers/staging/media/tw686x-kh/TODO @@ -0,0 +1,3 @@ +TODO: implement V4L2_FIELD_INTERLACED* mode(s). + +Please Cc: patches to Krzysztof Halasa . diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c new file mode 100644 index 000000000000..c3c5c2602541 --- /dev/null +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * + * Written by Krzysztof Ha?asa. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include "tw686x-kh.h" +#include "tw686x-kh-regs.h" + +static irqreturn_t tw686x_irq(int irq, void *dev_id) +{ + struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; + u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */ + unsigned long flags; + unsigned int handled = 0; + + if (int_status) { + spin_lock_irqsave(&dev->irq_lock, flags); + dev->dma_requests |= int_status; + spin_unlock_irqrestore(&dev->irq_lock, flags); + + if (int_status & 0xFF0000FF) + handled = tw686x_video_irq(dev); + } + + return IRQ_RETVAL(handled); +} + +static int tw686x_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw686x_dev *dev; + int err; + + dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev) + + (pci_id->driver_data & TYPE_MAX_CHANNELS) * + sizeof(dev->video_channels[0]), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + sprintf(dev->name, "TW%04X", pci_dev->device); + dev->type = pci_id->driver_data; + + pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, + pci_name(pci_dev), pci_dev->irq, + (unsigned long)pci_resource_start(pci_dev, 0)); + + dev->pci_dev = pci_dev; + if (pcim_enable_device(pci_dev)) + return -EIO; + + pci_set_master(pci_dev); + + if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { + pr_err("%s: 32-bit PCI DMA not supported\n", dev->name); + return -EIO; + } + + err = pci_request_regions(pci_dev, dev->name); + if (err < 0) { + pr_err("%s: Unable to get MMIO region\n", dev->name); + return err; + } + + dev->mmio = pci_ioremap_bar(pci_dev, 0); + if (!dev->mmio) { + pr_err("%s: Unable to remap MMIO region\n", dev->name); + return -EIO; + } + + reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */ + mdelay(1); + + reg_write(dev, SRST[0], 0x3F); + if (max_channels(dev) > 4) + reg_write(dev, SRST[1], 0x3F); + reg_write(dev, DMA_CMD, 0); + reg_write(dev, DMA_CHANNEL_ENABLE, 0); + reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0); + reg_write(dev, DMA_TIMER_INTERVAL, 0x38000); + reg_write(dev, DMA_CONFIG, 0xFFFFFF04); + + spin_lock_init(&dev->irq_lock); + + err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq, + IRQF_SHARED, dev->name, dev); + if (err < 0) { + pr_err("%s: Unable to get IRQ\n", dev->name); + return err; + } + + err = tw686x_video_init(dev); + if (err) + return err; + + pci_set_drvdata(pci_dev, dev); + return 0; +} + +static void tw686x_remove(struct pci_dev *pci_dev) +{ + struct tw686x_dev *dev = pci_get_drvdata(pci_dev); + + tw686x_video_free(dev); +} + +/* driver_data is number of A/V channels */ +static const struct pci_device_id tw686x_pci_tbl[] = { + {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4}, + /* not tested */ + {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN}, + /* TW6868 supports 8 A/V channels with an external TW2865 chip - + not supported by the driver */ + {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */ + {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN}, + {} +}; + +static struct pci_driver tw686x_pci_driver = { + .name = "tw686x-kh", + .id_table = tw686x_pci_tbl, + .probe = tw686x_probe, + .remove = tw686x_remove, +}; + +MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); +MODULE_AUTHOR("Krzysztof Halasa"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); +module_pci_driver(tw686x_pci_driver); diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h b/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h new file mode 100644 index 000000000000..53e1889babd0 --- /dev/null +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h @@ -0,0 +1,103 @@ +/* DMA controller registers */ +#define REG8_1(a0) ((const u16[8]) {a0, a0 + 1, a0 + 2, a0 + 3, \ + a0 + 4, a0 + 5, a0 + 6, a0 + 7}) +#define REG8_2(a0) ((const u16[8]) {a0, a0 + 2, a0 + 4, a0 + 6, \ + a0 + 8, a0 + 0xA, a0 + 0xC, a0 + 0xE}) +#define REG8_8(a0) ((const u16[8]) {a0, a0 + 8, a0 + 0x10, a0 + 0x18, \ + a0 + 0x20, a0 + 0x28, a0 + 0x30, a0 + 0x38}) +#define INT_STATUS 0x00 +#define PB_STATUS 0x01 +#define DMA_CMD 0x02 +#define VIDEO_FIFO_STATUS 0x03 +#define VIDEO_CHANNEL_ID 0x04 +#define VIDEO_PARSER_STATUS 0x05 +#define SYS_SOFT_RST 0x06 +#define DMA_PAGE_TABLE0_ADDR ((const u16[8]) {0x08, 0xD0, 0xD2, 0xD4, \ + 0xD6, 0xD8, 0xDA, 0xDC}) +#define DMA_PAGE_TABLE1_ADDR ((const u16[8]) {0x09, 0xD1, 0xD3, 0xD5, \ + 0xD7, 0xD9, 0xDB, 0xDD}) +#define DMA_CHANNEL_ENABLE 0x0A +#define DMA_CONFIG 0x0B +#define DMA_TIMER_INTERVAL 0x0C +#define DMA_CHANNEL_TIMEOUT 0x0D +#define VDMA_CHANNEL_CONFIG REG8_1(0x10) +#define ADMA_P_ADDR REG8_2(0x18) +#define ADMA_B_ADDR REG8_2(0x19) +#define DMA10_P_ADDR 0x28 /* ??? */ +#define DMA10_B_ADDR 0x29 +#define VIDEO_CONTROL1 0x2A +#define VIDEO_CONTROL2 0x2B +#define AUDIO_CONTROL1 0x2C +#define AUDIO_CONTROL2 0x2D +#define PHASE_REF 0x2E +#define GPIO_REG 0x2F +#define INTL_HBAR_CTRL REG8_1(0x30) +#define AUDIO_CONTROL3 0x38 +#define VIDEO_FIELD_CTRL REG8_1(0x39) +#define HSCALER_CTRL REG8_1(0x42) +#define VIDEO_SIZE REG8_1(0x4A) +#define VIDEO_SIZE_F2 REG8_1(0x52) +#define MD_CONF REG8_1(0x60) +#define MD_INIT REG8_1(0x68) +#define MD_MAP0 REG8_1(0x70) +#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */ +#define VDMA_WHP REG8_8(0x81) +#define VDMA_B_ADDR REG8_8(0x82) +#define VDMA_F2_P_ADDR REG8_8(0x84) +#define VDMA_F2_WHP REG8_8(0x85) +#define VDMA_F2_B_ADDR REG8_8(0x86) +#define EP_REG_ADDR 0xFE +#define EP_REG_DATA 0xFF + +/* Video decoder registers */ +#define VDREG8(a0) ((const u16[8]) { \ + a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \ + a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130}) +#define VIDSTAT VDREG8(0x100) +#define BRIGHT VDREG8(0x101) +#define CONTRAST VDREG8(0x102) +#define SHARPNESS VDREG8(0x103) +#define SAT_U VDREG8(0x104) +#define SAT_V VDREG8(0x105) +#define HUE VDREG8(0x106) +#define CROP_HI VDREG8(0x107) +#define VDELAY_LO VDREG8(0x108) +#define VACTIVE_LO VDREG8(0x109) +#define HDELAY_LO VDREG8(0x10A) +#define HACTIVE_LO VDREG8(0x10B) +#define MVSN VDREG8(0x10C) +#define STATUS2 VDREG8(0x10C) +#define SDT VDREG8(0x10E) +#define SDT_EN VDREG8(0x10F) + +#define VSCALE_LO VDREG8(0x144) +#define SCALE_HI VDREG8(0x145) +#define HSCALE_LO VDREG8(0x146) +#define F2CROP_HI VDREG8(0x147) +#define F2VDELAY_LO VDREG8(0x148) +#define F2VACTIVE_LO VDREG8(0x149) +#define F2HDELAY_LO VDREG8(0x14A) +#define F2HACTIVE_LO VDREG8(0x14B) +#define F2VSCALE_LO VDREG8(0x14C) +#define F2SCALE_HI VDREG8(0x14D) +#define F2HSCALE_LO VDREG8(0x14E) +#define F2CNT VDREG8(0x14F) + +#define VDREG2(a0) ((const u16[2]) {a0, a0 + 0x100}) +#define SRST VDREG2(0x180) +#define ACNTL VDREG2(0x181) +#define ACNTL2 VDREG2(0x182) +#define CNTRL1 VDREG2(0x183) +#define CKHY VDREG2(0x184) +#define SHCOR VDREG2(0x185) +#define CORING VDREG2(0x186) +#define CLMPG VDREG2(0x187) +#define IAGC VDREG2(0x188) +#define VCTRL1 VDREG2(0x18F) +#define MISC1 VDREG2(0x194) +#define LOOP VDREG2(0x195) +#define MISC2 VDREG2(0x196) + +#define CLMD VDREG2(0x197) +#define AIGAIN ((const u16[8]) {0x1D0, 0x1D1, 0x1D2, 0x1D3, \ + 0x2D0, 0x2D1, 0x2D2, 0x2D3}) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c new file mode 100644 index 000000000000..2fbc3cbd9eb0 --- /dev/null +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * + * Written by Krzysztof Ha?asa. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "tw686x-kh.h" +#include "tw686x-kh-regs.h" + +#define MAX_SG_ENTRY_SIZE (/* 8192 - 128 */ 4096) +#define MAX_SG_DESC_COUNT 256 /* PAL 704x576 needs up to 198 4-KB pages */ + +static const struct tw686x_format formats[] = { + { + .name = "4:2:2 packed, UYVY", /* aka Y422 */ + .fourcc = V4L2_PIX_FMT_UYVY, + .mode = 0, + .depth = 16, + }, { +#if 0 + .name = "4:2:0 packed, YUV", + .mode = 1, /* non-standard */ + .depth = 12, + }, { + .name = "4:1:1 packed, YUV", + .mode = 2, /* non-standard */ + .depth = 12, + }, { +#endif + .name = "4:1:1 packed, YUV", + .fourcc = V4L2_PIX_FMT_Y41P, + .mode = 3, + .depth = 12, + }, { + .name = "15 bpp RGB", + .fourcc = V4L2_PIX_FMT_RGB555, + .mode = 4, + .depth = 16, + }, { + .name = "16 bpp RGB", + .fourcc = V4L2_PIX_FMT_RGB565, + .mode = 5, + .depth = 16, + }, { + .name = "4:2:2 packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .mode = 6, + .depth = 16, + } + /* mode 7 is "reserved" */ +}; + +static const v4l2_std_id video_standards[7] = { + V4L2_STD_NTSC, + V4L2_STD_PAL, + V4L2_STD_SECAM, + V4L2_STD_NTSC_443, + V4L2_STD_PAL_M, + V4L2_STD_PAL_N, + V4L2_STD_PAL_60, +}; + +static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) +{ + unsigned int cnt; + + for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) + if (formats[cnt].fourcc == fourcc) + return &formats[cnt]; + return NULL; +} + +static void tw686x_get_format(struct tw686x_video_channel *vc, + struct v4l2_format *f) +{ + const struct tw686x_format *format; + unsigned int width, height, height_div = 1; + + format = format_by_fourcc(f->fmt.pix.pixelformat); + if (!format) { + format = &formats[0]; + f->fmt.pix.pixelformat = format->fourcc; + } + + width = 704; + if (f->fmt.pix.width < width * 3 / 4 /* halfway */) + width /= 2; + + height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480; + if (f->fmt.pix.height < height * 3 / 4 /* halfway */) + height_div = 2; + + switch (f->fmt.pix.field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + height_div = 2; + break; + case V4L2_FIELD_SEQ_BT: + if (height_div > 1) + f->fmt.pix.field = V4L2_FIELD_BOTTOM; + break; + default: + if (height_div > 1) + f->fmt.pix.field = V4L2_FIELD_TOP; + else + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + } + height /= height_div; + + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; +} + +/* video queue operations */ + +static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + void *alloc_ctxs[]) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + unsigned int size = vc->width * vc->height * vc->format->depth / 8; + + alloc_ctxs[0] = vc->alloc_ctx; + if (*nbuffers < 2) + *nbuffers = 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + sizes[0] = size; + *nplanes = 1; /* packed formats only */ + return 0; +} + +static void tw686x_buf_queue(struct vb2_buffer *vb) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct tw686x_vb2_buf *buf; + + buf = container_of(vbuf, struct tw686x_vb2_buf, vb); + + spin_lock(&vc->qlock); + list_add_tail(&buf->list, &vc->vidq_queued); + spin_unlock(&vc->qlock); +} + +static void setup_descs(struct tw686x_video_channel *vc, unsigned int n) +{ +loop: + while (!list_empty(&vc->vidq_queued)) { + struct vdma_desc *descs = vc->sg_descs[n]; + struct tw686x_vb2_buf *buf; + struct sg_table *vbuf; + struct scatterlist *sg; + unsigned int buf_len, count = 0; + int i; + + buf = list_first_entry(&vc->vidq_queued, struct tw686x_vb2_buf, + list); + list_del(&buf->list); + + buf_len = vc->width * vc->height * vc->format->depth / 8; + if (vb2_plane_size(&buf->vb.vb2_buf, 0) < buf_len) { + pr_err("Video buffer size too small\n"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + goto loop; /* try another */ + } + + vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); + for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { + dma_addr_t phys = sg_dma_address(sg); + unsigned int len = sg_dma_len(sg); + + while (len && buf_len) { + unsigned int entry_len = min_t(unsigned int, len, + MAX_SG_ENTRY_SIZE); + entry_len = min(entry_len, buf_len); + if (count == MAX_SG_DESC_COUNT) { + pr_err("Video buffer size too fragmented\n"); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + goto loop; + } + descs[count].phys = cpu_to_le32(phys); + descs[count++].flags_length = + cpu_to_le32(0x40000000 /* available */ | + entry_len); + phys += entry_len; + len -= entry_len; + buf_len -= entry_len; + } + if (!buf_len) + break; + } + + /* clear the remaining entries */ + while (count < MAX_SG_DESC_COUNT) { + descs[count].phys = 0; + descs[count++].flags_length = 0; /* unavailable */ + } + + buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; + vc->curr_bufs[n] = buf; + return; + } + vc->curr_bufs[n] = NULL; +} + +/* On TW6864 and TW6868, all channels share the pair of video DMA SG tables, + with 10-bit start_idx and end_idx determining start and end of frame buffer + for particular channel. + TW6868 with all its 8 channels would be problematic (only 127 SG entries per + channel) but we support only 4 channels on this chip anyway (the first + 4 channels are driven with internal video decoder, the other 4 would require + an external TW286x part). + + On TW6865 and TW6869, each channel has its own DMA SG table, with indexes + starting with 0. Both chips have complete sets of internal video decoders + (respectively 4 or 8-channel). + + All chips have separate SG tables for two video frames. */ + +static void setup_dma_cfg(struct tw686x_video_channel *vc) +{ + unsigned int field_width = 704; + unsigned int field_height = (vc->video_standard & V4L2_STD_625_50) ? + 288 : 240; + unsigned int start_idx = is_second_gen(vc->dev) ? 0 : + vc->ch * MAX_SG_DESC_COUNT; + unsigned int end_idx = start_idx + MAX_SG_DESC_COUNT - 1; + u32 dma_cfg = (0 << 30) /* input selection */ | + (1 << 29) /* field2 dropped (if any) */ | + ((vc->height < 300) << 28) /* field dropping */ | + (1 << 27) /* master */ | + (0 << 25) /* master channel (for slave only) */ | + (0 << 24) /* (no) vertical (line) decimation */ | + ((vc->width < 400) << 23) /* horizontal decimation */ | + (vc->format->mode << 20) /* output video format */ | + (end_idx << 10) /* DMA end index */ | + start_idx /* DMA start index */; + u32 reg; + + reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], dma_cfg); + reg_write(vc->dev, VIDEO_SIZE[vc->ch], (1 << 31) | (field_height << 16) + | field_width); + reg = reg_read(vc->dev, VIDEO_CONTROL1); + if (vc->video_standard & V4L2_STD_625_50) + reg |= 1 << (vc->ch + 13); + else + reg &= ~(1 << (vc->ch + 13)); + reg_write(vc->dev, VIDEO_CONTROL1, reg); +} + +static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + struct tw686x_dev *dev = vc->dev; + u32 dma_ch_mask; + unsigned int n; + + setup_dma_cfg(vc); + + /* queue video buffers if available */ + spin_lock(&vc->qlock); + for (n = 0; n < 2; n++) + setup_descs(vc, n); + spin_unlock(&vc->qlock); + + dev->video_active |= 1 << vc->ch; + vc->seq = 0; + dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE) | (1 << vc->ch); + reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); + reg_write(dev, DMA_CMD, (1 << 31) | dma_ch_mask); + return 0; +} + +static void tw686x_stop_streaming(struct vb2_queue *vq) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + struct tw686x_dev *dev = vc->dev; + u32 dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); + u32 dma_cmd = reg_read(dev, DMA_CMD); + unsigned int n; + + dma_ch_mask &= ~(1 << vc->ch); + reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); + + dev->video_active &= ~(1 << vc->ch); + + dma_cmd &= ~(1 << vc->ch); + reg_write(dev, DMA_CMD, dma_cmd); + + if (!dev->video_active) { + reg_write(dev, DMA_CMD, 0); + reg_write(dev, DMA_CHANNEL_ENABLE, 0); + } + + spin_lock(&vc->qlock); + while (!list_empty(&vc->vidq_queued)) { + struct tw686x_vb2_buf *buf; + + buf = list_entry(vc->vidq_queued.next, struct tw686x_vb2_buf, + list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + for (n = 0; n < 2; n++) + if (vc->curr_bufs[n]) + vb2_buffer_done(&vc->curr_bufs[n]->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + + spin_unlock(&vc->qlock); +} + +static struct vb2_ops tw686x_video_qops = { + .queue_setup = tw686x_queue_setup, + .buf_queue = tw686x_buf_queue, + .start_streaming = tw686x_start_streaming, + .stop_streaming = tw686x_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw686x_video_channel *vc; + struct tw686x_dev *dev; + unsigned int ch; + + vc = container_of(ctrl->handler, struct tw686x_video_channel, + ctrl_handler); + dev = vc->dev; + ch = vc->ch; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + reg_write(dev, BRIGHT[ch], ctrl->val & 0xFF); + return 0; + + case V4L2_CID_CONTRAST: + reg_write(dev, CONTRAST[ch], ctrl->val); + return 0; + + case V4L2_CID_SATURATION: + reg_write(dev, SAT_U[ch], ctrl->val); + reg_write(dev, SAT_V[ch], ctrl->val); + return 0; + + case V4L2_CID_HUE: + reg_write(dev, HUE[ch], ctrl->val & 0xFF); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ctrl_ops = { + .s_ctrl = tw686x_s_ctrl, +}; + +static int tw686x_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + f->fmt.pix.width = vc->width; + f->fmt.pix.height = vc->height; + f->fmt.pix.field = vc->field; + f->fmt.pix.pixelformat = vc->format->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.bytesperline = f->fmt.pix.width * vc->format->depth / 8; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + tw686x_get_format(video_drvdata(file), f); + return 0; +} + +static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + tw686x_get_format(vc, f); + vc->format = format_by_fourcc(f->fmt.pix.pixelformat); + vc->field = f->fmt.pix.field; + vc->width = f->fmt.pix.width; + vc->height = f->fmt.pix.height; + return 0; +} + +static int tw686x_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; + + strcpy(cap->driver, "tw686x-kh"); + strcpy(cap->card, dev->name); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci_dev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + unsigned int cnt; + u32 sdt = 0; /* default */ + + for (cnt = 0; cnt < ARRAY_SIZE(video_standards); cnt++) + if (id & video_standards[cnt]) { + sdt = cnt; + break; + } + + reg_write(vc->dev, SDT[vc->ch], sdt); + vc->video_standard = video_standards[sdt]; + return 0; +} + +static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + *id = vc->video_standard; + return 0; +} + +static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; + return 0; +} + +static int tw686x_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(&sp->parm.capture, 0, sizeof(sp->parm.capture)); + sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + v4l2_video_std_frame_period(vc->video_standard, + &sp->parm.capture.timeperframe); + + return 0; +} + +static int tw686x_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + /* the chip has internal multiplexer, support can be added + if the actual hw uses it */ + if (inp->index) + return -EINVAL; + + snprintf(inp->name, sizeof(inp->name), "Composite"); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; + inp->capabilities = V4L2_IN_CAP_STD; + return 0; +} + +static int tw686x_g_input(struct file *file, void *priv, unsigned int *v) +{ + *v = 0; + return 0; +} + +static int tw686x_s_input(struct file *file, void *priv, unsigned int v) +{ + if (v) + return -EINVAL; + return 0; +} + +const struct v4l2_file_operations tw686x_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .unlocked_ioctl = video_ioctl2, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, +}; + +const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { + .vidioc_querycap = tw686x_querycap, + .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_std = tw686x_g_std, + .vidioc_s_std = tw686x_s_std, + .vidioc_g_parm = tw686x_g_parm, + .vidioc_enum_input = tw686x_enum_input, + .vidioc_g_input = tw686x_g_input, + .vidioc_s_input = tw686x_s_input, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int video_thread(void *arg) +{ + struct tw686x_dev *dev = arg; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&dev->video_thread_wait, &wait); + + while (1) { + long timeout = schedule_timeout_interruptible(HZ); + unsigned int ch; + + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc; + unsigned long flags; + u32 request, n, stat = VB2_BUF_STATE_DONE; + + vc = &dev->video_channels[ch]; + if (!(dev->video_active & (1 << ch))) + continue; + + spin_lock_irq(&dev->irq_lock); + request = dev->dma_requests & (0x01000001 << ch); + if (request) + dev->dma_requests &= ~request; + spin_unlock_irq(&dev->irq_lock); + + if (!request) + continue; + + request >>= ch; + + /* handle channel events */ + if ((request & 0x01000000) | + (reg_read(dev, VIDEO_FIFO_STATUS) & (0x01010001 << ch)) | + (reg_read(dev, VIDEO_PARSER_STATUS) & (0x00000101 << ch))) { + /* DMA Errors - reset channel */ + u32 reg; + + spin_lock_irqsave(&dev->irq_lock, flags); + reg = reg_read(dev, DMA_CMD); + /* Reset DMA channel */ + reg_write(dev, DMA_CMD, reg & ~(1 << ch)); + reg_write(dev, DMA_CMD, reg); + spin_unlock_irqrestore(&dev->irq_lock, flags); + stat = VB2_BUF_STATE_ERROR; + } + + /* handle video stream */ + mutex_lock(&vc->vb_mutex); + spin_lock(&vc->qlock); + n = !!(reg_read(dev, PB_STATUS) & (1 << ch)); + if (vc->curr_bufs[n]) { + struct vb2_v4l2_buffer *vb; + + vb = &vc->curr_bufs[n]->vb; + vb->vb2_buf.timestamp = ktime_get_ns(); + vb->field = vc->field; + if (V4L2_FIELD_HAS_BOTH(vc->field)) + vb->sequence = vc->seq++; + else + vb->sequence = (vc->seq++) / 2; + vb2_set_plane_payload(&vb->vb2_buf, 0, + vc->width * vc->height * vc->format->depth / 8); + vb2_buffer_done(&vb->vb2_buf, stat); + } + setup_descs(vc, n); + spin_unlock(&vc->qlock); + mutex_unlock(&vc->vb_mutex); + } + try_to_freeze(); + } + + remove_wait_queue(&dev->video_thread_wait, &wait); + return 0; +} + +int tw686x_video_irq(struct tw686x_dev *dev) +{ + unsigned long flags, handled = 0; + u32 requests; + + spin_lock_irqsave(&dev->irq_lock, flags); + requests = dev->dma_requests; + spin_unlock_irqrestore(&dev->irq_lock, flags); + + if (dev->dma_requests & dev->video_active) { + wake_up_interruptible_all(&dev->video_thread_wait); + handled = 1; + } + return handled; +} + +void tw686x_video_free(struct tw686x_dev *dev) +{ + unsigned int ch, n; + + if (dev->video_thread) + kthread_stop(dev->video_thread); + + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + v4l2_ctrl_handler_free(&vc->ctrl_handler); + if (vc->device) + video_unregister_device(vc->device); + vb2_dma_sg_cleanup_ctx(vc->alloc_ctx); + for (n = 0; n < 2; n++) { + struct dma_desc *descs = &vc->sg_tables[n]; + + if (descs->virt) + pci_free_consistent(dev->pci_dev, descs->size, + descs->virt, descs->phys); + } + } + + v4l2_device_unregister(&dev->v4l2_dev); +} + +#define SG_TABLE_SIZE (MAX_SG_DESC_COUNT * sizeof(struct vdma_desc)) + +int tw686x_video_init(struct tw686x_dev *dev) +{ + unsigned int ch, n; + int err; + + init_waitqueue_head(&dev->video_thread_wait); + + err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); + if (err) + return err; + + reg_write(dev, VIDEO_CONTROL1, 0); /* NTSC, disable scaler */ + reg_write(dev, PHASE_REF, 0x00001518); /* Scatter-gather DMA mode */ + + /* setup required SG table sizes */ + for (n = 0; n < 2; n++) + if (is_second_gen(dev)) { + /* TW 6865, TW6869 - each channel needs a pair of + descriptor tables */ + for (ch = 0; ch < max_channels(dev); ch++) + dev->video_channels[ch].sg_tables[n].size = + SG_TABLE_SIZE; + + } else + /* TW 6864, TW6868 - we need to allocate a pair of + descriptor tables, common for all channels. + Each table will be bigger than 4 KB. */ + dev->video_channels[0].sg_tables[n].size = + max_channels(dev) * SG_TABLE_SIZE; + + /* allocate SG tables and initialize video channels */ + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + struct video_device *vdev; + + mutex_init(&vc->vb_mutex); + spin_lock_init(&vc->qlock); + INIT_LIST_HEAD(&vc->vidq_queued); + + vc->dev = dev; + vc->ch = ch; + + /* default settings: NTSC */ + vc->format = &formats[0]; + vc->video_standard = V4L2_STD_NTSC; + reg_write(vc->dev, SDT[vc->ch], 0); + vc->field = V4L2_FIELD_SEQ_BT; + vc->width = 704; + vc->height = 480; + + for (n = 0; n < 2; n++) { + void *cpu; + + if (vc->sg_tables[n].size) { + unsigned int reg = n ? DMA_PAGE_TABLE1_ADDR[ch] : + DMA_PAGE_TABLE0_ADDR[ch]; + + cpu = pci_alloc_consistent(dev->pci_dev, + vc->sg_tables[n].size, + &vc->sg_tables[n].phys); + if (!cpu) { + pr_err("Error allocating video DMA scatter-gather tables\n"); + err = -ENOMEM; + goto error; + } + vc->sg_tables[n].virt = cpu; + reg_write(dev, reg, vc->sg_tables[n].phys); + } else + cpu = dev->video_channels[0].sg_tables[n].virt + + ch * SG_TABLE_SIZE; + + vc->sg_descs[n] = cpu; + } + + reg_write(dev, VCTRL1[0], 0x24); + reg_write(dev, LOOP[0], 0xA5); + if (max_channels(dev) > 4) { + reg_write(dev, VCTRL1[1], 0x24); + reg_write(dev, LOOP[1], 0xA5); + } + reg_write(dev, VIDEO_FIELD_CTRL[ch], 0); + reg_write(dev, VDELAY_LO[ch], 0x14); + + vdev = video_device_alloc(); + if (!vdev) { + pr_warn("Unable to allocate video device\n"); + err = -ENOMEM; + goto error; + } + + vc->alloc_ctx = vb2_dma_sg_init_ctx(&dev->pci_dev->dev); + if (IS_ERR(vc->alloc_ctx)) { + pr_warn("Unable to initialize DMA scatter-gather context\n"); + err = PTR_ERR(vc->alloc_ctx); + goto error; + } + + vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vc->vidq.drv_priv = vc; + vc->vidq.buf_struct_size = sizeof(struct tw686x_vb2_buf); + vc->vidq.ops = &tw686x_video_qops; + vc->vidq.mem_ops = &vb2_dma_sg_memops; + vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vc->vidq.min_buffers_needed = 2; + vc->vidq.lock = &vc->vb_mutex; + + err = vb2_queue_init(&vc->vidq); + if (err) + goto error; + + strcpy(vdev->name, "TW686x-video"); + snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); + vdev->fops = &tw686x_video_fops; + vdev->ioctl_ops = &tw686x_video_ioctl_ops; + vdev->release = video_device_release; + vdev->v4l2_dev = &dev->v4l2_dev; + vdev->queue = &vc->vidq; + vdev->tvnorms = V4L2_STD_ALL; + vdev->minor = -1; + vdev->lock = &vc->vb_mutex; + + dev->video_channels[ch].device = vdev; + video_set_drvdata(vdev, vc); + err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (err < 0) + goto error; + + v4l2_ctrl_handler_init(&vc->ctrl_handler, + 4 /* number of controls */); + vdev->ctrl_handler = &vc->ctrl_handler; + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 64); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, V4L2_CID_HUE, + -124, 127, 1, 0); + err = vc->ctrl_handler.error; + if (err) + goto error; + + v4l2_ctrl_handler_setup(&vc->ctrl_handler); + } + + dev->video_thread = kthread_run(video_thread, dev, "tw686x_video"); + if (IS_ERR(dev->video_thread)) { + err = PTR_ERR(dev->video_thread); + goto error; + } + + return 0; + +error: + tw686x_video_free(dev); + return err; +} diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh.h b/drivers/staging/media/tw686x-kh/tw686x-kh.h new file mode 100644 index 000000000000..46c4ebd7f00c --- /dev/null +++ b/drivers/staging/media/tw686x-kh/tw686x-kh.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * + * Written by Krzysztof Ha?asa. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TYPE_MAX_CHANNELS 0x0F +#define TYPE_SECOND_GEN 0x10 + +struct tw686x_format { + char *name; + unsigned int fourcc; + unsigned int depth; + unsigned int mode; +}; + +struct dma_desc { + dma_addr_t phys; + void *virt; + unsigned int size; +}; + +struct vdma_desc { + __le32 flags_length; /* 3 MSBits for flags, 13 LSBits for length */ + __le32 phys; +}; + +struct tw686x_vb2_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct tw686x_video_channel { + struct tw686x_dev *dev; + + struct vb2_queue vidq; + struct list_head vidq_queued; + struct video_device *device; + struct dma_desc sg_tables[2]; + struct tw686x_vb2_buf *curr_bufs[2]; + void *alloc_ctx; + struct vdma_desc *sg_descs[2]; + + struct v4l2_ctrl_handler ctrl_handler; + const struct tw686x_format *format; + struct mutex vb_mutex; + spinlock_t qlock; + v4l2_std_id video_standard; + unsigned int width, height; + enum v4l2_field field; /* supported TOP, BOTTOM, SEQ_TB and SEQ_BT */ + unsigned int seq; /* video field or frame counter */ + unsigned int ch; +}; + +/* global device status */ +struct tw686x_dev { + spinlock_t irq_lock; + + struct v4l2_device v4l2_dev; + struct snd_card *card; /* sound card */ + + unsigned int video_active; /* active video channel mask */ + + char name[32]; + unsigned int type; + struct pci_dev *pci_dev; + __u32 __iomem *mmio; + + struct task_struct *video_thread; + wait_queue_head_t video_thread_wait; + u32 dma_requests; + + struct tw686x_video_channel video_channels[0]; +}; + +static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned int reg) +{ + return readl(dev->mmio + reg); +} + +static inline void reg_write(struct tw686x_dev *dev, unsigned int reg, + uint32_t value) +{ + writel(value, dev->mmio + reg); +} + +static inline unsigned int max_channels(struct tw686x_dev *dev) +{ + return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ +} + +static inline unsigned int is_second_gen(struct tw686x_dev *dev) +{ + /* each channel has its own DMA SG table */ + return dev->type & TYPE_SECOND_GEN; +} + +int tw686x_video_irq(struct tw686x_dev *dev); +int tw686x_video_init(struct tw686x_dev *dev); +void tw686x_video_free(struct tw686x_dev *dev); From 704a84ccdbf19fdce9adfda0b936dfdcac52fa49 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 2 Mar 2016 11:30:16 -0300 Subject: [PATCH 092/173] [media] media: Support Intersil/Techwell TW686x-based video capture cards This commit introduces the support for the Techwell TW686x video capture IC. This hardware supports a few DMA modes, including scatter-gather and frame (contiguous). This commit makes little use of the DMA engine and instead has a memcpy based implementation. DMA frame and scatter-gather modes support may be added in the future. Currently supported chips: - TW6864 (4 video channels), - TW6865 (4 video channels, not tested, second generation chip), - TW6868 (8 video channels but only 4 first channels using built-in video decoder are supported, not tested), - TW6869 (8 video channels, second generation chip). [mchehab@osg.samsung.com: make checkpatch happy by using "unsigned int" instead of just "unsigned"] Cc: Krzysztof Halasa Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 + drivers/media/pci/Kconfig | 1 + drivers/media/pci/Makefile | 1 + drivers/media/pci/tw686x/Kconfig | 18 + drivers/media/pci/tw686x/Makefile | 3 + drivers/media/pci/tw686x/tw686x-audio.c | 386 ++++++++++ drivers/media/pci/tw686x/tw686x-core.c | 415 +++++++++++ drivers/media/pci/tw686x/tw686x-regs.h | 122 ++++ drivers/media/pci/tw686x/tw686x-video.c | 927 ++++++++++++++++++++++++ drivers/media/pci/tw686x/tw686x.h | 158 ++++ 10 files changed, 2039 insertions(+) create mode 100644 drivers/media/pci/tw686x/Kconfig create mode 100644 drivers/media/pci/tw686x/Makefile create mode 100644 drivers/media/pci/tw686x/tw686x-audio.c create mode 100644 drivers/media/pci/tw686x/tw686x-core.c create mode 100644 drivers/media/pci/tw686x/tw686x-regs.h create mode 100644 drivers/media/pci/tw686x/tw686x-video.c create mode 100644 drivers/media/pci/tw686x/tw686x.h diff --git a/MAINTAINERS b/MAINTAINERS index 2ec50793c3ba..bfcb7ea42e20 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11258,6 +11258,14 @@ W: https://linuxtv.org S: Odd Fixes F: drivers/media/pci/tw68/ +TW686X VIDEO4LINUX DRIVER +M: Ezequiel Garcia +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/pci/tw686x/ + TPM DEVICE DRIVER M: Peter Huewe M: Marcel Selhorst diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 48a611bc3e18..4f6467fbaeb4 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -14,6 +14,7 @@ source "drivers/media/pci/meye/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw68/Kconfig" +source "drivers/media/pci/tw686x/Kconfig" source "drivers/media/pci/zoran/Kconfig" endif diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 5f8aacb8b9b8..2e54c36441f7 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_TW68) += tw68/ +obj-$(CONFIG_VIDEO_TW686X) += tw686x/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig new file mode 100644 index 000000000000..fb8536974052 --- /dev/null +++ b/drivers/media/pci/tw686x/Kconfig @@ -0,0 +1,18 @@ +config VIDEO_TW686X + tristate "Intersil/Techwell TW686x video capture cards" + depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND + depends on HAS_DMA + select VIDEOBUF2_VMALLOC + select SND_PCM + help + Support for Intersil/Techwell TW686x-based frame grabber cards. + + Currently supported chips: + - TW6864 (4 video channels), + - TW6865 (4 video channels, not tested, second generation chip), + - TW6868 (8 video channels but only 4 first channels using + built-in video decoder are supported, not tested), + - TW6869 (8 video channels, second generation chip). + + To compile this driver as a module, choose M here: the module + will be named tw686x. diff --git a/drivers/media/pci/tw686x/Makefile b/drivers/media/pci/tw686x/Makefile new file mode 100644 index 000000000000..99819542b733 --- /dev/null +++ b/drivers/media/pci/tw686x/Makefile @@ -0,0 +1,3 @@ +tw686x-objs := tw686x-core.o tw686x-video.o tw686x-audio.o + +obj-$(CONFIG_VIDEO_TW686X) += tw686x.o diff --git a/drivers/media/pci/tw686x/tw686x-audio.c b/drivers/media/pci/tw686x/tw686x-audio.c new file mode 100644 index 000000000000..91459ab715b2 --- /dev/null +++ b/drivers/media/pci/tw686x/tw686x-audio.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar + * + * Based on the audio support from the tw6869 driver: + * Copyright 2015 www.starterkit.ru + * + * Based on: + * Driver for Intersil|Techwell TW6869 based DVR cards + * (c) 2011-12 liran [Intersil|Techwell China] + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "tw686x.h" +#include "tw686x-regs.h" + +#define AUDIO_CHANNEL_OFFSET 8 + +void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, + unsigned int pb_status) +{ + unsigned long flags; + unsigned int ch, pb; + + for_each_set_bit(ch, &requests, max_channels(dev)) { + struct tw686x_audio_channel *ac = &dev->audio_channels[ch]; + struct tw686x_audio_buf *done = NULL; + struct tw686x_audio_buf *next = NULL; + struct tw686x_dma_desc *desc; + + pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch)); + + spin_lock_irqsave(&ac->lock, flags); + + /* Sanity check */ + if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) { + spin_unlock_irqrestore(&ac->lock, flags); + continue; + } + + if (!list_empty(&ac->buf_list)) { + next = list_first_entry(&ac->buf_list, + struct tw686x_audio_buf, list); + list_move_tail(&next->list, &ac->buf_list); + done = ac->curr_bufs[!pb]; + ac->curr_bufs[pb] = next; + } + spin_unlock_irqrestore(&ac->lock, flags); + + desc = &ac->dma_descs[pb]; + if (done && next && desc->virt) { + memcpy(done->virt, desc->virt, desc->size); + ac->ptr = done->dma - ac->buf[0].dma; + snd_pcm_period_elapsed(ac->ss); + } + } +} + +static int tw686x_pcm_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); +} + +static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss) +{ + return snd_pcm_lib_free_pages(ss); +} + +/* + * The audio device rate is global and shared among all + * capture channels. The driver makes no effort to prevent + * rate modifications. User is free change the rate, but it + * means changing the rate for all capture sub-devices. + */ +static const struct snd_pcm_hardware tw686x_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, + .period_bytes_min = TW686X_AUDIO_PAGE_SZ, + .period_bytes_max = TW686X_AUDIO_PAGE_SZ, + .periods_min = TW686X_AUDIO_PERIODS_MIN, + .periods_max = TW686X_AUDIO_PERIODS_MAX, +}; + +static int tw686x_pcm_open(struct snd_pcm_substream *ss) +{ + struct tw686x_dev *dev = snd_pcm_substream_chip(ss); + struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; + struct snd_pcm_runtime *rt = ss->runtime; + int err; + + ac->ss = ss; + rt->hw = tw686x_capture_hw; + + err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; + + return 0; +} + +static int tw686x_pcm_close(struct snd_pcm_substream *ss) +{ + struct tw686x_dev *dev = snd_pcm_substream_chip(ss); + struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; + + ac->ss = NULL; + return 0; +} + +static int tw686x_pcm_prepare(struct snd_pcm_substream *ss) +{ + struct tw686x_dev *dev = snd_pcm_substream_chip(ss); + struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; + struct snd_pcm_runtime *rt = ss->runtime; + unsigned int period_size = snd_pcm_lib_period_bytes(ss); + struct tw686x_audio_buf *p_buf, *b_buf; + unsigned long flags; + int i; + + spin_lock_irqsave(&dev->lock, flags); + tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); + spin_unlock_irqrestore(&dev->lock, flags); + + if (dev->audio_rate != rt->rate) { + u32 reg; + + dev->audio_rate = rt->rate; + reg = ((125000000 / rt->rate) << 16) + + ((125000000 % rt->rate) << 16) / rt->rate; + + reg_write(dev, AUDIO_CONTROL2, reg); + } + + if (period_size != TW686X_AUDIO_PAGE_SZ || + rt->periods < TW686X_AUDIO_PERIODS_MIN || + rt->periods > TW686X_AUDIO_PERIODS_MAX) { + return -EINVAL; + } + + spin_lock_irqsave(&ac->lock, flags); + INIT_LIST_HEAD(&ac->buf_list); + + for (i = 0; i < rt->periods; i++) { + ac->buf[i].dma = rt->dma_addr + period_size * i; + ac->buf[i].virt = rt->dma_area + period_size * i; + INIT_LIST_HEAD(&ac->buf[i].list); + list_add_tail(&ac->buf[i].list, &ac->buf_list); + } + + p_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); + list_move_tail(&p_buf->list, &ac->buf_list); + + b_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); + list_move_tail(&b_buf->list, &ac->buf_list); + + ac->curr_bufs[0] = p_buf; + ac->curr_bufs[1] = b_buf; + ac->ptr = 0; + spin_unlock_irqrestore(&ac->lock, flags); + + return 0; +} + +static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct tw686x_dev *dev = snd_pcm_substream_chip(ss); + struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; + unsigned long flags; + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (ac->curr_bufs[0] && ac->curr_bufs[1]) { + spin_lock_irqsave(&dev->lock, flags); + tw686x_enable_channel(dev, + AUDIO_CHANNEL_OFFSET + ac->ch); + spin_unlock_irqrestore(&dev->lock, flags); + + mod_timer(&dev->dma_delay_timer, + jiffies + msecs_to_jiffies(100)); + } else { + err = -EIO; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&dev->lock, flags); + tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); + spin_unlock_irqrestore(&dev->lock, flags); + + spin_lock_irqsave(&ac->lock, flags); + ac->curr_bufs[0] = NULL; + ac->curr_bufs[1] = NULL; + spin_unlock_irqrestore(&ac->lock, flags); + break; + default: + err = -EINVAL; + } + return err; +} + +static snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct tw686x_dev *dev = snd_pcm_substream_chip(ss); + struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; + + return bytes_to_frames(ss->runtime, ac->ptr); +} + +static struct snd_pcm_ops tw686x_pcm_ops = { + .open = tw686x_pcm_open, + .close = tw686x_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = tw686x_pcm_hw_params, + .hw_free = tw686x_pcm_hw_free, + .prepare = tw686x_pcm_prepare, + .trigger = tw686x_pcm_trigger, + .pointer = tw686x_pcm_pointer, +}; + +static int tw686x_snd_pcm_init(struct tw686x_dev *dev) +{ + struct snd_card *card = dev->snd_card; + struct snd_pcm *pcm; + struct snd_pcm_substream *ss; + unsigned int i; + int err; + + err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops); + snd_pcm_chip(pcm) = dev; + pcm->info_flags = 0; + strlcpy(pcm->name, "tw686x PCM", sizeof(pcm->name)); + + for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ss; ss = ss->next, i++) + snprintf(ss->name, sizeof(ss->name), "vch%u audio", i); + + return snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(dev->pci_dev), + TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ, + TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ); +} + +static void tw686x_audio_dma_free(struct tw686x_dev *dev, + struct tw686x_audio_channel *ac) +{ + int pb; + + for (pb = 0; pb < 2; pb++) { + if (!ac->dma_descs[pb].virt) + continue; + pci_free_consistent(dev->pci_dev, ac->dma_descs[pb].size, + ac->dma_descs[pb].virt, + ac->dma_descs[pb].phys); + ac->dma_descs[pb].virt = NULL; + } +} + +static int tw686x_audio_dma_alloc(struct tw686x_dev *dev, + struct tw686x_audio_channel *ac) +{ + int pb; + + for (pb = 0; pb < 2; pb++) { + u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch]; + void *virt; + + virt = pci_alloc_consistent(dev->pci_dev, TW686X_AUDIO_PAGE_SZ, + &ac->dma_descs[pb].phys); + if (!virt) { + dev_err(&dev->pci_dev->dev, + "dma%d: unable to allocate audio DMA %s-buffer\n", + ac->ch, pb ? "B" : "P"); + return -ENOMEM; + } + ac->dma_descs[pb].virt = virt; + ac->dma_descs[pb].size = TW686X_AUDIO_PAGE_SZ; + reg_write(dev, reg, ac->dma_descs[pb].phys); + } + return 0; +} + +void tw686x_audio_free(struct tw686x_dev *dev) +{ + unsigned long flags; + u32 dma_ch_mask; + u32 dma_cmd; + + spin_lock_irqsave(&dev->lock, flags); + dma_cmd = reg_read(dev, DMA_CMD); + dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); + reg_write(dev, DMA_CMD, dma_cmd & ~0xff00); + reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00); + spin_unlock_irqrestore(&dev->lock, flags); + + if (!dev->snd_card) + return; + snd_card_free(dev->snd_card); + dev->snd_card = NULL; +} + +int tw686x_audio_init(struct tw686x_dev *dev) +{ + struct pci_dev *pci_dev = dev->pci_dev; + struct snd_card *card; + int err, ch; + + /* + * AUDIO_CONTROL1 + * DMA byte length [31:19] = 4096 (i.e. ALSA period) + * External audio enable [0] = enabled + */ + reg_write(dev, AUDIO_CONTROL1, 0x80000001); + + err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1, + SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err < 0) + return err; + + dev->snd_card = card; + strlcpy(card->driver, "tw686x", sizeof(card->driver)); + strlcpy(card->shortname, "tw686x", sizeof(card->shortname)); + strlcpy(card->longname, pci_name(pci_dev), sizeof(card->longname)); + snd_card_set_dev(card, &pci_dev->dev); + + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_audio_channel *ac; + + ac = &dev->audio_channels[ch]; + spin_lock_init(&ac->lock); + ac->dev = dev; + ac->ch = ch; + + err = tw686x_audio_dma_alloc(dev, ac); + if (err < 0) + goto err_cleanup; + } + + err = tw686x_snd_pcm_init(dev); + if (err < 0) + goto err_cleanup; + + err = snd_card_register(card); + if (!err) + return 0; + +err_cleanup: + for (ch = 0; ch < max_channels(dev); ch++) { + if (!dev->audio_channels[ch].dev) + continue; + tw686x_audio_dma_free(dev, &dev->audio_channels[ch]); + } + snd_card_free(card); + dev->snd_card = NULL; + return err; +} diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c new file mode 100644 index 000000000000..cf53b0e97be2 --- /dev/null +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar + * + * Based on original driver by Krzysztof Ha?asa: + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * Notes + * ----- + * + * 1. Under stress-testing, it has been observed that the PCIe link + * goes down, without reason. Therefore, the driver takes special care + * to allow device hot-unplugging. + * + * 2. TW686X devices are capable of setting a few different DMA modes, + * including: scatter-gather, field and frame modes. However, + * under stress testings it has been found that the machine can + * freeze completely if DMA registers are programmed while streaming + * is active. + * This driver tries to access hardware registers as infrequently + * as possible by: + * i. allocating fixed DMA buffers and memcpy'ing into + * vmalloc'ed buffers + * ii. using a timer to mitigate the rate of DMA reset operations, + * on DMA channels error. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tw686x.h" +#include "tw686x-regs.h" + +/* + * This module parameter allows to control the DMA_TIMER_INTERVAL value. + * The DMA_TIMER_INTERVAL register controls the minimum DMA interrupt + * time span (iow, the maximum DMA interrupt rate) thus allowing for + * IRQ coalescing. + * + * The chip datasheet does not mention a time unit for this value, so + * users wanting fine-grain control over the interrupt rate should + * determine the desired value through testing. + */ +static u32 dma_interval = 0x00098968; +module_param(dma_interval, int, 0444); +MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host"); + +void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel) +{ + u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); + u32 dma_cmd = reg_read(dev, DMA_CMD); + + dma_en &= ~BIT(channel); + dma_cmd &= ~BIT(channel); + + /* Must remove it from pending too */ + dev->pending_dma_en &= ~BIT(channel); + dev->pending_dma_cmd &= ~BIT(channel); + + /* Stop DMA if no channels are enabled */ + if (!dma_en) + dma_cmd = 0; + reg_write(dev, DMA_CHANNEL_ENABLE, dma_en); + reg_write(dev, DMA_CMD, dma_cmd); +} + +void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel) +{ + u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); + u32 dma_cmd = reg_read(dev, DMA_CMD); + + dev->pending_dma_en |= dma_en | BIT(channel); + dev->pending_dma_cmd |= dma_cmd | DMA_CMD_ENABLE | BIT(channel); +} + +/* + * The purpose of this awful hack is to avoid enabling the DMA + * channels "too fast" which makes some TW686x devices very + * angry and freeze the CPU (see note 1). + */ +static void tw686x_dma_delay(unsigned long data) +{ + struct tw686x_dev *dev = (struct tw686x_dev *)data; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + reg_write(dev, DMA_CHANNEL_ENABLE, dev->pending_dma_en); + reg_write(dev, DMA_CMD, dev->pending_dma_cmd); + dev->pending_dma_en = 0; + dev->pending_dma_cmd = 0; + + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void tw686x_reset_channels(struct tw686x_dev *dev, unsigned int ch_mask) +{ + u32 dma_en, dma_cmd; + + dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); + dma_cmd = reg_read(dev, DMA_CMD); + + /* + * Save pending register status, the timer will + * restore them. + */ + dev->pending_dma_en |= dma_en; + dev->pending_dma_cmd |= dma_cmd; + + /* Disable the reset channels */ + reg_write(dev, DMA_CHANNEL_ENABLE, dma_en & ~ch_mask); + + if ((dma_en & ~ch_mask) == 0) { + dev_dbg(&dev->pci_dev->dev, "reset: stopping DMA\n"); + dma_cmd &= ~DMA_CMD_ENABLE; + } + reg_write(dev, DMA_CMD, dma_cmd & ~ch_mask); +} + +static irqreturn_t tw686x_irq(int irq, void *dev_id) +{ + struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; + unsigned int video_requests, audio_requests, reset_ch; + u32 fifo_status, fifo_signal, fifo_ov, fifo_bad, fifo_errors; + u32 int_status, dma_en, video_en, pb_status; + unsigned long flags; + + int_status = reg_read(dev, INT_STATUS); /* cleared on read */ + fifo_status = reg_read(dev, VIDEO_FIFO_STATUS); + + /* INT_STATUS does not include FIFO_STATUS errors! */ + if (!int_status && !TW686X_FIFO_ERROR(fifo_status)) + return IRQ_NONE; + + if (int_status & INT_STATUS_DMA_TOUT) { + dev_dbg(&dev->pci_dev->dev, + "DMA timeout. Resetting DMA for all channels\n"); + reset_ch = ~0; + goto reset_channels; + } + + spin_lock_irqsave(&dev->lock, flags); + dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); + spin_unlock_irqrestore(&dev->lock, flags); + + video_en = dma_en & 0xff; + fifo_signal = ~(fifo_status & 0xff) & video_en; + fifo_ov = fifo_status >> 24; + fifo_bad = fifo_status >> 16; + + /* Mask of channels with signal and FIFO errors */ + fifo_errors = fifo_signal & (fifo_ov | fifo_bad); + + reset_ch = 0; + pb_status = reg_read(dev, PB_STATUS); + + /* Coalesce video frame/error events */ + video_requests = (int_status & video_en) | fifo_errors; + audio_requests = (int_status & dma_en) >> 8; + + if (video_requests) + tw686x_video_irq(dev, video_requests, pb_status, + fifo_status, &reset_ch); + if (audio_requests) + tw686x_audio_irq(dev, audio_requests, pb_status); + +reset_channels: + if (reset_ch) { + spin_lock_irqsave(&dev->lock, flags); + tw686x_reset_channels(dev, reset_ch); + spin_unlock_irqrestore(&dev->lock, flags); + mod_timer(&dev->dma_delay_timer, + jiffies + msecs_to_jiffies(100)); + } + + return IRQ_HANDLED; +} + +static void tw686x_dev_release(struct v4l2_device *v4l2_dev) +{ + struct tw686x_dev *dev = container_of(v4l2_dev, struct tw686x_dev, + v4l2_dev); + unsigned int ch; + + for (ch = 0; ch < max_channels(dev); ch++) + v4l2_ctrl_handler_free(&dev->video_channels[ch].ctrl_handler); + + v4l2_device_unregister(&dev->v4l2_dev); + + kfree(dev->audio_channels); + kfree(dev->video_channels); + kfree(dev); +} + +static int tw686x_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw686x_dev *dev; + int err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->type = pci_id->driver_data; + sprintf(dev->name, "tw%04X", pci_dev->device); + + dev->video_channels = kcalloc(max_channels(dev), + sizeof(*dev->video_channels), GFP_KERNEL); + if (!dev->video_channels) { + err = -ENOMEM; + goto free_dev; + } + + dev->audio_channels = kcalloc(max_channels(dev), + sizeof(*dev->audio_channels), GFP_KERNEL); + if (!dev->audio_channels) { + err = -ENOMEM; + goto free_video; + } + + pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, + pci_name(pci_dev), pci_dev->irq, + (unsigned long)pci_resource_start(pci_dev, 0)); + + dev->pci_dev = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto free_audio; + } + + pci_set_master(pci_dev); + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pci_dev->dev, "32-bit PCI DMA not supported\n"); + err = -EIO; + goto disable_pci; + } + + err = pci_request_regions(pci_dev, dev->name); + if (err) { + dev_err(&pci_dev->dev, "unable to request PCI region\n"); + goto disable_pci; + } + + dev->mmio = pci_ioremap_bar(pci_dev, 0); + if (!dev->mmio) { + dev_err(&pci_dev->dev, "unable to remap PCI region\n"); + err = -ENOMEM; + goto free_region; + } + + /* Reset all subsystems */ + reg_write(dev, SYS_SOFT_RST, 0x0f); + mdelay(1); + + reg_write(dev, SRST[0], 0x3f); + if (max_channels(dev) > 4) + reg_write(dev, SRST[1], 0x3f); + + /* Disable the DMA engine */ + reg_write(dev, DMA_CMD, 0); + reg_write(dev, DMA_CHANNEL_ENABLE, 0); + + /* Enable DMA FIFO overflow and pointer check */ + reg_write(dev, DMA_CONFIG, 0xffffff04); + reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x140c8584); + reg_write(dev, DMA_TIMER_INTERVAL, dma_interval); + + spin_lock_init(&dev->lock); + + err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, + dev->name, dev); + if (err < 0) { + dev_err(&pci_dev->dev, "unable to request interrupt\n"); + goto iounmap; + } + + setup_timer(&dev->dma_delay_timer, + tw686x_dma_delay, (unsigned long) dev); + + /* + * This must be set right before initializing v4l2_dev. + * It's used to release resources after the last handle + * held is released. + */ + dev->v4l2_dev.release = tw686x_dev_release; + err = tw686x_video_init(dev); + if (err) { + dev_err(&pci_dev->dev, "can't register video\n"); + goto free_irq; + } + + err = tw686x_audio_init(dev); + if (err) + dev_warn(&pci_dev->dev, "can't register audio\n"); + + pci_set_drvdata(pci_dev, dev); + return 0; + +free_irq: + free_irq(pci_dev->irq, dev); +iounmap: + pci_iounmap(pci_dev, dev->mmio); +free_region: + pci_release_regions(pci_dev); +disable_pci: + pci_disable_device(pci_dev); +free_audio: + kfree(dev->audio_channels); +free_video: + kfree(dev->video_channels); +free_dev: + kfree(dev); + return err; +} + +static void tw686x_remove(struct pci_dev *pci_dev) +{ + struct tw686x_dev *dev = pci_get_drvdata(pci_dev); + unsigned long flags; + + /* This guarantees the IRQ handler is no longer running, + * which means we can kiss good-bye some resources. + */ + free_irq(pci_dev->irq, dev); + + tw686x_video_free(dev); + tw686x_audio_free(dev); + del_timer_sync(&dev->dma_delay_timer); + + pci_iounmap(pci_dev, dev->mmio); + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + + /* + * Setting pci_dev to NULL allows to detect hardware is no longer + * available and will be used by vb2_ops. This is required because + * the device sometimes hot-unplugs itself as the result of a PCIe + * link down. + * The lock is really important here. + */ + spin_lock_irqsave(&dev->lock, flags); + dev->pci_dev = NULL; + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * This calls tw686x_dev_release if it's the last reference. + * Otherwise, release is postponed until there are no users left. + */ + v4l2_device_put(&dev->v4l2_dev); +} + +/* + * On TW6864 and TW6868, all channels share the pair of video DMA SG tables, + * with 10-bit start_idx and end_idx determining start and end of frame buffer + * for particular channel. + * TW6868 with all its 8 channels would be problematic (only 127 SG entries per + * channel) but we support only 4 channels on this chip anyway (the first + * 4 channels are driven with internal video decoder, the other 4 would require + * an external TW286x part). + * + * On TW6865 and TW6869, each channel has its own DMA SG table, with indexes + * starting with 0. Both chips have complete sets of internal video decoders + * (respectively 4 or 8-channel). + * + * All chips have separate SG tables for two video frames. + */ + +/* driver_data is number of A/V channels */ +static const struct pci_device_id tw686x_pci_tbl[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6864), + .driver_data = 4 + }, + { + PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6865), /* not tested */ + .driver_data = 4 | TYPE_SECOND_GEN + }, + /* + * TW6868 supports 8 A/V channels with an external TW2865 chip; + * not supported by the driver. + */ + { + PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6868), /* not tested */ + .driver_data = 4 + }, + { + PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6869), + .driver_data = 8 | TYPE_SECOND_GEN}, + {} +}; +MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); + +static struct pci_driver tw686x_pci_driver = { + .name = "tw686x", + .id_table = tw686x_pci_tbl, + .probe = tw686x_probe, + .remove = tw686x_remove, +}; +module_pci_driver(tw686x_pci_driver); + +MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); +MODULE_AUTHOR("Ezequiel Garcia "); +MODULE_AUTHOR("Krzysztof Ha?asa "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/pci/tw686x/tw686x-regs.h b/drivers/media/pci/tw686x/tw686x-regs.h new file mode 100644 index 000000000000..fcef586a4c8c --- /dev/null +++ b/drivers/media/pci/tw686x/tw686x-regs.h @@ -0,0 +1,122 @@ +/* DMA controller registers */ +#define REG8_1(a0) ((const u16[8]) { a0, a0 + 1, a0 + 2, a0 + 3, \ + a0 + 4, a0 + 5, a0 + 6, a0 + 7}) +#define REG8_2(a0) ((const u16[8]) { a0, a0 + 2, a0 + 4, a0 + 6, \ + a0 + 8, a0 + 0xa, a0 + 0xc, a0 + 0xe}) +#define REG8_8(a0) ((const u16[8]) { a0, a0 + 8, a0 + 0x10, a0 + 0x18, \ + a0 + 0x20, a0 + 0x28, a0 + 0x30, \ + a0 + 0x38}) +#define INT_STATUS 0x00 +#define PB_STATUS 0x01 +#define DMA_CMD 0x02 +#define VIDEO_FIFO_STATUS 0x03 +#define VIDEO_CHANNEL_ID 0x04 +#define VIDEO_PARSER_STATUS 0x05 +#define SYS_SOFT_RST 0x06 +#define DMA_PAGE_TABLE0_ADDR ((const u16[8]) { 0x08, 0xd0, 0xd2, 0xd4, \ + 0xd6, 0xd8, 0xda, 0xdc }) +#define DMA_PAGE_TABLE1_ADDR ((const u16[8]) { 0x09, 0xd1, 0xd3, 0xd5, \ + 0xd7, 0xd9, 0xdb, 0xdd }) +#define DMA_CHANNEL_ENABLE 0x0a +#define DMA_CONFIG 0x0b +#define DMA_TIMER_INTERVAL 0x0c +#define DMA_CHANNEL_TIMEOUT 0x0d +#define VDMA_CHANNEL_CONFIG REG8_1(0x10) +#define ADMA_P_ADDR REG8_2(0x18) +#define ADMA_B_ADDR REG8_2(0x19) +#define DMA10_P_ADDR 0x28 +#define DMA10_B_ADDR 0x29 +#define VIDEO_CONTROL1 0x2a +#define VIDEO_CONTROL2 0x2b +#define AUDIO_CONTROL1 0x2c +#define AUDIO_CONTROL2 0x2d +#define PHASE_REF 0x2e +#define GPIO_REG 0x2f +#define INTL_HBAR_CTRL REG8_1(0x30) +#define AUDIO_CONTROL3 0x38 +#define VIDEO_FIELD_CTRL REG8_1(0x39) +#define HSCALER_CTRL REG8_1(0x42) +#define VIDEO_SIZE REG8_1(0x4A) +#define VIDEO_SIZE_F2 REG8_1(0x52) +#define MD_CONF REG8_1(0x60) +#define MD_INIT REG8_1(0x68) +#define MD_MAP0 REG8_1(0x70) +#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */ +#define VDMA_WHP REG8_8(0x81) +#define VDMA_B_ADDR REG8_8(0x82) +#define VDMA_F2_P_ADDR REG8_8(0x84) +#define VDMA_F2_WHP REG8_8(0x85) +#define VDMA_F2_B_ADDR REG8_8(0x86) +#define EP_REG_ADDR 0xfe +#define EP_REG_DATA 0xff + +/* Video decoder registers */ +#define VDREG8(a0) ((const u16[8]) { \ + a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \ + a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130}) +#define VIDSTAT VDREG8(0x100) +#define BRIGHT VDREG8(0x101) +#define CONTRAST VDREG8(0x102) +#define SHARPNESS VDREG8(0x103) +#define SAT_U VDREG8(0x104) +#define SAT_V VDREG8(0x105) +#define HUE VDREG8(0x106) +#define CROP_HI VDREG8(0x107) +#define VDELAY_LO VDREG8(0x108) +#define VACTIVE_LO VDREG8(0x109) +#define HDELAY_LO VDREG8(0x10a) +#define HACTIVE_LO VDREG8(0x10b) +#define MVSN VDREG8(0x10c) +#define STATUS2 VDREG8(0x10d) +#define SDT VDREG8(0x10e) +#define SDT_EN VDREG8(0x10f) + +#define VSCALE_LO VDREG8(0x144) +#define SCALE_HI VDREG8(0x145) +#define HSCALE_LO VDREG8(0x146) +#define F2CROP_HI VDREG8(0x147) +#define F2VDELAY_LO VDREG8(0x148) +#define F2VACTIVE_LO VDREG8(0x149) +#define F2HDELAY_LO VDREG8(0x14a) +#define F2HACTIVE_LO VDREG8(0x14b) +#define F2VSCALE_LO VDREG8(0x14c) +#define F2SCALE_HI VDREG8(0x14d) +#define F2HSCALE_LO VDREG8(0x14e) +#define F2CNT VDREG8(0x14f) + +#define VDREG2(a0) ((const u16[2]) { a0, a0 + 0x100 }) +#define SRST VDREG2(0x180) +#define ACNTL VDREG2(0x181) +#define ACNTL2 VDREG2(0x182) +#define CNTRL1 VDREG2(0x183) +#define CKHY VDREG2(0x184) +#define SHCOR VDREG2(0x185) +#define CORING VDREG2(0x186) +#define CLMPG VDREG2(0x187) +#define IAGC VDREG2(0x188) +#define VCTRL1 VDREG2(0x18f) +#define MISC1 VDREG2(0x194) +#define LOOP VDREG2(0x195) +#define MISC2 VDREG2(0x196) + +#define CLMD VDREG2(0x197) +#define ANPWRDOWN VDREG2(0x1ce) +#define AIGAIN ((const u16[8]) { 0x1d0, 0x1d1, 0x1d2, 0x1d3, \ + 0x2d0, 0x2d1, 0x2d2, 0x2d3 }) + +#define SYS_MODE_DMA_SHIFT 13 + +#define DMA_CMD_ENABLE BIT(31) +#define INT_STATUS_DMA_TOUT BIT(17) +#define TW686X_VIDSTAT_HLOCK BIT(6) +#define TW686X_VIDSTAT_VDLOSS BIT(7) + +#define TW686X_STD_NTSC_M 0 +#define TW686X_STD_PAL 1 +#define TW686X_STD_SECAM 2 +#define TW686X_STD_NTSC_443 3 +#define TW686X_STD_PAL_M 4 +#define TW686X_STD_PAL_CN 5 +#define TW686X_STD_PAL_60 6 + +#define TW686X_FIFO_ERROR(x) (x & ~(0xff)) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c new file mode 100644 index 000000000000..35ad8e650717 --- /dev/null +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -0,0 +1,927 @@ +/* + * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar + * + * Based on original driver by Krzysztof Ha?asa: + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tw686x.h" +#include "tw686x-regs.h" + +#define TW686X_INPUTS_PER_CH 4 +#define TW686X_VIDEO_WIDTH 720 +#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_625_50) ? 576 : 480) + +static const struct tw686x_format formats[] = { + { + .fourcc = V4L2_PIX_FMT_UYVY, + .mode = 0, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .mode = 5, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .mode = 6, + .depth = 16, + } +}; + +static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps) +{ + static const unsigned int map[15] = { + 0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041, + 0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445, + 0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555 + }; + + static const unsigned int std_625_50[26] = { + 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, + 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0 + }; + + static const unsigned int std_525_60[31] = { + 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0 + }; + + unsigned int i = + (std & V4L2_STD_625_50) ? std_625_50[fps] : std_525_60[fps]; + + return map[i]; +} + +static void tw686x_set_framerate(struct tw686x_video_channel *vc, + unsigned int fps) +{ + unsigned int map; + + if (vc->fps == fps) + return; + + map = tw686x_fields_map(vc->video_standard, fps) << 1; + map |= map << 1; + if (map > 0) + map |= BIT(31); + reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map); + vc->fps = fps; +} + +static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) +{ + unsigned int cnt; + + for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) + if (formats[cnt].fourcc == fourcc) + return &formats[cnt]; + return NULL; +} + +static int tw686x_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + unsigned int szimage = + (vc->width * vc->height * vc->format->depth) >> 3; + + /* + * Let's request at least three buffers: two for the + * DMA engine and one for userspace. + */ + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (*nplanes != 1 || sizes[0] < szimage) + return -EINVAL; + return 0; + } + + sizes[0] = szimage; + *nplanes = 1; + return 0; +} + +static void tw686x_buf_queue(struct vb2_buffer *vb) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); + struct tw686x_dev *dev = vc->dev; + struct pci_dev *pci_dev; + unsigned long flags; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct tw686x_v4l2_buf *buf = + container_of(vbuf, struct tw686x_v4l2_buf, vb); + + /* Check device presence */ + spin_lock_irqsave(&dev->lock, flags); + pci_dev = dev->pci_dev; + spin_unlock_irqrestore(&dev->lock, flags); + if (!pci_dev) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&vc->qlock, flags); + list_add_tail(&buf->list, &vc->vidq_queued); + spin_unlock_irqrestore(&vc->qlock, flags); +} + +/* + * We can call this even when alloc_dma failed for the given channel + */ +static void tw686x_free_dma(struct tw686x_video_channel *vc, unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + struct pci_dev *pci_dev; + unsigned long flags; + + /* Check device presence. Shouldn't really happen! */ + spin_lock_irqsave(&dev->lock, flags); + pci_dev = dev->pci_dev; + spin_unlock_irqrestore(&dev->lock, flags); + if (!pci_dev) { + WARN(1, "trying to deallocate on missing device\n"); + return; + } + + if (desc->virt) { + pci_free_consistent(dev->pci_dev, desc->size, + desc->virt, desc->phys); + desc->virt = NULL; + } +} + +static int tw686x_alloc_dma(struct tw686x_video_channel *vc, unsigned int pb) +{ + struct tw686x_dev *dev = vc->dev; + u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; + unsigned int len; + void *virt; + + WARN(vc->dma_descs[pb].virt, + "Allocating buffer but previous still here\n"); + + len = (vc->width * vc->height * vc->format->depth) >> 3; + virt = pci_alloc_consistent(dev->pci_dev, len, + &vc->dma_descs[pb].phys); + if (!virt) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to allocate %s-buffer\n", + vc->ch, pb ? "B" : "P"); + return -ENOMEM; + } + vc->dma_descs[pb].size = len; + vc->dma_descs[pb].virt = virt; + reg_write(dev, reg, vc->dma_descs[pb].phys); + + return 0; +} + +static void tw686x_buffer_refill(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_v4l2_buf *buf; + + while (!list_empty(&vc->vidq_queued)) { + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + + vc->curr_bufs[pb] = buf; + return; + } + vc->curr_bufs[pb] = NULL; +} + +static void tw686x_clear_queue(struct tw686x_video_channel *vc, + enum vb2_buffer_state state) +{ + unsigned int pb; + + while (!list_empty(&vc->vidq_queued)) { + struct tw686x_v4l2_buf *buf; + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + for (pb = 0; pb < 2; pb++) { + if (vc->curr_bufs[pb]) + vb2_buffer_done(&vc->curr_bufs[pb]->vb.vb2_buf, state); + vc->curr_bufs[pb] = NULL; + } +} + +static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + struct tw686x_dev *dev = vc->dev; + struct pci_dev *pci_dev; + unsigned long flags; + int pb, err; + + /* Check device presence */ + spin_lock_irqsave(&dev->lock, flags); + pci_dev = dev->pci_dev; + spin_unlock_irqrestore(&dev->lock, flags); + if (!pci_dev) { + err = -ENODEV; + goto err_clear_queue; + } + + spin_lock_irqsave(&vc->qlock, flags); + + /* Sanity check */ + if (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt) { + spin_unlock_irqrestore(&vc->qlock, flags); + v4l2_err(&dev->v4l2_dev, + "video%d: refusing to start without DMA buffers\n", + vc->num); + err = -ENOMEM; + goto err_clear_queue; + } + + for (pb = 0; pb < 2; pb++) + tw686x_buffer_refill(vc, pb); + spin_unlock_irqrestore(&vc->qlock, flags); + + vc->sequence = 0; + vc->pb = 0; + + spin_lock_irqsave(&dev->lock, flags); + tw686x_enable_channel(dev, vc->ch); + spin_unlock_irqrestore(&dev->lock, flags); + + mod_timer(&dev->dma_delay_timer, jiffies + msecs_to_jiffies(100)); + + return 0; + +err_clear_queue: + spin_lock_irqsave(&vc->qlock, flags); + tw686x_clear_queue(vc, VB2_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&vc->qlock, flags); + return err; +} + +static void tw686x_stop_streaming(struct vb2_queue *vq) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); + struct tw686x_dev *dev = vc->dev; + struct pci_dev *pci_dev; + unsigned long flags; + + /* Check device presence */ + spin_lock_irqsave(&dev->lock, flags); + pci_dev = dev->pci_dev; + spin_unlock_irqrestore(&dev->lock, flags); + if (pci_dev) + tw686x_disable_channel(dev, vc->ch); + + spin_lock_irqsave(&vc->qlock, flags); + tw686x_clear_queue(vc, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&vc->qlock, flags); +} + +static int tw686x_buf_prepare(struct vb2_buffer *vb) +{ + struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = + (vc->width * vc->height * vc->format->depth) >> 3; + + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + vb2_set_plane_payload(vb, 0, size); + return 0; +} + +static struct vb2_ops tw686x_video_qops = { + .queue_setup = tw686x_queue_setup, + .buf_queue = tw686x_buf_queue, + .buf_prepare = tw686x_buf_prepare, + .start_streaming = tw686x_start_streaming, + .stop_streaming = tw686x_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw686x_video_channel *vc; + struct tw686x_dev *dev; + unsigned int ch; + + vc = container_of(ctrl->handler, struct tw686x_video_channel, + ctrl_handler); + dev = vc->dev; + ch = vc->ch; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + reg_write(dev, BRIGHT[ch], ctrl->val & 0xff); + return 0; + + case V4L2_CID_CONTRAST: + reg_write(dev, CONTRAST[ch], ctrl->val); + return 0; + + case V4L2_CID_SATURATION: + reg_write(dev, SAT_U[ch], ctrl->val); + reg_write(dev, SAT_V[ch], ctrl->val); + return 0; + + case V4L2_CID_HUE: + reg_write(dev, HUE[ch], ctrl->val & 0xff); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ctrl_ops = { + .s_ctrl = tw686x_s_ctrl, +}; + +static int tw686x_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + f->fmt.pix.width = vc->width; + f->fmt.pix.height = vc->height; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.pixelformat = vc->format->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard); + const struct tw686x_format *format; + + format = format_by_fourcc(f->fmt.pix.pixelformat); + if (!format) { + format = &formats[0]; + f->fmt.pix.pixelformat = format->fourcc; + } + + if (f->fmt.pix.width <= TW686X_VIDEO_WIDTH / 2) + f->fmt.pix.width = TW686X_VIDEO_WIDTH / 2; + else + f->fmt.pix.width = TW686X_VIDEO_WIDTH; + + if (f->fmt.pix.height <= video_height / 2) + f->fmt.pix.height = video_height / 2; + else + f->fmt.pix.height = video_height; + + f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + u32 val, width, line_width, height; + unsigned long bitsperframe; + int err, pb; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + bitsperframe = vc->width * vc->height * vc->format->depth; + err = tw686x_try_fmt_vid_cap(file, priv, f); + if (err) + return err; + + vc->format = format_by_fourcc(f->fmt.pix.pixelformat); + vc->width = f->fmt.pix.width; + vc->height = f->fmt.pix.height; + + /* We need new DMA buffers if the framesize has changed */ + if (bitsperframe != vc->width * vc->height * vc->format->depth) { + for (pb = 0; pb < 2; pb++) + tw686x_free_dma(vc, pb); + + for (pb = 0; pb < 2; pb++) { + err = tw686x_alloc_dma(vc, pb); + if (err) { + if (pb > 0) + tw686x_free_dma(vc, 0); + return err; + } + } + } + + val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); + + if (vc->width <= TW686X_VIDEO_WIDTH / 2) + val |= BIT(23); + else + val &= ~BIT(23); + + if (vc->height <= TW686X_VIDEO_HEIGHT(vc->video_standard) / 2) + val |= BIT(24); + else + val &= ~BIT(24); + + val &= ~(0x7 << 20); + val |= vc->format->mode << 20; + reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); + + /* Program the DMA frame size */ + width = (vc->width * 2) & 0x7ff; + height = vc->height / 2; + line_width = (vc->width * 2) & 0x7ff; + val = (height << 22) | (line_width << 11) | width; + reg_write(vc->dev, VDMA_WHP[vc->ch], val); + return 0; +} + +static int tw686x_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; + + strlcpy(cap->driver, "tw686x", sizeof(cap->driver)); + strlcpy(cap->card, dev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "PCI:%s", pci_name(dev->pci_dev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct v4l2_format f; + u32 val, ret; + + if (vc->video_standard == id) + return 0; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + if (id & V4L2_STD_NTSC) + val = 0; + else if (id & V4L2_STD_PAL) + val = 1; + else if (id & V4L2_STD_SECAM) + val = 2; + else if (id & V4L2_STD_NTSC_443) + val = 3; + else if (id & V4L2_STD_PAL_M) + val = 4; + else if (id & V4L2_STD_PAL_Nc) + val = 5; + else if (id & V4L2_STD_PAL_60) + val = 6; + else + return -EINVAL; + + vc->video_standard = id; + reg_write(vc->dev, SDT[vc->ch], val); + + val = reg_read(vc->dev, VIDEO_CONTROL1); + if (id & V4L2_STD_625_50) + val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); + else + val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch)); + reg_write(vc->dev, VIDEO_CONTROL1, val); + + /* + * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change, + * calling g_fmt and s_fmt will sanitize the height + * according to the standard. + */ + ret = tw686x_g_fmt_vid_cap(file, priv, &f); + if (!ret) + tw686x_s_fmt_vid_cap(file, priv, &f); + return 0; +} + +static int tw686x_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct tw686x_dev *dev = vc->dev; + unsigned int old_std, detected_std = 0; + unsigned long end; + + if (vb2_is_streaming(&vc->vidq)) + return -EBUSY; + + /* Enable and start standard detection */ + old_std = reg_read(dev, SDT[vc->ch]); + reg_write(dev, SDT[vc->ch], 0x7); + reg_write(dev, SDT_EN[vc->ch], 0xff); + + end = jiffies + msecs_to_jiffies(500); + while (time_is_after_jiffies(end)) { + + detected_std = reg_read(dev, SDT[vc->ch]); + if (!(detected_std & BIT(7))) + break; + msleep(100); + } + reg_write(dev, SDT[vc->ch], old_std); + + /* Exit if still busy */ + if (detected_std & BIT(7)) + return 0; + + detected_std = (detected_std >> 4) & 0x7; + switch (detected_std) { + case TW686X_STD_NTSC_M: + *std &= V4L2_STD_NTSC; + break; + case TW686X_STD_NTSC_443: + *std &= V4L2_STD_NTSC_443; + break; + case TW686X_STD_PAL_M: + *std &= V4L2_STD_PAL_M; + break; + case TW686X_STD_PAL_60: + *std &= V4L2_STD_PAL_60; + break; + case TW686X_STD_PAL: + *std &= V4L2_STD_PAL; + break; + case TW686X_STD_PAL_CN: + *std &= V4L2_STD_PAL_Nc; + break; + case TW686X_STD_SECAM: + *std &= V4L2_STD_SECAM; + break; + default: + *std = 0; + } + return 0; +} + +static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + *id = vc->video_standard; + return 0; +} + +static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + f->pixelformat = formats[f->index].fourcc; + return 0; +} + +static int tw686x_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + u32 val; + + if (i >= TW686X_INPUTS_PER_CH) + return -EINVAL; + if (i == vc->input) + return 0; + /* + * Not sure we are able to support on the fly input change + */ + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + vc->input = i; + + val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); + val &= ~(0x3 << 30); + val |= i << 30; + reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); + return 0; +} + +static int tw686x_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + *i = vc->input; + return 0; +} + +static int tw686x_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + unsigned int vidstat; + + if (i->index >= TW686X_INPUTS_PER_CH) + return -EINVAL; + + snprintf(i->name, sizeof(i->name), "Composite%d", i->index); + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = vc->device->tvnorms; + i->capabilities = V4L2_IN_CAP_STD; + + vidstat = reg_read(vc->dev, VIDSTAT[vc->ch]); + i->status = 0; + if (vidstat & TW686X_VIDSTAT_VDLOSS) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (!(vidstat & TW686X_VIDSTAT_HLOCK)) + i->status |= V4L2_IN_ST_NO_H_LOCK; + + return 0; +} + +const struct v4l2_file_operations tw686x_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .unlocked_ioctl = video_ioctl2, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, +}; + +const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { + .vidioc_querycap = tw686x_querycap, + .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, + + .vidioc_querystd = tw686x_querystd, + .vidioc_g_std = tw686x_g_std, + .vidioc_s_std = tw686x_s_std, + + .vidioc_enum_input = tw686x_enum_input, + .vidioc_g_input = tw686x_g_input, + .vidioc_s_input = tw686x_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void tw686x_buffer_copy(struct tw686x_video_channel *vc, + unsigned int pb, struct vb2_v4l2_buffer *vb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct vb2_buffer *vb2_buf = &vb->vb2_buf; + + vb->field = V4L2_FIELD_INTERLACED; + vb->sequence = vc->sequence++; + + memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, desc->size); + vb2_buf->timestamp = ktime_get_ns(); + vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); +} + +void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, + unsigned int pb_status, unsigned int fifo_status, + unsigned int *reset_ch) +{ + struct tw686x_video_channel *vc; + struct vb2_v4l2_buffer *vb; + unsigned long flags; + unsigned int ch, pb; + + for_each_set_bit(ch, &requests, max_channels(dev)) { + vc = &dev->video_channels[ch]; + + /* + * This can either be a blue frame (with signal-lost bit set) + * or a good frame (with signal-lost bit clear). If we have just + * got signal, then this channel needs resetting. + */ + if (vc->no_signal && !(fifo_status & BIT(ch))) { + v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, + "video%d: signal recovered\n", vc->num); + vc->no_signal = false; + *reset_ch |= BIT(ch); + vc->pb = 0; + continue; + } + vc->no_signal = !!(fifo_status & BIT(ch)); + + /* Check FIFO errors only if there's signal */ + if (!vc->no_signal) { + u32 fifo_ov, fifo_bad; + + fifo_ov = (fifo_status >> 24) & BIT(ch); + fifo_bad = (fifo_status >> 16) & BIT(ch); + if (fifo_ov || fifo_bad) { + /* Mark this channel for reset */ + v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, + "video%d: FIFO error\n", vc->num); + *reset_ch |= BIT(ch); + vc->pb = 0; + continue; + } + } + + pb = !!(pb_status & BIT(ch)); + if (vc->pb != pb) { + /* Mark this channel for reset */ + v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, + "video%d: unexpected p-b buffer!\n", + vc->num); + *reset_ch |= BIT(ch); + vc->pb = 0; + continue; + } + + /* handle video stream */ + spin_lock_irqsave(&vc->qlock, flags); + if (vc->curr_bufs[pb]) { + vb = &vc->curr_bufs[pb]->vb; + tw686x_buffer_copy(vc, pb, vb); + } + vc->pb = !pb; + tw686x_buffer_refill(vc, pb); + spin_unlock_irqrestore(&vc->qlock, flags); + } +} + +void tw686x_video_free(struct tw686x_dev *dev) +{ + unsigned int ch, pb; + + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + if (vc->device) + video_unregister_device(vc->device); + + for (pb = 0; pb < 2; pb++) + tw686x_free_dma(vc, pb); + } +} + +int tw686x_video_init(struct tw686x_dev *dev) +{ + unsigned int ch, val, pb; + int err; + + err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); + if (err) + return err; + + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + struct video_device *vdev; + + mutex_init(&vc->vb_mutex); + spin_lock_init(&vc->qlock); + INIT_LIST_HEAD(&vc->vidq_queued); + + vc->dev = dev; + vc->ch = ch; + + /* default settings */ + vc->format = &formats[0]; + vc->video_standard = V4L2_STD_NTSC; + vc->width = TW686X_VIDEO_WIDTH; + vc->height = TW686X_VIDEO_HEIGHT(vc->video_standard); + vc->input = 0; + + reg_write(vc->dev, SDT[ch], 0); + tw686x_set_framerate(vc, 30); + + reg_write(dev, VDELAY_LO[ch], 0x14); + reg_write(dev, HACTIVE_LO[ch], 0xd0); + reg_write(dev, VIDEO_SIZE[ch], 0); + + for (pb = 0; pb < 2; pb++) { + err = tw686x_alloc_dma(vc, pb); + if (err) + goto error; + } + + vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; + vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vc->vidq.drv_priv = vc; + vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf); + vc->vidq.ops = &tw686x_video_qops; + vc->vidq.mem_ops = &vb2_vmalloc_memops; + vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vc->vidq.min_buffers_needed = 2; + vc->vidq.lock = &vc->vb_mutex; + + err = vb2_queue_init(&vc->vidq); + if (err) { + v4l2_err(&dev->v4l2_dev, + "dma%d: cannot init vb2 queue\n", ch); + goto error; + } + + err = v4l2_ctrl_handler_init(&vc->ctrl_handler, 4); + if (err) { + v4l2_err(&dev->v4l2_dev, + "dma%d: cannot init ctrl handler\n", ch); + goto error; + } + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 100); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + err = vc->ctrl_handler.error; + if (err) + goto error; + + err = v4l2_ctrl_handler_setup(&vc->ctrl_handler); + if (err) + goto error; + + vdev = video_device_alloc(); + if (!vdev) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to allocate device\n", ch); + err = -ENOMEM; + goto error; + } + + snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); + vdev->fops = &tw686x_video_fops; + vdev->ioctl_ops = &tw686x_video_ioctl_ops; + vdev->release = video_device_release; + vdev->v4l2_dev = &dev->v4l2_dev; + vdev->queue = &vc->vidq; + vdev->tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50; + vdev->minor = -1; + vdev->lock = &vc->vb_mutex; + vdev->ctrl_handler = &vc->ctrl_handler; + vc->device = vdev; + video_set_drvdata(vdev, vc); + + err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (err < 0) + goto error; + vc->num = vdev->num; + } + + /* Set DMA frame mode on all channels. Only supported mode for now. */ + val = TW686X_DEF_PHASE_REF; + for (ch = 0; ch < max_channels(dev); ch++) + val |= TW686X_FRAME_MODE << (16 + ch * 2); + reg_write(dev, PHASE_REF, val); + + reg_write(dev, MISC2[0], 0xe7); + reg_write(dev, VCTRL1[0], 0xcc); + reg_write(dev, LOOP[0], 0xa5); + if (max_channels(dev) > 4) { + reg_write(dev, VCTRL1[1], 0xcc); + reg_write(dev, LOOP[1], 0xa5); + reg_write(dev, MISC2[1], 0xe7); + } + return 0; + +error: + tw686x_video_free(dev); + return err; +} diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h new file mode 100644 index 000000000000..44b5755acf02 --- /dev/null +++ b/drivers/media/pci/tw686x/tw686x.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar + * + * Copyright (C) 2015 Industrial Research Institute for Automation + * and Measurements PIAP + * Written by Krzysztof Ha?asa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tw686x-regs.h" + +#define TYPE_MAX_CHANNELS 0x0f +#define TYPE_SECOND_GEN 0x10 +#define TW686X_DEF_PHASE_REF 0x1518 + +#define TW686X_FIELD_MODE 0x3 +#define TW686X_FRAME_MODE 0x2 +/* 0x1 is reserved */ +#define TW686X_SG_MODE 0x0 + +#define TW686X_AUDIO_PAGE_SZ 4096 +#define TW686X_AUDIO_PAGE_MAX 16 +#define TW686X_AUDIO_PERIODS_MIN 2 +#define TW686X_AUDIO_PERIODS_MAX TW686X_AUDIO_PAGE_MAX + +struct tw686x_format { + char *name; + unsigned int fourcc; + unsigned int depth; + unsigned int mode; +}; + +struct tw686x_dma_desc { + dma_addr_t phys; + void *virt; + unsigned int size; +}; + +struct tw686x_audio_buf { + dma_addr_t dma; + void *virt; + struct list_head list; +}; + +struct tw686x_v4l2_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct tw686x_audio_channel { + struct tw686x_dev *dev; + struct snd_pcm_substream *ss; + unsigned int ch; + struct tw686x_audio_buf *curr_bufs[2]; + struct tw686x_dma_desc dma_descs[2]; + dma_addr_t ptr; + + struct tw686x_audio_buf buf[TW686X_AUDIO_PAGE_MAX]; + struct list_head buf_list; + spinlock_t lock; +}; + +struct tw686x_video_channel { + struct tw686x_dev *dev; + + struct vb2_queue vidq; + struct list_head vidq_queued; + struct video_device *device; + struct tw686x_v4l2_buf *curr_bufs[2]; + struct tw686x_dma_desc dma_descs[2]; + + struct v4l2_ctrl_handler ctrl_handler; + const struct tw686x_format *format; + struct mutex vb_mutex; + spinlock_t qlock; + v4l2_std_id video_standard; + unsigned int width, height; + unsigned int h_halve, v_halve; + unsigned int ch; + unsigned int num; + unsigned int fps; + unsigned int input; + unsigned int sequence; + unsigned int pb; + bool no_signal; +}; + +/** + * struct tw686x_dev - global device status + * @lock: spinlock controlling access to the + * shared device registers (DMA enable/disable). + */ +struct tw686x_dev { + spinlock_t lock; + + struct v4l2_device v4l2_dev; + struct snd_card *snd_card; + + char name[32]; + unsigned int type; + struct pci_dev *pci_dev; + __u32 __iomem *mmio; + + void *alloc_ctx; + + struct tw686x_video_channel *video_channels; + struct tw686x_audio_channel *audio_channels; + + int audio_rate; /* per-device value */ + + struct timer_list dma_delay_timer; + u32 pending_dma_en; /* must be protected by lock */ + u32 pending_dma_cmd; /* must be protected by lock */ +}; + +static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned int reg) +{ + return readl(dev->mmio + reg); +} + +static inline void reg_write(struct tw686x_dev *dev, unsigned int reg, + uint32_t value) +{ + writel(value, dev->mmio + reg); +} + +static inline unsigned int max_channels(struct tw686x_dev *dev) +{ + return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ +} + +void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel); +void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel); + +int tw686x_video_init(struct tw686x_dev *dev); +void tw686x_video_free(struct tw686x_dev *dev); +void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, + unsigned int pb_status, unsigned int fifo_status, + unsigned int *reset_ch); + +int tw686x_audio_init(struct tw686x_dev *dev); +void tw686x_audio_free(struct tw686x_dev *dev); +void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, + unsigned int pb_status); From 13431509a41889a70cc76b31180f63fd10d0a5b3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 20 Apr 2016 13:39:20 -0300 Subject: [PATCH 093/173] [media] tw686x-kh: use the cached value the dma_requests field is cached, but cache is not used: drivers/staging/media/tw686x-kh/tw686x-kh-video.c: In function 'tw686x_video_irq': drivers/staging/media/tw686x-kh/tw686x-kh-video.c:622:6: warning: variable 'requests' set but not used [-Wunused-but-set-variable] u32 requests; ^ Use the cache instead, as it seems reading it needs to be done with spin lock taken. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tw686x-kh/tw686x-kh-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c index 2fbc3cbd9eb0..0650c29f78eb 100644 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c @@ -625,7 +625,7 @@ int tw686x_video_irq(struct tw686x_dev *dev) requests = dev->dma_requests; spin_unlock_irqrestore(&dev->irq_lock, flags); - if (dev->dma_requests & dev->video_active) { + if (requests & dev->video_active) { wake_up_interruptible_all(&dev->video_thread_wait); handled = 1; } From 304d2a7fc8acc1bb337596e0fc301092ea079b4d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Mar 2016 08:07:44 -0300 Subject: [PATCH 094/173] [media] tw686x-kh: specify that the DMA is 32 bits Set vb2_queue.gfp_flags to GFP_DMA32. Otherwise it will start to create bounce buffers which is something you want to avoid since those are in limited supply. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tw686x-kh/tw686x-kh-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c index 0650c29f78eb..2065c6c906bc 100644 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c @@ -766,6 +766,7 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vc->vidq.min_buffers_needed = 2; vc->vidq.lock = &vc->vb_mutex; + vc->vidq.gfp_flags = GFP_DMA32; err = vb2_queue_init(&vc->vidq); if (err) From e4c32b495fd06831e3f1553a6ebac5c68431b2a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Mar 2016 08:17:14 -0300 Subject: [PATCH 095/173] [media] tw686x-kh: add audio support to the TODO list The mainline tw686x driver also supports audio, that's missing here as well. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tw686x-kh/TODO | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/tw686x-kh/TODO b/drivers/staging/media/tw686x-kh/TODO index f226e808390e..480a495b11fb 100644 --- a/drivers/staging/media/tw686x-kh/TODO +++ b/drivers/staging/media/tw686x-kh/TODO @@ -1,3 +1,6 @@ -TODO: implement V4L2_FIELD_INTERLACED* mode(s). +TODO: + +- implement V4L2_FIELD_INTERLACED* mode(s). +- add audio support Please Cc: patches to Krzysztof Halasa . From 2e2dedb96ddd9e4f8d8a0330bc31ac56670a36b4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Mar 2016 12:09:59 -0300 Subject: [PATCH 096/173] [media] tw686x: add missing statics Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 35ad8e650717..2356fa2e951d 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -665,7 +665,7 @@ static int tw686x_enum_input(struct file *file, void *priv, return 0; } -const struct v4l2_file_operations tw686x_video_fops = { +static const struct v4l2_file_operations tw686x_video_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .unlocked_ioctl = video_ioctl2, @@ -675,7 +675,7 @@ const struct v4l2_file_operations tw686x_video_fops = { .mmap = vb2_fop_mmap, }; -const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { +static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_querycap = tw686x_querycap, .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, From e3a900a8fae22949523e0b6c3f0626ce5f3f48c3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Mar 2016 12:10:12 -0300 Subject: [PATCH 097/173] [media] tw686x-kh: rename three functions to prevent clash with tw686x driver Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tw686x-kh/tw686x-kh-core.c | 6 +++--- drivers/staging/media/tw686x-kh/tw686x-kh-video.c | 12 ++++++------ drivers/staging/media/tw686x-kh/tw686x-kh.h | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c index c3c5c2602541..03b3b62c59c4 100644 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c @@ -30,7 +30,7 @@ static irqreturn_t tw686x_irq(int irq, void *dev_id) spin_unlock_irqrestore(&dev->irq_lock, flags); if (int_status & 0xFF0000FF) - handled = tw686x_video_irq(dev); + handled = tw686x_kh_video_irq(dev); } return IRQ_RETVAL(handled); @@ -99,7 +99,7 @@ static int tw686x_probe(struct pci_dev *pci_dev, return err; } - err = tw686x_video_init(dev); + err = tw686x_kh_video_init(dev); if (err) return err; @@ -111,7 +111,7 @@ static void tw686x_remove(struct pci_dev *pci_dev) { struct tw686x_dev *dev = pci_get_drvdata(pci_dev); - tw686x_video_free(dev); + tw686x_kh_video_free(dev); } /* driver_data is number of A/V channels */ diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c index 2065c6c906bc..6ecb504a79f9 100644 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c +++ b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c @@ -503,7 +503,7 @@ static int tw686x_s_input(struct file *file, void *priv, unsigned int v) return 0; } -const struct v4l2_file_operations tw686x_video_fops = { +static const struct v4l2_file_operations tw686x_video_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .unlocked_ioctl = video_ioctl2, @@ -513,7 +513,7 @@ const struct v4l2_file_operations tw686x_video_fops = { .mmap = vb2_fop_mmap, }; -const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { +static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_querycap = tw686x_querycap, .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, @@ -616,7 +616,7 @@ static int video_thread(void *arg) return 0; } -int tw686x_video_irq(struct tw686x_dev *dev) +int tw686x_kh_video_irq(struct tw686x_dev *dev) { unsigned long flags, handled = 0; u32 requests; @@ -632,7 +632,7 @@ int tw686x_video_irq(struct tw686x_dev *dev) return handled; } -void tw686x_video_free(struct tw686x_dev *dev) +void tw686x_kh_video_free(struct tw686x_dev *dev) { unsigned int ch, n; @@ -660,7 +660,7 @@ void tw686x_video_free(struct tw686x_dev *dev) #define SG_TABLE_SIZE (MAX_SG_DESC_COUNT * sizeof(struct vdma_desc)) -int tw686x_video_init(struct tw686x_dev *dev) +int tw686x_kh_video_init(struct tw686x_dev *dev) { unsigned int ch, n; int err; @@ -816,6 +816,6 @@ int tw686x_video_init(struct tw686x_dev *dev) return 0; error: - tw686x_video_free(dev); + tw686x_kh_video_free(dev); return err; } diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh.h b/drivers/staging/media/tw686x-kh/tw686x-kh.h index 46c4ebd7f00c..dc257967dbc7 100644 --- a/drivers/staging/media/tw686x-kh/tw686x-kh.h +++ b/drivers/staging/media/tw686x-kh/tw686x-kh.h @@ -113,6 +113,6 @@ static inline unsigned int is_second_gen(struct tw686x_dev *dev) return dev->type & TYPE_SECOND_GEN; } -int tw686x_video_irq(struct tw686x_dev *dev); -int tw686x_video_init(struct tw686x_dev *dev); -void tw686x_video_free(struct tw686x_dev *dev); +int tw686x_kh_video_irq(struct tw686x_dev *dev); +int tw686x_kh_video_init(struct tw686x_dev *dev); +void tw686x_kh_video_free(struct tw686x_dev *dev); From 1c9f47195ef83ffd59ff665f2006789c0610cff0 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 1 Apr 2016 19:38:21 -0300 Subject: [PATCH 098/173] [media] tw686x: Specify that the DMA is 32 bits Set vb2_queue.gfp_flags to GFP_DMA32. Otherwise it will start to create bounce buffers which is something you want to avoid since those are in limited supply. Without this patch, DMA scatter-gather may not work because machines can ran out of buffers easily. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 2356fa2e951d..118e9fac9f28 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -848,6 +848,7 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vc->vidq.min_buffers_needed = 2; vc->vidq.lock = &vc->vb_mutex; + vc->vidq.gfp_flags = GFP_DMA32; err = vb2_queue_init(&vc->vidq); if (err) { From 86b2749ba79e78130dd9f678aa52d57992fbdf1c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Apr 2016 12:35:31 -0300 Subject: [PATCH 099/173] [media] vivid: fix smatch errors The smatch utility got really confused about the grp % 22 code. Rewrote it so it now understands that there really isn't a buffer overwrite. vivid-rds-gen.c:82 vivid_rds_generate() error: buffer overflow 'rds->psname' 9 <= 43 vivid-rds-gen.c:83 vivid_rds_generate() error: buffer overflow 'rds->psname' 9 <= 42 vivid-rds-gen.c:89 vivid_rds_generate() error: buffer overflow 'rds->radiotext' 65 <= 84 vivid-rds-gen.c:90 vivid_rds_generate() error: buffer overflow 'rds->radiotext' 65 <= 85 vivid-rds-gen.c:92 vivid_rds_generate() error: buffer overflow 'rds->radiotext' 65 <= 86 vivid-rds-gen.c:93 vivid_rds_generate() error: buffer overflow 'rds->radiotext' 65 <= 87 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-rds-gen.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c index c382343fdb66..53c7777dc001 100644 --- a/drivers/media/platform/vivid/vivid-rds-gen.c +++ b/drivers/media/platform/vivid/vivid-rds-gen.c @@ -55,6 +55,7 @@ void vivid_rds_generate(struct vivid_rds_gen *rds) { struct v4l2_rds_data *data = rds->data; unsigned grp; + unsigned idx; struct tm tm; unsigned date; unsigned time; @@ -73,24 +74,26 @@ void vivid_rds_generate(struct vivid_rds_gen *rds) case 0 ... 3: case 22 ... 25: case 44 ... 47: /* Group 0B */ + idx = (grp % 22) % 4; data[1].lsb |= (rds->ta << 4) | (rds->ms << 3); - data[1].lsb |= vivid_get_di(rds, grp % 22); + data[1].lsb |= vivid_get_di(rds, idx); data[1].msb |= 1 << 3; data[2].lsb = rds->picode & 0xff; data[2].msb = rds->picode >> 8; data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3); - data[3].lsb = rds->psname[2 * (grp % 22) + 1]; - data[3].msb = rds->psname[2 * (grp % 22)]; + data[3].lsb = rds->psname[2 * idx + 1]; + data[3].msb = rds->psname[2 * idx]; break; case 4 ... 19: case 26 ... 41: /* Group 2A */ - data[1].lsb |= (grp - 4) % 22; + idx = ((grp - 4) % 22) % 16; + data[1].lsb |= idx; data[1].msb |= 4 << 3; - data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)]; - data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1]; + data[2].msb = rds->radiotext[4 * idx]; + data[2].lsb = rds->radiotext[4 * idx + 1]; data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3); - data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2]; - data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 3]; + data[3].msb = rds->radiotext[4 * idx + 2]; + data[3].lsb = rds->radiotext[4 * idx + 3]; break; case 56: /* From 622202938952e43471c31835906c8a4f4e16f050 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Apr 2016 12:35:32 -0300 Subject: [PATCH 100/173] [media] pvrusb2: fix smatch errors These are false positives, but still easy to fix. pvrusb2-hdw.c:3676 pvr2_send_request_ex() error: we previously assumed 'write_data' could be null (see line 3648) pvrusb2-hdw.c:3829 pvr2_send_request_ex() error: we previously assumed 'read_data' could be null (see line 3649) Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pvrusb2/pvrusb2-hdw.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 1a093e5953fd..83e9a3eb3859 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -3672,11 +3672,10 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, hdw->cmd_debug_state = 1; - if (write_len) { + if (write_len && write_data) hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; - } else { + else hdw->cmd_debug_code = 0; - } hdw->cmd_debug_write_len = write_len; hdw->cmd_debug_read_len = read_len; @@ -3688,7 +3687,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, setup_timer(&timer, pvr2_ctl_timeout, (unsigned long)hdw); timer.expires = jiffies + timeout; - if (write_len) { + if (write_len && write_data) { hdw->cmd_debug_state = 2; /* Transfer write data to internal buffer */ for (idx = 0; idx < write_len; idx++) { @@ -3795,7 +3794,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, goto done; } } - if (read_len) { + if (read_len && read_data) { /* Validate results of read request */ if ((hdw->ctl_read_urb->status != 0) && (hdw->ctl_read_urb->status != -ENOENT) && From 5848adbe43034c8a1a378f9fed9afe501d9a4988 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Apr 2016 12:35:33 -0300 Subject: [PATCH 101/173] [media] dib0090: fix smatch error Fix this smatch error: dib0090.c:1124 dib0090_pwm_gain_reset() error: we previously assumed 'state->rf_ramp' could be null (see line 1086) Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/dib0090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index dc2d41e144fd..d879dc0607f4 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -1121,7 +1121,7 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe) (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", state->identity.version & 0x1f); - if (rf_ramp && ((state->rf_ramp[0] == 0) || + if (rf_ramp && ((state->rf_ramp && state->rf_ramp[0] == 0) || (state->current_band == BAND_CBAND && (state->identity.version & 0x1f) <= P1D_E_F))) { dprintk("DE-Engage mux for direct gain reg control"); From 72777724881f90c6efa027cc0dde9466371d740c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Mar 2016 06:59:02 -0300 Subject: [PATCH 102/173] [media] tc358743: zero the reserved array v4l2-compliance complained about this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 972e0d47259d..73e0cef0ea61 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1551,6 +1551,8 @@ static int tc358743_g_edid(struct v4l2_subdev *sd, { struct tc358743_state *state = to_state(sd); + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (edid->pad != 0) return -EINVAL; @@ -1585,6 +1587,8 @@ static int tc358743_s_edid(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", __func__, edid->pad, edid->start_block, edid->blocks); + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (edid->pad != 0) return -EINVAL; From 0be67c40d4e8c5b3d96581680fa797f28a3025ec Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Mar 2016 07:30:27 -0300 Subject: [PATCH 103/173] [media] vidioc-g-edid.xml: be explicit about zeroing the reserved array The G/S_EDID documentation did not explicitly state that the reserved array should be zeroed by the application. Also add the missing VIDIOC_SUBDEV_G/S_EDID ioctl names to the header. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-g-edid.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-g-edid.xml b/Documentation/DocBook/media/v4l/vidioc-g-edid.xml index 2702536bbc7c..b7602d30f596 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-edid.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-edid.xml @@ -1,6 +1,6 @@ - ioctl VIDIOC_G_EDID, VIDIOC_S_EDID + ioctl VIDIOC_G_EDID, VIDIOC_S_EDID, VIDIOC_SUBDEV_G_EDID, VIDIOC_SUBDEV_S_EDID &manvol; @@ -71,7 +71,8 @@ To get the EDID data the application has to fill in the pad, start_block, blocks and edid - fields and call VIDIOC_G_EDID. The current EDID from block + fields, zero the reserved array and call + VIDIOC_G_EDID. The current EDID from block start_block and of size blocks will be placed in the memory edid points to. The edid pointer must point to memory at least blocks * 128 bytes @@ -92,8 +93,9 @@ the driver will set blocks to 0 and it returns 0. To set the EDID blocks of a receiver the application has to fill in the pad, - blocks and edid fields and set - start_block to 0. It is not possible to set part of an EDID, + blocks and edid fields, set + start_block to 0 and zero the reserved array. + It is not possible to set part of an EDID, it is always all or nothing. Setting the EDID data is only valid for receivers as it makes no sense for a transmitter. From 0c53fee973a9adfa4ad21433d397adae12b2120b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Mar 2016 07:30:28 -0300 Subject: [PATCH 104/173] [media] vidioc-enum-dv-timings.xml: explicitly state that pad and reserved should be zeroed The ENUM_DV_TIMINGS documentation did not clearly state that the pad and reserved fields should be zeroed (pad only when used with a video device node). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml index 6e3cadd4e1f9..70ca76d1e6d2 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -61,8 +61,9 @@ of known supported timings. Call &VIDIOC-DV-TIMINGS-CAP; to check if it also sup standards or even custom timings that are not in this list. To query the available timings, applications initialize the -index field and zero the reserved array of &v4l2-enum-dv-timings; -and call the VIDIOC_ENUM_DV_TIMINGS ioctl on a video node with a +index field, set the pad field to 0, +zero the reserved array of &v4l2-enum-dv-timings; and call the +VIDIOC_ENUM_DV_TIMINGS ioctl on a video node with a pointer to this structure. Drivers fill the rest of the structure or return an &EINVAL; when the index is out of bounds. To enumerate all supported DV timings, applications shall begin at index zero, incrementing by one until the From c5c631416789004e7205a56ab1aaab2b3a011b1b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Mar 2016 07:30:29 -0300 Subject: [PATCH 105/173] [media] vidioc-dv-timings-cap.xml: explicitly state that pad and reserved should be zeroed The DV_TIMINGS_CAP documentation didn't state clearly that the pad and reserved fields should be zeroed by the application. For subdev pad can be other values as well. It also mistakenly said that only drivers would have to zero the reserved field, that's not correct. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/vidioc-dv-timings-cap.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml index a2017bfcaed2..b6f47a6b14d5 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -55,8 +55,9 @@ interface and may change in the future. - To query the capabilities of the DV receiver/transmitter applications -can call the VIDIOC_DV_TIMINGS_CAP ioctl on a video node + To query the capabilities of the DV receiver/transmitter applications initialize the +pad field to 0, zero the reserved array of &v4l2-dv-timings-cap; +and call the VIDIOC_DV_TIMINGS_CAP ioctl on a video node and the driver will fill in the structure. Note that drivers may return different values after switching the video input or output. @@ -65,8 +66,8 @@ queried by calling the VIDIOC_SUBDEV_DV_TIMINGS_CAP ioctl directly on a subdevice node. The capabilities are specific to inputs (for DV receivers) or outputs (for DV transmitters), applications must specify the desired pad number in the &v4l2-dv-timings-cap; pad -field. Attempts to query capabilities on a pad that doesn't support them will -return an &EINVAL;. +field and zero the reserved array. Attempts to query +capabilities on a pad that doesn't support them will return an &EINVAL;. struct <structname>v4l2_bt_timings_cap</structname> @@ -145,7 +146,8 @@ return an &EINVAL;. __u32 reserved[2] - Reserved for future extensions. Drivers must set the array to zero. + Reserved for future extensions. Drivers and applications must + set the array to zero. union From b05a4fa66a8d4589ba9f027db11d383250a4b774 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Sun, 27 Mar 2016 18:15:14 -0300 Subject: [PATCH 106/173] [media] Staging: media: bcm2048: defined region_configs[] array as const array This patch defines region_configs[] array as const array since it is not changed anywhere in code. Signed-off-by: Claudiu Beznea Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/bcm2048/radio-bcm2048.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index abf330f92c0b..8dade197f053 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -308,7 +308,7 @@ module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Minor number for radio device (-1 ==> auto assign)"); -static struct region_info region_configs[] = { +static const struct region_info region_configs[] = { /* USA */ { .channel_spacing = 20, From d0fadc869349e0469abc80109d4251be432736bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sat, 2 Apr 2016 14:42:18 -0300 Subject: [PATCH 107/173] [media] adv7180: Add g_std operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to get the standard to the adv7180 driver. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index ff57c1dcb8af..d680d7656e2f 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -434,6 +434,15 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return ret; } +static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct adv7180_state *state = to_state(sd); + + *norm = state->curr_norm; + + return 0; +} + static int adv7180_set_power(struct adv7180_state *state, bool on) { u8 val; @@ -719,6 +728,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, + .g_std = adv7180_g_std, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, From 64b3df9223aa4d0e809bbf5a8b006fcc884b7c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sat, 2 Apr 2016 14:42:19 -0300 Subject: [PATCH 108/173] [media] adv7180: Add cropcap operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to get the pixel aspect ratio depending on the current standard (50 vs 60 Hz). Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index d680d7656e2f..80ded7007e55 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -726,6 +726,21 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, return 0; } +static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap) +{ + struct adv7180_state *state = to_state(sd); + + if (state->curr_norm & V4L2_STD_525_60) { + cropcap->pixelaspect.numerator = 11; + cropcap->pixelaspect.denominator = 10; + } else { + cropcap->pixelaspect.numerator = 54; + cropcap->pixelaspect.denominator = 59; + } + + return 0; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, @@ -733,6 +748,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, .g_mbus_config = adv7180_g_mbus_config, + .cropcap = adv7180_cropcap, }; From bae4c757a5255a149249294dab3a6ab16e5d598b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sat, 2 Apr 2016 14:42:20 -0300 Subject: [PATCH 109/173] [media] adv7180: Add g_tvnorms operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV7180 supports NTSC, PAL and SECAM. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 80ded7007e55..51a92b3b20cc 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -741,6 +741,12 @@ static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap) return 0; } +static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + *norm = V4L2_STD_ALL; + return 0; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, @@ -749,9 +755,9 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_routing = adv7180_s_routing, .g_mbus_config = adv7180_g_mbus_config, .cropcap = adv7180_cropcap, + .g_tvnorms = adv7180_g_tvnorms, }; - static const struct v4l2_subdev_core_ops adv7180_core_ops = { .s_power = adv7180_s_power, }; From 96655553e5f9af6a8d908386685b7c865a138283 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Apr 2016 17:44:16 -0300 Subject: [PATCH 110/173] [media] v4l2-device.h: add v4l2_device_mask_ variants The v4l2_device_call_* defines filter subdevs based on the grp_id value. But some drivers use a bitmask, so instead of filtering by grp_id == value, you want to filter by grp_id & value. Make variants of these defines to do this. The 'has_op' define has been extended to have a grp_id argument as well, and a mask variant has been added. This extra argument required a change to go7007. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/go7007/go7007-v4l2.c | 2 +- include/media/v4l2-device.h | 55 +++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index 358c1c186d03..ea01ee5df60a 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -1125,7 +1125,7 @@ int go7007_v4l2_init(struct go7007 *go) vdev->queue = &go->vidq; video_set_drvdata(vdev, go); vdev->v4l2_dev = &go->v4l2_dev; - if (!v4l2_device_has_op(&go->v4l2_dev, video, querystd)) + if (!v4l2_device_has_op(&go->v4l2_dev, 0, video, querystd)) v4l2_disable_ioctl(vdev, VIDIOC_QUERYSTD); if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) { v4l2_disable_ioctl(vdev, VIDIOC_S_FREQUENCY); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 9c581578783f..d5d45a8d3998 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -196,11 +196,64 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, ##args); \ }) -#define v4l2_device_has_op(v4l2_dev, o, f) \ +/* + * Call the specified callback for all subdevs where grp_id & grpmsk != 0 + * (if grpmsk == `0, then match them all). Ignore any errors. Note that you + * cannot add or delete a subdev while walking the subdevs list. + */ +#define v4l2_device_mask_call_all(v4l2_dev, grpmsk, o, f, args...) \ + do { \ + struct v4l2_subdev *__sd; \ + \ + __v4l2_device_call_subdevs_p(v4l2_dev, __sd, \ + !(grpmsk) || (__sd->grp_id & (grpmsk)), o, f , \ + ##args); \ + } while (0) + +/* + * Call the specified callback for all subdevs where grp_id & grpmsk != 0 + * (if grpmsk == `0, then match them all). If the callback returns an error + * other than 0 or -ENOIOCTLCMD, then return with that error code. Note that + * you cannot add or delete a subdev while walking the subdevs list. + */ +#define v4l2_device_mask_call_until_err(v4l2_dev, grpmsk, o, f, args...) \ +({ \ + struct v4l2_subdev *__sd; \ + __v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd, \ + !(grpmsk) || (__sd->grp_id & (grpmsk)), o, f , \ + ##args); \ +}) + +/* + * Does any subdev with matching grpid (or all if grpid == 0) has the given + * op? + */ +#define v4l2_device_has_op(v4l2_dev, grpid, o, f) \ ({ \ struct v4l2_subdev *__sd; \ bool __result = false; \ list_for_each_entry(__sd, &(v4l2_dev)->subdevs, list) { \ + if ((grpid) && __sd->grp_id != (grpid)) \ + continue; \ + if (v4l2_subdev_has_op(__sd, o, f)) { \ + __result = true; \ + break; \ + } \ + } \ + __result; \ +}) + +/* + * Does any subdev with matching grpmsk (or all if grpmsk == 0) has the given + * op? + */ +#define v4l2_device_mask_has_op(v4l2_dev, grpmsk, o, f) \ +({ \ + struct v4l2_subdev *__sd; \ + bool __result = false; \ + list_for_each_entry(__sd, &(v4l2_dev)->subdevs, list) { \ + if ((grpmsk) && !(__sd->grp_id & (grpmsk))) \ + continue; \ if (v4l2_subdev_has_op(__sd, o, f)) { \ __result = true; \ break; \ From fe29301122902a902c5c323ee14078b1ab3f1ad1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Apr 2016 17:44:17 -0300 Subject: [PATCH 111/173] [media] ivtv/cx18: use the new mask variants of the v4l2_device_call_* defines Instead of rolling our own define, just use the new mask defines. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-driver.h | 13 ++----------- drivers/media/pci/ivtv/ivtv-driver.h | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h index 7e31f2a2e085..47ce80fa73b9 100644 --- a/drivers/media/pci/cx18/cx18-driver.h +++ b/drivers/media/pci/cx18/cx18-driver.h @@ -707,11 +707,7 @@ static inline int cx18_raw_vbi(const struct cx18 *cx) /* Call the specified callback for all subdevs with a grp_id bit matching the * mask in hw (if 0, then match them all). Ignore any errors. */ #define cx18_call_hw(cx, hw, o, f, args...) \ - do { \ - struct v4l2_subdev *__sd; \ - __v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd, \ - !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ - } while (0) + v4l2_device_mask_call_all(&(cx)->v4l2_dev, hw, o, f, ##args) #define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args) @@ -719,12 +715,7 @@ static inline int cx18_raw_vbi(const struct cx18 *cx) * mask in hw (if 0, then match them all). If the callback returns an error * other than 0 or -ENOIOCTLCMD, then return with that error code. */ #define cx18_call_hw_err(cx, hw, o, f, args...) \ -({ \ - struct v4l2_subdev *__sd; \ - __v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev, \ - __sd, !(hw) || (__sd->grp_id & (hw)), o, f, \ - ##args); \ -}) + v4l2_device_mask_call_until_err(&(cx)->v4l2_dev, hw, o, f, ##args) #define cx18_call_all_err(cx, o, f, args...) \ cx18_call_hw_err(cx, 0, o, f , ##args) diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h index 6c08dae67a73..10cba305dbd2 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.h +++ b/drivers/media/pci/ivtv/ivtv-driver.h @@ -827,12 +827,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) /* Call the specified callback for all subdevs matching hw (if 0, then match them all). Ignore any errors. */ #define ivtv_call_hw(itv, hw, o, f, args...) \ - do { \ - struct v4l2_subdev *__sd; \ - __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \ - !(hw) ? true : (__sd->grp_id & (hw)), \ - o, f, ##args); \ - } while (0) + v4l2_device_mask_call_all(&(itv)->v4l2_dev, hw, o, f, ##args) #define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) @@ -840,11 +835,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) match them all). If the callback returns an error other than 0 or -ENOIOCTLCMD, then return with that error code. */ #define ivtv_call_hw_err(itv, hw, o, f, args...) \ -({ \ - struct v4l2_subdev *__sd; \ - __v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \ - !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ -}) + v4l2_device_mask_call_until_err(&(itv)->v4l2_dev, hw, o, f, ##args) #define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) From ac49de8c49d770b5ded5c717c1eaf339d7649da1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Apr 2016 16:42:42 -0300 Subject: [PATCH 112/173] [media] v4l2-rect.h: new header with struct v4l2_rect helper functions This makes it easier to share this code with any driver that needs to manipulate the v4l2_rect datastructure. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/device-drivers.tmpl | 1 + include/media/v4l2-rect.h | 173 ++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 include/media/v4l2-rect.h diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 184f3c7b5145..893b2cabf7e4 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -233,6 +233,7 @@ X!Isound/sound_firmware.c !Iinclude/media/v4l2-mediabus.h !Iinclude/media/v4l2-mem2mem.h !Iinclude/media/v4l2-of.h +!Iinclude/media/v4l2-rect.h !Iinclude/media/v4l2-subdev.h !Iinclude/media/videobuf2-core.h !Iinclude/media/videobuf2-v4l2.h diff --git a/include/media/v4l2-rect.h b/include/media/v4l2-rect.h new file mode 100644 index 000000000000..d2125f0cc7cd --- /dev/null +++ b/include/media/v4l2-rect.h @@ -0,0 +1,173 @@ +/* + * v4l2-rect.h - v4l2_rect helper functions + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _V4L2_RECT_H_ +#define _V4L2_RECT_H_ + +#include + +/** + * v4l2_rect_set_size_to() - copy the width/height values. + * @r: rect whose width and height fields will be set + * @size: rect containing the width and height fields you need. + */ +static inline void v4l2_rect_set_size_to(struct v4l2_rect *r, + const struct v4l2_rect *size) +{ + r->width = size->width; + r->height = size->height; +} + +/** + * v4l2_rect_set_min_size() - width and height of r should be >= min_size. + * @r: rect whose width and height will be modified + * @min_size: rect containing the minimal width and height + */ +static inline void v4l2_rect_set_min_size(struct v4l2_rect *r, + const struct v4l2_rect *min_size) +{ + if (r->width < min_size->width) + r->width = min_size->width; + if (r->height < min_size->height) + r->height = min_size->height; +} + +/** + * v4l2_rect_set_max_size() - width and height of r should be <= max_size + * @r: rect whose width and height will be modified + * @max_size: rect containing the maximum width and height + */ +static inline void v4l2_rect_set_max_size(struct v4l2_rect *r, + const struct v4l2_rect *max_size) +{ + if (r->width > max_size->width) + r->width = max_size->width; + if (r->height > max_size->height) + r->height = max_size->height; +} + +/** + * v4l2_rect_map_inside()- r should be inside boundary. + * @r: rect that will be modified + * @boundary: rect containing the boundary for @r + */ +static inline void v4l2_rect_map_inside(struct v4l2_rect *r, + const struct v4l2_rect *boundary) +{ + v4l2_rect_set_max_size(r, boundary); + if (r->left < boundary->left) + r->left = boundary->left; + if (r->top < boundary->top) + r->top = boundary->top; + if (r->left + r->width > boundary->width) + r->left = boundary->width - r->width; + if (r->top + r->height > boundary->height) + r->top = boundary->height - r->height; +} + +/** + * v4l2_rect_same_size() - return true if r1 has the same size as r2 + * @r1: rectangle. + * @r2: rectangle. + * + * Return true if both rectangles have the same size. + */ +static inline bool v4l2_rect_same_size(const struct v4l2_rect *r1, + const struct v4l2_rect *r2) +{ + return r1->width == r2->width && r1->height == r2->height; +} + +/** + * v4l2_rect_intersect() - calculate the intersection of two rects. + * @r: intersection of @r1 and @r2. + * @r1: rectangle. + * @r2: rectangle. + */ +static inline void v4l2_rect_intersect(struct v4l2_rect *r, + const struct v4l2_rect *r1, + const struct v4l2_rect *r2) +{ + int right, bottom; + + r->top = max(r1->top, r2->top); + r->left = max(r1->left, r2->left); + bottom = min(r1->top + r1->height, r2->top + r2->height); + right = min(r1->left + r1->width, r2->left + r2->width); + r->height = max(0, bottom - r->top); + r->width = max(0, right - r->left); +} + +/** + * v4l2_rect_scale() - scale rect r by to/from + * @r: rect to be scaled. + * @from: from rectangle. + * @to: to rectangle. + * + * This scales rectangle @r horizontally by @to->width / @from->width and + * vertically by @to->height / @from->height. + * + * Typically @r is a rectangle inside @from and you want the rectangle as + * it would appear after scaling @from to @to. So the resulting @r will + * be the scaled rectangle inside @to. + */ +static inline void v4l2_rect_scale(struct v4l2_rect *r, + const struct v4l2_rect *from, + const struct v4l2_rect *to) +{ + if (from->width == 0 || from->height == 0) { + r->left = r->top = r->width = r->height = 0; + return; + } + r->left = (((r->left - from->left) * to->width) / from->width) & ~1; + r->width = ((r->width * to->width) / from->width) & ~1; + r->top = ((r->top - from->top) * to->height) / from->height; + r->height = (r->height * to->height) / from->height; +} + +/** + * v4l2_rect_overlap() - do r1 and r2 overlap? + * @r1: rectangle. + * @r2: rectangle. + * + * Returns true if @r1 and @r2 overlap. + */ +static inline bool v4l2_rect_overlap(const struct v4l2_rect *r1, + const struct v4l2_rect *r2) +{ + /* + * IF the left side of r1 is to the right of the right side of r2 OR + * the left side of r2 is to the right of the right side of r1 THEN + * they do not overlap. + */ + if (r1->left >= r2->left + r2->width || + r2->left >= r1->left + r1->width) + return false; + /* + * IF the top side of r1 is below the bottom of r2 OR + * the top side of r2 is below the bottom of r1 THEN + * they do not overlap. + */ + if (r1->top >= r2->top + r2->height || + r2->top >= r1->top + r1->height) + return false; + return true; +} + +#endif From d1e5d8bd49d9a830b6b5f4da906f868b2ceb83a4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Apr 2016 16:42:43 -0300 Subject: [PATCH 113/173] [media] vivid: use new v4l2-rect.h header The v4l2_rect helper functions have been moved to include/media/v4l2-rect.h. Use this new header, dropping the functions from vivid. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/vivid/vivid-kthread-cap.c | 13 ++- drivers/media/platform/vivid/vivid-vid-cap.c | 101 ++++++++--------- .../media/platform/vivid/vivid-vid-common.c | 97 ----------------- .../media/platform/vivid/vivid-vid-common.h | 9 -- drivers/media/platform/vivid/vivid-vid-out.c | 103 +++++++++--------- 5 files changed, 110 insertions(+), 213 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 9034281944a4..3b8c10108dfa 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "vivid-core.h" #include "vivid-vid-common.h" @@ -184,15 +185,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) dev->compose_out.width, dev->compose_out.height }; - dev->loop_vid_copy = rect_intersect(&dev->crop_cap, &dev->compose_out); + v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out); dev->loop_vid_out = dev->loop_vid_copy; - rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); + v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); dev->loop_vid_out.left += dev->crop_out.left; dev->loop_vid_out.top += dev->crop_out.top; dev->loop_vid_cap = dev->loop_vid_copy; - rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); + v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n", @@ -203,13 +204,13 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) dev->loop_vid_cap.width, dev->loop_vid_cap.height, dev->loop_vid_cap.left, dev->loop_vid_cap.top); - r_overlay = rect_intersect(&r_fb, &r_overlay); + v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay); /* shift r_overlay to the same origin as compose_out */ r_overlay.left += dev->compose_out.left - dev->overlay_out_left; r_overlay.top += dev->compose_out.top - dev->overlay_out_top; - dev->loop_vid_overlay = rect_intersect(&r_overlay, &dev->loop_vid_copy); + v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy); dev->loop_fb_copy = dev->loop_vid_overlay; /* shift dev->loop_fb_copy back again to the fb origin */ @@ -217,7 +218,7 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top; dev->loop_vid_overlay_cap = dev->loop_vid_overlay; - rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); + v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n", diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index b84f081c1b92..4f730f355a17 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "vivid-core.h" #include "vivid-vid-common.h" @@ -590,16 +591,16 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, } else { struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; - rect_set_min_size(&r, &vivid_min_rect); - rect_set_max_size(&r, &vivid_max_rect); + v4l2_rect_set_min_size(&r, &vivid_min_rect); + v4l2_rect_set_max_size(&r, &vivid_max_rect); if (dev->has_scaler_cap && !dev->has_compose_cap) { struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; - rect_set_max_size(&r, &max_r); + v4l2_rect_set_max_size(&r, &max_r); } else if (!dev->has_scaler_cap && dev->has_crop_cap && !dev->has_compose_cap) { - rect_set_max_size(&r, &dev->src_rect); + v4l2_rect_set_max_size(&r, &dev->src_rect); } else if (!dev->has_scaler_cap && !dev->has_crop_cap) { - rect_set_min_size(&r, &dev->src_rect); + v4l2_rect_set_min_size(&r, &dev->src_rect); } mp->width = r.width; mp->height = r.height / factor; @@ -668,7 +669,7 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, if (dev->has_scaler_cap) { if (dev->has_compose_cap) - rect_map_inside(compose, &r); + v4l2_rect_map_inside(compose, &r); else *compose = r; if (dev->has_crop_cap && !dev->has_compose_cap) { @@ -683,9 +684,9 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, factor * r.height * MAX_ZOOM }; - rect_set_min_size(crop, &min_r); - rect_set_max_size(crop, &max_r); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_min_size(crop, &min_r); + v4l2_rect_set_max_size(crop, &max_r); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); } else if (dev->has_crop_cap) { struct v4l2_rect min_r = { 0, 0, @@ -698,27 +699,27 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, factor * compose->height * MAX_ZOOM }; - rect_set_min_size(crop, &min_r); - rect_set_max_size(crop, &max_r); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_min_size(crop, &min_r); + v4l2_rect_set_max_size(crop, &max_r); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); } } else if (dev->has_crop_cap && !dev->has_compose_cap) { r.height *= factor; - rect_set_size_to(crop, &r); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_size_to(crop, &r); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); r = *crop; r.height /= factor; - rect_set_size_to(compose, &r); + v4l2_rect_set_size_to(compose, &r); } else if (!dev->has_crop_cap) { - rect_map_inside(compose, &r); + v4l2_rect_map_inside(compose, &r); } else { r.height *= factor; - rect_set_max_size(crop, &r); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_max_size(crop, &r); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); compose->top *= factor; compose->height *= factor; - rect_set_size_to(compose, crop); - rect_map_inside(compose, &r); + v4l2_rect_set_size_to(compose, crop); + v4l2_rect_map_inside(compose, &r); compose->top /= factor; compose->height /= factor; } @@ -735,9 +736,9 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, } else { struct v4l2_rect r = { 0, 0, mp->width, mp->height }; - rect_set_size_to(compose, &r); + v4l2_rect_set_size_to(compose, &r); r.height *= factor; - rect_set_size_to(crop, &r); + v4l2_rect_set_size_to(crop, &r); } dev->fmt_cap_rect.width = mp->width; @@ -886,9 +887,9 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection ret = vivid_vid_adjust_sel(s->flags, &s->r); if (ret) return ret; - rect_set_min_size(&s->r, &vivid_min_rect); - rect_set_max_size(&s->r, &dev->src_rect); - rect_map_inside(&s->r, &dev->crop_bounds_cap); + v4l2_rect_set_min_size(&s->r, &vivid_min_rect); + v4l2_rect_set_max_size(&s->r, &dev->src_rect); + v4l2_rect_map_inside(&s->r, &dev->crop_bounds_cap); s->r.top /= factor; s->r.height /= factor; if (dev->has_scaler_cap) { @@ -904,36 +905,36 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection s->r.height / MAX_ZOOM }; - rect_set_min_size(&fmt, &min_rect); + v4l2_rect_set_min_size(&fmt, &min_rect); if (!dev->has_compose_cap) - rect_set_max_size(&fmt, &max_rect); - if (!rect_same_size(&dev->fmt_cap_rect, &fmt) && + v4l2_rect_set_max_size(&fmt, &max_rect); + if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && vb2_is_busy(&dev->vb_vid_cap_q)) return -EBUSY; if (dev->has_compose_cap) { - rect_set_min_size(compose, &min_rect); - rect_set_max_size(compose, &max_rect); + v4l2_rect_set_min_size(compose, &min_rect); + v4l2_rect_set_max_size(compose, &max_rect); } dev->fmt_cap_rect = fmt; tpg_s_buf_height(&dev->tpg, fmt.height); } else if (dev->has_compose_cap) { struct v4l2_rect fmt = dev->fmt_cap_rect; - rect_set_min_size(&fmt, &s->r); - if (!rect_same_size(&dev->fmt_cap_rect, &fmt) && + v4l2_rect_set_min_size(&fmt, &s->r); + if (!v4l2_rect_same_size(&dev->fmt_cap_rect, &fmt) && vb2_is_busy(&dev->vb_vid_cap_q)) return -EBUSY; dev->fmt_cap_rect = fmt; tpg_s_buf_height(&dev->tpg, fmt.height); - rect_set_size_to(compose, &s->r); - rect_map_inside(compose, &dev->fmt_cap_rect); + v4l2_rect_set_size_to(compose, &s->r); + v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); } else { - if (!rect_same_size(&s->r, &dev->fmt_cap_rect) && + if (!v4l2_rect_same_size(&s->r, &dev->fmt_cap_rect) && vb2_is_busy(&dev->vb_vid_cap_q)) return -EBUSY; - rect_set_size_to(&dev->fmt_cap_rect, &s->r); - rect_set_size_to(compose, &s->r); - rect_map_inside(compose, &dev->fmt_cap_rect); + v4l2_rect_set_size_to(&dev->fmt_cap_rect, &s->r); + v4l2_rect_set_size_to(compose, &s->r); + v4l2_rect_map_inside(compose, &dev->fmt_cap_rect); tpg_s_buf_height(&dev->tpg, dev->fmt_cap_rect.height); } s->r.top *= factor; @@ -946,8 +947,8 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection ret = vivid_vid_adjust_sel(s->flags, &s->r); if (ret) return ret; - rect_set_min_size(&s->r, &vivid_min_rect); - rect_set_max_size(&s->r, &dev->fmt_cap_rect); + v4l2_rect_set_min_size(&s->r, &vivid_min_rect); + v4l2_rect_set_max_size(&s->r, &dev->fmt_cap_rect); if (dev->has_scaler_cap) { struct v4l2_rect max_rect = { 0, 0, @@ -955,7 +956,7 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection (dev->src_rect.height / factor) * MAX_ZOOM }; - rect_set_max_size(&s->r, &max_rect); + v4l2_rect_set_max_size(&s->r, &max_rect); if (dev->has_crop_cap) { struct v4l2_rect min_rect = { 0, 0, @@ -968,23 +969,23 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection (s->r.height * factor) * MAX_ZOOM }; - rect_set_min_size(crop, &min_rect); - rect_set_max_size(crop, &max_rect); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_min_size(crop, &min_rect); + v4l2_rect_set_max_size(crop, &max_rect); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); } } else if (dev->has_crop_cap) { s->r.top *= factor; s->r.height *= factor; - rect_set_max_size(&s->r, &dev->src_rect); - rect_set_size_to(crop, &s->r); - rect_map_inside(crop, &dev->crop_bounds_cap); + v4l2_rect_set_max_size(&s->r, &dev->src_rect); + v4l2_rect_set_size_to(crop, &s->r); + v4l2_rect_map_inside(crop, &dev->crop_bounds_cap); s->r.top /= factor; s->r.height /= factor; } else { - rect_set_size_to(&s->r, &dev->src_rect); + v4l2_rect_set_size_to(&s->r, &dev->src_rect); s->r.height /= factor; } - rect_map_inside(&s->r, &dev->fmt_cap_rect); + v4l2_rect_map_inside(&s->r, &dev->fmt_cap_rect); if (dev->bitmap_cap && (compose->width != s->r.width || compose->height != s->r.height)) { kfree(dev->bitmap_cap); @@ -1124,7 +1125,7 @@ int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, for (j = i + 1; j < win->clipcount; j++) { struct v4l2_rect *r2 = &dev->try_clips_cap[j].c; - if (rect_overlap(r1, r2)) + if (v4l2_rect_overlap(r1, r2)) return -EINVAL; } } diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index b0d4e3a0acf0..39ea2284789c 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -653,103 +653,6 @@ int fmt_sp2mp_func(struct file *file, void *priv, return ret; } -/* v4l2_rect helper function: copy the width/height values */ -void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size) -{ - r->width = size->width; - r->height = size->height; -} - -/* v4l2_rect helper function: width and height of r should be >= min_size */ -void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size) -{ - if (r->width < min_size->width) - r->width = min_size->width; - if (r->height < min_size->height) - r->height = min_size->height; -} - -/* v4l2_rect helper function: width and height of r should be <= max_size */ -void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size) -{ - if (r->width > max_size->width) - r->width = max_size->width; - if (r->height > max_size->height) - r->height = max_size->height; -} - -/* v4l2_rect helper function: r should be inside boundary */ -void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary) -{ - rect_set_max_size(r, boundary); - if (r->left < boundary->left) - r->left = boundary->left; - if (r->top < boundary->top) - r->top = boundary->top; - if (r->left + r->width > boundary->width) - r->left = boundary->width - r->width; - if (r->top + r->height > boundary->height) - r->top = boundary->height - r->height; -} - -/* v4l2_rect helper function: return true if r1 has the same size as r2 */ -bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->width == r2->width && r1->height == r2->height; -} - -/* v4l2_rect helper function: calculate the intersection of two rects */ -struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b) -{ - struct v4l2_rect r; - int right, bottom; - - r.top = max(a->top, b->top); - r.left = max(a->left, b->left); - bottom = min(a->top + a->height, b->top + b->height); - right = min(a->left + a->width, b->left + b->width); - r.height = max(0, bottom - r.top); - r.width = max(0, right - r.left); - return r; -} - -/* - * v4l2_rect helper function: scale rect r by to->width / from->width and - * to->height / from->height. - */ -void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from, - const struct v4l2_rect *to) -{ - if (from->width == 0 || from->height == 0) { - r->left = r->top = r->width = r->height = 0; - return; - } - r->left = (((r->left - from->left) * to->width) / from->width) & ~1; - r->width = ((r->width * to->width) / from->width) & ~1; - r->top = ((r->top - from->top) * to->height) / from->height; - r->height = (r->height * to->height) / from->height; -} - -bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - /* - * IF the left side of r1 is to the right of the right side of r2 OR - * the left side of r2 is to the right of the right side of r1 THEN - * they do not overlap. - */ - if (r1->left >= r2->left + r2->width || - r2->left >= r1->left + r1->width) - return false; - /* - * IF the top side of r1 is below the bottom of r2 OR - * the top side of r2 is below the bottom of r1 THEN - * they do not overlap. - */ - if (r1->top >= r2->top + r2->height || - r2->top >= r1->top + r1->height) - return false; - return true; -} int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r) { unsigned w = r->width; diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h index 3ec4fa85c9b9..4b6175eab8a2 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.h +++ b/drivers/media/platform/vivid/vivid-vid-common.h @@ -37,15 +37,6 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) bool vivid_vid_can_loop(struct vivid_dev *dev); void vivid_send_source_change(struct vivid_dev *dev, unsigned type); -bool rect_overlap(const struct v4l2_rect *r1, const struct v4l2_rect *r2); -void rect_set_size_to(struct v4l2_rect *r, const struct v4l2_rect *size); -void rect_set_min_size(struct v4l2_rect *r, const struct v4l2_rect *min_size); -void rect_set_max_size(struct v4l2_rect *r, const struct v4l2_rect *max_size); -void rect_map_inside(struct v4l2_rect *r, const struct v4l2_rect *boundary); -bool rect_same_size(const struct v4l2_rect *r1, const struct v4l2_rect *r2); -struct v4l2_rect rect_intersect(const struct v4l2_rect *a, const struct v4l2_rect *b); -void rect_scale(struct v4l2_rect *r, const struct v4l2_rect *from, - const struct v4l2_rect *to); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 64e4d66482c1..f92f4496d527 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "vivid-core.h" #include "vivid-vid-common.h" @@ -376,16 +377,16 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, } else { struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor }; - rect_set_min_size(&r, &vivid_min_rect); - rect_set_max_size(&r, &vivid_max_rect); + v4l2_rect_set_min_size(&r, &vivid_min_rect); + v4l2_rect_set_max_size(&r, &vivid_max_rect); if (dev->has_scaler_out && !dev->has_crop_out) { struct v4l2_rect max_r = { 0, 0, MAX_ZOOM * w, MAX_ZOOM * h }; - rect_set_max_size(&r, &max_r); + v4l2_rect_set_max_size(&r, &max_r); } else if (!dev->has_scaler_out && dev->has_compose_out && !dev->has_crop_out) { - rect_set_max_size(&r, &dev->sink_rect); + v4l2_rect_set_max_size(&r, &dev->sink_rect); } else if (!dev->has_scaler_out && !dev->has_compose_out) { - rect_set_min_size(&r, &dev->sink_rect); + v4l2_rect_set_min_size(&r, &dev->sink_rect); } mp->width = r.width; mp->height = r.height / factor; @@ -473,7 +474,7 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, if (dev->has_scaler_out) { if (dev->has_crop_out) - rect_map_inside(crop, &r); + v4l2_rect_map_inside(crop, &r); else *crop = r; if (dev->has_compose_out && !dev->has_crop_out) { @@ -488,9 +489,9 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, factor * r.height * MAX_ZOOM }; - rect_set_min_size(compose, &min_r); - rect_set_max_size(compose, &max_r); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_min_size(compose, &min_r); + v4l2_rect_set_max_size(compose, &max_r); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); } else if (dev->has_compose_out) { struct v4l2_rect min_r = { 0, 0, @@ -503,36 +504,36 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, factor * crop->height * MAX_ZOOM }; - rect_set_min_size(compose, &min_r); - rect_set_max_size(compose, &max_r); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_min_size(compose, &min_r); + v4l2_rect_set_max_size(compose, &max_r); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); } } else if (dev->has_compose_out && !dev->has_crop_out) { - rect_set_size_to(crop, &r); + v4l2_rect_set_size_to(crop, &r); r.height *= factor; - rect_set_size_to(compose, &r); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_size_to(compose, &r); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); } else if (!dev->has_compose_out) { - rect_map_inside(crop, &r); + v4l2_rect_map_inside(crop, &r); r.height /= factor; - rect_set_size_to(compose, &r); + v4l2_rect_set_size_to(compose, &r); } else { r.height *= factor; - rect_set_max_size(compose, &r); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_max_size(compose, &r); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); crop->top *= factor; crop->height *= factor; - rect_set_size_to(crop, compose); - rect_map_inside(crop, &r); + v4l2_rect_set_size_to(crop, compose); + v4l2_rect_map_inside(crop, &r); crop->top /= factor; crop->height /= factor; } } else { struct v4l2_rect r = { 0, 0, mp->width, mp->height }; - rect_set_size_to(crop, &r); + v4l2_rect_set_size_to(crop, &r); r.height /= factor; - rect_set_size_to(compose, &r); + v4l2_rect_set_size_to(compose, &r); } dev->fmt_out_rect.width = mp->width; @@ -683,8 +684,8 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection ret = vivid_vid_adjust_sel(s->flags, &s->r); if (ret) return ret; - rect_set_min_size(&s->r, &vivid_min_rect); - rect_set_max_size(&s->r, &dev->fmt_out_rect); + v4l2_rect_set_min_size(&s->r, &vivid_min_rect); + v4l2_rect_set_max_size(&s->r, &dev->fmt_out_rect); if (dev->has_scaler_out) { struct v4l2_rect max_rect = { 0, 0, @@ -692,7 +693,7 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection (dev->sink_rect.height / factor) * MAX_ZOOM }; - rect_set_max_size(&s->r, &max_rect); + v4l2_rect_set_max_size(&s->r, &max_rect); if (dev->has_compose_out) { struct v4l2_rect min_rect = { 0, 0, @@ -705,23 +706,23 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection (s->r.height * factor) * MAX_ZOOM }; - rect_set_min_size(compose, &min_rect); - rect_set_max_size(compose, &max_rect); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_min_size(compose, &min_rect); + v4l2_rect_set_max_size(compose, &max_rect); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); } } else if (dev->has_compose_out) { s->r.top *= factor; s->r.height *= factor; - rect_set_max_size(&s->r, &dev->sink_rect); - rect_set_size_to(compose, &s->r); - rect_map_inside(compose, &dev->compose_bounds_out); + v4l2_rect_set_max_size(&s->r, &dev->sink_rect); + v4l2_rect_set_size_to(compose, &s->r); + v4l2_rect_map_inside(compose, &dev->compose_bounds_out); s->r.top /= factor; s->r.height /= factor; } else { - rect_set_size_to(&s->r, &dev->sink_rect); + v4l2_rect_set_size_to(&s->r, &dev->sink_rect); s->r.height /= factor; } - rect_map_inside(&s->r, &dev->fmt_out_rect); + v4l2_rect_map_inside(&s->r, &dev->fmt_out_rect); *crop = s->r; break; case V4L2_SEL_TGT_COMPOSE: @@ -730,9 +731,9 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection ret = vivid_vid_adjust_sel(s->flags, &s->r); if (ret) return ret; - rect_set_min_size(&s->r, &vivid_min_rect); - rect_set_max_size(&s->r, &dev->sink_rect); - rect_map_inside(&s->r, &dev->compose_bounds_out); + v4l2_rect_set_min_size(&s->r, &vivid_min_rect); + v4l2_rect_set_max_size(&s->r, &dev->sink_rect); + v4l2_rect_map_inside(&s->r, &dev->compose_bounds_out); s->r.top /= factor; s->r.height /= factor; if (dev->has_scaler_out) { @@ -748,35 +749,35 @@ int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection s->r.height / MAX_ZOOM }; - rect_set_min_size(&fmt, &min_rect); + v4l2_rect_set_min_size(&fmt, &min_rect); if (!dev->has_crop_out) - rect_set_max_size(&fmt, &max_rect); - if (!rect_same_size(&dev->fmt_out_rect, &fmt) && + v4l2_rect_set_max_size(&fmt, &max_rect); + if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && vb2_is_busy(&dev->vb_vid_out_q)) return -EBUSY; if (dev->has_crop_out) { - rect_set_min_size(crop, &min_rect); - rect_set_max_size(crop, &max_rect); + v4l2_rect_set_min_size(crop, &min_rect); + v4l2_rect_set_max_size(crop, &max_rect); } dev->fmt_out_rect = fmt; } else if (dev->has_crop_out) { struct v4l2_rect fmt = dev->fmt_out_rect; - rect_set_min_size(&fmt, &s->r); - if (!rect_same_size(&dev->fmt_out_rect, &fmt) && + v4l2_rect_set_min_size(&fmt, &s->r); + if (!v4l2_rect_same_size(&dev->fmt_out_rect, &fmt) && vb2_is_busy(&dev->vb_vid_out_q)) return -EBUSY; dev->fmt_out_rect = fmt; - rect_set_size_to(crop, &s->r); - rect_map_inside(crop, &dev->fmt_out_rect); + v4l2_rect_set_size_to(crop, &s->r); + v4l2_rect_map_inside(crop, &dev->fmt_out_rect); } else { - if (!rect_same_size(&s->r, &dev->fmt_out_rect) && + if (!v4l2_rect_same_size(&s->r, &dev->fmt_out_rect) && vb2_is_busy(&dev->vb_vid_out_q)) return -EBUSY; - rect_set_size_to(&dev->fmt_out_rect, &s->r); - rect_set_size_to(crop, &s->r); + v4l2_rect_set_size_to(&dev->fmt_out_rect, &s->r); + v4l2_rect_set_size_to(crop, &s->r); crop->height /= factor; - rect_map_inside(crop, &dev->fmt_out_rect); + v4l2_rect_map_inside(crop, &dev->fmt_out_rect); } s->r.top *= factor; s->r.height *= factor; @@ -901,7 +902,7 @@ int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, for (j = i + 1; j < win->clipcount; j++) { struct v4l2_rect *r2 = &dev->try_clips_out[j].c; - if (rect_overlap(r1, r2)) + if (v4l2_rect_overlap(r1, r2)) return -EINVAL; } } From e07d46e7e0da86c146f199dae76f879096bc436a Mon Sep 17 00:00:00 2001 From: Helen Mae Koike Fornazier Date: Fri, 8 Apr 2016 17:28:58 -0300 Subject: [PATCH 114/173] [media] tpg: Export the tpg code from vivid as a module The test pattern generator will be used by other drivers as the virtual media controller (vimc) Signed-off-by: Helen Mae Koike Fornazier Acked-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 1 + drivers/media/common/Makefile | 2 +- drivers/media/common/v4l2-tpg/Kconfig | 2 ++ drivers/media/common/v4l2-tpg/Makefile | 3 +++ .../v4l2-tpg/v4l2-tpg-colors.c} | 7 +++--- .../v4l2-tpg/v4l2-tpg-core.c} | 25 +++++++++++++++++-- drivers/media/platform/vivid/Kconfig | 1 + drivers/media/platform/vivid/Makefile | 2 +- drivers/media/platform/vivid/vivid-core.h | 2 +- .../media/v4l2-tpg-colors.h | 6 ++--- .../vivid-tpg.h => include/media/v4l2-tpg.h | 9 +++---- 11 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 drivers/media/common/v4l2-tpg/Kconfig create mode 100644 drivers/media/common/v4l2-tpg/Makefile rename drivers/media/{platform/vivid/vivid-tpg-colors.c => common/v4l2-tpg/v4l2-tpg-colors.c} (99%) rename drivers/media/{platform/vivid/vivid-tpg.c => common/v4l2-tpg/v4l2-tpg-core.c} (98%) rename drivers/media/platform/vivid/vivid-tpg-colors.h => include/media/v4l2-tpg-colors.h (93%) rename drivers/media/platform/vivid/vivid-tpg.h => include/media/v4l2-tpg.h (99%) diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 21154dd87b0b..326df0ad75c0 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -19,3 +19,4 @@ config CYPRESS_FIRMWARE source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" +source "drivers/media/common/v4l2-tpg/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 89b795df2cdd..2d1b0a025084 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,4 +1,4 @@ -obj-y += b2c2/ saa7146/ siano/ +obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o diff --git a/drivers/media/common/v4l2-tpg/Kconfig b/drivers/media/common/v4l2-tpg/Kconfig new file mode 100644 index 000000000000..7456fc1c41ed --- /dev/null +++ b/drivers/media/common/v4l2-tpg/Kconfig @@ -0,0 +1,2 @@ +config VIDEO_V4L2_TPG + tristate diff --git a/drivers/media/common/v4l2-tpg/Makefile b/drivers/media/common/v4l2-tpg/Makefile new file mode 100644 index 000000000000..f588df466ae3 --- /dev/null +++ b/drivers/media/common/v4l2-tpg/Makefile @@ -0,0 +1,3 @@ +v4l2-tpg-objs := v4l2-tpg-core.o v4l2-tpg-colors.o + +obj-$(CONFIG_VIDEO_V4L2_TPG) += v4l2-tpg.o diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c similarity index 99% rename from drivers/media/platform/vivid/vivid-tpg-colors.c rename to drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c index 2299f0ce47c8..9bcbd318489b 100644 --- a/drivers/media/platform/vivid/vivid-tpg-colors.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c @@ -1,5 +1,5 @@ /* - * vivid-color.c - A table that converts colors to various colorspaces + * v4l2-tpg-colors.c - A table that converts colors to various colorspaces * * The test pattern generator uses the tpg_colors for its test patterns. * For testing colorspaces the first 8 colors of that table need to be @@ -12,7 +12,7 @@ * This source also contains the code used to generate the tpg_csc_colors * table. Run the following command to compile it: * - * gcc vivid-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm + * gcc v4l2-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm * * and run the utility. * @@ -36,8 +36,7 @@ */ #include - -#include "vivid-tpg-colors.h" +#include /* sRGB colors with range [0-255] */ const struct color tpg_colors[TPG_COLOR_MAX] = { diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c similarity index 98% rename from drivers/media/platform/vivid/vivid-tpg.c rename to drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index da862bb2e5f8..cf1dadd0be9e 100644 --- a/drivers/media/platform/vivid/vivid-tpg.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1,5 +1,5 @@ /* - * vivid-tpg.c - Test Pattern Generator + * v4l2-tpg-core.c - Test Pattern Generator * * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the * vivi.c source for the copyright information of those functions. @@ -20,7 +20,8 @@ * SOFTWARE. */ -#include "vivid-tpg.h" +#include +#include /* Must remain in sync with enum tpg_pattern */ const char * const tpg_pattern_strings[] = { @@ -48,6 +49,7 @@ const char * const tpg_pattern_strings[] = { "Noise", NULL }; +EXPORT_SYMBOL_GPL(tpg_pattern_strings); /* Must remain in sync with enum tpg_aspect */ const char * const tpg_aspect_strings[] = { @@ -58,6 +60,7 @@ const char * const tpg_aspect_strings[] = { "16x9 Anamorphic", NULL }; +EXPORT_SYMBOL_GPL(tpg_aspect_strings); /* * Sine table: sin[0] = 127 * sin(-180 degrees) @@ -93,6 +96,7 @@ void tpg_set_font(const u8 *f) { font8x16 = f; } +EXPORT_SYMBOL_GPL(tpg_set_font); void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h) { @@ -114,6 +118,7 @@ void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h) tpg->colorspace = V4L2_COLORSPACE_SRGB; tpg->perc_fill = 100; } +EXPORT_SYMBOL_GPL(tpg_init); int tpg_alloc(struct tpg_data *tpg, unsigned max_w) { @@ -150,6 +155,7 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w) } return 0; } +EXPORT_SYMBOL_GPL(tpg_alloc); void tpg_free(struct tpg_data *tpg) { @@ -174,6 +180,7 @@ void tpg_free(struct tpg_data *tpg) tpg->random_line[plane] = NULL; } } +EXPORT_SYMBOL_GPL(tpg_free); bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) { @@ -403,6 +410,7 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) } return true; } +EXPORT_SYMBOL_GPL(tpg_s_fourcc); void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, const struct v4l2_rect *compose) @@ -418,6 +426,7 @@ void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, tpg->scaled_width = 2; tpg->recalc_lines = true; } +EXPORT_SYMBOL_GPL(tpg_s_crop_compose); void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, u32 field) @@ -442,6 +451,7 @@ void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, (2 * tpg->hdownsampling[p]); tpg->recalc_square_border = true; } +EXPORT_SYMBOL_GPL(tpg_reset_source); static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg) { @@ -1250,6 +1260,7 @@ unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line) return 0; } } +EXPORT_SYMBOL_GPL(tpg_g_interleaved_plane); /* Return how many pattern lines are used by the current pattern. */ static unsigned tpg_get_pat_lines(const struct tpg_data *tpg) @@ -1725,6 +1736,7 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], } } } +EXPORT_SYMBOL_GPL(tpg_gen_text); void tpg_update_mv_step(struct tpg_data *tpg) { @@ -1773,6 +1785,7 @@ void tpg_update_mv_step(struct tpg_data *tpg) if (factor < 0) tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step; } +EXPORT_SYMBOL_GPL(tpg_update_mv_step); /* Map the line number relative to the crop rectangle to a frame line number */ static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y, @@ -1862,6 +1875,7 @@ void tpg_calc_text_basep(struct tpg_data *tpg, if (p == 0 && tpg->interleaved) tpg_calc_text_basep(tpg, basep, 1, vbuf); } +EXPORT_SYMBOL_GPL(tpg_calc_text_basep); static int tpg_pattern_avg(const struct tpg_data *tpg, unsigned pat1, unsigned pat2) @@ -1891,6 +1905,7 @@ void tpg_log_status(struct tpg_data *tpg) pr_info("tpg quantization: %d/%d\n", tpg->quantization, tpg->real_quantization); pr_info("tpg RGB range: %d/%d\n", tpg->rgb_range, tpg->real_rgb_range); } +EXPORT_SYMBOL_GPL(tpg_log_status); /* * This struct contains common parameters used by both the drawing of the @@ -2296,6 +2311,7 @@ void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, vbuf + buf_line * params.stride); } } +EXPORT_SYMBOL_GPL(tpg_fill_plane_buffer); void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) { @@ -2312,3 +2328,8 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) offset += tpg_calc_plane_size(tpg, i); } } +EXPORT_SYMBOL_GPL(tpg_fillbuffer); + +MODULE_DESCRIPTION("V4L2 Test Pattern Generator"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig index 0885e93ad436..f535f576913d 100644 --- a/drivers/media/platform/vivid/Kconfig +++ b/drivers/media/platform/vivid/Kconfig @@ -7,6 +7,7 @@ config VIDEO_VIVID select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select VIDEOBUF2_VMALLOC + select VIDEO_V4L2_TPG default n ---help--- Enables a virtual video driver. This driver emulates a webcam, diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile index 756fc12851df..633c8a1b2c27 100644 --- a/drivers/media/platform/vivid/Makefile +++ b/drivers/media/platform/vivid/Makefile @@ -2,5 +2,5 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ - vivid-osd.o vivid-tpg.o vivid-tpg-colors.o + vivid-osd.o obj-$(CONFIG_VIDEO_VIVID) += vivid.o diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index 751c1ba391e9..776783bec227 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -25,7 +25,7 @@ #include #include #include -#include "vivid-tpg.h" +#include #include "vivid-rds-gen.h" #include "vivid-vbi-gen.h" diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.h b/include/media/v4l2-tpg-colors.h similarity index 93% rename from drivers/media/platform/vivid/vivid-tpg-colors.h rename to include/media/v4l2-tpg-colors.h index 4e5a76a1e25b..2a88d1fae0cd 100644 --- a/drivers/media/platform/vivid/vivid-tpg-colors.h +++ b/include/media/v4l2-tpg-colors.h @@ -1,5 +1,5 @@ /* - * vivid-color.h - Color definitions for the test pattern generator + * v4l2-tpg-colors.h - Color definitions for the test pattern generator * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * @@ -17,8 +17,8 @@ * SOFTWARE. */ -#ifndef _VIVID_COLORS_H_ -#define _VIVID_COLORS_H_ +#ifndef _V4L2_TPG_COLORS_H_ +#define _V4L2_TPG_COLORS_H_ struct color { unsigned char r, g, b; diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/include/media/v4l2-tpg.h similarity index 99% rename from drivers/media/platform/vivid/vivid-tpg.h rename to include/media/v4l2-tpg.h index 93fbaee69675..329bebfa930c 100644 --- a/drivers/media/platform/vivid/vivid-tpg.h +++ b/include/media/v4l2-tpg.h @@ -1,5 +1,5 @@ /* - * vivid-tpg.h - Test Pattern Generator + * v4l2-tpg.h - Test Pattern Generator * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * @@ -17,8 +17,8 @@ * SOFTWARE. */ -#ifndef _VIVID_TPG_H_ -#define _VIVID_TPG_H_ +#ifndef _V4L2_TPG_H_ +#define _V4L2_TPG_H_ #include #include @@ -26,8 +26,7 @@ #include #include #include - -#include "vivid-tpg-colors.h" +#include enum tpg_pattern { TPG_PAT_75_COLORBAR, From bde569874b2afa6571eee6c373b236a8cd292115 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 21 Apr 2016 03:23:58 -0300 Subject: [PATCH 115/173] [media] tw686x-video: test for 60Hz instead of 50Hz When determining if the standard is 50 or 60 Hz it is standard practice to test for 60 Hz instead of 50 Hz. This doesn't matter normally, except if the user specifies both 60 and 50 Hz standards. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 118e9fac9f28..60d38f19134b 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -25,7 +25,7 @@ #define TW686X_INPUTS_PER_CH 4 #define TW686X_VIDEO_WIDTH 720 -#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_625_50) ? 576 : 480) +#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576) static const struct tw686x_format formats[] = { { @@ -517,10 +517,10 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) reg_write(vc->dev, SDT[vc->ch], val); val = reg_read(vc->dev, VIDEO_CONTROL1); - if (id & V4L2_STD_625_50) - val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); - else + if (id & V4L2_STD_525_60) val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch)); + else + val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); reg_write(vc->dev, VIDEO_CONTROL1, val); /* From 57c7598711b820b4253bcee94ec750ebc1c63af6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 06:06:58 -0300 Subject: [PATCH 116/173] [media] videodev2.h: remove 'experimental' annotations Most of what is marked as 'experimental' has been around for years. Time to drop that annotation. The only remaining 'experimental' bits of the API are the debug ioctls and structs: these should remain experimental since the only application that should use this is v4l2-dbg. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 38 +++++++++------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e895975c5b0e..8f951917be74 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -138,10 +138,7 @@ enum v4l2_buf_type { V4L2_BUF_TYPE_VBI_OUTPUT = 5, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, -#if 1 - /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, -#endif V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, V4L2_BUF_TYPE_SDR_CAPTURE = 11, @@ -657,8 +654,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_COMPRESSED 0x0001 #define V4L2_FMT_FLAG_EMULATED 0x0002 -#if 1 - /* Experimental Frame Size and frame rate enumeration */ + /* Frame Size and frame rate enumeration */ /* * F R A M E S I Z E E N U M E R A T I O N */ @@ -724,7 +720,6 @@ struct v4l2_frmivalenum { __u32 reserved[2]; /* Reserved space for future use */ }; -#endif /* * T I M E C O D E @@ -1728,8 +1723,6 @@ struct v4l2_audioout { /* * M P E G S E R V I C E S - * - * NOTE: EXPERIMENTAL API */ #if 1 #define V4L2_ENC_IDX_FRAME_I (0) @@ -2259,46 +2252,35 @@ struct v4l2_create_buffers { #define VIDIOC_ENCODER_CMD _IOWR('V', 77, struct v4l2_encoder_cmd) #define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) -/* Experimental, meant for debugging, testing and internal use. - Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined. - You must be root to use these ioctls. Never use these in applications! */ +/* + * Experimental, meant for debugging, testing and internal use. + * Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined. + * You must be root to use these ioctls. Never use these in applications! + */ #define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register) #define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_dbg_register) #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) - #define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) #define VIDIOC_DQEVENT _IOR('V', 89, struct v4l2_event) #define VIDIOC_SUBSCRIBE_EVENT _IOW('V', 90, struct v4l2_event_subscription) #define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription) - -/* Experimental, the below two ioctls may change over the next couple of kernel - versions */ #define VIDIOC_CREATE_BUFS _IOWR('V', 92, struct v4l2_create_buffers) #define VIDIOC_PREPARE_BUF _IOWR('V', 93, struct v4l2_buffer) - -/* Experimental selection API */ #define VIDIOC_G_SELECTION _IOWR('V', 94, struct v4l2_selection) #define VIDIOC_S_SELECTION _IOWR('V', 95, struct v4l2_selection) - -/* Experimental, these two ioctls may change over the next couple of kernel - versions. */ #define VIDIOC_DECODER_CMD _IOWR('V', 96, struct v4l2_decoder_cmd) #define VIDIOC_TRY_DECODER_CMD _IOWR('V', 97, struct v4l2_decoder_cmd) - -/* Experimental, these three ioctls may change over the next couple of kernel - versions. */ #define VIDIOC_ENUM_DV_TIMINGS _IOWR('V', 98, struct v4l2_enum_dv_timings) #define VIDIOC_QUERY_DV_TIMINGS _IOR('V', 99, struct v4l2_dv_timings) #define VIDIOC_DV_TIMINGS_CAP _IOWR('V', 100, struct v4l2_dv_timings_cap) - -/* Experimental, this ioctl may change over the next couple of kernel - versions. */ #define VIDIOC_ENUM_FREQ_BANDS _IOWR('V', 101, struct v4l2_frequency_band) -/* Experimental, meant for debugging, testing and internal use. - Never use these in applications! */ +/* + * Experimental, meant for debugging, testing and internal use. + * Never use this in applications! + */ #define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info) #define VIDIOC_QUERY_EXT_CTRL _IOWR('V', 103, struct v4l2_query_ext_ctrl) From 079dda54fe6e43b3db919e3ac3838a1355464c78 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 06:06:59 -0300 Subject: [PATCH 117/173] [media] DocBook media: drop 'experimental' annotations Drop the 'experimental' annotations. The only remaining part of the API that is still marked 'experimental' are the debug ioctls/structs, and that is intentional. Only the v4l2-dbg application should use those. All others have been around for years, so it is time to drop the 'experimental' designation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 38 ------------------- Documentation/DocBook/media/v4l/controls.xml | 31 --------------- Documentation/DocBook/media/v4l/dev-sdr.xml | 6 --- .../DocBook/media/v4l/dev-subdev.xml | 6 --- Documentation/DocBook/media/v4l/io.xml | 6 --- .../DocBook/media/v4l/selection-api.xml | 9 +---- .../DocBook/media/v4l/subdev-formats.xml | 6 --- .../DocBook/media/v4l/vidioc-create-bufs.xml | 6 --- .../media/v4l/vidioc-dv-timings-cap.xml | 6 --- .../media/v4l/vidioc-enum-dv-timings.xml | 6 --- .../media/v4l/vidioc-enum-freq-bands.xml | 6 --- .../DocBook/media/v4l/vidioc-expbuf.xml | 6 --- .../DocBook/media/v4l/vidioc-g-selection.xml | 6 --- .../DocBook/media/v4l/vidioc-prepare-buf.xml | 6 --- .../media/v4l/vidioc-query-dv-timings.xml | 6 --- .../v4l/vidioc-subdev-enum-frame-interval.xml | 6 --- .../v4l/vidioc-subdev-enum-frame-size.xml | 6 --- .../v4l/vidioc-subdev-enum-mbus-code.xml | 6 --- .../DocBook/media/v4l/vidioc-subdev-g-fmt.xml | 6 --- .../v4l/vidioc-subdev-g-frame-interval.xml | 6 --- .../media/v4l/vidioc-subdev-g-selection.xml | 6 --- 21 files changed, 1 insertion(+), 185 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 5399e8904715..82fa328abd58 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2685,10 +2685,6 @@ hardware may support both. and may change in the future. - - Video Output Overlay (OSD) Interface, . - &VIDIOC-DBG-G-REGISTER; and &VIDIOC-DBG-S-REGISTER; ioctls. @@ -2696,40 +2692,6 @@ ioctls. &VIDIOC-DBG-G-CHIP-INFO; ioctl. - - &VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and - &VIDIOC-DV-TIMINGS-CAP; ioctls. - - - Flash API. - - - &VIDIOC-CREATE-BUFS; and &VIDIOC-PREPARE-BUF; ioctls. - - - Selection API. - - - Sub-device selection API: &VIDIOC-SUBDEV-G-SELECTION; - and &VIDIOC-SUBDEV-S-SELECTION; ioctls. - - - Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl. - - - Vendor and device specific media bus pixel formats. - . - - - Importing DMABUF file descriptors as a new IO method described - in . - - - Exporting DMABUF files using &VIDIOC-EXPBUF; ioctl. - - - Software Defined Radio (SDR) Interface, . - diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 361040e6b0f4..81efa883f67d 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -4272,13 +4272,6 @@ manually or automatically if set to zero. Unit, range and step are driver-specif
Flash Control Reference - - Experimental - - This is an experimental -interface and may change in the future. - - The V4L2 flash controls are intended to provide generic access to flash controller devices. Flash controller devices are @@ -4743,14 +4736,6 @@ interface and may change in the future.
Image Source Control Reference - - Experimental - - This is an experimental interface and may - change in the future. - - The Image Source control class is intended for low-level control of image source devices such as image sensors. The @@ -4862,14 +4847,6 @@ interface and may change in the future.
Image Process Control Reference - - Experimental - - This is an experimental interface and may - change in the future. - - The Image Process control class is intended for low-level control of image processing functions. Unlike @@ -4955,14 +4932,6 @@ interface and may change in the future.
Digital Video Control Reference - - Experimental - - This is an experimental interface and may - change in the future. - - The Digital Video control class is intended to control receivers and transmitters for VGA, diff --git a/Documentation/DocBook/media/v4l/dev-sdr.xml b/Documentation/DocBook/media/v4l/dev-sdr.xml index a659771f7b7c..6da1157fb5bd 100644 --- a/Documentation/DocBook/media/v4l/dev-sdr.xml +++ b/Documentation/DocBook/media/v4l/dev-sdr.xml @@ -1,11 +1,5 @@ Software Defined Radio Interface (SDR) - - Experimental - This is an experimental - interface and may change in the future. - - SDR is an abbreviation of Software Defined Radio, the radio device which uses application software for modulation or demodulation. This interface diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml index 4f0ba58c9bd9..f4bc27af83eb 100644 --- a/Documentation/DocBook/media/v4l/dev-subdev.xml +++ b/Documentation/DocBook/media/v4l/dev-subdev.xml @@ -1,11 +1,5 @@ Sub-device Interface - - Experimental - This is an experimental - interface and may change in the future. - - The complex nature of V4L2 devices, where hardware is often made of several integrated circuits that need to interact with each other in a controlled way, leads to complex V4L2 drivers. The drivers usually reflect diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 144158b3a5ac..e09025db92bd 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -475,12 +475,6 @@ rest should be evident.
Streaming I/O (DMA buffer importing) - - Experimental - This is an experimental - interface and may change in the future. - - The DMABUF framework provides a generic method for sharing buffers between multiple devices. Device drivers that support DMABUF can export a DMA buffer to userspace as a file descriptor (known as the exporter role), import a diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml index 28cbded766c9..b764cba150d1 100644 --- a/Documentation/DocBook/media/v4l/selection-api.xml +++ b/Documentation/DocBook/media/v4l/selection-api.xml @@ -1,13 +1,6 @@
- Experimental API for cropping, composing and scaling - - - Experimental - - This is an experimental -interface and may change in the future. - + API for cropping, composing and scaling
Introduction diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 4e73345e3eab..199c84e3aede 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -4002,12 +4002,6 @@ see .
Vendor and Device Specific Formats - - Experimental - This is an experimental -interface and may change in the future. - - This section lists complex data formats that are either vendor or device specific. diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index d81fa0d4016b..6528e97b8990 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - This ioctl is used to create buffers for memory mapped or user pointer or DMA buffer I/O. It can be used as an alternative or in diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml index b6f47a6b14d5..ca9ffce9b4c1 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - To query the capabilities of the DV receiver/transmitter applications initialize the pad field to 0, zero the reserved array of &v4l2-dv-timings-cap; and call the VIDIOC_DV_TIMINGS_CAP ioctl on a video node diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml index 70ca76d1e6d2..9b3d42018b69 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - While some DV receivers or transmitters support a wide range of timings, others support only a limited number of timings. With this ioctl applications can enumerate a list of known supported timings. Call &VIDIOC-DV-TIMINGS-CAP; to check if it also supports other diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml index 4e8ea65f7282..a0608abc1ab8 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - Enumerates the frequency bands that a tuner or modulator supports. To do this applications initialize the tuner, type and index fields, diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml index 0ae0b6a915d0..a6558a676ef3 100644 --- a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - This ioctl is an extension to the memory mapping I/O method, therefore it is available only for V4L2_MEMORY_MMAP buffers. It can be used to export a diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml index 7865351688da..9523bc5650f9 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml @@ -50,12 +50,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - The ioctls are used to query and configure selection rectangles. To query the cropping (composing) rectangle set &v4l2-selection; diff --git a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml index fa7ad7e33228..7bde698760e4 100644 --- a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml @@ -48,12 +48,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - Applications can optionally call the VIDIOC_PREPARE_BUF ioctl to pass ownership of the buffer to the driver before actually enqueuing it, using the diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml index 0c93677d16b4..d41bf47ee5a2 100644 --- a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml @@ -50,12 +50,6 @@ input Description - - Experimental - This is an experimental - interface and may change in the future. - - The hardware may be able to detect the current DV timings automatically, similar to sensing the video standard. To do so, applications call VIDIOC_QUERY_DV_TIMINGS with a pointer to a diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml index cff59f5cbf04..9d0251a27e5f 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - This ioctl lets applications enumerate available frame intervals on a given sub-device pad. Frame intervals only makes sense for sub-devices that can control the frame period on their own. This includes, for instance, diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml index abd545ede67a..9b91b8332ba9 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - This ioctl allows applications to enumerate all frame sizes supported by a sub-device on the given pad for the given media bus format. Supported formats can be retrieved with the &VIDIOC-SUBDEV-ENUM-MBUS-CODE; diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml index 0bcb278fd062..c67256ada87a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - To enumerate media bus formats available at a given sub-device pad applications initialize the pad, which and index fields of &v4l2-subdev-mbus-code-enum; and diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml index a67cde6f8c54..781089cba453 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml @@ -50,12 +50,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - These ioctls are used to negotiate the frame format at specific subdev pads in the image pipeline. diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml index 0bc3ea22d31f..848ec789ddaa 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml @@ -50,12 +50,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - These ioctls are used to get and set the frame interval at specific subdev pads in the image pipeline. The frame interval only makes sense for sub-devices that can control the frame period on their own. This includes, diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml index c62a7360719b..8346b2e4a703 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml @@ -49,12 +49,6 @@ Description - - Experimental - This is an experimental - interface and may change in the future. - - The selections are used to configure various image processing functionality performed by the subdevs which affect the image size. This currently includes cropping, scaling and From 20e01b264cc8557def19671defbad275828132f1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Apr 2016 07:03:30 -0300 Subject: [PATCH 118/173] [media] cx231xx: silence uninitialized variable warning We print an uninitialized "actlen" variable on the error path. Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index f497888d94bf..6741fd02b50f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -752,7 +752,8 @@ EXPORT_SYMBOL_GPL(cx231xx_set_mode); int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) { int errCode = 0; - int actlen, ret = -ENOMEM; + int actlen = -1; + int ret = -ENOMEM; u32 *buffer; buffer = kzalloc(4096, GFP_KERNEL); From 937feeed3f0ae8a0389d5732f6db63dd912acd99 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 10:03:37 -0300 Subject: [PATCH 119/173] [media] adv7180: fix broken standards handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adv7180 attempts to autodetect the standard. Unfortunately this is seriously broken. This patch removes the autodetect completely. Only the querystd op will detect the standard. Since the design of the adv7180 requires that you switch to a special autodetect mode you cannot call querystd when you are streaming. So the s_stream op has been added so we know whether we are streaming or not, and if we are, then querystd returns EBUSY. After testing this with a signal generator is became obvious that a sleep is needed between changing the standard to autodetect and reading the status. So the autodetect can never have worked well. The s_std call now just sets the new standard without any querying. If the driver supports the interrupt, then when it detects a standard change it will signal that by sending the V4L2_EVENT_SOURCE_CHANGE event. With these changes this driver now behaves like all other video receivers. Signed-off-by: Hans Verkuil Acked-by: Federico Vaga Tested-by: Niklas Söderlund Acked-by: Lars-Peter Clausen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 118 ++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 51a92b3b20cc..5a75a9154e2e 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -26,8 +26,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -192,8 +193,8 @@ struct adv7180_state { struct mutex mutex; /* mutual excl. when accessing chip */ int irq; v4l2_std_id curr_norm; - bool autodetect; bool powered; + bool streaming; u8 input; struct i2c_client *client; @@ -338,12 +339,26 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) if (err) return err; - /* when we are interrupt driven we know the state */ - if (!state->autodetect || state->irq > 0) - *std = state->curr_norm; - else - err = __adv7180_status(state, NULL, std); + if (state->streaming) { + err = -EBUSY; + goto unlock; + } + err = adv7180_set_video_standard(state, + ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); + if (err) + goto unlock; + + msleep(100); + __adv7180_status(state, NULL, std); + + err = v4l2_std_to_adv7180(state->curr_norm); + if (err < 0) + goto unlock; + + err = adv7180_set_video_standard(state, err); + +unlock: mutex_unlock(&state->mutex); return err; } @@ -387,23 +402,13 @@ static int adv7180_program_std(struct adv7180_state *state) { int ret; - if (state->autodetect) { - ret = adv7180_set_video_standard(state, - ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); - if (ret < 0) - return ret; - - __adv7180_status(state, NULL, &state->curr_norm); - } else { - ret = v4l2_std_to_adv7180(state->curr_norm); - if (ret < 0) - return ret; - - ret = adv7180_set_video_standard(state, ret); - if (ret < 0) - return ret; - } + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + ret = adv7180_set_video_standard(state, ret); + if (ret < 0) + return ret; return 0; } @@ -415,18 +420,12 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) if (ret) return ret; - /* all standards -> autodetect */ - if (std == V4L2_STD_ALL) { - state->autodetect = true; - } else { - /* Make sure we can support this std */ - ret = v4l2_std_to_adv7180(std); - if (ret < 0) - goto out; + /* Make sure we can support this std */ + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; - state->curr_norm = std; - state->autodetect = false; - } + state->curr_norm = std; ret = adv7180_program_std(state); out: @@ -747,6 +746,40 @@ static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } +static int adv7180_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7180_state *state = to_state(sd); + int ret; + + /* It's always safe to stop streaming, no need to take the lock */ + if (!enable) { + state->streaming = enable; + return 0; + } + + /* Must wait until querystd released the lock */ + ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + state->streaming = enable; + mutex_unlock(&state->mutex); + return 0; +} + +static int adv7180_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, @@ -756,10 +789,13 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .g_mbus_config = adv7180_g_mbus_config, .cropcap = adv7180_cropcap, .g_tvnorms = adv7180_g_tvnorms, + .s_stream = adv7180_s_stream, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .s_power = adv7180_s_power, + .subscribe_event = adv7180_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { @@ -784,8 +820,14 @@ static irqreturn_t adv7180_irq(int irq, void *devid) /* clear */ adv7180_write(state, ADV7180_REG_ICR3, isr3); - if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) - __adv7180_status(state, NULL, &state->curr_norm); + if (isr3 & ADV7180_IRQ3_AD_CHANGE) { + static const struct v4l2_event src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + v4l2_subdev_notify_event(&state->sd, &src_ch); + } mutex_unlock(&state->mutex); return IRQ_HANDLED; @@ -1230,7 +1272,7 @@ static int adv7180_probe(struct i2c_client *client, state->irq = client->irq; mutex_init(&state->mutex); - state->autodetect = true; + state->curr_norm = V4L2_STD_NTSC; if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) state->powered = true; else @@ -1238,7 +1280,7 @@ static int adv7180_probe(struct i2c_client *client, state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ret = adv7180_init_controls(state); if (ret) From 7b9f31f3b3cab9bc367ba4da45af129ff858ce52 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 10:03:38 -0300 Subject: [PATCH 120/173] [media] sta2x11_vip: fix s_std The s_std ioctl was broken in this driver, partially due to the changes to the adv7180 driver (this affected the handling of V4L2_STD_ALL) and partially because the new standard was never stored in vip->std. The handling of V4L2_STD_ALL has been rewritten to just call querystd and the new standard is now stored correctly. Signed-off-by: Hans Verkuil Acked-by: Federico Vaga Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 26 ++++++++++--------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 753411cbbc9a..c79623ca5fc3 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -444,27 +444,21 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) { struct sta2x11_vip *vip = video_drvdata(file); - v4l2_std_id oldstd = vip->std, newstd; + v4l2_std_id oldstd = vip->std; int status; - if (V4L2_STD_ALL == std) { - v4l2_subdev_call(vip->decoder, video, s_std, std); - ssleep(2); - v4l2_subdev_call(vip->decoder, video, querystd, &newstd); - v4l2_subdev_call(vip->decoder, video, g_input_status, &status); - if (status & V4L2_IN_ST_NO_SIGNAL) + /* + * This is here for backwards compatibility only. + * The use of V4L2_STD_ALL to trigger a querystd is non-standard. + */ + if (std == V4L2_STD_ALL) { + v4l2_subdev_call(vip->decoder, video, querystd, &std); + if (std == V4L2_STD_UNKNOWN) return -EIO; - std = vip->std = newstd; - if (oldstd != std) { - if (V4L2_STD_525_60 & std) - vip->format = formats_60[0]; - else - vip->format = formats_50[0]; - } - return 0; } - if (oldstd != std) { + if (vip->std != std) { + vip->std = std; if (V4L2_STD_525_60 & std) vip->format = formats_60[0]; else From dc96208582156fafd946368853bc108096dee9f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 25 Apr 2016 08:11:41 -0300 Subject: [PATCH 121/173] [media] sta2x11: remove unused vars Changeset 7b9f31f3b3ca ("[media] sta2x11_vip: fix s_std") removed autodetect code, but it kept two vars unused: drivers/media/pci/sta2x11/sta2x11_vip.c: In function 'vidioc_s_std': drivers/media/pci/sta2x11/sta2x11_vip.c:448:6: warning: unused variable 'status' [-Wunused-variable] int status; ^ drivers/media/pci/sta2x11/sta2x11_vip.c:447:14: warning: unused variable 'oldstd' [-Wunused-variable] v4l2_std_id oldstd = vip->std; ^ Remove them. Fixes: 7b9f31f3b3ca ("[media] sta2x11_vip: fix s_std") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index c79623ca5fc3..1fc195f89686 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -444,8 +444,6 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) { struct sta2x11_vip *vip = video_drvdata(file); - v4l2_std_id oldstd = vip->std; - int status; /* * This is here for backwards compatibility only. From c040a71f1a79caa132f718ef533d47ec63a0efd2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 10:03:40 -0300 Subject: [PATCH 122/173] [media] tc358743: drop bogus comment The control in question is not a private control, so drop that comment. Copy-and-paste left-over. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 73e0cef0ea61..6cf6d06737a5 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1863,7 +1863,6 @@ static int tc358743_probe(struct i2c_client *client, /* control handlers */ v4l2_ctrl_handler_init(&state->hdl, 3); - /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); From 035677761fec4a472491d53f4dfa5dbc8edd2f7a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Apr 2016 10:03:41 -0300 Subject: [PATCH 123/173] [media] media/i2c/adv*: make controls inheritable instead of private Marking these controls as private seemed a good idea at one time, but in practice it makes no sense. So drop this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 8 -------- drivers/media/i2c/adv7511.c | 6 ------ drivers/media/i2c/adv7604.c | 8 -------- drivers/media/i2c/adv7842.c | 6 ------ 4 files changed, 28 deletions(-) diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 788967dadd29..0462f461e679 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1130,8 +1130,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * hdl = &state->hdl; v4l2_ctrl_handler_init(hdl, 5); - /* private controls */ - state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_DVI_D); @@ -1151,12 +1149,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * goto err_hdl; } - state->hdmi_mode_ctrl->is_private = true; - state->hotplug_ctrl->is_private = true; - state->rx_sense_ctrl->is_private = true; - state->have_edid0_ctrl->is_private = true; - state->rgb_quantization_range_ctrl->is_private = true; - state->pad.flags = MEDIA_PAD_FL_SINK; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index bd822f032b08..39271c35da48 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1502,12 +1502,6 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * err = hdl->error; goto err_hdl; } - state->hdmi_mode_ctrl->is_private = true; - state->hotplug_ctrl->is_private = true; - state->rx_sense_ctrl->is_private = true; - state->have_edid0_ctrl->is_private = true; - state->rgb_quantization_range_ctrl->is_private = true; - state->pad.flags = MEDIA_PAD_FL_SINK; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 41a1bfc5eaa7..beb2841ceae5 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3141,7 +3141,6 @@ static int adv76xx_probe(struct i2c_client *client, if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, (1 << state->info->num_dv_ports) - 1, 0, 0); @@ -3164,13 +3163,6 @@ static int adv76xx_probe(struct i2c_client *client, err = hdl->error; goto err_hdl; } - state->detect_tx_5v_ctrl->is_private = true; - state->rgb_quantization_range_ctrl->is_private = true; - if (adv76xx_has_afe(state)) - state->analog_sampling_phase_ctrl->is_private = true; - state->free_run_color_manual_ctrl->is_private = true; - state->free_run_color_ctrl->is_private = true; - if (adv76xx_s_detect_tx_5v_ctrl(sd)) { err = -ENODEV; goto err_hdl; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 7ccb85d45224..ecaacb0a6fa1 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3300,12 +3300,6 @@ static int adv7842_probe(struct i2c_client *client, err = hdl->error; goto err_hdl; } - state->detect_tx_5v_ctrl->is_private = true; - state->rgb_quantization_range_ctrl->is_private = true; - state->analog_sampling_phase_ctrl->is_private = true; - state->free_run_color_ctrl_manual->is_private = true; - state->free_run_color_ctrl->is_private = true; - if (adv7842_s_detect_tx_5v_ctrl(sd)) { err = -ENODEV; goto err_hdl; From 511257e6ae1fb87a2d0ad59ccdd61d1d4a0b9384 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Sun, 24 Apr 2016 21:24:11 -0300 Subject: [PATCH 124/173] [media] Documentation: dt: media: fix spelling mistake Fix spelling mistake. Signed-off-by: Eric Engestrom Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/xilinx/video.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/xilinx/video.txt b/Documentation/devicetree/bindings/media/xilinx/video.txt index cbd46fa0988f..68ac210e688e 100644 --- a/Documentation/devicetree/bindings/media/xilinx/video.txt +++ b/Documentation/devicetree/bindings/media/xilinx/video.txt @@ -20,7 +20,7 @@ The following properties are common to all Xilinx video IP cores. - xlnx,video-format: This property represents a video format transmitted on an AXI bus between video IP cores, using its VF code as defined in "AXI4-Stream Video IP and System Design Guide" [UG934]. How the format relates to the IP - core is decribed in the IP core bindings documentation. + core is described in the IP core bindings documentation. - xlnx,video-width: This property qualifies the video format with the sample width expressed as a number of bits per pixel component. All components must From 0ba20cdcc51b1acb6ba5387ccae47ef5ef620a5b Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Sun, 24 Apr 2016 21:24:20 -0300 Subject: [PATCH 125/173] [media] Documentation: DocBook: fix spelling mistake Fix spelling mistake. Signed-off-by: Eric Engestrom Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/net.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/dvb/net.xml b/Documentation/DocBook/media/dvb/net.xml index d2e44b7e07df..da095ed0b75c 100644 --- a/Documentation/DocBook/media/dvb/net.xml +++ b/Documentation/DocBook/media/dvb/net.xml @@ -15,7 +15,7 @@ that are present on the transport stream. This is done through /dev/dvb/adapter?/net? device node. The data will be available via virtual dvb?_? - network interfaces, and will be controled/routed via the standard + network interfaces, and will be controlled/routed via the standard ip tools (like ip, route, netstat, ifconfig, etc). Data types and and ioctl definitions are defined via linux/dvb/net.h header. From 1a2b2c708c5f7187c4c33d002bed6130381a6698 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Mon, 25 Apr 2016 03:37:03 -0300 Subject: [PATCH 126/173] [media] Documentation: video4linux: fix spelling mistakes Fix spelling mistakes. Signed-off-by: Eric Engestrom Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/vivid.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/video4linux/vivid.txt b/Documentation/video4linux/vivid.txt index e35d376b7f64..8da5d2a576bc 100644 --- a/Documentation/video4linux/vivid.txt +++ b/Documentation/video4linux/vivid.txt @@ -294,7 +294,7 @@ the result will be. These inputs support all combinations of the field setting. Special care has been taken to faithfully reproduce how fields are handled for the different -TV standards. This is particularly noticable when generating a horizontally +TV standards. This is particularly noticeable when generating a horizontally moving image so the temporal effect of using interlaced formats becomes clearly visible. For 50 Hz standards the top field is the oldest and the bottom field is the newest in time. For 60 Hz standards that is reversed: the bottom field @@ -313,7 +313,7 @@ will be SMPTE-170M. The pixel aspect ratio will depend on the TV standard. The video aspect ratio can be selected through the 'Standard Aspect Ratio' Vivid control. Choices are '4x3', '16x9' which will give letterboxed widescreen video and -'16x9 Anomorphic' which will give full screen squashed anamorphic widescreen +'16x9 Anamorphic' which will give full screen squashed anamorphic widescreen video that will need to be scaled accordingly. The TV 'tuner' supports a frequency range of 44-958 MHz. Channels are available @@ -862,7 +862,7 @@ RDS Radio Text: RDS Stereo: RDS Artificial Head: RDS Compressed: -RDS Dymanic PTY: +RDS Dynamic PTY: RDS Traffic Announcement: RDS Traffic Program: RDS Music: these are all controls that set the RDS data that is transmitted by From 363d79f1d5bd09158cc28db543ca18549a5d7e52 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 23 Apr 2016 06:21:09 -0300 Subject: [PATCH 127/173] [media] tw686x: Don't go past array Depending on the compiler version, currently it produces the following warnings: tw686x-video.c: In function 'tw686x_video_init': tw686x-video.c:65:543: warning: array subscript is above array bounds [-Warray-bounds] This is actually bogus with the current code, as it currently hardcodes the framerate to 30 frames/sec, however a potential use after the array size could happen when the driver adds support for setting the framerate. So, fix it. Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 60d38f19134b..d2a0147e6492 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -61,8 +61,17 @@ static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps) 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0 }; - unsigned int i = - (std & V4L2_STD_625_50) ? std_625_50[fps] : std_525_60[fps]; + unsigned int i; + + if (std & V4L2_STD_525_60) { + if (fps > ARRAY_SIZE(std_525_60)) + fps = 30; + i = std_525_60[fps]; + } else { + if (fps > ARRAY_SIZE(std_625_50)) + fps = 25; + i = std_625_50[fps]; + } return map[i]; } From 45c175c4ae9695d6d2f30a45ab7f3866cfac184b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 26 Apr 2016 06:33:06 -0300 Subject: [PATCH 128/173] [media] tw686x: avoid going past array Fix those two warnings: drivers/media/pci/tw686x/tw686x-video.c:69 tw686x_fields_map() error: buffer overflow 'std_525_60' 31 <= 31 drivers/media/pci/tw686x/tw686x-video.c:73 tw686x_fields_map() error: buffer overflow 'std_625_50' 26 <= 26 I had those changes at the last version of my patch, but I ended by merging the previous version by mistake. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw686x/tw686x-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index d2a0147e6492..253e10823ba3 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -64,11 +64,11 @@ static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps) unsigned int i; if (std & V4L2_STD_525_60) { - if (fps > ARRAY_SIZE(std_525_60)) + if (fps >= ARRAY_SIZE(std_525_60)) fps = 30; i = std_525_60[fps]; } else { - if (fps > ARRAY_SIZE(std_625_50)) + if (fps >= ARRAY_SIZE(std_625_50)) fps = 25; i = std_625_50[fps]; } From 47dfdb3a888733ab76702fc2801b6715c9d77937 Mon Sep 17 00:00:00 2001 From: Helen Mae Koike Fornazier Date: Sun, 28 Jun 2015 16:14:10 -0300 Subject: [PATCH 129/173] [media] media: change pipeline validation return error According to the V4L2 API, the VIDIOC_STREAMON ioctl should return EPIPE if there is a format mismatch in the pipeline configuration. As the .vidioc_streamon in the v4l2_ioctl_ops usually forwards the error caused by the v4l2_subdev_link_validate_default (if it is in use), it should return -EPIPE when it detect the mismatch. When an entity is connected to a non enabled link, media_entity_pipeline_start should return -ENOLINK, as the link does not exist. Signed-off-by: Helen Mae Koike Fornazier Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-entity.c | 2 +- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index c53c1d5589a0..d8a2299f0c2a 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -445,7 +445,7 @@ __must_check int __media_entity_pipeline_start(struct media_entity *entity, bitmap_or(active, active, has_no_links, entity->num_pads); if (!bitmap_full(active, entity->num_pads)) { - ret = -EPIPE; + ret = -ENOLINK; dev_dbg(entity->graph_obj.mdev->dev, "\"%s\":%u must be connected by an enabled link\n", entity->name, diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 224ea6028b55..953eab08e420 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -510,7 +510,7 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, if (source_fmt->format.width != sink_fmt->format.width || source_fmt->format.height != sink_fmt->format.height || source_fmt->format.code != sink_fmt->format.code) - return -EINVAL; + return -EPIPE; /* The field order must match, or the sink field order must be NONE * to support interlaced hardware connected to bridges that support @@ -518,7 +518,7 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, */ if (source_fmt->format.field != sink_fmt->format.field && sink_fmt->format.field != V4L2_FIELD_NONE) - return -EINVAL; + return -EPIPE; return 0; } From 1e166c77fdbc71bd237a3d1ac27abef6221e3a5e Mon Sep 17 00:00:00 2001 From: Helen Mae Koike Fornazier Date: Wed, 30 Mar 2016 12:40:58 -0300 Subject: [PATCH 130/173] [media] DocBook: update error code in videoc-streamon Add description of ENOLINK error Signed-off-by: Helen Mae Koike Fornazier Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-streamon.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml index df2c63d07bac..89fd7ce964f9 100644 --- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml +++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml @@ -123,6 +123,14 @@ synchronize with other events. + + ENOLINK + + The driver implements Media Controller interface and + the pipeline link configuration is invalid. + + + From 8a73faab0a6b6c620c62f24fc991d0ad653b848b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Feb 2016 12:27:18 -0200 Subject: [PATCH 131/173] [media] zl10353: use div_u64 instead of do_div Using div_u64() instead of do_div() makes the code slightly more readable by humans. [mchehab@osg.samsung.org: originally, this patch was proposed as a bug fix for a gcc bug. This was solved already, but it is still better to use div_u64, instead of do_div, so I'm applying it, removing the comments about the gcc bug] Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/zl10353.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c index 1832c2f7695c..3b08176d7bec 100644 --- a/drivers/media/dvb-frontends/zl10353.c +++ b/drivers/media/dvb-frontends/zl10353.c @@ -135,8 +135,7 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, value = (u64)10 * (1 << 23) / 7 * 125; value = (bw * value) + adc_clock / 2; - do_div(value, adc_clock); - *nominal_rate = value; + *nominal_rate = div_u64(value, adc_clock); dprintk("%s: bw %d, adc_clock %d => 0x%x\n", __func__, bw, adc_clock, *nominal_rate); @@ -163,8 +162,7 @@ static void zl10353_calc_input_freq(struct dvb_frontend *fe, if (ife > adc_clock / 2) ife = adc_clock - ife; } - value = (u64)65536 * ife + adc_clock / 2; - do_div(value, adc_clock); + value = div_u64((u64)65536 * ife + adc_clock / 2, adc_clock); *input_freq = -value; dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", From 78bab93026ab95b6c8b9e8770ef7089f7d40b2d5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 8 Feb 2016 09:51:16 -0200 Subject: [PATCH 132/173] [media] si2157: detect if firmware is running Detect if firmware is running run-time and download / start it only when needed. Detection is done by reading IF frequency value. Garbage value is returned by firmware when it is not running, otherwise correct value is returned. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 19 +++++++++++++------ drivers/media/tuners/si2157_priv.h | 1 - 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 243ac3816028..b07a681f3fbc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -84,11 +84,22 @@ static int si2157_init(struct dvb_frontend *fe) struct si2157_cmd cmd; const struct firmware *fw; const char *fw_name; - unsigned int chip_id; + unsigned int uitmp, chip_id; dev_dbg(&client->dev, "\n"); - if (dev->fw_loaded) + /* Returned IF frequency is garbage when firmware is not running */ + memcpy(cmd.args, "\x15\x00\x06\x07", 4); + cmd.wlen = 4; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + uitmp = cmd.args[2] << 0 | cmd.args[3] << 8; + dev_dbg(&client->dev, "if_frequency kHz=%u\n", uitmp); + + if (uitmp == dev->if_frequency / 1000) goto warm; /* power up */ @@ -203,9 +214,6 @@ static int si2157_init(struct dvb_frontend *fe) dev_info(&client->dev, "firmware version: %c.%c.%d\n", cmd.args[6], cmd.args[7], cmd.args[8]); - - dev->fw_loaded = true; - warm: /* init statistics in order signal app which are supported */ c->strength.len = 1; @@ -422,7 +430,6 @@ static int si2157_probe(struct i2c_client *client, dev->fe = cfg->fe; dev->inversion = cfg->inversion; dev->if_port = cfg->if_port; - dev->fw_loaded = false; dev->chiptype = (u8)id->driver_data; dev->if_frequency = 5000000; /* default value of property 0x0706 */ mutex_init(&dev->i2c_mutex); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 589d558d381c..d6b2c7b44053 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -26,7 +26,6 @@ struct si2157_dev { struct mutex i2c_mutex; struct dvb_frontend *fe; bool active; - bool fw_loaded; bool inversion; u8 chiptype; u8 if_port; From b9894728c2de35bab54f0f47b92b43a927ad7b37 Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Tue, 23 Feb 2016 18:11:20 -0300 Subject: [PATCH 133/173] [media] media: adv7180: Add device tree binding document Add device tree binding documentation for the adv7180 video decoder family. Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/adv7180.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/adv7180.txt diff --git a/Documentation/devicetree/bindings/media/i2c/adv7180.txt b/Documentation/devicetree/bindings/media/i2c/adv7180.txt new file mode 100644 index 000000000000..2f39f268fdb0 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adv7180.txt @@ -0,0 +1,20 @@ +* Analog Devices ADV7180 analog video decoder family + +The adv7180 family devices are used to capture analog video to different +digital interfaces like parallel video. + +Required Properties : +- compatible : value must be "adi,adv7180" + +Example: + + i2c0@1c22000 { + ... + ... + adv7180@21 { + compatible = "adi,adv7180"; + reg = <0x21>; + }; + ... + }; + From bf14e74cef06efc18fc769cbc331c6612dca214b Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Tue, 23 Feb 2016 18:11:21 -0300 Subject: [PATCH 134/173] [media] media: adv7180: Add of compatible strings for full family Add entries for all supported chip variants into the of_match list, so that the matching driver_info can be selected when using dt. Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/adv7180.txt | 13 +++++++++++-- drivers/media/i2c/adv7180.c | 8 ++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/adv7180.txt b/Documentation/devicetree/bindings/media/i2c/adv7180.txt index 2f39f268fdb0..0d501154dfb2 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7180.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv7180.txt @@ -1,10 +1,19 @@ * Analog Devices ADV7180 analog video decoder family The adv7180 family devices are used to capture analog video to different -digital interfaces like parallel video. +digital interfaces like MIPI CSI-2 or parallel video. Required Properties : -- compatible : value must be "adi,adv7180" +- compatible : value must be one of + "adi,adv7180" + "adi,adv7182" + "adi,adv7280" + "adi,adv7280-m" + "adi,adv7281" + "adi,adv7281-m" + "adi,adv7281-ma" + "adi,adv7282" + "adi,adv7282-m" Example: diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 5a75a9154e2e..b77b0a4dbf68 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1402,6 +1402,14 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #ifdef CONFIG_OF static const struct of_device_id adv7180_of_id[] = { { .compatible = "adi,adv7180", }, + { .compatible = "adi,adv7182", }, + { .compatible = "adi,adv7280", }, + { .compatible = "adi,adv7280-m", }, + { .compatible = "adi,adv7281", }, + { .compatible = "adi,adv7281-m", }, + { .compatible = "adi,adv7281-ma", }, + { .compatible = "adi,adv7282", }, + { .compatible = "adi,adv7282-m", }, { }, }; From e8364275f9a34481c9a38be757b675f8d1689d46 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Mar 2016 14:50:17 -0300 Subject: [PATCH 135/173] [media] dw2102: move USB IDs to dvb-usb-ids.h Right now, dw2102 assumes that the USB IDs will be either at an external header or defined internally. That doesn't sound right. So, let's move the definitions to just one place. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 13 ++++++ drivers/media/usb/dvb-usb/dw2102.c | 60 ++-------------------------- 2 files changed, 16 insertions(+), 57 deletions(-) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 0afad395ef97..fc90a39ab9b7 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -58,6 +58,14 @@ #define USB_VID_TELESTAR 0x10b9 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_SONY 0x1415 +#define USB_PID_TEVII_S421 0xd421 +#define USB_PID_TEVII_S480_1 0xd481 +#define USB_PID_TEVII_S480_2 0xd482 +#define USB_PID_TEVII_S630 0xd630 +#define USB_PID_TEVII_S632 0xd632 +#define USB_PID_TEVII_S650 0xd650 +#define USB_PID_TEVII_S660 0xd660 +#define USB_PID_TEVII_S662 0xd662 #define USB_VID_TWINHAN 0x1822 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 #define USB_VID_UNIWILL 0x1584 @@ -141,6 +149,7 @@ #define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_GOTVIEW_SAT_HD 0x5456 #define USB_PID_INTEL_CE9500 0x9500 #define USB_PID_ITETECH_IT9135 0x9135 #define USB_PID_ITETECH_IT9135_9005 0x9005 @@ -159,6 +168,8 @@ #define USB_PID_KWORLD_UB499_2T_T09 0xe409 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df +#define USB_PID_PROF_1100 0xb012 +#define USB_PID_TERRATEC_CINERGY_S 0x0064 #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 #define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 @@ -361,6 +372,8 @@ #define USB_PID_YUAN_STK7700D 0x1efc #define USB_PID_YUAN_STK7700D_2 0x1e8c #define USB_PID_DW2102 0x2102 +#define USB_PID_DW2104 0x2104 +#define USB_PID_DW3101 0x3101 #define USB_PID_XTENSIONS_XD_380 0x0381 #define USB_PID_TELESTAR_STARSTICK_2 0x8000 #define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 1f35f3decf39..49b55d7069b1 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -13,6 +13,7 @@ * * see Documentation/dvb/README.dvb-usb for more information */ +#include "dvb-usb-ids.h" #include "dw2102.h" #include "si21xx.h" #include "stv0299.h" @@ -38,61 +39,6 @@ /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 64 -#ifndef USB_PID_DW2102 -#define USB_PID_DW2102 0x2102 -#endif - -#ifndef USB_PID_DW2104 -#define USB_PID_DW2104 0x2104 -#endif - -#ifndef USB_PID_DW3101 -#define USB_PID_DW3101 0x3101 -#endif - -#ifndef USB_PID_CINERGY_S -#define USB_PID_CINERGY_S 0x0064 -#endif - -#ifndef USB_PID_TEVII_S630 -#define USB_PID_TEVII_S630 0xd630 -#endif - -#ifndef USB_PID_TEVII_S650 -#define USB_PID_TEVII_S650 0xd650 -#endif - -#ifndef USB_PID_TEVII_S660 -#define USB_PID_TEVII_S660 0xd660 -#endif - -#ifndef USB_PID_TEVII_S662 -#define USB_PID_TEVII_S662 0xd662 -#endif - -#ifndef USB_PID_TEVII_S480_1 -#define USB_PID_TEVII_S480_1 0xd481 -#endif - -#ifndef USB_PID_TEVII_S480_2 -#define USB_PID_TEVII_S480_2 0xd482 -#endif - -#ifndef USB_PID_PROF_1100 -#define USB_PID_PROF_1100 0xb012 -#endif - -#ifndef USB_PID_TEVII_S421 -#define USB_PID_TEVII_S421 0xd421 -#endif - -#ifndef USB_PID_TEVII_S632 -#define USB_PID_TEVII_S632 0xd632 -#endif - -#ifndef USB_PID_GOTVIEW_SAT_HD -#define USB_PID_GOTVIEW_SAT_HD 0x5456 -#endif #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -1709,7 +1655,7 @@ static struct usb_device_id dw2102_table[] = { [CYPRESS_DW2101] = {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, [CYPRESS_DW2104] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2104)}, [TEVII_S650] = {USB_DEVICE(0x9022, USB_PID_TEVII_S650)}, - [TERRATEC_CINERGY_S] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, + [TERRATEC_CINERGY_S] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S)}, [CYPRESS_DW3101] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, [TEVII_S630] = {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, [PROF_1100] = {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, @@ -1801,7 +1747,7 @@ static int dw2102_load_firmware(struct usb_device *dev, dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); break; - case USB_PID_CINERGY_S: + case USB_PID_TERRATEC_CINERGY_S: case USB_PID_DW2102: dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); From 806f8ffa8a0fa9a6f0481c5648c27aa51d10fdc6 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 7 Mar 2016 15:39:32 -0300 Subject: [PATCH 136/173] [media] media: i2c/adp1653: fix check of devm_gpiod_get() error code The devm_gpiod_get() function returns either a valid pointer to struct gpio_desc or ERR_PTR() error value, check for NULL is bogus. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adp1653.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index fb7ed730d932..9e1731c565e7 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -466,9 +466,9 @@ static int adp1653_of_init(struct i2c_client *client, of_node_put(child); pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW); - if (!pd->enable_gpio) { + if (IS_ERR(pd->enable_gpio)) { dev_err(&client->dev, "Error getting GPIO\n"); - return -EINVAL; + return PTR_ERR(pd->enable_gpio); } return 0; From 92021e074afe25a607e24ec8f28d3daebca5d434 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Sat, 16 Apr 2016 05:12:20 -0300 Subject: [PATCH 137/173] [media] smiapp: provide g_skip_top_lines method in sensor ops Some sensors (like the one in Nokia N900) provide metadata in the first couple of lines. Make that information information available to the pipeline. Use u16 instead, this is a 16-bit value. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 12 ++++++++++++ drivers/media/i2c/smiapp/smiapp.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index a215efe7a8ba..3dfe387abf6e 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -188,6 +188,8 @@ static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) embedded_end = 0; } + sensor->image_start = image_start; + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", embedded_start, embedded_end); dev_dbg(&client->dev, "image data starts at line %d\n", image_start); @@ -2280,6 +2282,15 @@ static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) return 0; } +static int smiapp_get_skip_top_lines(struct v4l2_subdev *subdev, u32 *lines) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *lines = sensor->image_start; + + return 0; +} + /* ----------------------------------------------------------------------------- * sysfs attributes */ @@ -2890,6 +2901,7 @@ static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { .g_skip_frames = smiapp_get_skip_frames, + .g_skip_top_lines = smiapp_get_skip_top_lines, }; static const struct v4l2_subdev_ops smiapp_ops = { diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index f6af0cc4a256..2174f89a00db 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -217,6 +217,7 @@ struct smiapp_sensor { u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ u8 frame_skip; + u16 image_start; /* Offset to first line after metadata lines */ int power_count; From 95dd7b7e30f385c1c2d5e41457c082c5f6c535b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Apr 2016 06:27:28 -0300 Subject: [PATCH 138/173] [media] v4l2-ioctl.c: improve cropcap compatibility code - Add a check for the case that both the cropcap and g_selection ops are NULL. This shouldn't happen, but I feel happier if the code guards against this. - If g_selection exists, then ignore ENOTTY and ENOIOCTLCMD error codes from cropcap. Just assume square pixelaspect ratio in that case. This situation can happen if the bridge driver's cropcap op calls the corresponding subdev's op. So the cropcap ioctl is set, but it might return ENOIOCTLCMD anyway. In the past this would just return an error which is wrong. - Call cropcap first and let g_selection overwrite the bounds and defrect. This safeguards against subdev cropcap implementations that set those rectangles as well. What g_selection returns has priority over what such cropcap implementations return. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 72 +++++++++++++++++----------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 6bf5a3ecd126..28e5be2c2eef 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2160,40 +2160,56 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_cropcap *p = arg; - - if (ops->vidioc_g_selection) { - struct v4l2_selection s = { .type = p->type }; - int ret; - - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; - - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; - } + struct v4l2_selection s = { .type = p->type }; + int ret = 0; /* setting trivial pixelaspect */ p->pixelaspect.numerator = 1; p->pixelaspect.denominator = 1; + /* + * The determine_valid_ioctls() call already should ensure + * that this can never happen, but just in case... + */ + if (WARN_ON(!ops->vidioc_cropcap && !ops->vidioc_cropcap)) + return -ENOTTY; + if (ops->vidioc_cropcap) - return ops->vidioc_cropcap(file, fh, p); + ret = ops->vidioc_cropcap(file, fh, p); + + if (!ops->vidioc_g_selection) + return ret; + + /* + * Ignore ENOTTY or ENOIOCTLCMD error returns, just use the + * square pixel aspect ratio in that case. + */ + if (ret && ret != -ENOTTY && ret != -ENOIOCTLCMD) + return ret; + + /* Use g_selection() to fill in the bounds and defrect rectangles */ + + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; + + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; + + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; return 0; } From 7e31223ff0196dc5c5e8124b3c05ab4a25847c59 Mon Sep 17 00:00:00 2001 From: Kevin Fitch Date: Wed, 23 Mar 2016 01:23:32 -0300 Subject: [PATCH 139/173] [media] i2c: saa7115: Support CJC7113 detection It's been reported that CJC7113 devices are returning all 1s when reading register 0: "1111111111111111" found @ 0x4a (stk1160) This new device is apparently compatible with SA7113, so let's add a quirk to allow its autodetection. Given there isn't any known differences with SAA7113, this commit does not introduces a new saa711x_model value. Reported-by: Philippe Desrochers Signed-off-by: Kevin Fitch Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/saa7115.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index d2a1ce2bc7f5..bd3526bdd539 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1798,6 +1798,21 @@ static int saa711x_detect_chip(struct i2c_client *client, return GM7113C; } + /* Check if it is a CJC7113 */ + if (!memcmp(name, "1111111111111111", CHIP_VER_SIZE)) { + strlcpy(name, "cjc7113", CHIP_VER_SIZE); + + if (!autodetect && strcmp(name, id->name)) + return -EINVAL; + + v4l_dbg(1, debug, client, + "It seems to be a %s chip (%*ph) @ 0x%x.\n", + name, 16, chip_ver, client->addr << 1); + + /* CJC7113 seems to be SAA7113-compatible */ + return SAA7113; + } + /* Chip was not discovered. Return its ID and don't bind */ v4l_dbg(1, debug, client, "chip %*ph @ 0x%x is unknown.\n", 16, chip_ver, client->addr << 1); From 7977a15ede1d2d95446ff26a9e34a7b16c0fd12f Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 16 Mar 2016 08:56:30 -0300 Subject: [PATCH 140/173] [media] az6027: Add support for Elgato EyeTV Sat v3 Another version of Elgato EyeTV Sat USB DVB-S2 adapter needs just a USB ID addition. Signed-off-by: Christian Knippel Reported-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/az6027.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index fc90a39ab9b7..a7a4674ccc40 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -386,6 +386,7 @@ #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_ELGATO_EYETV_SAT 0x002a #define USB_PID_ELGATO_EYETV_SAT_V2 0x0025 +#define USB_PID_ELGATO_EYETV_SAT_V3 0x0036 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 #define USB_PID_FRIIO_WHITE 0x0001 diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c index 92e47d6c3ee3..2e711362847e 100644 --- a/drivers/media/usb/dvb-usb/az6027.c +++ b/drivers/media/usb/dvb-usb/az6027.c @@ -1090,6 +1090,7 @@ static struct usb_device_id az6027_usb_table[] = { { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) }, { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V2) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V3) }, { }, }; @@ -1138,7 +1139,7 @@ static struct dvb_usb_device_properties az6027_properties = { .i2c_algo = &az6027_i2c_algo, - .num_device_descs = 7, + .num_device_descs = 8, .devices = { { .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", @@ -1168,6 +1169,10 @@ static struct dvb_usb_device_properties az6027_properties = { .name = "Elgato EyeTV Sat", .cold_ids = { &az6027_usb_table[6], NULL }, .warm_ids = { NULL }, + }, { + .name = "Elgato EyeTV Sat", + .cold_ids = { &az6027_usb_table[7], NULL }, + .warm_ids = { NULL }, }, { NULL }, } From ab4d14528fdf946dfa7177b53e64f78bf8cce03a Mon Sep 17 00:00:00 2001 From: Satoshi Nagahama Date: Fri, 6 May 2016 16:35:05 -0300 Subject: [PATCH 141/173] [media] em28xx: add support for PLEX PX-BCUD (ISDB-S) PX-BCUD has the following components: USB interface: Empia EM28178 Demodulator: Toshiba TC90532 (works by code for TC90522) Tuner: Next version of Sharp QM1D1C0042 em28xx_dvb_init(): add init code for PLEX PX-BCUD with calling px_bcud_init() that does things like pin configuration. qm1d1c0042_init(): support the next version of QM1D1C0042, change to choose an appropriate array of initial registers by reading chip id. [mchehab@osg.samsung.com: fold a fixup patch and fix checkpatch.pl errors/warnings, where applicable] Signed-off-by: Satoshi Nagahama Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/qm1d1c0042.c | 36 +++++-- drivers/media/usb/em28xx/Kconfig | 2 + drivers/media/usb/em28xx/em28xx-cards.c | 30 ++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 121 ++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 5 files changed, 180 insertions(+), 10 deletions(-) diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 18bc745ed108..9af2a155cfca 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -32,14 +32,24 @@ #include "qm1d1c0042.h" #define QM1D1C0042_NUM_REGS 0x20 +#define QM1D1C0042_NUM_REG_ROWS 2 -static const u8 reg_initval[QM1D1C0042_NUM_REGS] = { - 0x48, 0x1c, 0xa0, 0x10, 0xbc, 0xc5, 0x20, 0x33, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0xff, 0xf3, 0x00, 0x2a, 0x64, 0xa6, 0x86, - 0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00 +static const u8 +reg_initval[QM1D1C0042_NUM_REG_ROWS][QM1D1C0042_NUM_REGS] = { { + 0x48, 0x1c, 0xa0, 0x10, 0xbc, 0xc5, 0x20, 0x33, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xf3, 0x00, 0x2a, 0x64, 0xa6, 0x86, + 0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00 + }, { + 0x68, 0x1c, 0xc0, 0x10, 0xbc, 0xc1, 0x11, 0x33, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xf3, 0x00, 0x3f, 0x25, 0x5c, 0xd6, + 0x55, 0xcf, 0x95, 0xf6, 0x36, 0xf2, 0x09, 0x00 + } }; +static int reg_index; + static const struct qm1d1c0042_config default_cfg = { .xtal_freq = 16000, .lpf = 1, @@ -320,7 +330,6 @@ static int qm1d1c0042_init(struct dvb_frontend *fe) int i, ret; state = fe->tuner_priv; - memcpy(state->regs, reg_initval, sizeof(reg_initval)); reg_write(state, 0x01, 0x0c); reg_write(state, 0x01, 0x0c); @@ -330,15 +339,22 @@ static int qm1d1c0042_init(struct dvb_frontend *fe) goto failed; usleep_range(2000, 3000); - val = state->regs[0x01] | 0x10; - ret = reg_write(state, 0x01, val); /* soft reset off */ + ret = reg_write(state, 0x01, 0x1c); /* soft reset off */ if (ret < 0) goto failed; - /* check ID */ + /* check ID and choose initial registers corresponding ID */ ret = reg_read(state, 0x00, &val); - if (ret < 0 || val != 0x48) + if (ret < 0) goto failed; + for (reg_index = 0; reg_index < QM1D1C0042_NUM_REG_ROWS; + reg_index++) { + if (val == reg_initval[reg_index][0x00]) + break; + } + if (reg_index >= QM1D1C0042_NUM_REG_ROWS) + goto failed; + memcpy(state->regs, reg_initval[reg_index], QM1D1C0042_NUM_REGS); usleep_range(2000, 3000); state->regs[0x0c] |= 0x40; diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index e382210c4ada..d917b0a2beb1 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -59,6 +59,8 @@ config VIDEO_EM28XX_DVB select DVB_DRX39XYJ if MEDIA_SUBDRV_AUTOSELECT select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 930e3e3fc948..57c3b905bf0a 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -492,6 +492,20 @@ static struct em28xx_reg_seq terratec_t2_stick_hd[] = { {-1, -1, -1, -1}, }; +static struct em28xx_reg_seq plex_px_bcud[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 0}, + {EM2874_R50_IR_CONFIG, 0x01, 0xff, 0}, + {EM28XX_R06_I2C_CLK, 0x40, 0xff, 0}, + {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 100}, + {EM28XX_R12_VINENABLE, 0x20, 0x20, 0}, + {0x0d, 0x42, 0xff, 1000}, + {EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 10}, + {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 10}, + {0x73, 0xfd, 0xff, 100}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -2306,6 +2320,20 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, .ir_codes = RC_MAP_TERRATEC_SLIM_2, }, + + /* + * 3275:0085 PLEX PX-BCUD. + * Empia EM28178, TOSHIBA TC90532XBG, Sharp QM1D1C0042 + */ + [EM28178_BOARD_PLEX_PX_BCUD] = { + .name = "PLEX PX-BCUD", + .xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = plex_px_bcud, + .has_dvb = 1, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2495,6 +2523,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2861_BOARD_LEADTEK_VC100 }, { USB_DEVICE(0xeb1a, 0x8179), .driver_info = EM28178_BOARD_TERRATEC_T2_STICK_HD }, + { USB_DEVICE(0x3275, 0x0085), + .driver_info = EM28178_BOARD_PLEX_PX_BCUD }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 5d209c7c54d5..fbc4e4b3268e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -58,6 +58,8 @@ #include "ts2020.h" #include "si2168.h" #include "si2157.h" +#include "tc90522.h" +#include "qm1d1c0042.h" MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL"); @@ -787,6 +789,68 @@ static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) return 0; } +static void px_bcud_init(struct em28xx *dev) +{ + int i; + struct { + unsigned char r[4]; + int len; + } regs1[] = { + {{ 0x0e, 0x77 }, 2}, + {{ 0x0f, 0x77 }, 2}, + {{ 0x03, 0x90 }, 2}, + }, regs2[] = { + {{ 0x07, 0x01 }, 2}, + {{ 0x08, 0x10 }, 2}, + {{ 0x13, 0x00 }, 2}, + {{ 0x17, 0x00 }, 2}, + {{ 0x03, 0x01 }, 2}, + {{ 0x10, 0xb1 }, 2}, + {{ 0x11, 0x40 }, 2}, + {{ 0x85, 0x7a }, 2}, + {{ 0x87, 0x04 }, 2}, + }; + static struct em28xx_reg_seq gpio[] = { + {EM28XX_R06_I2C_CLK, 0x40, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 60}, + {EM28XX_R15_RGAIN, 0x20, 0xff, 0}, + {EM28XX_R16_GGAIN, 0x20, 0xff, 0}, + {EM28XX_R17_BGAIN, 0x20, 0xff, 0}, + {EM28XX_R18_ROFFSET, 0x00, 0xff, 0}, + {EM28XX_R19_GOFFSET, 0x00, 0xff, 0}, + {EM28XX_R1A_BOFFSET, 0x00, 0xff, 0}, + {EM28XX_R23_UOFFSET, 0x00, 0xff, 0}, + {EM28XX_R24_VOFFSET, 0x00, 0xff, 0}, + {EM28XX_R26_COMPR, 0x00, 0xff, 0}, + {0x13, 0x08, 0xff, 0}, + {EM28XX_R12_VINENABLE, 0x27, 0xff, 0}, + {EM28XX_R0C_USBSUSP, 0x10, 0xff, 0}, + {EM28XX_R27_OUTFMT, 0x00, 0xff, 0}, + {EM28XX_R10_VINMODE, 0x00, 0xff, 0}, + {EM28XX_R11_VINCTRL, 0x11, 0xff, 0}, + {EM2874_R50_IR_CONFIG, 0x01, 0xff, 0}, + {EM2874_R5F_TS_ENABLE, 0x80, 0xff, 0}, + {EM28XX_R06_I2C_CLK, 0x46, 0xff, 0}, + }; + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x46); + /* sleeping ISDB-T */ + dev->dvb->i2c_client_demod->addr = 0x14; + for (i = 0; i < ARRAY_SIZE(regs1); i++) + i2c_master_send(dev->dvb->i2c_client_demod, regs1[i].r, + regs1[i].len); + /* sleeping ISDB-S */ + dev->dvb->i2c_client_demod->addr = 0x15; + for (i = 0; i < ARRAY_SIZE(regs2); i++) + i2c_master_send(dev->dvb->i2c_client_demod, regs2[i].r, + regs2[i].len); + for (i = 0; i < ARRAY_SIZE(gpio); i++) { + em28xx_write_reg_bits(dev, gpio[i].reg, gpio[i].val, + gpio[i].mask); + if (gpio[i].sleep > 0) + msleep(gpio[i].sleep); + } +}; + static struct mt352_config terratec_xs_mt352_cfg = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -1762,6 +1826,63 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->i2c_client_tuner = client; } break; + + case EM28178_BOARD_PLEX_PX_BCUD: + { + struct i2c_client *client; + struct i2c_board_info info; + struct tc90522_config tc90522_config; + struct qm1d1c0042_config qm1d1c0042_config; + + /* attach demod */ + memset(&tc90522_config, 0, sizeof(tc90522_config)); + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "tc90522sat", I2C_NAME_SIZE); + info.addr = 0x15; + info.platform_data = &tc90522_config; + request_module("tc90522"); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + dvb->i2c_client_demod = client; + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + /* attach tuner */ + memset(&qm1d1c0042_config, 0, + sizeof(qm1d1c0042_config)); + qm1d1c0042_config.fe = tc90522_config.fe; + qm1d1c0042_config.lpf = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "qm1d1c0042", I2C_NAME_SIZE); + info.addr = 0x61; + info.platform_data = &qm1d1c0042_config; + request_module(info.type); + client = i2c_new_device(tc90522_config.tuner_i2c, + &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + dvb->i2c_client_tuner = client; + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + dvb->fe[0] = tc90522_config.fe; + px_bcud_init(dev); + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 267444961775..9ad1240d5e22 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -145,6 +145,7 @@ #define EM2861_BOARD_LEADTEK_VC100 95 #define EM28178_BOARD_TERRATEC_T2_STICK_HD 96 #define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008 97 +#define EM28178_BOARD_PLEX_PX_BCUD 98 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 From 9c574ad4d360353ec8dd6bc85e78d8b2d0f8e775 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 27 Apr 2016 17:07:03 -0300 Subject: [PATCH 142/173] [media] af9035: correct eeprom offsets Used memory mapped eeprom offsets were off-by 8 bytes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index df22001f9e41..89e629a24aec 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -118,20 +118,20 @@ static const u32 clock_lut_it9135[] = { * Values 0, 3 and 5 are seen to this day. 0 for single TS and 3/5 for dual TS. */ -#define EEPROM_BASE_AF9035 0x42fd -#define EEPROM_BASE_IT9135 0x499c +#define EEPROM_BASE_AF9035 0x42f5 +#define EEPROM_BASE_IT9135 0x4994 #define EEPROM_SHIFT 0x10 -#define EEPROM_IR_MODE 0x10 -#define EEPROM_TS_MODE 0x29 -#define EEPROM_2ND_DEMOD_ADDR 0x2a -#define EEPROM_IR_TYPE 0x2c -#define EEPROM_1_IF_L 0x30 -#define EEPROM_1_IF_H 0x31 -#define EEPROM_1_TUNER_ID 0x34 -#define EEPROM_2_IF_L 0x40 -#define EEPROM_2_IF_H 0x41 -#define EEPROM_2_TUNER_ID 0x44 +#define EEPROM_IR_MODE 0x18 +#define EEPROM_TS_MODE 0x31 +#define EEPROM_2ND_DEMOD_ADDR 0x32 +#define EEPROM_IR_TYPE 0x34 +#define EEPROM_1_IF_L 0x38 +#define EEPROM_1_IF_H 0x39 +#define EEPROM_1_TUNER_ID 0x3c +#define EEPROM_2_IF_L 0x48 +#define EEPROM_2_IF_H 0x49 +#define EEPROM_2_TUNER_ID 0x4c /* USB commands */ #define CMD_MEM_RD 0x00 From e57b36c0c46d7288d6d5bd602003b8fcd17efe3f Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Mar 2016 17:40:51 -0300 Subject: [PATCH 143/173] [media] drivers/media/pci/zoran: avoid fragile snprintf use Appending to a string by doing snprintf(buf, bufsize, "%s...", buf, ...) is not guaranteed to work. Signed-off-by: Rasmus Villemoes Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/videocodec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c index c01071635290..13a3c07cd259 100644 --- a/drivers/media/pci/zoran/videocodec.c +++ b/drivers/media/pci/zoran/videocodec.c @@ -116,8 +116,9 @@ videocodec_attach (struct videocodec_master *master) goto out_module_put; } - snprintf(codec->name, sizeof(codec->name), - "%s[%d]", codec->name, h->attached); + res = strlen(codec->name); + snprintf(codec->name + res, sizeof(codec->name) - res, + "[%d]", h->attached); codec->master_data = master; res = codec->setup(codec); if (res == 0) { From 869f076bd68b27e0990e3fc5036a8eb571014473 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 8 Mar 2016 17:40:53 -0300 Subject: [PATCH 144/173] [media] ati_remote: avoid fragile snprintf use Passing overlapping source and destination to snprintf is fragile. Replace with a single (mostly) equivalent call. If one wants to preserve the space preceding udev->product whether or not there was a manufacturer, just remove udev->manufacturer from the && expression. Signed-off-by: Rasmus Villemoes Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 3f61d77d4147..9f5b59706741 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -873,13 +873,10 @@ static int ati_remote_probe(struct usb_interface *interface, strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys)); strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys)); - if (udev->manufacturer) - strlcpy(ati_remote->rc_name, udev->manufacturer, - sizeof(ati_remote->rc_name)); - - if (udev->product) - snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name), - "%s %s", ati_remote->rc_name, udev->product); + snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name), "%s%s%s", + udev->manufacturer ?: "", + udev->manufacturer && udev->product ? " " : "", + udev->product ?: ""); if (!strlen(ati_remote->rc_name)) snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name), From 0ed8289bd6fff6eda3d57ff09e4b54fd823930ab Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 9 Mar 2016 19:38:27 -0300 Subject: [PATCH 145/173] [media] smipcie: add support for TechnoTrend S2-4200 Twin Add support for TechnoTrend TT-budget S2-4200 Twin DVB-S2 tuner. The device seems to be rather similar to DVBSky S952 V3. This is a PCIe card with 2 tuners. SMI PCIe bridge is used and the card has two Montage M88RS6000 demod/tuners. The M88RS6000 demod/tuner package needs firmware. You can download one here: http://palosaari.fi/linux/v4l-dvb/firmware/M88RS6000/ Signed-off-by: Olli Salonen Reviewed-by: Max Nibble Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-ir.c | 5 ++++- drivers/media/pci/smipcie/smipcie-main.c | 10 ++++++++++ drivers/media/pci/smipcie/smipcie.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index d018673c71f6..d737b5e767d0 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -203,7 +203,10 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->driver_type = RC_DRIVER_SCANCODE; - rc_dev->map_name = RC_MAP_DVBSKY; + if (dev->info->type == SMI_TECHNOTREND_S2_4200) + rc_dev->map_name = RC_MAP_TT_1500; + else + rc_dev->map_name = RC_MAP_DVBSKY; ir->rc_dev = rc_dev; ir->dev = dev; diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index b039a229b7d2..993a2d19bd54 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -1086,6 +1086,15 @@ static struct smi_cfg_info dvbsky_t9580_cfg = { .fe_1 = DVBSKY_FE_M88DS3103, }; +static struct smi_cfg_info technotrend_s2_4200_cfg = { + .type = SMI_TECHNOTREND_S2_4200, + .name = "TechnoTrend TT-budget S2-4200 Twin", + .ts_0 = SMI_TS_DMA_BOTH, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_M88RS6000, + .fe_1 = DVBSKY_FE_M88RS6000, +}; + /* PCI IDs */ #define SMI_ID(_subvend, _subdev, _driverdata) { \ .vendor = SMI_VID, .device = SMI_PID, \ @@ -1096,6 +1105,7 @@ static const struct pci_device_id smi_id_table[] = { SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg), SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), + SMI_ID(0x13c2, 0x3016, technotrend_s2_4200_cfg), {0} }; MODULE_DEVICE_TABLE(pci, smi_id_table); diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 68cdda28fd98..5528e483ebc5 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -216,6 +216,7 @@ struct smi_cfg_info { #define SMI_DVBSKY_S950 1 #define SMI_DVBSKY_T9580 2 #define SMI_DVBSKY_T982 3 +#define SMI_TECHNOTREND_S2_4200 4 int type; char *name; #define SMI_TS_NULL 0 From 9b8537de47f4a4fbca571a393d36ba725c9795f7 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 9 Mar 2016 19:38:28 -0300 Subject: [PATCH 146/173] [media] smipcie: MAC address printout formatting Modify the printout for MAC address to be more vendor agnostic. Print also the port number. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 993a2d19bd54..4a9275a331b6 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -716,7 +716,8 @@ static int smi_fe_init(struct smi_port *port) /* init MAC.*/ ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16); dev_info(&port->dev->pci_dev->dev, - "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8); + "%s port %d MAC: %pM\n", dev->info->name, + port->idx, mac_ee + (port->idx)*8); memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6); return ret; } From 6dfe991113504108b04b1c36d09436e1712942b8 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Fri, 11 Mar 2016 03:48:03 -0300 Subject: [PATCH 147/173] [media] smipcie: add RC map into card configuration options Remove the if..else statement from smipcie-ir.c and add the remote controller map as a configuration parameter for the card. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-ir.c | 5 +---- drivers/media/pci/smipcie/smipcie-main.c | 4 ++++ drivers/media/pci/smipcie/smipcie.h | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index d737b5e767d0..826c7c75e64d 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -203,10 +203,7 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->driver_type = RC_DRIVER_SCANCODE; - if (dev->info->type == SMI_TECHNOTREND_S2_4200) - rc_dev->map_name = RC_MAP_TT_1500; - else - rc_dev->map_name = RC_MAP_DVBSKY; + rc_dev->map_name = dev->info->rc_map; ir->rc_dev = rc_dev; ir->dev = dev; diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 4a9275a331b6..83981d611a79 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -1067,6 +1067,7 @@ static struct smi_cfg_info dvbsky_s950_cfg = { .ts_1 = SMI_TS_DMA_BOTH, .fe_0 = DVBSKY_FE_NULL, .fe_1 = DVBSKY_FE_M88DS3103, + .rc_map = RC_MAP_DVBSKY, }; static struct smi_cfg_info dvbsky_s952_cfg = { @@ -1076,6 +1077,7 @@ static struct smi_cfg_info dvbsky_s952_cfg = { .ts_1 = SMI_TS_DMA_BOTH, .fe_0 = DVBSKY_FE_M88RS6000, .fe_1 = DVBSKY_FE_M88RS6000, + .rc_map = RC_MAP_DVBSKY, }; static struct smi_cfg_info dvbsky_t9580_cfg = { @@ -1085,6 +1087,7 @@ static struct smi_cfg_info dvbsky_t9580_cfg = { .ts_1 = SMI_TS_DMA_BOTH, .fe_0 = DVBSKY_FE_SIT2, .fe_1 = DVBSKY_FE_M88DS3103, + .rc_map = RC_MAP_DVBSKY, }; static struct smi_cfg_info technotrend_s2_4200_cfg = { @@ -1094,6 +1097,7 @@ static struct smi_cfg_info technotrend_s2_4200_cfg = { .ts_1 = SMI_TS_DMA_BOTH, .fe_0 = DVBSKY_FE_M88RS6000, .fe_1 = DVBSKY_FE_M88RS6000, + .rc_map = RC_MAP_TT_1500, }; /* PCI IDs */ diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 5528e483ebc5..611e4f02cadd 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -233,6 +233,7 @@ struct smi_cfg_info { #define DVBSKY_FE_SIT2 3 int fe_0; int fe_1; + char *rc_map; }; struct smi_rc { From 27924dccedb141ac965076f332126236aad65e6f Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 16 Mar 2016 08:04:50 -0300 Subject: [PATCH 148/173] [media] ds3000: return meaningful return codes The ds3000 driver returned 1 as an error code in many places. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index e8fc0329ea64..addffc33993a 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -458,7 +458,7 @@ static int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status) break; default: - return 1; + return -EINVAL; } if (state->config->set_lock_led) @@ -528,7 +528,7 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) *ber = 0xffffffff; break; default: - return 1; + return -EINVAL; } return 0; @@ -623,7 +623,7 @@ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) snr_reading, *snr); break; default: - return 1; + return -EINVAL; } return 0; @@ -661,7 +661,7 @@ static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) state->prevUCBS2 = _ucblocks; break; default: - return 1; + return -EINVAL; } return 0; @@ -754,7 +754,7 @@ static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, data |= 0x80; ds3000_writereg(state, 0xa2, data); - return 1; + return -ETIMEDOUT; } data = ds3000_readreg(state, 0xa2); @@ -808,7 +808,7 @@ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, data |= 0x80; ds3000_writereg(state, 0xa2, data); - return 1; + return -ETIMEDOUT; } data = ds3000_readreg(state, 0xa2); @@ -951,7 +951,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) ds3000_writereg(state, 0xfe, 0x98); break; default: - return 1; + return -EINVAL; } /* enable 27MHz clock output */ From a403ceeb6968b7e796c47331d10269cd9c8f7f7e Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 16 Mar 2016 16:16:41 -0300 Subject: [PATCH 149/173] [media] pctv452e: correct parameters for TechnoTrend TT S2-3600 2008-02-25 Andre Weidemann added support for TT S2-3600 and noted that he still gets image distortions every now and then. It seems to be common knowledge in many projects that changing the USB parameters seems to help. OpenELEC has included this patch for a few years, for example. Nobody bothered to report the issue upstream though, it seems. https://github.com/OpenELEC/OpenELEC.tv/issues/1957 http://www.vdr-portal.de/board60-linux/board14-betriebssystem/board96-yavdr/p1033458-darstellungsproblem-bei-2-tt-3600-usb/#post1033458 (in German) Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/pctv452e.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index ec397c4b7cc8..c05de1b088a4 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -995,11 +995,11 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_ISOC, - .count = 7, + .count = 4, .endpoint = 0x02, .u = { .isoc = { - .framesperurb = 4, + .framesperurb = 64, .framesize = 940, .interval = 1 } From 36ac2f32598fb4d1d4ab3b15fa63063b8e81a7ab Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 16 Mar 2016 18:32:56 -0300 Subject: [PATCH 150/173] [media] media: rc: remove unneeded mutex in rc_register_device Access to dev->initialized is atomic and dev->initialized isn't accessed in any other code protected by this mutex. Therefore we don't need to get the mutex here. Signed-off-by: Heiner Kallweit Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 4e9bbe735ae9..68541b1e3bcb 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1492,9 +1492,7 @@ int rc_register_device(struct rc_dev *dev) } /* Allow the RC sysfs nodes to be accessible */ - mutex_lock(&dev->lock); atomic_set(&dev->initialized, 1); - mutex_unlock(&dev->lock); IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", dev->minor, From e186613aed01867b73ca0dcdab91ac29c8da6e2a Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Thu, 17 Mar 2016 10:11:09 -0300 Subject: [PATCH 151/173] [media] mceusb: add support for Adaptec eHome receiver New USB ID for Adaptec eHome receiver in some HP laptops. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 35155ae500c7..09ca9f638811 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -188,6 +188,7 @@ #define VENDOR_TWISTEDMELON 0x2596 #define VENDOR_HAUPPAUGE 0x2040 #define VENDOR_PCTV 0x2013 +#define VENDOR_ADAPTEC 0x03f3 enum mceusb_model_type { MCE_GEN2 = 0, /* Most boards */ @@ -405,6 +406,8 @@ static struct usb_device_id mceusb_dev_table[] = { .driver_info = HAUPPAUGE_CX_HYBRID_TV }, { USB_DEVICE(VENDOR_PCTV, 0x025e), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + /* Adaptec / HP eHome Receiver */ + { USB_DEVICE(VENDOR_ADAPTEC, 0x0094) }, /* Terminating entry */ { } From 18693843870bfc3ebd682874c1171398f4c8a002 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Thu, 17 Mar 2016 10:11:10 -0300 Subject: [PATCH 152/173] [media] mceusb: add support for SMK eHome receiver Add USB ID of SMK RXX6000 series IR receiver. Often branded as Lenovo receiver. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 09ca9f638811..5cf2e749b9eb 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -303,6 +303,9 @@ static struct usb_device_id mceusb_dev_table[] = { /* SMK/I-O Data GV-MC7/RCKIT Receiver */ { USB_DEVICE(VENDOR_SMK, 0x0353), .driver_info = MCE_GEN2_NO_TX }, + /* SMK RXX6000 Infrared Receiver */ + { USB_DEVICE(VENDOR_SMK, 0x0357), + .driver_info = MCE_GEN2_NO_TX }, /* Tatung eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_TATUNG, 0x9150) }, /* Shuttle eHome Infrared Transceiver */ From 5f61ff86116cf637ae926f44c1857716ec89e9fb Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 17 Mar 2016 16:39:13 -0300 Subject: [PATCH 153/173] [media] media: rc: reduce size of struct ir_raw_event struct ir_raw_event currently has a size of 12 bytes on most (all?) architectures. This can be reduced to 8 bytes whilst maintaining full backwards compatibility. This saves 2KB in size of struct ir_raw_event_ctrl (as element kfifo is reduced by 512 * 4 bytes) and it allows to copy the full struct ir_raw_event with a single 64 bit operation. Successfully tested with the Nuvoton driver and successfully compile-tested with the ene_ir driver (as it uses the carrier / duty_cycle elements). Signed-off-by: Heiner Kallweit Signed-off-by: Mauro Carvalho Chehab --- include/media/rc-core.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 0f77b3dffb37..b6586a91129c 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -215,12 +215,9 @@ enum raw_event_type { struct ir_raw_event { union { u32 duration; - - struct { - u32 carrier; - u8 duty_cycle; - }; + u32 carrier; }; + u8 duty_cycle; unsigned pulse:1; unsigned reset:1; @@ -228,13 +225,7 @@ struct ir_raw_event { unsigned carrier_report:1; }; -#define DEFINE_IR_RAW_EVENT(event) \ - struct ir_raw_event event = { \ - { .duration = 0 } , \ - .pulse = 0, \ - .reset = 0, \ - .timeout = 0, \ - .carrier_report = 0 } +#define DEFINE_IR_RAW_EVENT(event) struct ir_raw_event event = {} static inline void init_ir_raw_event(struct ir_raw_event *ev) { @@ -256,8 +247,7 @@ void ir_raw_event_set_idle(struct rc_dev *dev, bool idle); static inline void ir_raw_event_reset(struct rc_dev *dev) { - DEFINE_IR_RAW_EVENT(ev); - ev.reset = true; + struct ir_raw_event ev = { .reset = true }; ir_raw_event_store(dev, &ev); ir_raw_event_handle(dev); From acc37e8f86a8f2a3e1d1dc97d99b7625ccc95424 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 18 Mar 2016 18:31:35 -0300 Subject: [PATCH 154/173] [media] media/dvb-core: forward media_create_pad_links() return value Instead of always return -ENOMEM, return the real error that should come from media_create_pad_link(). Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvbdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index e1684c570e2f..75a3f4b57fd4 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -676,13 +676,13 @@ int dvb_create_media_graph(struct dvb_adapter *adap, demux, 0, MEDIA_LNK_FL_ENABLED, false); if (ret) - return -ENOMEM; + return ret; } if (demux && ca) { ret = media_create_pad_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED); if (ret) - return -ENOMEM; + return ret; } /* Create demux links for each ringbuffer/pad */ From 47cae1e1cfdb6a07ca19c61345c4d96f828b14d7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 21 Mar 2016 08:33:05 -0300 Subject: [PATCH 155/173] [media] drivers/media/rc: postpone kfree(rc_dev) CONFIG_DEBUG_KOBJECT_RELEASE found this bug. Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 68541b1e3bcb..7dfc7c2188f0 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1263,6 +1263,9 @@ static ssize_t store_filter(struct device *device, static void rc_dev_release(struct device *device) { + struct rc_dev *dev = to_rc_dev(device); + + kfree(dev); } #define ADD_HOTPLUG_VAR(fmt, val...) \ @@ -1384,7 +1387,9 @@ void rc_free_device(struct rc_dev *dev) put_device(&dev->dev); - kfree(dev); + /* kfree(dev) will be called by the callback function + rc_dev_release() */ + module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(rc_free_device); From c56d34a73ea3622fbc3f5a85140f2353f14b591c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 21 Mar 2016 10:30:33 -0300 Subject: [PATCH 156/173] [media] drivers/media/media-device: move debug log before _devnode_unregister() After media_devnode_unregister(), the struct media_device may be freed already, and dereferencing it may crash. Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 898a3cf814ba..de92a6bf3751 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -801,9 +801,8 @@ void media_device_unregister(struct media_device *mdev) mutex_unlock(&mdev->graph_mutex); device_remove_file(&mdev->devnode.dev, &dev_attr_model); + dev_dbg(mdev->dev, "Media device unregistering\n"); media_devnode_unregister(&mdev->devnode); - - dev_dbg(mdev->dev, "Media device unregistered\n"); } EXPORT_SYMBOL_GPL(media_device_unregister); From bf244f665d76d20312c80524689b32a752888838 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 21 Mar 2016 10:30:28 -0300 Subject: [PATCH 157/173] [media] drivers/media/media-devnode: clear private_data before put_device() Callbacks invoked from put_device() may free the struct media_devnode pointer, so any cleanup needs to be done before put_device(). Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-devnode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 64a4b1ef3dcd..b66dc9d0766b 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -197,10 +197,11 @@ static int media_release(struct inode *inode, struct file *filp) if (mdev->fops->release) mdev->fops->release(filp); + filp->private_data = NULL; + /* decrease the refcount unconditionally since the release() return value is ignored. */ put_device(&mdev->dev); - filp->private_data = NULL; return 0; } From c64ee34712eeeee95de0376f424dd4dc7661d3ed Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Tue, 22 Mar 2016 07:43:58 -0300 Subject: [PATCH 158/173] [media] xilinx-vipp: remove unnecessary of_node_put of_graph_get_next_endpoint(node, ep) decrements refcount on ep. When next==NULL we break and refcount on ep is decremented again. Signed-off-by: Franck Jullien Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-vipp.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index e795a4501e8b..feb3b2f1d874 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -351,19 +351,15 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, struct xvip_graph_entity *entity; struct device_node *remote; struct device_node *ep = NULL; - struct device_node *next; int ret = 0; dev_dbg(xdev->dev, "parsing node %s\n", node->full_name); while (1) { - next = of_graph_get_next_endpoint(node, ep); - if (next == NULL) + ep = of_graph_get_next_endpoint(node, ep); + if (ep == NULL) break; - of_node_put(ep); - ep = next; - dev_dbg(xdev->dev, "handling endpoint %s\n", ep->full_name); remote = of_graph_get_remote_port_parent(ep); From e8e20f1f02eb34b4d446101547159418346b65d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 23 Mar 2016 07:30:38 -0300 Subject: [PATCH 159/173] [media] dvb-usb: hide unused functions A couple of data structures in the dibusb-common file are only accessed when CONFIG_DVB_DIB3000MC is enabled, otherwise we get a harmless gcc warning: usb/dvb-usb/dibusb-common.c:223:34: error: 'dib3000p_panasonic_agc_config' defined but not used usb/dvb-usb/dibusb-common.c:211:32: error: 'stk3000p_dib3000p_config' defined but not used This moves the existing #ifdef a few lines up to correctly cover all the conditional data structures, which gets rid of the warning. Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dibusb-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index 35de6095926d..6eea4e68891d 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -184,6 +184,8 @@ int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) } EXPORT_SYMBOL(dibusb_read_eeprom_byte); +#if IS_ENABLED(CONFIG_DVB_DIB3000MC) + /* 3000MC/P stuff */ // Config Adjacent channels Perf -cal22 static struct dibx000_agc_config dib3000p_mt2060_agc_config = { @@ -242,8 +244,6 @@ static struct dibx000_agc_config dib3000p_panasonic_agc_config = { .agc2_slope2 = 0x1e, }; -#if IS_ENABLED(CONFIG_DVB_DIB3000MC) - static struct dib3000mc_config mod3000p_dib3000p_config = { &dib3000p_panasonic_agc_config, From 00303f9134c2db5d17bacda541c07d46517485d9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 23 Mar 2016 10:03:03 -0300 Subject: [PATCH 160/173] [media] media: i2c: ths7303: remove redundant assignment on bt The extraneous assignment on bt is redundant and can be removed. Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ths7303.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 5bbfcab01c75..71a31352135c 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -285,7 +285,7 @@ static int ths7303_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off"); if (state->bt.pixelclock) { - struct v4l2_bt_timings *bt = bt = &state->bt; + struct v4l2_bt_timings *bt = &state->bt; u32 frame_width, frame_height; frame_width = V4L2_DV_BT_FRAME_WIDTH(bt); From f73696275e64d55c59947b42979b531cb026d718 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Mar 2016 09:19:31 -0300 Subject: [PATCH 161/173] [media] media-device: Simplify compat32 logic Only MEDIA_IOC_ENUM_LINKS32 require an special logic when userspace is 32 bits and Kernel is 64 bits. For the rest, media_device_ioctl() will do the right thing, and will return -ENOIOCTLCMD if the ioctl is unknown. Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index de92a6bf3751..47a99af5525e 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -499,12 +499,6 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, long ret; switch (cmd) { - case MEDIA_IOC_DEVICE_INFO: - case MEDIA_IOC_ENUM_ENTITIES: - case MEDIA_IOC_SETUP_LINK: - case MEDIA_IOC_G_TOPOLOGY: - return media_device_ioctl(filp, cmd, arg); - case MEDIA_IOC_ENUM_LINKS32: mutex_lock(&dev->graph_mutex); ret = media_device_enum_links32(dev, @@ -513,7 +507,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, break; default: - ret = -ENOIOCTLCMD; + return media_device_ioctl(filp, cmd, arg); } return ret; From a6311d275126f65e4ccb88d3d384003b8dab47d8 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 24 Mar 2016 08:23:50 -0300 Subject: [PATCH 162/173] [media] c8sectpfe: Fix broken circular buffer wp management During the review process, a regression was intoduced in the circular buffer write pointer management. This means that wp doesn't get managed properly once the buffer becomes full. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 78e3cb9a628f..875d38455fa7 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -130,7 +130,7 @@ static void channel_swdemux_tsklet(unsigned long data) writel(channel->back_buffer_busaddr, channel->irec + DMA_PRDS_BUSRP_TP(0)); else - writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0)); + writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0)); } static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) From ee105cac24693c8f3ea92e1700d5d16c4b035eb5 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 24 Mar 2016 08:23:51 -0300 Subject: [PATCH 163/173] [media] c8sectpfe: Demote print to dev_dbg Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 875d38455fa7..acd076780827 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -585,7 +585,7 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->pid_buffer_busaddr, fei->io + PIDF_BASE(tsin->tsin_id)); - dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", + dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), &tsin->pid_buffer_busaddr); From c23ac90f78aa9190643c82c1975a0cfe480d7c60 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 24 Mar 2016 08:23:52 -0300 Subject: [PATCH 164/173] [media] c8sectpfe: Rework firmware loading mechanism c8sectpfe driver relied on CONFIG_FW_LOADER_USER_HELPER_FALLBACK option for loading its xp70 firmware. A previous commit removed this Kconfig option, as it is apparently harmful, but did not update the driver code which relied on it. This patch reworks the firmware loading into the start_feed callback. At this point we can be sure the rootfs is present, thereby removing the depedency on CONFIG_FW_LOADER_USER_HELPER_FALLBACK. Fixes: 79f5b6ae960d ('[media] c8sectpfe: Remove select on CONFIG_FW_LOADER_USER_HELPER_FALLBACK') Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../platform/sti/c8sectpfe/c8sectpfe-core.c | 65 +++++++------------ 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index acd076780827..7dddf77a62cf 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -49,7 +49,7 @@ MODULE_FIRMWARE(FIRMWARE_MEMDMA); #define PID_TABLE_SIZE 1024 #define POLL_MSECS 50 -static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei); +static int load_c8sectpfe_fw(struct c8sectpfei *fei); #define TS_PKT_SIZE 188 #define HEADER_SIZE (4) @@ -141,6 +141,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) struct channel_info *channel; u32 tmp; unsigned long *bitmap; + int ret; switch (dvbdmxfeed->type) { case DMX_TYPE_TS: @@ -169,8 +170,9 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) } if (!atomic_read(&fei->fw_loaded)) { - dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); - return -EINVAL; + ret = load_c8sectpfe_fw(fei); + if (ret) + return ret; } mutex_lock(&fei->lock); @@ -265,8 +267,9 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) unsigned long *bitmap; if (!atomic_read(&fei->fw_loaded)) { - dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); - return -EINVAL; + ret = load_c8sectpfe_fw(fei); + if (ret) + return ret; } mutex_lock(&fei->lock); @@ -880,13 +883,6 @@ static int c8sectpfe_probe(struct platform_device *pdev) goto err_clk_disable; } - /* ensure all other init has been done before requesting firmware */ - ret = load_c8sectpfe_fw_step1(fei); - if (ret) { - dev_err(dev, "Couldn't load slim core firmware\n"); - goto err_clk_disable; - } - c8sectpfe_debugfs_init(fei); return 0; @@ -1091,15 +1087,14 @@ static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, phdr->p_memsz - phdr->p_filesz); } -static int load_slim_core_fw(const struct firmware *fw, void *context) +static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei) { - struct c8sectpfei *fei = context; Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; u8 __iomem *dst; int err = 0, i; - if (!fw || !context) + if (!fw || !fei) return -EINVAL; ehdr = (Elf32_Ehdr *)fw->data; @@ -1151,29 +1146,35 @@ static int load_slim_core_fw(const struct firmware *fw, void *context) return err; } -static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) +static int load_c8sectpfe_fw(struct c8sectpfei *fei) { - struct c8sectpfei *fei = context; + const struct firmware *fw; int err; + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); + + err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev); + if (err) + return err; + err = c8sectpfe_elf_sanity_check(fei, fw); if (err) { dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" , err); - goto err; + return err; } - err = load_slim_core_fw(fw, context); + err = load_slim_core_fw(fw, fei); if (err) { dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); - goto err; + return err; } /* now the firmware is loaded configure the input blocks */ err = configure_channels(fei); if (err) { dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); - goto err; + return err; } /* @@ -1186,28 +1187,6 @@ static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) writel(0x1, fei->io + DMA_CPU_RUN); atomic_set(&fei->fw_loaded, 1); -err: - complete_all(&fei->fw_ack); -} - -static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) -{ - int err; - - dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); - - init_completion(&fei->fw_ack); - atomic_set(&fei->fw_loaded, 0); - - err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, - FIRMWARE_MEMDMA, fei->dev, GFP_KERNEL, fei, - load_c8sectpfe_fw_cb); - - if (err) { - dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err); - complete_all(&fei->fw_ack); - return err; - } return 0; } From eda220acd5f2cef789430110f66a74cb49309b68 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 28 Mar 2016 21:25:29 -0300 Subject: [PATCH 165/173] [media] media: au0828 fix au0828_v4l2_device_register() to not unlock and free au0828_v4l2_device_register() unlocks au0828_dev->lock and frees au0828 dev in error legs before return. au0828_usb_probe() does the same when au0828_v4l2_device_register() returns error. Fix au0828_v4l2_device_register() to not to unlock and free in its error legs. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-video.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 32d7db96479c..7d0ec4cb248c 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -679,8 +679,6 @@ int au0828_v4l2_device_register(struct usb_interface *interface, if (retval) { pr_err("%s() v4l2_device_register failed\n", __func__); - mutex_unlock(&dev->lock); - kfree(dev); return retval; } @@ -691,8 +689,6 @@ int au0828_v4l2_device_register(struct usb_interface *interface, if (retval) { pr_err("%s() v4l2_ctrl_handler_init failed\n", __func__); - mutex_unlock(&dev->lock); - kfree(dev); return retval; } dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl; From 685f11097ab21221d734a259f2ba20684ffe3f05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 9 May 2016 14:06:09 -0300 Subject: [PATCH 166/173] [media] update cx23885 and em28xx cardlists Some new boards were added without updating those lists. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 2 ++ Documentation/video4linux/CARDLIST.em28xx | 1 + 2 files changed, 3 insertions(+) diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 44a4cfbfdc40..85a8fdcfcdaa 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -52,3 +52,5 @@ 51 -> DVBSky T982 [4254:0982] 52 -> Hauppauge WinTV-HVR5525 [0070:f038] 53 -> Hauppauge WinTV Starburst [0070:c12a] + 54 -> ViewCast 260e [1576:0260] + 55 -> ViewCast 460e [1576:0460] diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 67209998a439..ae9d5a852305 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -96,3 +96,4 @@ 95 -> Leadtek VC100 (em2861) [0413:6f07] 96 -> Terratec Cinergy T2 Stick HD (em28178) 97 -> Elgato EyeTV Hybrid 2008 INT (em2884) [0fd9:0018] + 98 -> PLEX PX-BCUD (em28178) From 628288d08a6162aa50e4dbd5918a9867b4c40e2f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 9 May 2016 14:15:00 -0300 Subject: [PATCH 167/173] [media] em28xx: add missing USB IDs Add missing USB id's for em281xx devices. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index ae9d5a852305..82eb4e4b6f69 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -76,9 +76,9 @@ 75 -> Dikom DK300 (em2882) 76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340] 77 -> EM2874 Leadership ISDBT (em2874) - 78 -> PCTV nanoStick T2 290e (em28174) + 78 -> PCTV nanoStick T2 290e (em28174) [2013:024f] 79 -> Terratec Cinergy H5 (em2884) [eb1a:2885,0ccd:10a2,0ccd:10ad,0ccd:10b6] - 80 -> PCTV DVB-S2 Stick (460e) (em28174) + 80 -> PCTV DVB-S2 Stick (460e) (em28174) [2013:024c] 81 -> Hauppauge WinTV HVR 930C (em2884) [2040:1605] 82 -> Terratec Cinergy HTC Stick (em2884) [0ccd:00b2] 83 -> Honestech Vidbox NW03 (em2860) [eb1a:5006] @@ -90,10 +90,10 @@ 89 -> Delock 61959 (em2874) [1b80:e1cc] 90 -> KWorld USB ATSC TV Stick UB435-Q V2 (em2874) [1b80:e346] 91 -> SpeedLink Vicious And Devine Laplace webcam (em2765) [1ae7:9003,1ae7:9004] - 92 -> PCTV DVB-S2 Stick (461e) (em28178) + 92 -> PCTV DVB-S2 Stick (461e) (em28178) [2013:0258] 93 -> KWorld USB ATSC TV Stick UB435-Q V3 (em2874) [1b80:e34c] - 94 -> PCTV tripleStick (292e) (em28178) + 94 -> PCTV tripleStick (292e) (em28178) [2013:025f,2040:0264] 95 -> Leadtek VC100 (em2861) [0413:6f07] - 96 -> Terratec Cinergy T2 Stick HD (em28178) + 96 -> Terratec Cinergy T2 Stick HD (em28178) [eb1a:8179] 97 -> Elgato EyeTV Hybrid 2008 INT (em2884) [0fd9:0018] - 98 -> PLEX PX-BCUD (em28178) + 98 -> PLEX PX-BCUD (em28178) [3275:0085] From 11a2a949d05e9d2d9823f0c45fa476743d9e462b Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Mon, 4 Apr 2016 12:12:52 -0300 Subject: [PATCH 168/173] [media] em28xx: add support for Hauppauge WinTV-dualHD DVB tuner Hauppauge WinTV-dualHD is a USB 2.0 dual DVB-T/T2/C tuner with following components: USB bridge: Empia EM28274 (chip id is the same as EM28174) Demodulator: 2x Silicon Labs Si2168-B40 Tuner: 2x Silicon Labs Si2157-A30 This patch adds support only for the first tuner. The demodulator needs firmware, available for example here: http://palosaari.fi/linux/v4l-dvb/firmware/Si2168/Si2168-B40/4.0.11/ The demodulators sit on the same I2C bus and their addresses are 0x64 and 0x67. The tuners are behind the demodulators and their addresses are 0x60 and 0x63. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 58 ++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 64 +++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-reg.h | 13 +++++ drivers/media/usb/em28xx/em28xx.h | 2 + 5 files changed, 138 insertions(+) diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 82eb4e4b6f69..6784220c6a16 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -97,3 +97,4 @@ 96 -> Terratec Cinergy T2 Stick HD (em28178) [eb1a:8179] 97 -> Elgato EyeTV Hybrid 2008 INT (em2884) [0fd9:0018] 98 -> PLEX PX-BCUD (em28178) [3275:0085] + 99 -> Hauppauge WinTV-dualHD DVB (em28174) [2040:0265] diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 57c3b905bf0a..e397f544f108 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -506,6 +506,30 @@ static struct em28xx_reg_seq plex_px_bcud[] = { {-1, -1, -1, -1}, }; +/* + * 2040:0265 Hauppauge WinTV-dualHD DVB + * reg 0x80/0x84: + * GPIO_0: Yellow LED tuner 1, 0=on, 1=off + * GPIO_1: Green LED tuner 1, 0=on, 1=off + * GPIO_2: Yellow LED tuner 2, 0=on, 1=off + * GPIO_3: Green LED tuner 2, 0=on, 1=off + * GPIO_5: Reset #2, 0=active + * GPIO_6: Reset #1, 0=active + */ +static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 200}, + {0x50, 0x04, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0xbf, 0xff, 100}, /* demod 1 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0xdf, 0xff, 100}, /* demod 2 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, + {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, + {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -585,6 +609,22 @@ static struct em28xx_led terratec_grabby_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led hauppauge_dualhd_leds[] = { + { + .role = EM28XX_LED_DIGITAL_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_1, + .inverted = 1, + }, + { + .role = EM28XX_LED_DIGITAL_CAPTURING_TS2, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_3, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2334,6 +2374,21 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = plex_px_bcud, .has_dvb = 1, }, + /* + * 2040:0265 Hauppauge WinTV-dualHD (DVB version). + * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157 + */ + [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = { + .name = "Hauppauge WinTV-dualHD DVB", + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = hauppauge_dualhd_dvb, + .has_dvb = 1, + .ir_codes = RC_MAP_HAUPPAUGE, + .leds = hauppauge_dualhd_leds, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2457,6 +2512,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x651f), .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, + { USB_DEVICE(0x2040, 0x0265), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -2891,6 +2948,7 @@ static void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: + case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB: { struct tveeprom tv; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fbc4e4b3268e..1a5c01202f73 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1883,6 +1883,70 @@ static int em28xx_dvb_init(struct em28xx *dev) px_bcud_init(dev); } break; + case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB: + { + struct i2c_adapter *adapter; + struct i2c_client *client; + struct i2c_board_info info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* attach demod */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_tuner = client; + + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 13cbb7f3ea10..afe7a66d7dc8 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -193,6 +193,19 @@ /* em2874 registers */ #define EM2874_R50_IR_CONFIG 0x50 #define EM2874_R51_IR 0x51 +#define EM2874_R5D_TS1_PKT_SIZE 0x5d +#define EM2874_R5E_TS2_PKT_SIZE 0x5e + /* + * For both TS1 and TS2, In isochronous mode: + * 0x01 188 bytes + * 0x02 376 bytes + * 0x03 564 bytes + * 0x04 752 bytes + * 0x05 940 bytes + * In bulk mode: + * 0x01..0xff total packet count in 188-byte + */ + #define EM2874_R5F_TS_ENABLE 0x5f /* em2874/174/84, em25xx, em276x/7x/8x GPIO registers */ diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 9ad1240d5e22..d148463b22c1 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -146,6 +146,7 @@ #define EM28178_BOARD_TERRATEC_T2_STICK_HD 96 #define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008 97 #define EM28178_BOARD_PLEX_PX_BCUD 98 +#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -407,6 +408,7 @@ enum em28xx_adecoder { enum em28xx_led_role { EM28XX_LED_ANALOG_CAPTURING = 0, EM28XX_LED_DIGITAL_CAPTURING, + EM28XX_LED_DIGITAL_CAPTURING_TS2, EM28XX_LED_ILLUMINATION, EM28XX_NUM_LED_ROLES, /* must be the last */ }; From cacdd6a4a8d5115da2767769b44bd435455f6424 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 14 Apr 2016 22:00:07 -0300 Subject: [PATCH 169/173] [media] tvp5150: return I2C write operation failure to callers The tvp5150_write() function calls i2c_smbus_write_byte_data() that can fail but does not propagate the error to the caller. Instead it just prints a debug, so callers can't know if the operation failed. So change the function to return the error code to the caller so it knows that the write failed and also print an error instead of just printing a debug information. While being there remove the inline keyword from tvp5150_write() to make it consistent with tvp5150_read() and also because it's called in a lot of places, so making inline is in fact counter productive since it makes the kernel image size to be much bigger (~16 KiB). Signed-off-by: Javier Martinez Canillas Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index ff18444e19e4..283836514e6a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -83,7 +83,7 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) return rc; } -static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, +static int tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, unsigned char value) { struct i2c_client *c = v4l2_get_subdevdata(sd); @@ -92,7 +92,9 @@ static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", addr, value); rc = i2c_smbus_write_byte_data(c, addr, value); if (rc < 0) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d\n", rc); + v4l2_err(sd, "i2c i/o error: rc == %d\n", rc); + + return rc; } static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, From eca4ca84a965d7fcc3430439898d0728818edc56 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 14 Apr 2016 22:00:08 -0300 Subject: [PATCH 170/173] [media] tvp5150: propagate I2C write error in .s_register callback The tvp5150_write() function can fail so don't return 0 unconditionally in tvp5150_s_register() but propagate what's returned by tvp5150_write(). Signed-off-by: Javier Martinez Canillas Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 283836514e6a..0b6d46c453bf 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1161,8 +1161,7 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; + return tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); } #endif From ec788795dde419af4935c3c9d08d641707114eec Mon Sep 17 00:00:00 2001 From: Alejandro Torrado Date: Mon, 18 Apr 2016 13:37:00 -0300 Subject: [PATCH 171/173] [media] dib0700: add USB ID for another STK8096-PVR ref design based card USB_PID_DIBCOM_STK8096GP also comes with USB_VID_DIBCOM vendor ID. Signed-off-by: Alejandro Torrado Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_devices.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index ea0391e32d23..0857b56e652c 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -3814,6 +3814,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E) }, { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E_SE) }, { USB_DEVICE(USB_VID_PCTV, USB_PID_DIBCOM_STK8096PVR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096PVR) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -5017,7 +5018,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { { "DiBcom STK8096-PVR reference design", - { &dib0700_usb_id_table[83], NULL }, + { &dib0700_usb_id_table[83], + &dib0700_usb_id_table[84], NULL}, { NULL }, }, }, From 0185f850176201260e0f02c5bccca1defb8e0884 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Apr 2016 10:17:21 -0300 Subject: [PATCH 172/173] [media] samples: v4l: from Documentation to samples directory With the new autoksyms support, we can run into a situation where the v4l pci skeleton module is the only one using some exported symbols that get dropped because they are never referenced by the kernel otherwise, causing a build problem: ERROR: "vb2_dma_contig_memops" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "vb2_dma_contig_init_ctx_attrs" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "v4l2_match_dv_timings" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "v4l2_find_dv_timings_cap" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "v4l2_valid_dv_timings" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "v4l2_enum_dv_timings_cap" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! ERROR: "vb2_dma_contig_cleanup_ctx" [Documentation/video4linux/v4l2-pci-skeleton.ko] undefined! Specifically, we do look in the samples directory for users of symbols, but not the Documentation directory. This solves the build problem by moving the connector sample into the same directory as the other samples. Fixes: 23121ca2b56b ("kbuild: create/adjust generated/autoksyms.h") Signed-off-by: Arnd Bergmann --- Documentation/Makefile | 3 +-- Documentation/video4linux/v4l2-framework.txt | 2 +- samples/Makefile | 2 +- {Documentation/video4linux => samples/v4l}/Makefile | 0 {Documentation/video4linux => samples/v4l}/v4l2-pci-skeleton.c | 0 5 files changed, 3 insertions(+), 4 deletions(-) rename {Documentation/video4linux => samples/v4l}/Makefile (100%) rename {Documentation/video4linux => samples/v4l}/v4l2-pci-skeleton.c (100%) diff --git a/Documentation/Makefile b/Documentation/Makefile index 1207d7907650..f3b04d22957c 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -1,4 +1,3 @@ subdir-y := accounting auxdisplay blackfin connector \ filesystems filesystems ia64 laptops mic misc-devices \ - networking pcmcia prctl ptp timers vDSO video4linux \ - watchdog + networking pcmcia prctl ptp timers vDSO watchdog diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index fa41608ab2b4..cbefc7902f5f 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -35,7 +35,7 @@ need and this same framework should make it much easier to refactor common code into utility functions shared by all drivers. A good example to look at as a reference is the v4l2-pci-skeleton.c -source that is available in this directory. It is a skeleton driver for +source that is available in samples/v4l/. It is a skeleton driver for a PCI capture card, and demonstrates how to use the V4L2 driver framework. It can be used as a template for real PCI video capture driver. diff --git a/samples/Makefile b/samples/Makefile index 48001d7e23f0..ad440d670cdb 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ - configfs/ + configfs/ v4l/ diff --git a/Documentation/video4linux/Makefile b/samples/v4l/Makefile similarity index 100% rename from Documentation/video4linux/Makefile rename to samples/v4l/Makefile diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c similarity index 100% rename from Documentation/video4linux/v4l2-pci-skeleton.c rename to samples/v4l/v4l2-pci-skeleton.c From aff093d4bbca91f543e24cde2135f393b8130f4b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 Apr 2016 06:15:38 -0300 Subject: [PATCH 173/173] [media] exynos-gsc: avoid build warning without CONFIG_OF When building the exynos-gsc driver with CONFIG_OF disabled, we get a warning about an out-of-bounds access: drivers/media/platform/exynos-gsc/gsc-core.c: In function 'gsc_probe': drivers/media/platform/exynos-gsc/gsc-core.c:1078:34: error: array subscript is above array bounds [-Werror=array-bounds] This is harmless because the driver will never be used without CONFIG_OF, but it's better to avoid the warning anyway. Checking the return value of of_alias_get_id() for an error condition is probably a good idea anyway, and it makes sure the compiler can verify that we don't get into that situation. Fixes: 26a7ed9c1819 ("[media] exynos-gsc: remove an always false condition") Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c595723f5031..c04973669a47 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1063,13 +1063,17 @@ static int gsc_probe(struct platform_device *pdev) struct resource *res; struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev); struct device *dev = &pdev->dev; - int ret = 0; + int ret; gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL); if (!gsc) return -ENOMEM; - gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); + ret = of_alias_get_id(pdev->dev.of_node, "gsc"); + if (ret < 0) + return ret; + + gsc->id = ret; if (gsc->id >= drv_data->num_entities) { dev_err(dev, "Invalid platform device id: %d\n", gsc->id); return -EINVAL;