From 29debab0a94035a390801d1f177d171d014b7765 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 3 May 2016 16:27:16 -0400 Subject: s5p-mfc: Set device name for reserved memory region devs The devices don't have a name set, so makes dev_name() returns NULL which makes harder to identify the devices that are causing issues, for example: WARNING: CPU: 2 PID: 616 at drivers/base/core.c:251 device_release+0x8c/0x90 Device '(null)' does not have a release() function, it is broken and must be fixed. And after setting the device name: WARNING: CPU: 0 PID: 591 at drivers/base/core.c:251 device_release+0x8c/0x90 Device 's5p-mfc-l' does not have a release() function, it is broken and must be fixed. Cc: Fixes: 6e83e6e25eb4 ("[media] s5p-mfc: Fix kernel warning on memory init") Signed-off-by: Javier Martinez Canillas Tested-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b16466f..8fc1fd4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1062,6 +1062,8 @@ static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) mfc_err("Not enough memory\n"); return -ENOMEM; } + + dev_set_name(dev->mem_dev_l, "%s", "s5p-mfc-l"); device_initialize(dev->mem_dev_l); of_property_read_u32_array(dev->plat_dev->dev.of_node, "samsung,mfc-l", mem_info, 2); @@ -1079,6 +1081,8 @@ static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) mfc_err("Not enough memory\n"); return -ENOMEM; } + + dev_set_name(dev->mem_dev_r, "%s", "s5p-mfc-r"); device_initialize(dev->mem_dev_r); of_property_read_u32_array(dev->plat_dev->dev.of_node, "samsung,mfc-r", mem_info, 2); -- cgit v0.10.2 From 6311f1261f59ce5e51fbe5cc3b5e7737197316ac Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 3 May 2016 16:27:17 -0400 Subject: s5p-mfc: Add release callback for memory region devs When s5p_mfc_remove() calls put_device() for the reserved memory region devs, the driver core warns that the dev doesn't have a release callback: WARNING: CPU: 0 PID: 591 at drivers/base/core.c:251 device_release+0x8c/0x90 Device 's5p-mfc-l' does not have a release() function, it is broken and must be fixed. Also, the declared DMA memory using dma_declare_coherent_memory() isn't relased so add a dev .release that calls dma_release_declared_memory(). Cc: Fixes: 6e83e6e25eb4 ("[media] s5p-mfc: Fix kernel warning on memory init") Signed-off-by: Javier Martinez Canillas Tested-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8fc1fd4..beb4fd5 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1050,6 +1050,11 @@ static int match_child(struct device *dev, void *data) return !strcmp(dev_name(dev), (char *)data); } +static void s5p_mfc_memdev_release(struct device *dev) +{ + dma_release_declared_memory(dev); +} + static void *mfc_get_drv_data(struct platform_device *pdev); static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) @@ -1064,6 +1069,7 @@ static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) } dev_set_name(dev->mem_dev_l, "%s", "s5p-mfc-l"); + dev->mem_dev_l->release = s5p_mfc_memdev_release; device_initialize(dev->mem_dev_l); of_property_read_u32_array(dev->plat_dev->dev.of_node, "samsung,mfc-l", mem_info, 2); @@ -1083,6 +1089,7 @@ static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) } dev_set_name(dev->mem_dev_r, "%s", "s5p-mfc-r"); + dev->mem_dev_r->release = s5p_mfc_memdev_release; device_initialize(dev->mem_dev_r); of_property_read_u32_array(dev->plat_dev->dev.of_node, "samsung,mfc-r", mem_info, 2); -- cgit v0.10.2 From c974c436eaf406225b7f4c5d27ae033a12667a38 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 3 May 2016 16:27:18 -0400 Subject: s5p-mfc: Fix race between s5p_mfc_probe() and s5p_mfc_open() The s5p_mfc_probe() function registers the video devices before all the resources needed by s5p_mfc_open() are correctly initalized. So if s5p_mfc_open() function is called before s5p_mfc_probe() finishes (since the video dev is already registered), a NULL pointer dereference will happen due s5p_mfc_open() accessing uninitialized vars such as the struct s5p_mfc_dev .watchdog_timer and .mfc_ops fields. An example is following BUG caused by add_timer() getting a NULL pointer: [ 45.765374] kernel BUG at kernel/time/timer.c:790! [ 45.765381] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM ... [ 45.766149] [] (mod_timer) from [] (s5p_mfc_open+0x274/0x4d4 [s5p_mfc]) [ 45.766416] [] (s5p_mfc_open [s5p_mfc]) from [] (v4l2_open+0x9c/0x100 [videodev]) [ 45.766547] [] (v4l2_open [videodev]) from [] (chrdev_open+0x9c/0x178) [ 45.766575] [] (chrdev_open) from [] (do_dentry_open+0x1e0/0x300) [ 45.766595] [] (do_dentry_open) from [] (path_openat+0x800/0x10d4) [ 45.766610] [] (path_openat) from [] (do_filp_open+0x5c/0xc0) [ 45.766624] [] (do_filp_open) from [] (do_sys_open+0x10c/0x1bc) [ 45.766642] [] (do_sys_open) from [] (ret_fast_syscall+0x0/0x3c) [ 45.766655] Code: eaffffe3 e3a00001 e28dd008 e8bd81f0 (e7f001f2) Fix it by registering the video devs as the last step in s5p_mfc_probe(). Signed-off-by: Javier Martinez Canillas Tested-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index beb4fd5..501d822 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1212,14 +1212,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_dec_reg; - } - v4l2_info(&dev->v4l2_dev, - "decoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); /* encoder */ @@ -1237,14 +1229,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_enc_reg; - } - v4l2_info(&dev->v4l2_dev, - "encoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); @@ -1261,15 +1245,34 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_hw_cmds(dev); s5p_mfc_init_regs(dev); + /* Register decoder and encoder */ + ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_dec); + goto err_dec_reg; + } + v4l2_info(&dev->v4l2_dev, + "decoder registered as /dev/video%d\n", dev->vfd_dec->num); + + ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_enc); + goto err_enc_reg; + } + v4l2_info(&dev->v4l2_dev, + "encoder registered as /dev/video%d\n", dev->vfd_enc->num); + pr_debug("%s--\n", __func__); return 0; /* Deinit MFC if probe had failed */ err_enc_reg: - video_device_release(dev->vfd_enc); -err_enc_alloc: video_unregister_device(dev->vfd_dec); err_dec_reg: + video_device_release(dev->vfd_enc); +err_enc_alloc: video_device_release(dev->vfd_dec); err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); -- cgit v0.10.2 From 3f03396918962b2f8b888d02b23cd1e0c88bf5e5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 09:16:06 +0200 Subject: media: vb2-dma-contig: add helper for setting dma max seg size Add a helper function for device drivers to set DMA's max_seg_size. Setting it to largest possible value lets DMA-mapping API always create contiguous mappings in DMA address space. This is essential for all devices, which use dma-contig videobuf2 memory allocator and shared buffers. Till now, the only case when vb2-dma-contig really 'worked' was a case where userspace provided USERPTR buffer, which was in fact mmaped contiguous buffer from the other v4l2/drm device. Also DMABUF made of contiguous buffer worked only when its exporter did not split it into several chunks in the scatter-list. Any other buffer failed, regardless of the arch/platform used and the presence of the IOMMU of the device bus. This patch provides interface to fix this issue. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 5361197..e3e47ac 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -753,6 +753,59 @@ void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) } EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); +/** + * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size + * @dev: device for configuring DMA parameters + * @size: size of DMA max segment size to set + * + * To allow mapping the scatter-list into a single chunk in the DMA + * address space, the device is required to have the DMA max segment + * size parameter set to a value larger than the buffer size. Otherwise, + * the DMA-mapping subsystem will split the mapping into max segment + * size chunks. This function sets the DMA max segment size + * parameter to let DMA-mapping map a buffer as a single chunk in DMA + * address space. + * This code assumes that the DMA-mapping subsystem will merge all + * scatterlist segments if this is really possible (for example when + * an IOMMU is available and enabled). + * Ideally, this parameter should be set by the generic bus code, but it + * is left with the default 64KiB value due to historical litmiations in + * other subsystems (like limited USB host drivers) and there no good + * place to set it to the proper value. + * This function should be called from the drivers, which are known to + * operate on platforms with IOMMU and provide access to shared buffers + * (either USERPTR or DMABUF). This should be done before initializing + * videobuf2 queue. + */ +int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size) +{ + if (!dev->dma_parms) { + dev->dma_parms = kzalloc(sizeof(dev->dma_parms), GFP_KERNEL); + if (!dev->dma_parms) + return -ENOMEM; + } + if (dma_get_max_seg_size(dev) < size) + return dma_set_max_seg_size(dev, size); + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size); + +/* + * vb2_dma_contig_clear_max_seg_size() - release resources for DMA parameters + * @dev: device for configuring DMA parameters + * + * This function releases resources allocated to configure DMA parameters + * (see vb2_dma_contig_set_max_seg_size() function). It should be called from + * device drivers on driver remove. + */ +void vb2_dma_contig_clear_max_seg_size(struct device *dev) +{ + kfree(dev->dma_parms); + dev->dma_parms = NULL; +} +EXPORT_SYMBOL_GPL(vb2_dma_contig_clear_max_seg_size); + MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index 2087c9a..f7dc840 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -35,6 +35,8 @@ static inline void *vb2_dma_contig_init_ctx(struct device *dev) } void vb2_dma_contig_cleanup_ctx(void *alloc_ctx); +int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size); +void vb2_dma_contig_clear_max_seg_size(struct device *dev); extern const struct vb2_mem_ops vb2_dma_contig_memops; -- cgit v0.10.2 From 712b617e5e1a9084917c5f6ab1ccb1456b722f51 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 09:16:07 +0200 Subject: media: set proper max seg size for devices on Exynos SoCs All multimedia devices found on Exynos SoCs support only contiguous buffers, so set DMA max segment size to DMA_BIT_MASK(32) to let memory allocator to correctly create contiguous memory mappings. Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c049736..c9d2009 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1124,6 +1124,7 @@ static int gsc_probe(struct platform_device *pdev) goto err_m2m; /* Initialize continious memory allocator */ + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(gsc->alloc_ctx)) { ret = PTR_ERR(gsc->alloc_ctx); @@ -1153,6 +1154,7 @@ static int gsc_remove(struct platform_device *pdev) v4l2_device_unregister(&gsc->v4l2_dev); vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); gsc_clk_put(gsc); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index b1c1cea..368f44f 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1019,6 +1019,7 @@ static int fimc_probe(struct platform_device *pdev) } /* Initialize contiguous memory allocator */ + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); @@ -1124,6 +1125,7 @@ static int fimc_remove(struct platform_device *pdev) fimc_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 979c388..bd98b56 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -847,6 +847,7 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { ret = PTR_ERR(is->alloc_ctx); @@ -940,6 +941,7 @@ static int fimc_is_remove(struct platform_device *pdev) free_irq(is->irq, is); fimc_is_unregister_subdevs(is); vb2_dma_contig_cleanup_ctx(is->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); fimc_is_debugfs_remove(is); release_firmware(is->fw.f_w); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index dc1b929..27cb620 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1551,6 +1551,7 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_sd; } + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); @@ -1652,6 +1653,7 @@ static int fimc_lite_remove(struct platform_device *pdev) pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(dev); fimc_lite_clk_put(fimc); dev_info(dev, "Driver unloaded\n"); diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 612d1ea..d3e3469 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -681,6 +681,7 @@ static int g2d_probe(struct platform_device *pdev) goto put_clk_gate; } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(dev->alloc_ctx)) { ret = PTR_ERR(dev->alloc_ctx); @@ -757,6 +758,7 @@ static int g2d_remove(struct platform_device *pdev) video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); clk_unprepare(dev->gate); clk_put(dev->gate); clk_unprepare(dev->clk); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index caa19b4..17bc94092 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2843,6 +2843,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) goto device_register_rollback; } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(jpeg->alloc_ctx)) { v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n"); @@ -2942,6 +2943,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev) video_unregister_device(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 501d822..d1d9d388 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1175,11 +1175,13 @@ static int s5p_mfc_probe(struct platform_device *pdev) } } + vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32)); dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); if (IS_ERR(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); goto err_res; } + vb2_dma_contig_set_max_seg_size(dev->mem_dev_r, DMA_BIT_MASK(32)); dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); if (IS_ERR(dev->alloc_ctx[1])) { ret = PTR_ERR(dev->alloc_ctx[1]); @@ -1307,6 +1309,8 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l); + vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r); if (pdev->dev.of_node) { put_device(dev->mem_dev_l); put_device(dev->mem_dev_r); diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 7ab5578..123d271 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -80,6 +80,7 @@ int mxr_acquire_video(struct mxr_device *mdev, goto fail; } + vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32)); mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); if (IS_ERR(mdev->alloc_ctx)) { mxr_err(mdev, "could not acquire vb2 allocator\n"); @@ -152,6 +153,7 @@ void mxr_release_video(struct mxr_device *mdev) kfree(mdev->output[i]); vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); + vb2_dma_contig_clear_max_seg_size(mdev->dev); v4l2_device_unregister(&mdev->v4l2_dev); } -- cgit v0.10.2 From 59ce4039727ef408769ab0e1263ece893abe22bb Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:24 +0200 Subject: of: reserved_mem: add support for using more than one region for given device This patch allows device drivers to initialize more than one reserved memory region assigned to given device. When driver needs to use more than one reserved memory region, it should allocate child devices and initialize regions by index for each of its child devices. Signed-off-by: Marek Szyprowski Acked-by: Rob Herring Signed-off-by: Sylwester Nawrocki diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index ed01c01..04e4fe5 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -21,6 +21,7 @@ #include #include #include +#include #define MAX_RESERVED_REGIONS 16 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; @@ -289,53 +290,95 @@ static inline struct reserved_mem *__find_rmem(struct device_node *node) return NULL; } +struct rmem_assigned_device { + struct device *dev; + struct reserved_mem *rmem; + struct list_head list; +}; + +static LIST_HEAD(of_rmem_assigned_device_list); +static DEFINE_MUTEX(of_rmem_assigned_device_mutex); + /** - * of_reserved_mem_device_init() - assign reserved memory region to given device + * of_reserved_mem_device_init_by_idx() - assign reserved memory region to + * given device + * @dev: Pointer to the device to configure + * @np: Pointer to the device_node with 'reserved-memory' property + * @idx: Index of selected region * - * This function assign memory region pointed by "memory-region" device tree - * property to the given device. + * This function assigns respective DMA-mapping operations based on reserved + * memory region specified by 'memory-region' property in @np node to the @dev + * device. When driver needs to use more than one reserved memory region, it + * should allocate child devices and initialize regions by name for each of + * child device. + * + * Returns error code or zero on success. */ -int of_reserved_mem_device_init(struct device *dev) +int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx) { + struct rmem_assigned_device *rd; + struct device_node *target; struct reserved_mem *rmem; - struct device_node *np; int ret; - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) - return -ENODEV; + if (!np || !dev) + return -EINVAL; + + target = of_parse_phandle(np, "memory-region", idx); + if (!target) + return -EINVAL; - rmem = __find_rmem(np); - of_node_put(np); + rmem = __find_rmem(target); + of_node_put(target); if (!rmem || !rmem->ops || !rmem->ops->device_init) return -EINVAL; + rd = kmalloc(sizeof(struct rmem_assigned_device), GFP_KERNEL); + if (!rd) + return -ENOMEM; + ret = rmem->ops->device_init(rmem, dev); - if (ret == 0) + if (ret == 0) { + rd->dev = dev; + rd->rmem = rmem; + + mutex_lock(&of_rmem_assigned_device_mutex); + list_add(&rd->list, &of_rmem_assigned_device_list); + mutex_unlock(&of_rmem_assigned_device_mutex); + dev_info(dev, "assigned reserved memory node %s\n", rmem->name); + } else { + kfree(rd); + } return ret; } -EXPORT_SYMBOL_GPL(of_reserved_mem_device_init); +EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx); /** * of_reserved_mem_device_release() - release reserved memory device structures + * @dev: Pointer to the device to deconfigure * * This function releases structures allocated for memory region handling for * the given device. */ void of_reserved_mem_device_release(struct device *dev) { - struct reserved_mem *rmem; - struct device_node *np; - - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) - return; - - rmem = __find_rmem(np); - of_node_put(np); + struct rmem_assigned_device *rd; + struct reserved_mem *rmem = NULL; + + mutex_lock(&of_rmem_assigned_device_mutex); + list_for_each_entry(rd, &of_rmem_assigned_device_list, list) { + if (rd->dev == dev) { + rmem = rd->rmem; + list_del(&rd->list); + kfree(rd); + break; + } + } + mutex_unlock(&of_rmem_assigned_device_mutex); if (!rmem || !rmem->ops || !rmem->ops->device_release) return; diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index ad2f670..1779cda 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -1,7 +1,8 @@ #ifndef __OF_RESERVED_MEM_H #define __OF_RESERVED_MEM_H -struct device; +#include + struct of_phandle_args; struct reserved_mem_ops; @@ -28,14 +29,17 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) #ifdef CONFIG_OF_RESERVED_MEM -int of_reserved_mem_device_init(struct device *dev); + +int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx); void of_reserved_mem_device_release(struct device *dev); void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); #else -static inline int of_reserved_mem_device_init(struct device *dev) +static inline int of_reserved_mem_device_init_by_idx(struct device *dev, + struct device_node *np, int idx) { return -ENOSYS; } @@ -46,4 +50,19 @@ static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } #endif +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * @dev: Pointer to the device to configure + * + * This function assigns respective DMA-mapping operations based on the first + * reserved memory region specified by 'memory-region' property in device tree + * node of the given device. + * + * Returns error code or zero on success. + */ +static inline int of_reserved_mem_device_init(struct device *dev) +{ + return of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0); +} + #endif /* __OF_RESERVED_MEM_H */ -- cgit v0.10.2 From 6a8d73e2859df06540d37877b1c1a0c9b5d11292 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:25 +0200 Subject: media: s5p-mfc: use generic reserved memory bindings Use generic reserved memory bindings and mark old, custom properties as obsoleted. Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Acked-by: Rob Herring Acked-by: Krzysztof Kozlowski Signed-off-by: Sylwester Nawrocki diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index 2d5787e..92c94f5 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -21,15 +21,18 @@ Required properties: - clock-names : from common clock binding: must contain "mfc", corresponding to entry in the clocks property. - - samsung,mfc-r : Base address of the first memory bank used by MFC - for DMA contiguous memory allocation and its size. - - - samsung,mfc-l : Base address of the second memory bank used by MFC - for DMA contiguous memory allocation and its size. - Optional properties: - power-domains : power-domain property defined with a phandle to respective power domain. + - memory-region : from reserved memory binding: phandles to two reserved + memory regions, first is for "left" mfc memory bus interfaces, + second if for the "right" mfc memory bus, used when no SYSMMU + support is available + +Obsolete properties: + - samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region + property instead + Example: SoC specific DT entry: @@ -43,9 +46,29 @@ mfc: codec@13400000 { clock-names = "mfc"; }; +Reserved memory specific DT entry for given board (see reserved memory binding +for more information): + +reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; +}; + Board specific DT entry: codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; -- cgit v0.10.2 From c79667dd93b084fe412bcfe7fbf0ba43f7dec520 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:26 +0200 Subject: media: s5p-mfc: replace custom reserved memory handling code with generic one This patch removes custom code for initialization and handling of reserved memory regions in s5p-mfc driver and replaces it with generic reserved memory regions api. s5p-mfc driver now handles two reserved memory regions defined by generic reserved memory bindings. Support for non-dt platform has been removed, because all supported platforms have been already converted to device tree. Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index d1d9d388..fff5f43 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -1043,66 +1044,71 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; -static int match_child(struct device *dev, void *data) -{ - if (!dev_name(dev)) - return 0; - return !strcmp(dev_name(dev), (char *)data); -} - +/* DMA memory related helper functions */ static void s5p_mfc_memdev_release(struct device *dev) { - dma_release_declared_memory(dev); + of_reserved_mem_device_release(dev); } -static void *mfc_get_drv_data(struct platform_device *pdev); - -static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) +static struct device *s5p_mfc_alloc_memdev(struct device *dev, + const char *name, unsigned int idx) { - unsigned int mem_info[2] = { }; + struct device *child; + int ret; - dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - return -ENOMEM; + child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, "%s:%s", dev_name(dev), name); + child->parent = dev; + child->bus = dev->bus; + child->coherent_dma_mask = dev->coherent_dma_mask; + child->dma_mask = dev->dma_mask; + child->release = s5p_mfc_memdev_release; + + if (device_add(child) == 0) { + ret = of_reserved_mem_device_init_by_idx(child, dev->of_node, + idx); + if (ret == 0) + return child; } - dev_set_name(dev->mem_dev_l, "%s", "s5p-mfc-l"); - dev->mem_dev_l->release = s5p_mfc_memdev_release; - device_initialize(dev->mem_dev_l); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-l", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; - } + put_device(child); + return NULL; +} - dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - return -ENOMEM; - } +static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + struct device *dev = &mfc_dev->plat_dev->dev; - dev_set_name(dev->mem_dev_r, "%s", "s5p-mfc-r"); - dev->mem_dev_r->release = s5p_mfc_memdev_release; - device_initialize(dev->mem_dev_r); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-r", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + /* + * Create and initialize virtual devices for accessing + * reserved memory regions. + */ + mfc_dev->mem_dev_l = s5p_mfc_alloc_memdev(dev, "left", + MFC_BANK1_ALLOC_CTX); + if (!mfc_dev->mem_dev_l) + return -ENODEV; + mfc_dev->mem_dev_r = s5p_mfc_alloc_memdev(dev, "right", + MFC_BANK2_ALLOC_CTX); + if (!mfc_dev->mem_dev_r) { + device_unregister(mfc_dev->mem_dev_l); + return -ENODEV; } + return 0; } +static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev) +{ + device_unregister(mfc_dev->mem_dev_l); + device_unregister(mfc_dev->mem_dev_r); +} + +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1128,12 +1134,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->variant = mfc_get_drv_data(pdev); - ret = s5p_mfc_init_pm(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get mfc clock source\n"); - return ret; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->regs_base = devm_ioremap_resource(&pdev->dev, res); @@ -1154,25 +1154,16 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - if (pdev->dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret < 0) - goto err_res; - } else { - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-l", match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-r", match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + ret = s5p_mfc_configure_dma_memory(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to configure DMA memory\n"); + return ret; + } + + ret = s5p_mfc_init_pm(dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get mfc clock source\n"); + return ret; } vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32)); @@ -1309,12 +1300,9 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); + s5p_mfc_unconfigure_dma_memory(dev); vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l); vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r); - if (pdev->dev.of_node) { - put_device(dev->mem_dev_l); - put_device(dev->mem_dev_r); - } s5p_mfc_final_pm(dev); return 0; -- cgit v0.10.2 From 04f776734c4e03e33111d3d5a994b589870df623 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:27 +0200 Subject: media: s5p-mfc: add iommu support This patch adds support for IOMMU to s5p-mfc device driver. MFC firmware is limited and it cannot use the default configuration. If IOMMU is available, the patch disables the default DMA address space configuration and creates a new address space of size limited to 256M and base address set to 0x20000000. For now the same address space is shared by both 'left' and 'right' memory channels, because the DMA/IOMMU frameworks do not support configuring them separately. This is not optimal, but besides limiting total address space available has no other drawbacks (MFC firmware supports 256M of address space per each channel). Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Sylwester Nawrocki diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index fff5f43..6ee620e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -30,6 +30,7 @@ #include "s5p_mfc_dec.h" #include "s5p_mfc_enc.h" #include "s5p_mfc_intr.h" +#include "s5p_mfc_iommu.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_cmd.h" #include "s5p_mfc_pm.h" @@ -1084,6 +1085,22 @@ static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) struct device *dev = &mfc_dev->plat_dev->dev; /* + * When IOMMU is available, we cannot use the default configuration, + * because of MFC firmware requirements: address space limited to + * 256M and non-zero default start address. + * This is still simplified, not optimal configuration, but for now + * IOMMU core doesn't allow to configure device's IOMMUs channel + * separately. + */ + if (exynos_is_iommu_available(dev)) { + int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE, + S5P_MFC_IOMMU_DMA_SIZE); + if (ret == 0) + mfc_dev->mem_dev_l = mfc_dev->mem_dev_r = dev; + return ret; + } + + /* * Create and initialize virtual devices for accessing * reserved memory regions. */ @@ -1103,6 +1120,13 @@ static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev) { + struct device *dev = &mfc_dev->plat_dev->dev; + + if (exynos_is_iommu_available(dev)) { + exynos_unconfigure_iommu(dev); + return; + } + device_unregister(mfc_dev->mem_dev_l); device_unregister(mfc_dev->mem_dev_r); } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h new file mode 100644 index 0000000..5d1d1c2 --- /dev/null +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef S5P_MFC_IOMMU_H_ +#define S5P_MFC_IOMMU_H_ + +#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu +#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M + +#ifdef CONFIG_EXYNOS_IOMMU + +#include + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return dev->archdata.iommu != NULL; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + struct dma_iommu_mapping *mapping = NULL; + int ret; + + /* Disable the default mapping created by device core */ + if (to_dma_iommu_mapping(dev)) + exynos_unconfigure_iommu(dev); + + mapping = arm_iommu_create_mapping(dev->bus, base, size); + if (IS_ERR(mapping)) { + pr_warn("Failed to create IOMMU mapping for device %s\n", + dev_name(dev)); + return PTR_ERR(mapping); + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + pr_warn("Failed to attached device %s to IOMMU_mapping\n", + dev_name(dev)); + arm_iommu_release_mapping(mapping); + return ret; + } + + return 0; +} + +#else + +static inline bool exynos_is_iommu_available(struct device *dev) +{ + return false; +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + return -ENOSYS; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) { } + +#endif + +#endif /* S5P_MFC_IOMMU_H_ */ -- cgit v0.10.2 From 675f9bc7107c01f9459b2feb0d6064b399c16ca2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 7 Jun 2016 08:32:16 -0300 Subject: [media] media: s5p-mfc: fix compilation issue on archs other than ARM (32bit) This patch fixes build break caused by lack of dma-iommu API on ARM64 (this API is specific to ARM 32bit architecture). Signed-off-by: Marek Szyprowski diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h index 5d1d1c2..6962132 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h @@ -14,7 +14,7 @@ #define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu #define S5P_MFC_IOMMU_DMA_SIZE SZ_256M -#ifdef CONFIG_EXYNOS_IOMMU +#if defined(CONFIG_EXYNOS_IOMMU) && defined(CONFIG_ARM_DMA_USE_IOMMU) #include -- cgit v0.10.2 From 8b120e73e5d0c387767e7545be4dfcaaa05cfa00 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 2 May 2016 16:14:22 -0300 Subject: [media] s5p-mfc: Don't try to put pm->clock if lookup failed Failing to get the struct s5p_mfc_pm .clock is a non-fatal error so the clock field can have a errno pointer value. But s5p_mfc_final_pm() only checks if .clock is not NULL before attempting to unprepare and put it. This leads to the following warning in clk_put() due s5p_mfc_final_pm(): WARNING: CPU: 3 PID: 1023 at drivers/clk/clk.c:2814 s5p_mfc_final_pm+0x48/0x74 [s5p_mfc] CPU: 3 PID: 1023 Comm: rmmod Tainted: G W 4.6.0-rc6-next-20160502-00005-g5a15a49106bc #9 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x88/0x9c) [] (dump_stack) from [] (__warn+0xe8/0x100) [] (__warn) from [] (warn_slowpath_null+0x20/0x28) [] (warn_slowpath_null) from [] (s5p_mfc_final_pm+0x48/0x74 [s5p_mfc]) [] (s5p_mfc_final_pm [s5p_mfc]) from [] (s5p_mfc_remove+0x8c/0x94 [s5p_mfc]) [] (s5p_mfc_remove [s5p_mfc]) from [] (platform_drv_remove+0x24/0x3c) [] (platform_drv_remove) from [] (__device_release_driver+0x84/0x110) [] (__device_release_driver) from [] (driver_detach+0xac/0xb0) [] (driver_detach) from [] (bus_remove_driver+0x4c/0xa0) [] (bus_remove_driver) from [] (SyS_delete_module+0x174/0x1b8) [] (SyS_delete_module) from [] (ret_fast_syscall+0x0/0x3c) Assign the pointer to NULL in case of a lookup failure to fix the issue. Signed-off-by: Javier Martinez Canillas Acked-by: Arnd Bergmann Reviewed-by: Krzysztof Kozlowski Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 5f97a33..9f75221 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -54,6 +54,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME); if (IS_ERR(pm->clock)) { mfc_info("Failed to get MFC special clock control\n"); + pm->clock = NULL; } else { clk_set_rate(pm->clock, MFC_SCLK_RATE); ret = clk_prepare_enable(pm->clock); -- cgit v0.10.2 From 9bd5d8696fd50a10d830e2ad7f9d4e67e0bbbae2 Mon Sep 17 00:00:00 2001 From: ayaka Date: Fri, 6 May 2016 19:11:22 -0300 Subject: [media] s5p-mfc: don't close instance after free OUTPUT buffers User-space applications can use the VIDIOC_REQBUFS ioctl to determine if a memory mapped, user pointer or DMABUF based I/O is supported by the driver. So a set of VIDIOC_REQBUFS ioctl calls will be made with count 0 and then the real VIDIOC_REQBUFS call with count == n. But for count 0, the driver not only frees the buffer but also closes the MFC instance and s5p_mfc_ctx state is set to MFCINST_FREE. The VIDIOC_REQBUFS handler for the output device checks if the s5p_mfc_ctx state is set to MFCINST_INIT (which happens on an VIDIOC_S_FMT) and fails otherwise. So after a VIDIOC_REQBUFS(n), future VIDIOC_REQBUFS(n) calls will fails unless a VIDIOC_S_FMT ioctl calls happens before the reqbufs. But applications may first set the format and then attempt to determine the I/O methods supported by the driver (for example Gstramer does it) so the state won't be set to MFCINST_INIT again and VIDIOC_REQBUFS will fail. To avoid this issue, only free the buffers on VIDIOC_REQBUFS(0) but don't close the MFC instance to allow future VIDIOC_REQBUFS(n) calls to succeed. [javier: Rewrote changelog to explain the problem more detailed] Signed-off-by: ayaka Signed-off-by: Javier Martinez Canillas Acked-by: Nicolas Dufresne Tested-by: Marek Szyprowski Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index f2d6376..8b9467d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -474,7 +474,6 @@ static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, ret = vb2_reqbufs(&ctx->vq_src, reqbufs); if (ret) goto out; - s5p_mfc_close_mfc_inst(dev, ctx); ctx->src_bufs_cnt = 0; ctx->output_state = QUEUE_FREE; } else if (ctx->output_state == QUEUE_FREE) { -- cgit v0.10.2 From 23ce62d293b65b7965a5b5a416f897d52eafcee6 Mon Sep 17 00:00:00 2001 From: Andrew-CT Chen Date: Tue, 3 May 2016 07:11:20 -0300 Subject: [media] dt-bindings: Add a binding for Mediatek Video Processor Add a DT binding documentation of Video Processor Unit for the MT8173 SoC from Mediatek. Signed-off-by: Andrew-CT Chen Signed-off-by: Tiffany Lin Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt new file mode 100644 index 0000000..2a5bac3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt @@ -0,0 +1,31 @@ +* Mediatek Video Processor Unit + +Video Processor Unit is a HW video controller. It controls HW Codec including +H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert). + +Required properties: + - compatible: "mediatek,mt8173-vpu" + - reg: Must contain an entry for each entry in reg-names. + - reg-names: Must include the following entries: + "tcm": tcm base + "cfg_reg": Main configuration registers base + - interrupts: interrupt number to the cpu. + - clocks : clock name from clock manager + - clock-names: must be main. It is the main clock of VPU + +Optional properties: + - memory-region: phandle to a node describing memory (see + Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt) + to be used for VPU extended memory; if not present, VPU may be located + anywhere in the memory + +Example: + vpu: vpu@10020000 { + compatible = "mediatek,mt8173-vpu"; + reg = <0 0x10020000 0 0x30000>, + <0 0x10050000 0 0x100>; + reg-names = "tcm", "cfg_reg"; + interrupts = ; + clocks = <&topckgen TOP_SCP_SEL>; + clock-names = "main"; + }; -- cgit v0.10.2 From f222467a2c51a46f51516975f9b096f903c26ce4 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 7 Jan 2016 18:43:48 -0200 Subject: [media] uvcvideo: Fix bytesperline calculation for planar YUV The formula used to calculate bytesperline only works for packed format. So far, all planar format we support have their bytesperline equal to the image width (stride of the Y plane or a line of Y for M420). Signed-off-by: Nicolas Dufresne Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index d7723ce..cded5ef 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -142,6 +142,21 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } +static __u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, + const struct uvc_frame *frame) +{ + switch (format->fcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_M420: + return frame->wWidth; + + default: + return format->bpp * frame->wWidth / 8; + } +} + static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) @@ -245,7 +260,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; @@ -282,7 +297,7 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream, fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; -- cgit v0.10.2 From 92a63459c396386664b30415d1751593ee91a2cc Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 2 May 2016 08:23:21 -0300 Subject: [media] uvcvideo: Correct speed testing Allow for SS+ USB devices. Signed-off-by: Oliver Neukum Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 075a0fe..b5589d5 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1470,6 +1470,7 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev, switch (dev->speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); case USB_SPEED_HIGH: psize = usb_endpoint_maxp(&ep->desc); -- cgit v0.10.2 From 2dc1ed4edbac1d08e5bb73ae4a00a592011bde64 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 21 Mar 2016 22:19:12 -0300 Subject: [media] Add support Sony HELENE Sat/Ter Tuner This is Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER). Tuner is used on NetUP Dual Universal DVB CI card (hardware revision 1.4). Use 'helene_attach_s' to attach tuner in 'satellite mode'. Use 'helene_attach' for 'terrestrial mode'. Satellite delivery systems supported: DVB-S/S2, ISDB-S Terrestrial delivery systems supported: DVB-T/T2, ISDB-T Cable delivery systems supported: DVB-C/C2 Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a82f77c..8decfc3 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -853,6 +853,13 @@ config DVB_ASCOT2E help Say Y when you want to support this frontend. +config DVB_HELENE + tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index eb7191f..7fc5605 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -123,3 +123,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o +obj-$(CONFIG_DVB_HELENE) += helene.o diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c new file mode 100644 index 0000000..98099db --- /dev/null +++ b/drivers/media/dvb-frontends/helene.c @@ -0,0 +1,1042 @@ +/* + * helene.c + * + * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "helene.h" +#include "dvb_frontend.h" + +#define MAX_WRITE_REGSIZE 20 + +enum helene_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct helene_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum helene_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); + enum helene_xtal xtal; +}; + +#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \ + (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC_8)\ + && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \ + ((tv_system) != SONY_HELENE_DTV_DVBC2_8)) + +#define HELENE_AUTO 0xff +#define HELENE_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define HELENE_BW_6 0x00 +#define HELENE_BW_7 0x01 +#define HELENE_BW_8 0x02 +#define HELENE_BW_1_7 0x03 + +enum helene_tv_system_t { + SONY_HELENE_TV_SYSTEM_UNKNOWN, + /* Terrestrial Analog */ + SONY_HELENE_ATV_MN_EIAJ, + /**< System-M (Japan) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_SAP, + /**< System-M (US) (IF: Fp=5.75MHz in default) */ + SONY_HELENE_ATV_MN_A2, + /**< System-M (Korea) (IF: Fp=5.9MHz in default) */ + SONY_HELENE_ATV_BG, + /**< System-B/G (IF: Fp=7.3MHz in default) */ + SONY_HELENE_ATV_I, + /**< System-I (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_DK, + /**< System-D/K (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L, + /**< System-L (IF: Fp=7.85MHz in default) */ + SONY_HELENE_ATV_L_DASH, + /**< System-L DASH (IF: Fp=2.2MHz in default) */ + /* Terrestrial/Cable Digital */ + SONY_HELENE_DTV_8VSB, + /**< ATSC 8VSB (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_QAM, + /**< US QAM (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_ISDBT_6, + /**< ISDB-T 6MHzBW (IF: Fc=3.55MHz in default) */ + SONY_HELENE_DTV_ISDBT_7, + /**< ISDB-T 7MHzBW (IF: Fc=4.15MHz in default) */ + SONY_HELENE_DTV_ISDBT_8, + /**< ISDB-T 8MHzBW (IF: Fc=4.75MHz in default) */ + SONY_HELENE_DTV_DVBT_5, + /**< DVB-T 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_6, + /**< DVB-T 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT_7, + /**< DVB-T 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT_8, + /**< DVB-T 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBT2_1_7, + /**< DVB-T2 1.7MHzBW (IF: Fc=3.5MHz in default) */ + SONY_HELENE_DTV_DVBT2_5, + /**< DVB-T2 5MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_6, + /**< DVB-T2 6MHzBW (IF: Fc=3.6MHz in default) */ + SONY_HELENE_DTV_DVBT2_7, + /**< DVB-T2 7MHzBW (IF: Fc=4.2MHz in default) */ + SONY_HELENE_DTV_DVBT2_8, + /**< DVB-T2 8MHzBW (IF: Fc=4.8MHz in default) */ + SONY_HELENE_DTV_DVBC_6, + /**< DVB-C 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC_8, + /**< DVB-C 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DVBC2_6, + /**< DVB-C2 6MHzBW (IF: Fc=3.7MHz in default) */ + SONY_HELENE_DTV_DVBC2_8, + /**< DVB-C2 8MHzBW (IF: Fc=4.9MHz in default) */ + SONY_HELENE_DTV_DTMB, + /**< DTMB (IF: Fc=5.1MHz in default) */ + /* Satellite */ + SONY_HELENE_STV_ISDBS, + /**< ISDB-S */ + SONY_HELENE_STV_DVBS, + /**< DVB-S */ + SONY_HELENE_STV_DVBS2, + /**< DVB-S2 */ + + SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ, + /**< Minimum analog terrestrial system */ + SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH, + /**< Maximum analog terrestrial system */ + SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB, + /**< Minimum digital terrestrial system */ + SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB, + /**< Maximum digital terrestrial system */ + SONY_HELENE_TERR_TV_SYSTEM_NUM, + /**< Number of supported terrestrial broadcasting system */ + SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS, + /**< Minimum satellite system */ + SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2 + /**< Maximum satellite system */ +}; + +struct helene_terr_adjust_param_t { + /* < Addr:0x69 Bit[6:4] : RFVGA gain. + * 0xFF means Auto. (RF_GAIN_SEL = 1) + */ + uint8_t RF_GAIN; + /* < Addr:0x69 Bit[3:0] : IF_BPF gain. + */ + uint8_t IF_BPF_GC; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF <= 172MHz) + */ + uint8_t RFOVLD_DET_LV1_VL; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t RFOVLD_DET_LV1_VH; + /* < Addr:0x6B Bit[3:0] : RF overload + * RF input detect level. (FRF > 464MHz) + */ + uint8_t RFOVLD_DET_LV1_U; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF <= 172MHz) + */ + uint8_t IFOVLD_DET_LV_VL; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (172MHz < FRF <= 464MHz) + */ + uint8_t IFOVLD_DET_LV_VH; + /* < Addr:0x6C Bit[2:0] : + * Internal RFAGC detect level. (FRF > 464MHz) + */ + uint8_t IFOVLD_DET_LV_U; + /* < Addr:0x6D Bit[5:4] : + * IF filter center offset. + */ + uint8_t IF_BPF_F0; + /* < Addr:0x6D Bit[1:0] : + * 6MHzBW(0x00) or 7MHzBW(0x01) + * or 8MHzBW(0x02) or 1.7MHzBW(0x03) + */ + uint8_t BW; + /* < Addr:0x6E Bit[4:0] : + * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50 + */ + uint8_t FIF_OFFSET; + /* < Addr:0x6F Bit[4:0] : + * 5bit signed. BW offset (kHz) = + * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW) + */ + uint8_t BW_OFFSET; + /* < Addr:0x9C Bit[0] : + * Local polarity. (0: Upper Local, 1: Lower Local) + */ + uint8_t IS_LOWERLOCAL; +}; + +static const struct helene_terr_adjust_param_t +terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = { + /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */ + {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(0), 0x00}, + /* Analog */ + /**< SONY_HELENE_ATV_MN_EIAJ (System-M (Japan)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_SAP (System-M (US)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00}, + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(3), HELENE_OFFSET(1), 0x00}, + /**< SONY_HELENE_ATV_MN_A2 (System-M (Korea)) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_7, HELENE_OFFSET(11), HELENE_OFFSET(5), 0x00}, + /**< SONY_HELENE_ATV_BG (System-B/G) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_I (System-I) */ + {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_DK (System-D/K) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_ATV_L (System-L) */ + {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00, + HELENE_BW_8, HELENE_OFFSET(-1), HELENE_OFFSET(4), 0x00}, + /**< SONY_HELENE_ATV_L_DASH (System-L DASH) */ + /* Digital */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_8VSB (ATSC 8VSB) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_QAM (US QAM) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_6 (ISDB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_7 (ISDB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00}, + /**< SONY_HELENE_DTV_ISDBT_8 (ISDB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_5 (DVB-T 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT_6 (DVB-T 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT_7 (DVB-T 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT_8 (DVB-T 8MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_5 (DVB-T2 5MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_6 (DVB-T2 6MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_7 (DVB-T2 7MHzBW) */ + {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00}, + /**< SONY_HELENE_DTV_DVBT2_8 (DVB-T2 8MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00}, + /**< SONY_HELENE_DTV_DVBC_6 (DVB-C 6MHzBW) */ + {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00}, + /**< SONY_HELENE_DTV_DVBC_8 (DVB-C 8MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_6 (DVB-C2 6MHzBW) */ + {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(0), 0x00}, + /**< SONY_HELENE_DTV_DVBC2_8 (DVB-C2 8MHzBW) */ + {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00, + HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(1), 0x00} + /**< SONY_HELENE_DTV_DTMB (DTMB) */ +}; + +static void helene_i2c_debug(struct helene_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("helene: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int helene_write_regs(struct helene_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 > sizeof(buf)) { + dev_warn(&priv->i2c->dev, + "wr reg=%04x: len=%d vs %lu is too big!\n", + reg, len + 1, sizeof(buf)); + return -E2BIG; + } + + helene_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val) +{ + return helene_write_regs(priv, reg, &val, 1); +} + +static int helene_read_regs(struct helene_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + helene_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val) +{ + return helene_read_regs(priv, reg, val, 1); +} + +static int helene_set_reg_bits(struct helene_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = helene_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return helene_write_reg(priv, reg, data); +} + +static int helene_enter_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x0); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + priv->state = STATE_SLEEP; + return 0; +} + +static int helene_leave_power_save(struct helene_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC4); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x40); + + priv->state = STATE_ACTIVE; + return 0; +} + +static int helene_init(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return helene_leave_power_save(priv); +} + +static int helene_release(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int helene_sleep(struct dvb_frontend *fe) +{ + struct helene_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + helene_enter_power_save(priv); + return 0; +} + +static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe) +{ + enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT_8; + else { + system = SONY_HELENE_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = SONY_HELENE_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBT2_8; + else { + system = SONY_HELENE_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBS) { + system = SONY_HELENE_STV_DVBS; + } else if (p->delivery_system == SYS_DVBS2) { + system = SONY_HELENE_STV_DVBS2; + } else if (p->delivery_system == SYS_ISDBS) { + system = SONY_HELENE_STV_ISDBS; + } else if (p->delivery_system == SYS_ISDBT) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_ISDBT_6; + else if (p->bandwidth_hz <= 7000000) + system = SONY_HELENE_DTV_ISDBT_7; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_ISDBT_8; + else { + system = SONY_HELENE_DTV_ISDBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = SONY_HELENE_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = SONY_HELENE_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, + p->bandwidth_hz); + return system; +} + +static int helene_set_params_s(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency / 1000; + uint32_t frequency4kHz = 0; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n", + __func__, frequencykHz, symbol_rate); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + /* RF switch turn to satellite */ + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + frequency = roundup(p->frequency / 1000, 1); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x15, 0x02); + + /* RFIN matching in power save (Sat) reset */ + helene_write_reg(priv, 0x43, 0x06); + + /* Analog block setting (0x6A, 0x6B) */ + data[0] = 0x00; + data[1] = 0x00; + helene_write_regs(priv, 0x6A, data, 2); + helene_write_reg(priv, 0x75, 0x99); + helene_write_reg(priv, 0x9D, 0x00); + + /* Tuning setting for CPU (0x61) */ + helene_write_reg(priv, 0x61, 0x07); + + /* Satellite mode select (0x01) */ + helene_write_reg(priv, 0x01, 0x01); + + /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */ + data[0] = 0xC4; + data[1] = 0x40; + + switch (priv->xtal) { + case SONY_HELENE_XTAL_16000: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_20500: + data[2] = 0x02; + break; + case SONY_HELENE_XTAL_24000: + data[2] = 0x03; + break; + case SONY_HELENE_XTAL_41000: + data[2] = 0x05; + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + + /* Setting for analog block (0x07). LOOPFILTER INTERNAL */ + data[3] = 0x80; + + /* Tuning setting for analog block + * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL + */ + if (priv->xtal == SONY_HELENE_XTAL_20500) + data[4] = 0x58; + else + data[4] = 0x70; + + data[5] = 0x1E; + data[6] = 0x02; + data[7] = 0x24; + + /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */ + data[8] = 0x0F; + data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */ + data[9] = 0x02; + data[10] = 0x1E; + + /* Setting for LPF cutoff frequency (0x0F) */ + switch (tv_system) { + case SONY_HELENE_STV_ISDBS: + data[11] = 0x22; /* 22MHz */ + break; + case SONY_HELENE_STV_DVBS: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 47 + + (40000-1)) / 40000); + else + data[11] = (uint8_t)((symbol_rate * 27 + + (40000-1)) / 40000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + case SONY_HELENE_STV_DVBS2: + if (symbol_rate <= 4000) + data[11] = 0x05; + else if (symbol_rate <= 10000) + data[11] = (uint8_t)((symbol_rate * 11 + + (10000-1)) / 10000); + else + data[11] = (uint8_t)((symbol_rate * 3 + + (5000-1)) / 5000 + 5); + + if (data[11] > 36) + data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */ + break; + default: + dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n", + __func__, tv_system); + return -EINVAL; + } + + /* RF tuning frequency setting (0x10, 0x11, 0x12) */ + frequency4kHz = (frequencykHz + 2) / 4; + data[12] = (uint8_t)(frequency4kHz & 0xFF); /* FRF_L */ + data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF); /* FRF_M */ + /* FRF_H (bit[3:0]) */ + data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F); + + /* Tuning command (0x13) */ + data[15] = 0xFF; + + /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */ + data[16] = 0x00; + + /* Enable IQ output (0x15) */ + data[17] = 0x01; + + helene_write_regs(priv, 0x04, data, 18); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_set_params(struct dvb_frontend *fe) +{ + u8 data[MAX_WRITE_REGSIZE]; + u32 frequency; + enum helene_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct helene_priv *priv = fe->tuner_priv; + int frequencykHz = p->frequency / 1000; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, frequencykHz); + tv_system = helene_get_tv_system(fe); + + if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + if (priv->state == STATE_SLEEP) + helene_leave_power_save(priv); + + /* Initial setting for internal analog block (0x91, 0x92) */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[0] = 0x16; + data[1] = 0x26; + } else { + data[0] = 0x10; + data[1] = 0x20; + } + helene_write_regs(priv, 0x91, data, 2); + + /* Setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) + data[0] = 0x90; + else + data[0] = 0x00; + + /* Setting for local polarity (0x9D) */ + data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01); + helene_write_regs(priv, 0x9C, data, 2); + + /* Enable for analog block */ + data[0] = 0xEE; + data[1] = 0x02; + data[2] = 0x1E; + data[3] = 0x67; /* Tuning setting for CPU */ + + /* Setting for PLL reference divider for xtal=24MHz */ + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) + data[4] = 0x18; + else + data[4] = 0x03; + + /* Tuning setting for analog block */ + if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) { + data[5] = 0x38; + data[6] = 0x1E; + data[7] = 0x02; + data[8] = 0x24; + } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[5] = 0x1C; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x1C; + } else { + data[5] = 0xB4; + data[6] = 0x78; + data[7] = 0x08; + data[8] = 0x30; + }; + helene_write_regs(priv, 0x5E, data, 9); + + /* LT_AMP_EN should be 0 */ + helene_set_reg_bits(priv, 0x67, 0x0, 0x02); + + /* Setting for IFOUT_LIMIT */ + data[0] = 0x00; /* 1.5Vpp */ + + /* RF_GAIN setting */ + if (terr_params[tv_system].RF_GAIN == HELENE_AUTO) + data[1] = 0x80; /* RF_GAIN_SEL = 1 */ + else + data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN + << 4) & 0x70); + + /* IF_BPF_GC setting */ + data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F); + + /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */ + data[2] = 0x00; + if (frequencykHz <= 172000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL + & 0x07); + } else if (frequencykHz <= 464000) { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH + & 0x07); + } else { + data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U + & 0x0F); + data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U + & 0x07); + } + data[4] |= 0x20; + + /* Setting for IF frequency and bandwidth */ + + /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */ + data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30); + + /* IF filter band width (BW) (0x6D) */ + data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03); + + /* IF frequency offset value (FIF_OFFSET) (0x6E) */ + data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F); + + /* IF band width offset value (BW_OFFSET) (0x6F) */ + data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F); + + /* RF tuning frequency setting (0x70, 0x71, 0x72) */ + data[8] = (uint8_t)(frequencykHz & 0xFF); /* FRF_L */ + data[9] = (uint8_t)((frequencykHz >> 8) & 0xFF); /* FRF_M */ + data[10] = (uint8_t)((frequencykHz >> 16) + & 0x0F); /* FRF_H (bit[3:0]) */ + + /* Tuning command */ + data[11] = 0xFF; + + /* Enable IF output, AGC and IFOUT pin selection (0x74) */ + data[12] = 0x01; + + if ((tv_system == SONY_HELENE_DTV_DVBC_6) || + (tv_system == SONY_HELENE_DTV_DVBC_8)) { + data[13] = 0xD9; + data[14] = 0x0F; + data[15] = 0x24; + data[16] = 0x87; + } else { + data[13] = 0x99; + data[14] = 0x00; + data[15] = 0x24; + data[16] = 0x87; + } + + helene_write_regs(priv, 0x68, data, 17); + + dev_dbg(&priv->i2c->dev, "%s(): tune done\n", + __func__); + + priv->frequency = frequency; + return 0; +} + +static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct helene_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Ter tuner", + .frequency_min = 1000000, + .frequency_max = 1200000000, + .frequency_step = 25000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + +static struct dvb_tuner_ops helene_tuner_ops_s = { + .info = { + .name = "Sony HELENE Sat tuner", + .frequency_min = 500000, + .frequency_max = 2500000, + .frequency_step = 1000, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params_s, + .get_frequency = helene_get_frequency, +}; + +/* power-on tuner + * call once after reset + */ +static int helene_x_pon(struct helene_priv *priv) +{ + /* RFIN matching in power save (terrestrial) = ACTIVE */ + /* RFIN matching in power save (satellite) = ACTIVE */ + u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 }; + /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */ + u8 dataS[] = { 0x05, 0x06 }; + u8 cdata[] = {0x7A, 0x01}; + u8 data[20]; + u8 rdata[2]; + + /* mode select */ + helene_write_reg(priv, 0x01, 0x00); + + helene_write_reg(priv, 0x67, dataT[3]); + helene_write_reg(priv, 0x43, dataS[1]); + helene_write_regs(priv, 0x5E, dataT, 3); + helene_write_reg(priv, 0x0C, dataS[0]); + + /* Initial setting for internal logic block */ + helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); + + /* 0x81 - 0x94 */ + data[0] = 0x18; /* xtal 24 MHz */ + data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ + data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ + data[3] = 0x80; /* REFOUT signal output 500mVpp */ + data[4] = 0x00; /* GPIO settings */ + data[5] = 0x00; /* GPIO settings */ + data[6] = 0xC4; /* Clock enable for internal logic block */ + data[7] = 0x40; /* Start CPU boot-up */ + data[8] = 0x10; /* For burst-write */ + + /* Setting for internal RFAGC */ + data[9] = 0x00; + data[10] = 0x45; + data[11] = 0x75; + + data[12] = 0x07; /* Setting for analog block */ + + /* Initial setting for internal analog block */ + data[13] = 0x1C; + data[14] = 0x3F; + data[15] = 0x02; + data[16] = 0x10; + data[17] = 0x20; + data[18] = 0x0A; + data[19] = 0x00; + + helene_write_regs(priv, 0x81, data, sizeof(data)); + + /* Setting for internal RFAGC */ + helene_write_reg(priv, 0x9B, 0x00); + + msleep(20); + + /* Check CPU_STT/CPU_ERR */ + helene_read_regs(priv, 0x1A, rdata, sizeof(rdata)); + + if (rdata[0] != 0x00) { + dev_err(&priv->i2c->dev, + "HELENE tuner CPU error 0x%x\n", rdata[0]); + return -EIO; + } + + /* VCO current setting */ + cdata[0] = 0x90; + cdata[1] = 0x06; + helene_write_regs(priv, 0x17, cdata, sizeof(cdata)); + msleep(20); + helene_read_reg(priv, 0x19, data); + helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F)); + + /* Disable IF signal output */ + helene_write_reg(priv, 0x74, 0x02); + + /* Standby setting for CPU */ + helene_write_reg(priv, 0x88, 0x00); + + /* Standby setting for internal logic block */ + helene_write_reg(priv, 0x87, 0xC0); + + /* Load capacitance control setting for crystal oscillator */ + helene_write_reg(priv, 0x80, 0x01); + + /* Satellite initial setting */ + cdata[0] = 0x07; + cdata[1] = 0x00; + helene_write_regs(priv, 0x41, cdata, sizeof(cdata)); + + dev_info(&priv->i2c->dev, + "HELENE tuner x_pon done\n"); + + return 0; +} + +struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach_s); + +struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + struct helene_priv *priv = NULL; + + priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return NULL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(helene_attach); + +MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); +MODULE_AUTHOR("Abylay Ospan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h new file mode 100644 index 0000000..e1b9224 --- /dev/null +++ b/drivers/media/dvb-frontends/helene.h @@ -0,0 +1,79 @@ +/* + * helene.h + * + * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER) + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Abylay Ospan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DVB_HELENE_H__ +#define __DVB_HELENE_H__ + +#include +#include +#include + +enum helene_xtal { + SONY_HELENE_XTAL_16000, /* 16 MHz */ + SONY_HELENE_XTAL_20500, /* 20.5 MHz */ + SONY_HELENE_XTAL_24000, /* 24 MHz */ + SONY_HELENE_XTAL_41000 /* 41 MHz */ +}; + +/** + * struct helene_config - the configuration of 'Helene' tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct helene_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); + enum helene_xtal xtal; +}; + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#if IS_REACHABLE(CONFIG_DVB_HELENE) +extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, + const struct helene_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig index f277b0b..0ad3771 100644 --- a/drivers/media/pci/netup_unidvb/Kconfig +++ b/drivers/media/pci/netup_unidvb/Kconfig @@ -5,8 +5,13 @@ config DVB_NETUP_UNIDVB select VIDEOBUF2_VMALLOC select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT + select DVB_HELENE if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for NetUP PCI express Universal DVB card. - + help + Say Y when you want to support NetUP Dual Universal DVB card + Card can receive two independent streams in following standards: + DVB-S/S2, T/T2, C/C2 + Two CI slots available for CAM modules. -- cgit v0.10.2 From 83808c23e51a61f06f465dce8cb63dbac8d422f1 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Tue, 22 Mar 2016 19:20:34 -0300 Subject: [media] Add support Sony CXD2854ER demodulator CXD2854ER is identical to CXD2841ER except ISDB-T/S added. New method 'cxd2841er_attach_i' is added xtal frequency now configurable. Available options: 20.5MHz, 24MHz, 41MHz Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 900186b..db51790 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -1,7 +1,9 @@ /* * cxd2841er.c * - * Sony CXD2441ER digital demodulator driver + * Sony digital demodulator driver for + * CXD2441ER - DVB-S/S2/T/T2/C/C2 + * CXD2454ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S * * Copyright 2012 Sony Corporation * Copyright (C) 2014 NetUP Inc. @@ -51,6 +53,7 @@ struct cxd2841er_priv { const struct cxd2841er_config *config; enum cxd2841er_state state; u8 system; + enum cxd2841er_xtal xtal; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -188,6 +191,9 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = { }; #define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5)) +#define MAKE_IFFREQ_CONFIG_XTAL(xtal, iffreq) ((xtal == SONY_XTAL_24000) ? \ + (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \ + (u32)(((iffreq)/41.0)*16777216.0 + 0.5)) static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, u8 addr, u8 reg, u8 write, @@ -217,7 +223,7 @@ static int cxd2841er_write_regs(struct cxd2841er_priv *priv, }; if (len + 1 >= sizeof(buf)) { - dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; } @@ -669,6 +675,45 @@ static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) return 0; } +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + + /* TODO: Cancel demod parameter */ + + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) { dev_dbg(&priv->i2c->dev, "%s()\n", __func__); @@ -686,8 +731,25 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03); + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n", + __func__, priv->xtal); + return -EINVAL; + } + /* Set demod mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); /* Clear demod SW reset */ @@ -809,11 +871,14 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) { - u8 chip_id; + u8 chip_id = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); - cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); - cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0) + cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id); + return chip_id; } @@ -896,6 +961,25 @@ static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) return 0; } +static int cxd2841er_read_status_i(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + dev_dbg(&priv->i2c->dev, + "%s(): lock=0x%x\n", __func__, data); + *sync = ((data & 0x02) ? 1 : 0); + *tslock = ((data & 0x01) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + return 0; +} + static int cxd2841er_read_status_tc(struct dvb_frontend *fe, enum fe_status *status) { @@ -921,6 +1005,20 @@ static int cxd2841er_read_status_tc(struct dvb_frontend *fe, FE_HAS_SYNC; if (tslock) *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_ISDBT) { + ret = cxd2841er_read_status_i( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; } else if (priv->system == SYS_DVBC_ANNEX_A) { ret = cxd2841er_read_status_c(priv, &tslock); if (ret) @@ -1096,6 +1194,38 @@ static int cxd2841er_read_packet_errors_t2( return 0; } +static int cxd2841er_read_packet_errors_i( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[2]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1); + + if (!(data[0] & 0x01)) + return 0; + + /* Layer A */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data)); + *penum = ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer B */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + /* Layer C */ + cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data)); + *penum += ((u32)data[0] << 8) | (u32)data[1]; + + return 0; +} + static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) { u8 data[11]; @@ -1391,6 +1521,37 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) return 0; } +static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, + priv->state); + return -EINVAL; + } + + /* Freeze all registers */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + + + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 100 * intlog10(reg) - 9031; + return 0; +} + static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, u8 delsys) { @@ -1402,6 +1563,22 @@ static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; } +static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv) { u8 data[2]; @@ -1455,6 +1632,10 @@ static int cxd2841er_read_signal_strength(struct dvb_frontend *fe, *strength = 65535 - cxd2841er_read_agc_gain_t_t2( priv, p->delivery_system); break; + case SYS_ISDBT: + *strength = 65535 - cxd2841er_read_agc_gain_i( + priv, p->delivery_system); + break; case SYS_DVBS: case SYS_DVBS2: *strength = 65535 - cxd2841er_read_agc_gain_s(priv); @@ -1480,6 +1661,9 @@ static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr) case SYS_DVBT2: cxd2841er_read_snr_t2(priv, &tmp); break; + case SYS_ISDBT: + cxd2841er_read_snr_i(priv, &tmp); + break; case SYS_DVBS: case SYS_DVBS2: tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system); @@ -1506,6 +1690,9 @@ static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) case SYS_DVBT2: cxd2841er_read_packet_errors_t2(priv, ucblocks); break; + case SYS_ISDBT: + cxd2841er_read_packet_errors_i(priv, ucblocks); + break; default: *ucblocks = 0; break; @@ -1734,94 +1921,382 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_t_band( struct cxd2841er_priv *priv, u32 bandwidth) { - u8 b13_9c[2] = { 0x01, 0x14 }; - u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 }; - u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, - 0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 }; - u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 }; - u8 bw8mhz_b17_38[] = { 0x01, 0x02 }; - u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 }; - u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, - 0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 }; - u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 }; - u8 bw7mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA }; - u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC }; - u8 bw6mhz_b17_38[] = { 0x00, 0x03 }; - u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 }; - u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, - 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; - u8 bw5mhz_b10_d9[] = { 0x26, 0x3C }; - u8 bw5mhz_b17_38[] = { 0x00, 0x03 }; - u8 b10_b6[3]; - u8 d7val; + u8 data[MAX_WRITE_REGSIZE]; u32 iffreq; - u8 *b10_9f; - u8 *b10_a6; - u8 *b10_d9; - u8 *b17_38; + u8 nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + u8 nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + u8 nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + u8 itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5, + 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9, + 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + u8 itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2, + 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0, + 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + u8 itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4, + 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF, + 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x13 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); /* Echo performance optimization setting */ - cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c)); + data[0] = 0x01; + data[1] = 0x14; + cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2); + + /* Set SLV-T Bank : 0x10 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: - b10_9f = bw8mhz_b10_9f; - b10_a6 = bw8mhz_b10_a6; - b10_d9 = bw8mhz_b10_d9; - b17_38 = bw8mhz_b17_38; - d7val = 0; - iffreq = MAKE_IFFREQ_CONFIG(4.80); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x15; + data[1] = 0x28; + } else { + data[0] = 0x01; + data[1] = 0xE0; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x01; + data[1] = 0x02; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 7000000: - b10_9f = bw7mhz_b10_9f; - b10_a6 = bw7mhz_b10_a6; - b10_d9 = bw7mhz_b10_d9; - b17_38 = bw7mhz_b17_38; - d7val = 2; - iffreq = MAKE_IFFREQ_CONFIG(4.20); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0xF8; + } else { + data[0] = 0x12; + data[1] = 0xF8; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 6000000: - b10_9f = bw6mhz_b10_9f; - b10_a6 = bw6mhz_b10_a6; - b10_d9 = bw6mhz_b10_d9; - b17_38 = bw6mhz_b17_38; - d7val = 4; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x25; + data[1] = 0x4C; + } else { + data[0] = 0x1F; + data[1] = 0xDC; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); break; case 5000000: - b10_9f = bw5mhz_b10_9f; - b10_a6 = bw5mhz_b10_a6; - b10_d9 = bw5mhz_b10_d9; - b17_38 = bw5mhz_b17_38; - d7val = 6; - iffreq = MAKE_IFFREQ_CONFIG(3.60); + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x2C; + data[1] = 0xC2; + } else { + data[0] = 0x26; + data[1] = 0x3C; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Notch filter setting */ + data[0] = 0x00; + data[1] = 0x03; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2); + break; + } + + return 0; +} + +static int cxd2841er_sleep_tc_to_active_i_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u32 iffreq; + u8 data[3]; + + /* TRCG Nominal Rate */ + u8 nominalRate8bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate7bw[3][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 nominalRate6bw[3][5] = { + {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x2E, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + u8 itbCoef8bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, + 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */ + {0x0}, /* 41MHz XTal */ + }; + + u8 itbCoef7bw[3][14] = { + {0x00}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, + 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */ + {0x00}, /* 41MHz XTal */ + }; + + u8 itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, + 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, + 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal */ + }; + + dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* 20.5/41MHz Xtal support is not available + * on ISDB-T 7MHzBW and 8MHzBW + */ + if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) { + dev_err(&priv->i2c->dev, + "%s(): bandwidth %d supported only for 24MHz xtal\n", + __func__, bandwidth); + return -EINVAL; + } + + switch (bandwidth) { + case 8000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.75); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7); + + /* Demod core latency setting */ + data[0] = 0x13; + data[1] = 0xFC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03); + break; + case 7000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.15); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7); + + /* Demod core latency setting */ + data[0] = 0x1A; + data[1] = 0xFA; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); + break; + case 6000000: + /* TRCG Nominal Rate */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + /* Group delay equaliser settings for ASCOT tuners optimized */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + + /* IF freq setting */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.55); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7); + + /* Demod core latency setting */ + if (priv->xtal == SONY_XTAL_24000) { + data[0] = 0x1F; + data[1] = 0x79; + } else { + data[0] = 0x1A; + data[1] = 0xE2; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02); break; default: dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", - __func__, bandwidth); + __func__, bandwidth); return -EINVAL; } - /* */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f)); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6)); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7); - cxd2841er_write_regs( - priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9)); - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); - cxd2841er_write_regs( - priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38)); return 0; } @@ -1902,6 +2377,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, u32 bandwidth) { u8 data[2] = { 0x09, 0x54 }; + u8 data24m[3] = {0xDC, 0x6C, 0x00}; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); @@ -1919,7 +2395,11 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + /* Enable ADC 2 & 3 */ + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -1947,6 +2427,15 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, /* TSIF setting */ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3); + } + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); @@ -2032,6 +2521,84 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, return 0; } +/* ISDB-Tb part */ +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + u8 data24m[2] = {0x60, 0x00}; + u8 data24m2[3] = {0xB7, 0x1B, 0x00}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz or 24M */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01); + /* ISDB-T initial setting */ + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F); + /* Set SLV-T Bank : 0x15 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03); + /* Set SLV-T Bank : 0x1E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF); + /* Set SLV-T Bank : 0x63 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01); + + /* for xtal 24MHz */ + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2); + /* Set SLV-T Bank : 0x60 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3); + + cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, u32 bandwidth) { @@ -2142,10 +2709,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; u32 symbol_rate = p->symbol_rate/1000; - dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n", + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n", __func__, (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), - p->frequency, symbol_rate); + p->frequency, symbol_rate, priv->xtal); switch (priv->state) { case STATE_SLEEP_S: ret = cxd2841er_sleep_s_to_active_s( @@ -2233,6 +2800,21 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) __func__, priv->state); ret = -EINVAL; } + } else if (p->delivery_system == SYS_ISDBT) { + priv->system = SYS_ISDBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } } else if (p->delivery_system == SYS_DVBC_ANNEX_A || p->delivery_system == SYS_DVBC_ANNEX_C) { priv->system = SYS_DVBC_ANNEX_A; @@ -2382,6 +2964,9 @@ static int cxd2841er_sleep_tc(struct dvb_frontend *fe) case SYS_DVBT2: cxd2841er_active_t2_to_sleep_tc(priv); break; + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + break; case SYS_DVBC_ANNEX_A: cxd2841er_active_c_to_sleep_tc(priv); break; @@ -2544,6 +3129,7 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe) static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; static struct dvb_frontend_ops cxd2841er_dvbc_ops; +static struct dvb_frontend_ops cxd2841er_isdbt_ops; static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, struct i2c_adapter *i2c, @@ -2561,6 +3147,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, priv->config = cfg; priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; + priv->xtal = cfg->xtal; /* create dvb_frontend */ switch (system) { case SYS_DVBS: @@ -2575,6 +3162,12 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, sizeof(struct dvb_frontend_ops)); type = "T/T2"; break; + case SYS_ISDBT: + memcpy(&priv->frontend.ops, + &cxd2841er_isdbt_ops, + sizeof(struct dvb_frontend_ops)); + type = "ISDBT"; + break; case SYS_DVBC_ANNEX_A: memcpy(&priv->frontend.ops, &cxd2841er_dvbc_ops, @@ -2594,7 +3187,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, __func__, priv->i2c, priv->i2c_addr_slvx, priv->i2c_addr_slvt); chip_id = cxd2841er_chip_id(priv); - if (chip_id != CXD2841ER_CHIP_ID) { + if (chip_id != CXD2841ER_CHIP_ID && chip_id != CXD2854ER_CHIP_ID) { dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", __func__, chip_id); priv->frontend.demodulator_priv = NULL; @@ -2620,6 +3213,13 @@ struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, } EXPORT_SYMBOL(cxd2841er_attach_t); +struct dvb_frontend *cxd2841er_attach_i(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_ISDBT); +} +EXPORT_SYMBOL(cxd2841er_attach_i); + struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { @@ -2691,6 +3291,42 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { .get_frontend_algo = cxd2841er_get_algo }; +static struct dvb_frontend_ops cxd2841er_isdbt_ops = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "Sony CXD2854ER ISDBT demodulator", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION, + .frequency_min = 42000000, + .frequency_max = 1002000000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_sleep_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo +}; + static struct dvb_frontend_ops cxd2841er_dvbc_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { @@ -2722,6 +3358,6 @@ static struct dvb_frontend_ops cxd2841er_dvbc_ops = { .get_frontend_algo = cxd2841er_get_algo, }; -MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver"); -MODULE_AUTHOR("Sergey Kozlov "); +MODULE_DESCRIPTION("Sony CXD2841ER/CXD2854ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov , Abylay Ospan "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h index 3472bdd..9b0d2c1 100644 --- a/drivers/media/dvb-frontends/cxd2841er.h +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -25,8 +25,15 @@ #include #include +enum cxd2841er_xtal { + SONY_XTAL_20500, /* 20.5 MHz */ + SONY_XTAL_24000, /* 24 MHz */ + SONY_XTAL_41000 /* 41 MHz */ +}; + struct cxd2841er_config { u8 i2c_addr; + enum cxd2841er_xtal xtal; }; #if IS_REACHABLE(CONFIG_DVB_CXD2841ER) @@ -38,28 +45,38 @@ extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); +extern struct dvb_frontend *cxd2841er_attach_i(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); #else static inline struct dvb_frontend *cxd2841er_attach_s( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } static inline struct dvb_frontend *cxd2841er_attach_t( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } static inline struct dvb_frontend *cxd2841er_attach_c( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_i( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } + #endif #endif diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h index 33e2f49..0bbce45 100644 --- a/drivers/media/dvb-frontends/cxd2841er_priv.h +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -26,6 +26,7 @@ #define I2C_SLVT 1 #define CXD2841ER_CHIP_ID 0xa7 +#define CXD2854ER_CHIP_ID 0xc1 #define CXD2841ER_DVBS_POLLING_INVL 10 -- cgit v0.10.2 From c5eb643fe2a4b39d5179de20f6aefcf672404f98 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Wed, 23 Mar 2016 22:31:55 -0300 Subject: [media] Fix DVB-S/S2 tune for sony ascot3a tuner * fix buffer length check * do not rely on ROLLOFF Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 000606a..a98bca5 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -66,7 +66,7 @@ static int horus3a_write_regs(struct horus3a_priv *priv, } }; - if (len + 1 >= sizeof(buf)) { + if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", reg, len + 1); return -E2BIG; @@ -272,24 +272,6 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (fc_lpf > 36) fc_lpf = 36; } else if (p->delivery_system == SYS_DVBS2) { - int rolloff; - - switch (p->rolloff) { - case ROLLOFF_35: - rolloff = 35; - break; - case ROLLOFF_25: - rolloff = 25; - break; - case ROLLOFF_20: - rolloff = 20; - break; - case ROLLOFF_AUTO: - default: - dev_err(&priv->i2c->dev, - "horus3a: auto roll-off is not supported\n"); - return -EINVAL; - } /* * SR <= 4.5: * fc_lpf = 5 @@ -302,11 +284,9 @@ static int horus3a_set_params(struct dvb_frontend *fe) if (symbol_rate <= 4500) fc_lpf = 5; else if (symbol_rate <= 10000) - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (200 + rolloff), 200000); + fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000); else - fc_lpf = (u8)DIV_ROUND_UP( - symbol_rate * (100 + rolloff), 200000) + 5; + fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5); /* 5 <= fc_lpf <= 36 is valid */ if (fc_lpf > 36) fc_lpf = 36; -- cgit v0.10.2 From e4c645f48a8293c9af12fb63c520acb6a0f4ec68 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Wed, 23 Mar 2016 22:34:07 -0300 Subject: [media] New hw revision 1.4 of NetUP Universal DVB card added New hardware revision of NetUP Universal DVB card (rev 1.4): * changed tuners (CXD2861ER and CXD2832AER) to CXD2858ER * changed demodulator (CXD2841ER) to CXD2854ER card now supports: DVB-S/S2, DVB-T/T2, DVB-C/C2, ISDB-T PCI id's assigned for new hardware revision is: 1b55:18f7 Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h index a67b281..39b08ec 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb.h +++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h @@ -50,6 +50,15 @@ #define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) #define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) +/* NetUP Universal DVB card hardware revisions and it's PCI device id's: + * 1.3 - CXD2841ER demod, ASCOT2E and HORUS3A tuners + * 1.4 - CXD2854ER demod, HELENE tuner +*/ +enum netup_hw_rev { + NETUP_HW_REV_1_3 = 0x18F6, + NETUP_HW_REV_1_4 = 0x18F7 +}; + struct netup_dma { u8 num; spinlock_t lock; @@ -119,6 +128,7 @@ struct netup_unidvb_dev { struct netup_dma dma[2]; struct netup_ci_state ci[2]; struct netup_spi *spi; + enum netup_hw_rev rev; }; int netup_i2c_register(struct netup_unidvb_dev *ndev); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 2b667b3..9500923 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -34,6 +34,7 @@ #include "cxd2841er.h" #include "horus3a.h" #include "ascot2e.h" +#include "helene.h" #include "lnbh25.h" static int spi_enable; @@ -120,7 +121,8 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); static void netup_unidvb_queue_cleanup(struct netup_dma *dma); static struct cxd2841er_config demod_config = { - .i2c_addr = 0xc8 + .i2c_addr = 0xc8, + .xtal = SONY_XTAL_24000 }; static struct horus3a_config horus3a_conf = { @@ -134,6 +136,12 @@ static struct ascot2e_config ascot2e_conf = { .set_tuner_callback = netup_unidvb_tuner_ctrl }; +static struct helene_config helene_conf = { + .i2c_address = 0xc0, + .xtal = SONY_HELENE_XTAL_24000, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + static struct lnbh25_config lnbh25_conf = { .i2c_address = 0x10, .data2_config = LNBH25_TEN | LNBH25_EXTM @@ -152,6 +160,11 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) __func__, dma->num, is_dvb_tc); reg = readb(ndev->bmmio0 + GPIO_REG_IO); mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; + + /* inverted tuner control in hw rev. 1.4 */ + if (ndev->rev == NETUP_HW_REV_1_4) + is_dvb_tc = !is_dvb_tc; + if (!is_dvb_tc) reg |= mask; else @@ -372,7 +385,18 @@ static int netup_unidvb_queue_init(struct netup_dma *dma, static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, int num) { - struct vb2_dvb_frontend *fe0, *fe1, *fe2; + int fe_count = 0; + int i = 0; + struct vb2_dvb_frontend *fes[4]; + u8 fe_name[32]; + + if (ndev->rev == NETUP_HW_REV_1_3) { + fe_count = 3; + demod_config.xtal = SONY_XTAL_20500; + } else { + fe_count = 4; + demod_config.xtal = SONY_XTAL_24000; + } if (num < 0 || num > 1) { dev_dbg(&ndev->pci_dev->dev, @@ -381,84 +405,145 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, } mutex_init(&ndev->frontends[num].lock); INIT_LIST_HEAD(&ndev->frontends[num].felist); - if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 2) == NULL || - vb2_dvb_alloc_frontend( - &ndev->frontends[num], 3) == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to allocate vb2_dvb_frontend\n", - __func__); - return -ENOMEM; + + for (i = 0; i < fe_count; i++) { + if (vb2_dvb_alloc_frontend(&ndev->frontends[num], i+1) + == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to allocate vb2_dvb_frontend\n", + __func__); + return -ENOMEM; + } } - fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1); - fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2); - fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3); - if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): frontends has not been allocated\n", __func__); - return -EINVAL; + + for (i = 0; i < fe_count; i++) { + fes[i] = vb2_dvb_get_frontend(&ndev->frontends[num], i+1); + if (fes[i] == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): frontends has not been allocated\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < fe_count; i++) { + netup_unidvb_queue_init(&ndev->dma[num], &fes[i]->dvb.dvbq); + snprintf(fe_name, sizeof(fe_name), "netup_fe%d", i); + fes[i]->dvb.name = fe_name; } - netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq); - netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq); - fe0->dvb.name = "netup_fe0"; - fe1->dvb.name = "netup_fe1"; - fe2->dvb.name = "netup_fe2"; - fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s, + + fes[0]->dvb.frontend = dvb_attach(cxd2841er_attach_s, &demod_config, &ndev->i2c[num].adap); - if (fe0->dvb.frontend == NULL) { + if (fes[0]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach DVB-S/S2 frontend\n", __func__); goto frontend_detach; } - horus3a_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, - &horus3a_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-S/S2 tuner frontend\n", - __func__); - goto frontend_detach; + + if (ndev->rev == NETUP_HW_REV_1_3) { + horus3a_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(horus3a_attach, fes[0]->dvb.frontend, + &horus3a_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach HORUS3A DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach_s, fes[0]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } } - if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, + + if (!dvb_attach(lnbh25_attach, fes[0]->dvb.frontend, &lnbh25_conf, &ndev->i2c[num].adap)) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach SEC frontend\n", __func__); goto frontend_detach; } + /* DVB-T/T2 frontend */ - fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t, + fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t, &demod_config, &ndev->i2c[num].adap); - if (fe1->dvb.frontend == NULL) { + if (fes[1]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach DVB-T frontend\n", __func__); goto frontend_detach; } - fe1->dvb.frontend->id = 1; - ascot2e_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T tuner frontend\n", - __func__); - goto frontend_detach; + fes[1]->dvb.frontend->id = 1; + if (ndev->rev == NETUP_HW_REV_1_3) { + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(ascot2e_attach, fes[1]->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach, fes[1]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE DVB-T/T2 tuner frontend\n", + __func__); + goto frontend_detach; + } } + /* DVB-C/C2 frontend */ - fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c, + fes[2]->dvb.frontend = dvb_attach(cxd2841er_attach_c, &demod_config, &ndev->i2c[num].adap); - if (fe2->dvb.frontend == NULL) { + if (fes[2]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, "%s(): unable to attach DVB-C frontend\n", __func__); goto frontend_detach; } - fe2->dvb.frontend->id = 2; - if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T/C tuner frontend\n", - __func__); - goto frontend_detach; + fes[2]->dvb.frontend->id = 2; + if (ndev->rev == NETUP_HW_REV_1_3) { + if (!dvb_attach(ascot2e_attach, fes[2]->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T/C tuner frontend\n", + __func__); + goto frontend_detach; + } + } else { + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach, fes[2]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE Ter tuner frontend\n", + __func__); + goto frontend_detach; + } + } + + if (ndev->rev == NETUP_HW_REV_1_4) { + /* ISDB-T frontend */ + fes[3]->dvb.frontend = dvb_attach(cxd2841er_attach_i, + &demod_config, &ndev->i2c[num].adap); + if (fes[3]->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach ISDB-T frontend\n", + __func__); + goto frontend_detach; + } + fes[3]->dvb.frontend->id = 3; + helene_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(helene_attach, fes[3]->dvb.frontend, + &helene_conf, &ndev->i2c[num].adap)) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to attach HELENE Ter tuner frontend\n", + __func__); + goto frontend_detach; + } } if (vb2_dvb_register_bus(&ndev->frontends[num], @@ -730,7 +815,7 @@ static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) static int netup_unidvb_request_modules(struct device *dev) { static const char * const modules[] = { - "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL + "lnbh25", "ascot2e", "horus3a", "cxd2841er", "helene", NULL }; const char * const *curr_mod = modules; int err; @@ -774,6 +859,16 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, if (!ndev) goto dev_alloc_err; + /* detect hardware revision */ + if (pci_dev->device == NETUP_HW_REV_1_3) + ndev->rev = NETUP_HW_REV_1_3; + else + ndev->rev = NETUP_HW_REV_1_4; + + dev_info(&pci_dev->dev, + "%s(): board (0x%x) hardware revision 0x%x\n", + __func__, pci_dev->device, ndev->rev); + ndev->old_fw = old_firmware; ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME); if (!ndev->wq) { @@ -972,7 +1067,8 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev) static struct pci_device_id netup_unidvb_pci_tbl[] = { - { PCI_DEVICE(0x1b55, 0x18f6) }, + { PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */ + { PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */ { 0, } }; MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); -- cgit v0.10.2 From a09afd8fa307429f7514350d08f4a75f96cfae53 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Fri, 1 Apr 2016 18:00:53 -0300 Subject: [media] Fix CAM module memory read/write mistakenly membase8_io used instead of membase8_config in this case we can't read/write CAM module memory (TUPLES) Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c index f46ffac6..f535270 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c @@ -147,7 +147,7 @@ static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, { struct netup_ci_state *state = en50221->data; struct netup_unidvb_dev *dev = state->dev; - u8 val = *((u8 __force *)state->membase8_io + addr); + u8 val = *((u8 __force *)state->membase8_config + addr); dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); @@ -162,7 +162,7 @@ static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, dev_dbg(&dev->pci_dev->dev, "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); - *((u8 __force *)state->membase8_io + addr) = data; + *((u8 __force *)state->membase8_config + addr) = data; return 0; } -- cgit v0.10.2 From 1a76e1f75f6b90b77a48d0e26a1cab35c1379d04 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sat, 2 Apr 2016 00:00:58 -0300 Subject: [media] MAINTAINERS: Add a maintainer for netup_unidvb, cxd2841er, horus3a, ascot2e I'm second maintainer for netup_unidvb, cxd2841er, horus3a, ascot2e Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..bc2b5a6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7313,6 +7313,7 @@ F: drivers/media/platform/vsp1/ MEDIA DRIVERS FOR ASCOT2E M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7322,6 +7323,7 @@ F: drivers/media/dvb-frontends/ascot2e* MEDIA DRIVERS FOR CXD2841ER M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7331,6 +7333,7 @@ F: drivers/media/dvb-frontends/cxd2841er* MEDIA DRIVERS FOR HORUS3A M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7340,6 +7343,7 @@ F: drivers/media/dvb-frontends/horus3a* MEDIA DRIVERS FOR LNBH25 M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ @@ -7349,6 +7353,7 @@ F: drivers/media/dvb-frontends/lnbh25* MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices M: Sergey Kozlov +M: Abylay Ospan L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://netup.tv/ -- cgit v0.10.2 From c5ea46dae94875fa6656b014fee53a5847ee683c Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sat, 2 Apr 2016 23:31:50 -0300 Subject: [media] Add carrier offset calculation for DVB-T Adding cxd2841er_get_carrier_offset_t to calculate DVB-T offset for Sony demodulators CXD2841ER and CXD2854ER Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index db51790..b87d990 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -1095,6 +1095,30 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + return 0; +} + static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, u32 bandwidth, int *offset) { @@ -1560,6 +1584,10 @@ static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, cxd2841er_write_reg( priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + dev_dbg(&priv->i2c->dev, + "%s(): AGC value=%u\n", + __func__, (((u16)data[0] & 0x0F) << 8) | + (u16)(data[1] & 0xFF)); return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; } @@ -2912,6 +2940,10 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, if (*status & FE_HAS_LOCK) { switch (priv->system) { case SYS_DVBT: + ret = cxd2841er_get_carrier_offset_t( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT2: ret = cxd2841er_get_carrier_offset_t2( priv, p->bandwidth_hz, -- cgit v0.10.2 From 30ae3307adcf500229e95731a5e116bc3a8c31f8 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Tue, 5 Apr 2016 15:02:37 -0300 Subject: [media] Sanity check when initializing DVB-S/S2 demodulator Avoid error message: cxd2841er_read_status_s(): invalid state 1 Always force demod to shutdown state before initializing Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index b87d990..94613af6 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3133,6 +3133,18 @@ static int cxd2841er_init_s(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + /* sanity. force demod to SHUTDOWN state */ + if (priv->state == STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n", + __func__); + cxd2841er_sleep_s_to_shutdown(priv); + } else if (priv->state == STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n", + __func__); + cxd2841er_active_s_to_sleep_s(priv); + cxd2841er_sleep_s_to_shutdown(priv); + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_shutdown_to_sleep_s(priv); /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ -- cgit v0.10.2 From 4da093ce4e8bd36223763c900f623ce36a0d51c3 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Wed, 20 Apr 2016 14:02:58 -0300 Subject: [media] Fix DVB-T frequency offset calculation Fix offset calculation inside cxd2841er_get_carrier_offset_t Now DVB-T should be tuned correctly Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 94613af6..609187d5 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -1116,6 +1116,8 @@ static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, *offset = -1 * sign_extend32( ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | ((u32)data[2] << 8) | (u32)data[3], 29); + *offset *= (bandwidth / 1000000); + *offset /= 235; return 0; } -- cgit v0.10.2 From 76344a3fc54d04aaf71763a1428245c9e1d9a4ac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 4 May 2016 18:25:38 -0300 Subject: [media] ISDB-T retune and offset fix and DVB-C bw fix now when new tuning parameters specified demod should retune. Also ISDB-T frequency offset calculation added (cxd2841er_get_carrier_offset_i). While here, fix re-tune for DVB-C Annex A, using the desired bandwidth, instead of using 8MHz. Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 609187d5..45bc438 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -433,6 +433,15 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u32 bandwidth); +static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv); + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv); + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv); + static int cxd2841er_retune_active(struct cxd2841er_priv *priv, struct dtv_frontend_properties *p) { @@ -460,7 +469,13 @@ static int cxd2841er_retune_active(struct cxd2841er_priv *priv, priv, p->bandwidth_hz); case SYS_DVBC_ANNEX_A: return cxd2841er_sleep_tc_to_active_c_band( - priv, 8000000); + priv, p->bandwidth_hz); + case SYS_ISDBT: + cxd2841er_active_i_to_sleep_tc(priv); + cxd2841er_sleep_tc_to_shutdown(priv); + cxd2841er_shutdown_to_sleep_tc(priv); + return cxd2841er_sleep_tc_to_active_i( + priv, p->bandwidth_hz); } } dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", @@ -1095,6 +1110,50 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_ISDBT) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 29); + + switch (bandwidth) { + case 6000000: + *offset = -1 * ((*offset) * 8/264); + break; + case 7000000: + *offset = -1 * ((*offset) * 8/231); + break; + case 8000000: + *offset = -1 * ((*offset) * 8/198); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + + dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n", + __func__, bandwidth, *offset); + + return 0; +} + static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, u32 bandwidth, int *offset) { @@ -2941,6 +3000,11 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, cxd2841er_read_status_tc(fe, status); if (*status & FE_HAS_LOCK) { switch (priv->system) { + case SYS_ISDBT: + ret = cxd2841er_get_carrier_offset_i( + priv, p->bandwidth_hz, + &carrier_offset); + break; case SYS_DVBT: ret = cxd2841er_get_carrier_offset_t( priv, p->bandwidth_hz, -- cgit v0.10.2 From 1cdc4f09eb41e4fd22e17ded943ebdb08050a2ed Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Thu, 12 May 2016 00:02:04 -0300 Subject: [media] fix DVB-S/S2 tuning Fixed HELENE tuner frequency calculation Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 98099db..f385ebd 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -522,7 +522,7 @@ static int helene_set_params_s(struct dvb_frontend *fe) enum helene_tv_system_t tv_system; struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct helene_priv *priv = fe->tuner_priv; - int frequencykHz = p->frequency / 1000; + int frequencykHz = p->frequency; uint32_t frequency4kHz = 0; u32 symbol_rate = p->symbol_rate/1000; -- cgit v0.10.2 From 6c77161a18baa506bd5343c98764789146b72b7a Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 16 May 2016 11:43:25 -0300 Subject: [media] support DVB-T2 for SONY CXD2841/54 bandwidth 1.7,5,6,7,8Mhz support for DVB-T2 Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 45bc438..50bdd40 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -288,6 +288,7 @@ static int cxd2841er_read_regs(struct cxd2841er_priv *priv, KBUILD_MODNAME, ret, i2c_addr, reg); return ret; } + cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len); return 0; } @@ -789,6 +790,7 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) { + u8 data = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); if (priv->state != STATE_SHUTDOWN) { dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", @@ -804,9 +806,24 @@ static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); /* Set demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); - /* Set X'tal clock to 20.5Mhz */ + /* Select ADC clock mode */ cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); - cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + + switch (priv->xtal) { + case SONY_XTAL_20500: + data = 0x0; + break; + case SONY_XTAL_24000: + /* Select demod frequency */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x3; + break; + case SONY_XTAL_41000: + cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00); + data = 0x1; + break; + } + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data); /* Clear demod SW reset */ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); usleep_range(1000, 2000); @@ -1175,8 +1192,8 @@ static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv, *offset = -1 * sign_extend32( ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) | ((u32)data[2] << 8) | (u32)data[3], 29); - *offset *= (bandwidth / 1000000); - *offset /= 235; + *offset *= (bandwidth / 1000000); + *offset /= 235; return 0; } @@ -1800,15 +1817,18 @@ static int cxd2841er_dvbt2_set_profile( switch (profile) { case DVBT2_PROFILE_BASE: tune_mode = 0x01; - seq_not2d_time = 12; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C; break; case DVBT2_PROFILE_LITE: tune_mode = 0x05; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; case DVBT2_PROFILE_ANY: tune_mode = 0x00; - seq_not2d_time = 40; + /* Set early unlock time */ + seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28; break; default: return -EINVAL; @@ -1850,160 +1870,235 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, u32 bandwidth) { u32 iffreq; - u8 b20_9f[5]; - u8 b10_a6[14]; - u8 b10_b6[3]; - u8 b10_d7; + u8 data[MAX_WRITE_REGSIZE]; + + const uint8_t nominalRate8bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate7bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */ + {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */ + }; + + const uint8_t nominalRate6bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */ + {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */ + {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */ + }; + + const uint8_t nominalRate5bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */ + {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */ + {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */ + }; + + const uint8_t nominalRate17bw[3][5] = { + /* TRCG Nominal Rate [37:0] */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */ + {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */ + {0x58, 0xE2, 0xAF, 0xE0, 0xBC} /* 41MHz XTal */ + }; + + const uint8_t itbCoef8bw[3][14] = { + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */ + {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, + 0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */ + {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, + 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */ + }; + + const uint8_t itbCoef7bw[3][14] = { + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */ + {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, + 0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */ + {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, + 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */ + }; + + const uint8_t itbCoef6bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef5bw[3][14] = { + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */ + {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, + 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */ + {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */ + }; + + const uint8_t itbCoef17bw[3][14] = { + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */ + {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B, + 0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal */ + {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B, + 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99} /* 41MHz XTal */ + }; + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (bandwidth) { case 8000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x11; - b20_9f[1] = 0xf0; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x26; - b10_a6[1] = 0xaf; - b10_a6[2] = 0x06; - b10_a6[3] = 0xcd; - b10_a6[4] = 0x13; - b10_a6[5] = 0xbb; - b10_a6[6] = 0x28; - b10_a6[7] = 0xba; - b10_a6[8] = 0x23; - b10_a6[9] = 0xa9; - b10_a6[10] = 0x1f; - b10_a6[11] = 0xa8; - b10_a6[12] = 0x2c; - b10_a6[13] = 0xc8; - iffreq = MAKE_IFFREQ_CONFIG(4.80); - b10_d7 = 0x00; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate8bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef8bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x00, 0x07); break; case 7000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x14; - b20_9f[1] = 0x80; - b20_9f[2] = 0x00; - b20_9f[3] = 0x00; - b20_9f[4] = 0x00; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x2C; - b10_a6[1] = 0xBD; - b10_a6[2] = 0x02; - b10_a6[3] = 0xCF; - b10_a6[4] = 0x04; - b10_a6[5] = 0xF8; - b10_a6[6] = 0x23; - b10_a6[7] = 0xA6; - b10_a6[8] = 0x29; - b10_a6[9] = 0xB0; - b10_a6[10] = 0x26; - b10_a6[11] = 0xA9; - b10_a6[12] = 0x21; - b10_a6[13] = 0xA5; - iffreq = MAKE_IFFREQ_CONFIG(4.2); - b10_d7 = 0x02; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate7bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef7bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x02, 0x07); break; case 6000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x17; - b20_9f[1] = 0xEA; - b20_9f[2] = 0xAA; - b20_9f[3] = 0xAA; - b20_9f[4] = 0xAA; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x04; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate6bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef6bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x04, 0x07); break; case 5000000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x1C; - b20_9f[1] = 0xB3; - b20_9f[2] = 0x33; - b20_9f[3] = 0x33; - b20_9f[4] = 0x33; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x27; - b10_a6[1] = 0xA7; - b10_a6[2] = 0x28; - b10_a6[3] = 0xB3; - b10_a6[4] = 0x02; - b10_a6[5] = 0xF0; - b10_a6[6] = 0x01; - b10_a6[7] = 0xE8; - b10_a6[8] = 0x00; - b10_a6[9] = 0xCF; - b10_a6[10] = 0x00; - b10_a6[11] = 0xE6; - b10_a6[12] = 0x23; - b10_a6[13] = 0xA4; - iffreq = MAKE_IFFREQ_CONFIG(3.6); - b10_d7 = 0x06; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate5bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x00, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef5bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x06, 0x07); break; case 1712000: - /* bank 0x20, reg 0x9f */ - b20_9f[0] = 0x58; - b20_9f[1] = 0xE2; - b20_9f[2] = 0xAF; - b20_9f[3] = 0xE0; - b20_9f[4] = 0xBC; - /* bank 0x10, reg 0xa6 */ - b10_a6[0] = 0x25; - b10_a6[1] = 0xA0; - b10_a6[2] = 0x36; - b10_a6[3] = 0x8D; - b10_a6[4] = 0x2E; - b10_a6[5] = 0x94; - b10_a6[6] = 0x28; - b10_a6[7] = 0x9B; - b10_a6[8] = 0x32; - b10_a6[9] = 0x90; - b10_a6[10] = 0x2C; - b10_a6[11] = 0x9D; - b10_a6[12] = 0x29; - b10_a6[13] = 0x99; - iffreq = MAKE_IFFREQ_CONFIG(3.5); - b10_d7 = 0x03; + /* */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0x9F, nominalRate17bw[priv->xtal], 5); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits(priv, I2C_SLVT, + 0x7a, 0x03, 0x0f); + + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + /* Group delay equaliser settings for + * ASCOT2D, ASCOT2E and ASCOT3 tuners + */ + cxd2841er_write_regs(priv, I2C_SLVT, + 0xA6, itbCoef17bw[priv->xtal], 14); + /* */ + iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.50); + data[0] = (u8) ((iffreq >> 16) & 0xff); + data[1] = (u8)((iffreq >> 8) & 0xff); + data[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3); + /* System bandwidth setting */ + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xD7, 0x03, 0x07); break; default: return -EINVAL; } - /* Set SLV-T Bank : 0x20 */ - cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20); - cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f)); - /* Set SLV-T Bank : 0x27 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); - cxd2841er_set_reg_bits( - priv, I2C_SLVT, 0x7a, - (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f); - /* Set SLV-T Bank : 0x10 */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); - /* Group delay equaliser sett. for ASCOT2E */ - cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6)); - /* */ - b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); - b10_b6[1] = (u8)((iffreq >> 8) & 0xff); - b10_b6[2] = (u8)(iffreq & 0xff); - cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); - /* System bandwidth setting */ - cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07); return 0; } @@ -2539,7 +2634,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, u32 bandwidth) { - u8 data[2] = { 0x09, 0x54 }; + u8 data[MAX_WRITE_REGSIZE]; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); @@ -2552,12 +2647,21 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Enable demod clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); /* Enable ADC clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); /* Enable ADC 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); - /* xtal freq 20.5MHz */ + + if (priv->xtal == SONY_XTAL_41000) { + data[0] = 0x0A; + data[1] = 0xD4; + } else { + data[0] = 0x09; + data[1] = 0x54; + } + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); /* Enable ADC 4 */ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); @@ -2580,6 +2684,10 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, /* Set SLV-T Bank : 0x2b */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + /* L1 Control setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* TSIF setting */ @@ -2598,6 +2706,72 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + /* 24MHz Xtal setting */ + if (priv->xtal == SONY_XTAL_24000) { + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + data[0] = 0xEB; + data[1] = 0x03; + data[2] = 0x3B; + cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3); + + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + data[0] = 0x5E; + data[1] = 0x5E; + data[2] = 0x47; + cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18); + + data[0] = 0x3F; + data[1] = 0xFF; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2); + + /* Set SLV-T Bank : 0x24 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + data[0] = 0x0B; + data[1] = 0x72; + cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2); + + data[0] = 0x93; + data[1] = 0xF3; + data[2] = 0x00; + cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3); + + data[0] = 0x05; + data[1] = 0xB8; + data[2] = 0xD8; + cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3); + + cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00); + + /* Set SLV-T Bank : 0x25 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25); + cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60); + + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34); + + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B); + cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E); + + /* Set SLV-T Bank : 0x2D */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D); + data[0] = 0x89; + data[1] = 0x89; + cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2); + + /* Set SLV-T Bank : 0x5E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E); + data[0] = 0x24; + data[1] = 0x95; + cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2); + } + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ -- cgit v0.10.2 From 3f3b48a0c6caba3b1a16a79d6a75a6ea4eac6b9c Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sat, 14 May 2016 00:08:40 -0300 Subject: [media] Change frontend allocation strategy for NetUP Universal DVB cards Old behaviour: frontend0 - DVB-S/S2 frontend1 - DVB-T/T2 frontend2 - DVB-C frontend3 - ISDB-T New behaviour (DVBv5 API compliant): frontend0 - DVB-S/S2 frontend1 - DVB-T/T2/C/ISDB-T DTV standard should be selected by DTV_DELIVERY_SYSTEM call. And DVB-C default bandwidth now 8MHz Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 50bdd40..cc306b6 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -54,6 +54,7 @@ struct cxd2841er_priv { enum cxd2841er_state state; u8 system; enum cxd2841er_xtal xtal; + enum fe_caps caps; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -791,6 +792,7 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) { u8 data = 0; + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); if (priv->state != STATE_SHUTDOWN) { dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", @@ -2496,7 +2498,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u8 b10_b6[3]; u32 iffreq; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { case 8000000: @@ -2513,7 +2515,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, iffreq = MAKE_IFFREQ_CONFIG(3.7); break; default: - dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", __func__, bandwidth); return -EINVAL; } @@ -2909,7 +2911,7 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); - cxd2841er_sleep_tc_to_active_c_band(priv, 8000000); + cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth); /* Set SLV-T Bank : 0x00 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); /* Disable HiZ Setting 1 */ @@ -3029,7 +3031,8 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n", + __func__, p->delivery_system, p->bandwidth_hz); if (p->delivery_system == SYS_DVBT) { priv->system = SYS_DVBT; switch (priv->state) { @@ -3081,6 +3084,15 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) } else if (p->delivery_system == SYS_DVBC_ANNEX_A || p->delivery_system == SYS_DVBC_ANNEX_C) { priv->system = SYS_DVBC_ANNEX_A; + /* correct bandwidth */ + if (p->bandwidth_hz != 6000000 && + p->bandwidth_hz != 7000000 && + p->bandwidth_hz != 8000000) { + p->bandwidth_hz = 8000000; + dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n", + __func__, p->bandwidth_hz); + } + switch (priv->state) { case STATE_SLEEP_TC: ret = cxd2841er_sleep_tc_to_active_c( @@ -3166,7 +3178,8 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe, struct cxd2841er_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune); + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__, + re_tune, p->bandwidth_hz); if (re_tune) { ret = cxd2841er_set_frontend_tc(fe); if (ret) @@ -3396,8 +3409,10 @@ static int cxd2841er_init_s(struct dvb_frontend *fe) static int cxd2841er_init_tc(struct dvb_frontend *fe) { struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n", + __func__, p->bandwidth_hz); cxd2841er_shutdown_to_sleep_tc(priv); /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); @@ -3411,9 +3426,7 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe) } static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; -static struct dvb_frontend_ops cxd2841er_dvbc_ops; -static struct dvb_frontend_ops cxd2841er_isdbt_ops; +static struct dvb_frontend_ops cxd2841er_t_c_ops; static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, struct i2c_adapter *i2c, @@ -3421,6 +3434,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, { u8 chip_id = 0; const char *type; + const char *name; struct cxd2841er_priv *priv = NULL; /* allocate memory for the internal state */ @@ -3432,52 +3446,48 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; priv->xtal = cfg->xtal; - /* create dvb_frontend */ - switch (system) { - case SYS_DVBS: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbs_s2_ops, - sizeof(struct dvb_frontend_ops)); - type = "S/S2"; - break; - case SYS_DVBT: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbt_t2_ops, - sizeof(struct dvb_frontend_ops)); - type = "T/T2"; - break; - case SYS_ISDBT: - memcpy(&priv->frontend.ops, - &cxd2841er_isdbt_ops, - sizeof(struct dvb_frontend_ops)); - type = "ISDBT"; - break; - case SYS_DVBC_ANNEX_A: - memcpy(&priv->frontend.ops, - &cxd2841er_dvbc_ops, - sizeof(struct dvb_frontend_ops)); - type = "C/C2"; - break; - default: - kfree(priv); - return NULL; - } priv->frontend.demodulator_priv = priv; dev_info(&priv->i2c->dev, - "%s(): attaching CXD2841ER DVB-%s frontend\n", - __func__, type); - dev_info(&priv->i2c->dev, "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", __func__, priv->i2c, priv->i2c_addr_slvx, priv->i2c_addr_slvt); chip_id = cxd2841er_chip_id(priv); - if (chip_id != CXD2841ER_CHIP_ID && chip_id != CXD2854ER_CHIP_ID) { + switch (chip_id) { + case CXD2841ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2841ER DVB-T/T2/C demodulator"); + name = "CXD2841ER"; + break; + case CXD2854ER_CHIP_ID: + snprintf(cxd2841er_t_c_ops.info.name, 128, + "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator"); + cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT; + name = "CXD2854ER"; + break; + default: dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", - __func__, chip_id); + __func__, chip_id); priv->frontend.demodulator_priv = NULL; kfree(priv); return NULL; } + + /* create dvb_frontend */ + if (system == SYS_DVBS) { + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + } else { + memcpy(&priv->frontend.ops, + &cxd2841er_t_c_ops, + sizeof(struct dvb_frontend_ops)); + type = "T/T2/C/ISDB-T"; + } + + dev_info(&priv->i2c->dev, + "%s(): attaching %s DVB-%s frontend\n", + __func__, name, type); dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", __func__, chip_id); return &priv->frontend; @@ -3490,26 +3500,12 @@ struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, } EXPORT_SYMBOL(cxd2841er_attach_s); -struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c) -{ - return cxd2841er_attach(cfg, i2c, SYS_DVBT); -} -EXPORT_SYMBOL(cxd2841er_attach_t); - -struct dvb_frontend *cxd2841er_attach_i(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c) -{ - return cxd2841er_attach(cfg, i2c, SYS_ISDBT); -} -EXPORT_SYMBOL(cxd2841er_attach_i); - -struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, +struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { - return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A); + return cxd2841er_attach(cfg, i2c, 0); } -EXPORT_SYMBOL(cxd2841er_attach_c); +EXPORT_SYMBOL(cxd2841er_attach_t_c); static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, @@ -3539,46 +3535,10 @@ static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .tune = cxd2841er_tune_s }; -static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { - .delsys = { SYS_DVBT, SYS_DVBT2 }, - .info = { - .name = "Sony CXD2841ER DVB-T/T2 demodulator", - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | - FE_CAN_MUTE_TS | - FE_CAN_2G_MODULATION, - .frequency_min = 42000000, - .frequency_max = 1002000000 - }, - .init = cxd2841er_init_tc, - .sleep = cxd2841er_sleep_tc, - .release = cxd2841er_release, - .set_frontend = cxd2841er_set_frontend_tc, - .get_frontend = cxd2841er_get_frontend, - .read_status = cxd2841er_read_status_tc, - .tune = cxd2841er_tune_tc, - .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, - .get_frontend_algo = cxd2841er_get_algo -}; - -static struct dvb_frontend_ops cxd2841er_isdbt_ops = { - .delsys = { SYS_ISDBT }, +static struct dvb_frontend_ops cxd2841er_t_c_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, .info = { - .name = "Sony CXD2854ER ISDBT demodulator", + .name = "", /* will set in attach function */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | @@ -3611,37 +3571,6 @@ static struct dvb_frontend_ops cxd2841er_isdbt_ops = { .get_frontend_algo = cxd2841er_get_algo }; -static struct dvb_frontend_ops cxd2841er_dvbc_ops = { - .delsys = { SYS_DVBC_ANNEX_A }, - .info = { - .name = "Sony CXD2841ER DVB-C demodulator", - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO | - FE_CAN_INVERSION_AUTO, - .frequency_min = 42000000, - .frequency_max = 1002000000 - }, - .init = cxd2841er_init_tc, - .sleep = cxd2841er_sleep_tc, - .release = cxd2841er_release, - .set_frontend = cxd2841er_set_frontend_tc, - .get_frontend = cxd2841er_get_frontend, - .read_status = cxd2841er_read_status_tc, - .tune = cxd2841er_tune_tc, - .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, - .get_frontend_algo = cxd2841er_get_algo, -}; - MODULE_DESCRIPTION("Sony CXD2841ER/CXD2854ER DVB-C/C2/T/T2/S/S2 demodulator driver"); MODULE_AUTHOR("Sergey Kozlov , Abylay Ospan "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h index 9b0d2c1..62ad5f0 100644 --- a/drivers/media/dvb-frontends/cxd2841er.h +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -40,13 +40,8 @@ struct cxd2841er_config { extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); -extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, +extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c); - -extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c); -extern struct dvb_frontend *cxd2841er_attach_i(struct cxd2841er_config *cfg, - struct i2c_adapter *i2c); #else static inline struct dvb_frontend *cxd2841er_attach_s( struct cxd2841er_config *cfg, @@ -56,21 +51,7 @@ static inline struct dvb_frontend *cxd2841er_attach_s( return NULL; } -static inline struct dvb_frontend *cxd2841er_attach_t( - struct cxd2841er_config *cfg, struct i2c_adapter *i2c) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct dvb_frontend *cxd2841er_attach_c( - struct cxd2841er_config *cfg, struct i2c_adapter *i2c) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct dvb_frontend *cxd2841er_attach_i( +static inline struct dvb_frontend *cxd2841er_attach_t_c( struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { pr_warn("%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 9500923..d278d4e 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -385,18 +385,15 @@ static int netup_unidvb_queue_init(struct netup_dma *dma, static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, int num) { - int fe_count = 0; + int fe_count = 2; int i = 0; - struct vb2_dvb_frontend *fes[4]; + struct vb2_dvb_frontend *fes[2]; u8 fe_name[32]; - if (ndev->rev == NETUP_HW_REV_1_3) { - fe_count = 3; + if (ndev->rev == NETUP_HW_REV_1_3) demod_config.xtal = SONY_XTAL_20500; - } else { - fe_count = 4; + else demod_config.xtal = SONY_XTAL_24000; - } if (num < 0 || num > 1) { dev_dbg(&ndev->pci_dev->dev, @@ -469,11 +466,11 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, } /* DVB-T/T2 frontend */ - fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t, + fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t_c, &demod_config, &ndev->i2c[num].adap); if (fes[1]->dvb.frontend == NULL) { dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T frontend\n", __func__); + "%s(): unable to attach Ter frontend\n", __func__); goto frontend_detach; } fes[1]->dvb.frontend->id = 1; @@ -482,7 +479,7 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, if (!dvb_attach(ascot2e_attach, fes[1]->dvb.frontend, &ascot2e_conf, &ndev->i2c[num].adap)) { dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T tuner frontend\n", + "%s(): unable to attach Ter tuner frontend\n", __func__); goto frontend_detach; } @@ -491,55 +488,6 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, if (!dvb_attach(helene_attach, fes[1]->dvb.frontend, &helene_conf, &ndev->i2c[num].adap)) { dev_err(&ndev->pci_dev->dev, - "%s(): unable to attach HELENE DVB-T/T2 tuner frontend\n", - __func__); - goto frontend_detach; - } - } - - /* DVB-C/C2 frontend */ - fes[2]->dvb.frontend = dvb_attach(cxd2841er_attach_c, - &demod_config, &ndev->i2c[num].adap); - if (fes[2]->dvb.frontend == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-C frontend\n", __func__); - goto frontend_detach; - } - fes[2]->dvb.frontend->id = 2; - if (ndev->rev == NETUP_HW_REV_1_3) { - if (!dvb_attach(ascot2e_attach, fes[2]->dvb.frontend, - &ascot2e_conf, &ndev->i2c[num].adap)) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach DVB-T/C tuner frontend\n", - __func__); - goto frontend_detach; - } - } else { - helene_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(helene_attach, fes[2]->dvb.frontend, - &helene_conf, &ndev->i2c[num].adap)) { - dev_err(&ndev->pci_dev->dev, - "%s(): unable to attach HELENE Ter tuner frontend\n", - __func__); - goto frontend_detach; - } - } - - if (ndev->rev == NETUP_HW_REV_1_4) { - /* ISDB-T frontend */ - fes[3]->dvb.frontend = dvb_attach(cxd2841er_attach_i, - &demod_config, &ndev->i2c[num].adap); - if (fes[3]->dvb.frontend == NULL) { - dev_dbg(&ndev->pci_dev->dev, - "%s(): unable to attach ISDB-T frontend\n", - __func__); - goto frontend_detach; - } - fes[3]->dvb.frontend->id = 3; - helene_conf.set_tuner_priv = &ndev->dma[num]; - if (!dvb_attach(helene_attach, fes[3]->dvb.frontend, - &helene_conf, &ndev->i2c[num].adap)) { - dev_err(&ndev->pci_dev->dev, "%s(): unable to attach HELENE Ter tuner frontend\n", __func__); goto frontend_detach; -- cgit v0.10.2 From 9ca1736f8de176db72c2022b8a921a465cdf9a3f Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Mon, 16 May 2016 11:57:04 -0300 Subject: [media] fix typo in SONY demodulator description correct is CXD2841ER and CXD2854ER incorrect was CXD2441ER and CXD2454ER Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index cc306b6..d369a75 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -2,8 +2,8 @@ * cxd2841er.c * * Sony digital demodulator driver for - * CXD2441ER - DVB-S/S2/T/T2/C/C2 - * CXD2454ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S + * CXD2841ER - DVB-S/S2/T/T2/C/C2 + * CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S * * Copyright 2012 Sony Corporation * Copyright (C) 2014 NetUP Inc. -- cgit v0.10.2 From b6095d29a2406a6ea059bd019c53b69cd3a8fd99 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 7 Jun 2016 12:18:40 -0300 Subject: [media] helene: fix a warning when printing sizeof() drivers/media/dvb-frontends/helene.c: In function 'helene_write_regs': drivers/media/dvb-frontends/helene.c:312:5: warning: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'unsigned int' [-Wformat=] "wr reg=%04x: len=%d vs %lu is too big!\n", ^ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index f385ebd..693b5bf 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -309,7 +309,7 @@ static int helene_write_regs(struct helene_priv *priv, if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev, - "wr reg=%04x: len=%d vs %lu is too big!\n", + "wr reg=%04x: len=%d vs %Zu is too big!\n", reg, len + 1, sizeof(buf)); return -E2BIG; } -- cgit v0.10.2 From fae2080a3865b6512697531b0d8ab59de60ecffc Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Tue, 7 Jun 2016 11:33:57 -0400 Subject: MAINTAINERS: Add Sony Helene TV tuner entry I'm maintainer for Sony Helene tuner (drivers/media/dvb-frontends/helene.*) Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index bc2b5a6..1446778 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7311,6 +7311,15 @@ S: Supported F: Documentation/devicetree/bindings/media/renesas,vsp1.txt F: drivers/media/platform/vsp1/ +MEDIA DRIVERS FOR HELENE +M: Abylay Ospan +L: linux-media@vger.kernel.org +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/helene* + MEDIA DRIVERS FOR ASCOT2E M: Sergey Kozlov M: Abylay Ospan -- cgit v0.10.2 From bcbf298669a88bde7846c8ce0595f77aa4113e04 Mon Sep 17 00:00:00 2001 From: ayaka Date: Sat, 7 May 2016 02:05:24 -0300 Subject: [media] s5p-mfc: Add handling of buffer freeing reqbufs request The encoder forget the work to call hardware to release its buffers. This patch came from chromium project. I just change its code style and make the API match with new kernel. Signed-off-by: ayaka Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 034b5c1..a66a9f9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1144,7 +1144,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (reqbufs->count == 0) { + mfc_debug(2, "Freeing buffers\n"); ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, + ctx); ctx->capture_state = QUEUE_FREE; return ret; } -- cgit v0.10.2 From ee0829f4752d940567f73c5a0c2a1a7e136673f2 Mon Sep 17 00:00:00 2001 From: ayaka Date: Sat, 7 May 2016 02:05:25 -0300 Subject: [media] s5p-mfc: remove unnecessary check in try_fmt We don't need to request the sizeimage or num_planes in try_fmt. Signed-off-by: ayaka Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index a66a9f9..2f76aba 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1043,10 +1043,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - mfc_err("must be set encoding output size\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; @@ -1060,11 +1056,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - mfc_err("failed to try output format\n"); - return -EINVAL; - } if ((dev->variant->version_bit & fmt->versions) == 0) { mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; -- cgit v0.10.2 From 4cf11a481f0be471e069d2e61372875e0f12c166 Mon Sep 17 00:00:00 2001 From: ayaka Date: Sat, 7 May 2016 02:05:26 -0300 Subject: [media] s5p-mfc: fix a typo in s5p_mfc_dec It is a cosmetic commit. Signed-off-by: ayaka Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8b9467d..a01a373 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -572,7 +572,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); if (reqbufs->memory != V4L2_MEMORY_MMAP) { - mfc_err("Only V4L2_MEMORY_MAP is supported\n"); + mfc_err("Only V4L2_MEMORY_MMAP is supported\n"); return -EINVAL; } -- cgit v0.10.2 From 6b879edf75b31644089fec1fde301f2bdba32dcf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 2 Jun 2016 04:03:47 -0300 Subject: [media] staging/media: remove deprecated mx2 driver Remove this deprecated old driver. Signed-off-by: Hans Verkuil Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index de7e9f5..baf6fb3 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,8 +27,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/mn88472/Kconfig" -source "drivers/staging/media/mx2/Kconfig" - source "drivers/staging/media/mx3/Kconfig" source "drivers/staging/media/omap1/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 60a35b3..4ba7c6b 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ -obj-$(CONFIG_VIDEO_MX2) += mx2/ obj-$(CONFIG_VIDEO_MX3) += mx3/ obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ diff --git a/drivers/staging/media/mx2/Kconfig b/drivers/staging/media/mx2/Kconfig deleted file mode 100644 index beaa885..0000000 --- a/drivers/staging/media/mx2/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_MX2 - tristate "i.MX27 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on SOC_IMX27 || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX27 Camera Sensor Interface - - This driver is deprecated: it should become a stand-alone driver - instead of using the soc-camera framework. - - Unless someone is willing to take this on (unlikely with such - ancient hardware) it is going to be removed from the kernel - soon. diff --git a/drivers/staging/media/mx2/Makefile b/drivers/staging/media/mx2/Makefile deleted file mode 100644 index fc5b282..0000000 --- a/drivers/staging/media/mx2/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for i.MX27 Camera Sensor driver - -obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o diff --git a/drivers/staging/media/mx2/TODO b/drivers/staging/media/mx2/TODO deleted file mode 100644 index bc68fa4..0000000 --- a/drivers/staging/media/mx2/TODO +++ /dev/null @@ -1,10 +0,0 @@ -This driver is deprecated: it should become a stand-alone driver instead of -using the soc-camera framework. - -Unless someone is willing to take this on (unlikely with such ancient -hardware) it is going to be removed from the kernel soon. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/mx2/mx2_camera.c b/drivers/staging/media/mx2/mx2_camera.c deleted file mode 100644 index 48dd5b7..0000000 --- a/drivers/staging/media/mx2/mx2_camera.c +++ /dev/null @@ -1,1636 +0,0 @@ -/* - * V4L2 Driver for i.MX27 camera host - * - * Copyright (C) 2008, Sascha Hauer, Pengutronix - * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography - * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#define MX2_CAM_DRV_NAME "mx2-camera" -#define MX2_CAM_VERSION "0.0.6" -#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera" - -/* reset values */ -#define CSICR1_RESET_VAL 0x40000800 -#define CSICR2_RESET_VAL 0x0 -#define CSICR3_RESET_VAL 0x0 - -/* csi control reg 1 */ -#define CSICR1_SWAP16_EN (1 << 31) -#define CSICR1_EXT_VSYNC (1 << 30) -#define CSICR1_EOF_INTEN (1 << 29) -#define CSICR1_PRP_IF_EN (1 << 28) -#define CSICR1_CCIR_MODE (1 << 27) -#define CSICR1_COF_INTEN (1 << 26) -#define CSICR1_SF_OR_INTEN (1 << 25) -#define CSICR1_RF_OR_INTEN (1 << 24) -#define CSICR1_STATFF_LEVEL (3 << 22) -#define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) -#define CSICR1_RXFF_INTEN (1 << 18) -#define CSICR1_SOF_POL (1 << 17) -#define CSICR1_SOF_INTEN (1 << 16) -#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12) -#define CSICR1_HSYNC_POL (1 << 11) -#define CSICR1_CCIR_EN (1 << 10) -#define CSICR1_MCLKEN (1 << 9) -#define CSICR1_FCC (1 << 8) -#define CSICR1_PACK_DIR (1 << 7) -#define CSICR1_CLR_STATFIFO (1 << 6) -#define CSICR1_CLR_RXFIFO (1 << 5) -#define CSICR1_GCLK_MODE (1 << 4) -#define CSICR1_INV_DATA (1 << 3) -#define CSICR1_INV_PCLK (1 << 2) -#define CSICR1_REDGE (1 << 1) -#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN) - -#define SHIFT_STATFF_LEVEL 22 -#define SHIFT_RXFF_LEVEL 19 -#define SHIFT_MCLKDIV 12 - -#define SHIFT_FRMCNT 16 - -#define CSICR1 0x00 -#define CSICR2 0x04 -#define CSISR 0x08 -#define CSISTATFIFO 0x0c -#define CSIRFIFO 0x10 -#define CSIRXCNT 0x14 -#define CSICR3 0x1c -#define CSIDMASA_STATFIFO 0x20 -#define CSIDMATA_STATFIFO 0x24 -#define CSIDMASA_FB1 0x28 -#define CSIDMASA_FB2 0x2c -#define CSIFBUF_PARA 0x30 -#define CSIIMAG_PARA 0x34 - -/* EMMA PrP */ -#define PRP_CNTL 0x00 -#define PRP_INTR_CNTL 0x04 -#define PRP_INTRSTATUS 0x08 -#define PRP_SOURCE_Y_PTR 0x0c -#define PRP_SOURCE_CB_PTR 0x10 -#define PRP_SOURCE_CR_PTR 0x14 -#define PRP_DEST_RGB1_PTR 0x18 -#define PRP_DEST_RGB2_PTR 0x1c -#define PRP_DEST_Y_PTR 0x20 -#define PRP_DEST_CB_PTR 0x24 -#define PRP_DEST_CR_PTR 0x28 -#define PRP_SRC_FRAME_SIZE 0x2c -#define PRP_DEST_CH1_LINE_STRIDE 0x30 -#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 -#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 -#define PRP_CH1_OUT_IMAGE_SIZE 0x3c -#define PRP_CH2_OUT_IMAGE_SIZE 0x40 -#define PRP_SRC_LINE_STRIDE 0x44 -#define PRP_CSC_COEF_012 0x48 -#define PRP_CSC_COEF_345 0x4c -#define PRP_CSC_COEF_678 0x50 -#define PRP_CH1_RZ_HORI_COEF1 0x54 -#define PRP_CH1_RZ_HORI_COEF2 0x58 -#define PRP_CH1_RZ_HORI_VALID 0x5c -#define PRP_CH1_RZ_VERT_COEF1 0x60 -#define PRP_CH1_RZ_VERT_COEF2 0x64 -#define PRP_CH1_RZ_VERT_VALID 0x68 -#define PRP_CH2_RZ_HORI_COEF1 0x6c -#define PRP_CH2_RZ_HORI_COEF2 0x70 -#define PRP_CH2_RZ_HORI_VALID 0x74 -#define PRP_CH2_RZ_VERT_COEF1 0x78 -#define PRP_CH2_RZ_VERT_COEF2 0x7c -#define PRP_CH2_RZ_VERT_VALID 0x80 - -#define PRP_CNTL_CH1EN (1 << 0) -#define PRP_CNTL_CH2EN (1 << 1) -#define PRP_CNTL_CSIEN (1 << 2) -#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) -#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) -#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) -#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) -#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) -#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) -#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) -#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) -#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) -#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) -#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) -#define PRP_CNTL_CH1_LEN (1 << 9) -#define PRP_CNTL_CH2_LEN (1 << 10) -#define PRP_CNTL_SKIP_FRAME (1 << 11) -#define PRP_CNTL_SWRST (1 << 12) -#define PRP_CNTL_CLKEN (1 << 13) -#define PRP_CNTL_WEN (1 << 14) -#define PRP_CNTL_CH1BYP (1 << 15) -#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) -#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) -#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) -#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) -#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) -#define PRP_CNTL_CH2B1EN (1 << 29) -#define PRP_CNTL_CH2B2EN (1 << 30) -#define PRP_CNTL_CH2FEN (1 << 31) - -/* IRQ Enable and status register */ -#define PRP_INTR_RDERR (1 << 0) -#define PRP_INTR_CH1WERR (1 << 1) -#define PRP_INTR_CH2WERR (1 << 2) -#define PRP_INTR_CH1FC (1 << 3) -#define PRP_INTR_CH2FC (1 << 5) -#define PRP_INTR_LBOVF (1 << 7) -#define PRP_INTR_CH2OVF (1 << 8) - -/* Resizing registers */ -#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) -#define PRP_RZ_VALID_BILINEAR (1 << 31) - -#define MAX_VIDEO_MEM 16 - -#define RESIZE_NUM_MIN 1 -#define RESIZE_NUM_MAX 20 -#define BC_COEF 3 -#define SZ_COEF (1 << BC_COEF) - -#define RESIZE_DIR_H 0 -#define RESIZE_DIR_V 1 - -#define RESIZE_ALGO_BILINEAR 0 -#define RESIZE_ALGO_AVERAGING 1 - -struct mx2_prp_cfg { - int channel; - u32 in_fmt; - u32 out_fmt; - u32 src_pixel; - u32 ch1_pixel; - u32 irq_flags; - u32 csicr1; -}; - -/* prp resizing parameters */ -struct emma_prp_resize { - int algo; /* type of algorithm used */ - int len; /* number of coefficients */ - unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ -}; - -/* prp configuration for a client-host fmt pair */ -struct mx2_fmt_cfg { - u32 in_fmt; - u32 out_fmt; - struct mx2_prp_cfg cfg; -}; - -struct mx2_buf_internal { - struct list_head queue; - int bufnum; - bool discard; -}; - -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct mx2_buf_internal internal; -}; - -enum mx2_camera_type { - IMX27_CAMERA, -}; - -struct mx2_camera_dev { - struct device *dev; - struct soc_camera_host soc_host; - struct clk *clk_emma_ahb, *clk_emma_ipg; - struct clk *clk_csi_ahb, *clk_csi_per; - - void __iomem *base_csi, *base_emma; - - struct mx2_camera_platform_data *pdata; - unsigned long platform_flags; - - struct list_head capture; - struct list_head active_bufs; - struct list_head discard; - - spinlock_t lock; - - int dma; - struct mx2_buffer *active; - struct mx2_buffer *fb1_active; - struct mx2_buffer *fb2_active; - - u32 csicr1; - enum mx2_camera_type devtype; - - struct mx2_buf_internal buf_discard[2]; - void *discard_buffer; - dma_addr_t discard_buffer_dma; - size_t discard_size; - struct mx2_fmt_cfg *emma_prp; - struct emma_prp_resize resizing[2]; - unsigned int s_width, s_height; - u32 frame_count; - struct vb2_alloc_ctx *alloc_ctx; -}; - -static struct platform_device_id mx2_camera_devtype[] = { - { - .name = "imx27-camera", - .driver_data = IMX27_CAMERA, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); - -static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) -{ - return container_of(int_buf, struct mx2_buffer, internal); -} - -static struct mx2_fmt_cfg mx27_emma_prp_table[] = { - /* - * This is a generic configuration which is valid for most - * prp input-output format combinations. - * We set the incoming and outgoing pixelformat to a - * 16 Bit wide format and adjust the bytesperline - * accordingly. With this configuration the inputdata - * will not be changed by the emma and could be any type - * of 16 Bit Pixelformat. - */ - { - .in_fmt = 0, - .out_fmt = 0, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_RGB16, - .out_fmt = PRP_CNTL_CH1_OUT_RGB16, - .src_pixel = 0x2ca00565, /* RGB565 */ - .ch1_pixel = 0x2ca00565, /* RGB565 */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = 0, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, -}; - -static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt) -{ - int i; - - for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++) - if ((mx27_emma_prp_table[i].in_fmt == in_fmt) && - (mx27_emma_prp_table[i].out_fmt == out_fmt)) { - return &mx27_emma_prp_table[i]; - } - /* If no match return the most generic configuration */ - return &mx27_emma_prp_table[0]; -}; - -static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, - unsigned long phys, int bufnum) -{ - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - u32 imgsize = pcdev->soc_host.icd->user_height * - pcdev->soc_host.icd->user_width; - - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } -} - -static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) -{ - clk_disable_unprepare(pcdev->clk_csi_ahb); - clk_disable_unprepare(pcdev->clk_csi_per); - writel(0, pcdev->base_csi + CSICR1); - writel(0, pcdev->base_emma + PRP_CNTL); -} - -static int mx2_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx2_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver detached from camera %d\n", - icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on mx2 camera sensor interface - */ -static int mx2_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - int ret; - u32 csicr1; - - ret = clk_prepare_enable(pcdev->clk_csi_ahb); - if (ret < 0) - return ret; - - ret = clk_prepare_enable(pcdev->clk_csi_per); - if (ret < 0) - goto exit_csi_ahb; - - csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); - - pcdev->csicr1 = csicr1; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - pcdev->frame_count = 0; - - return 0; - -exit_csi_ahb: - clk_disable_unprepare(pcdev->clk_csi_ahb); - - return ret; -} - -static void mx2_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - - mx2_camera_deactivate(pcdev); -} - -/* - * Videobuf operations - */ -static int mx2_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); - - alloc_ctxs[0] = pcdev->alloc_ctx; - - sizes[0] = icd->sizeimage; - - if (0 == *count) - *count = 32; - if (!*num_planes && - sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - - *num_planes = 1; - - return 0; -} - -static int mx2_videobuf_prepare(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - int ret = 0; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - memset((void *)vb2_plane_vaddr(vb, 0), - 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - vb2_set_plane_payload(vb, 0, icd->sizeimage); - if (vb2_plane_vaddr(vb, 0) && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - ret = -EINVAL; - goto out; - } - - return 0; - -out: - return ret; -} - -static void mx2_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb); - unsigned long flags; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - spin_lock_irqsave(&pcdev->lock, flags); - - list_add_tail(&buf->internal.queue, &pcdev->capture); - - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) -{ - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - writel((pcdev->s_width << 16) | pcdev->s_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - if (prp->cfg.channel == 1) { - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); - } - - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); -} - -static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) -{ - int dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - unsigned char *s = pcdev->resizing[dir].s; - int len = pcdev->resizing[dir].len; - unsigned int coeff[2] = {0, 0}; - unsigned int valid = 0; - int i; - - if (len == 0) - continue; - - for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { - int j; - - j = i > 9 ? 1 : 0; - coeff[j] = (coeff[j] << BC_COEF) | - (s[i] & (SZ_COEF - 1)); - - if (i == 5 || i == 15) - coeff[j] <<= 1; - - valid = (valid << 1) | (s[i] >> BC_COEF); - } - - valid |= PRP_RZ_VALID_TBL_LEN(len); - - if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) - valid |= PRP_RZ_VALID_BILINEAR; - - if (pcdev->emma_prp->cfg.channel == 1) { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_VERT_VALID); - } - } else { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_VERT_VALID); - } - } - } -} - -static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - struct vb2_buffer *vb; - struct mx2_buffer *buf; - unsigned long phys; - int bytesperline; - unsigned long flags; - - if (count < 2) - return -ENOBUFS; - - spin_lock_irqsave(&pcdev->lock, flags); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, - &pcdev->discard_buffer_dma, GFP_ATOMIC); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } - - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); - - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); - - mx2_prp_resize_commit(pcdev); - - mx27_camera_emma_buf_init(icd, bytesperline); - - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } - spin_unlock_irqrestore(&pcdev->lock, flags); - - return 0; -} - -static void mx2_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - unsigned long flags; - void *b; - u32 cntl; - - spin_lock_irqsave(&pcdev->lock, flags); - - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; - - spin_unlock_irqrestore(&pcdev->lock, flags); - - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); -} - -static struct vb2_ops mx2_videobuf_ops = { - .queue_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .start_streaming = mx2_start_streaming, - .stop_streaming = mx2_stop_streaming, -}; - -static int mx2_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx2_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx2_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - return vb2_queue_init(q); -} - -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) - -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) -{ - int count = 0; - - readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } - - return -ETIMEDOUT; -} - -static int mx2_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - int ret; - int bytesperline; - u32 csicr1 = pcdev->csicr1; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, MX2_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = MX2_BUS_FLAGS; - } - - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - csicr1 |= CSICR1_REDGE; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_SOF_POL; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_HSYNC_POL; - if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) - csicr1 |= CSICR1_EXT_VSYNC; - if (pcdev->platform_flags & MX2_CAMERA_CCIR) - csicr1 |= CSICR1_CCIR_EN; - if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) - csicr1 |= CSICR1_CCIR_MODE; - if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) - csicr1 |= CSICR1_GCLK_MODE; - if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) - csicr1 |= CSICR1_INV_DATA; - - pcdev->csicr1 = csicr1; - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) - return bytesperline; - - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - return 0; -} - -static int mx2_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx2_camera_get_formats(struct soc_camera_device *icd, - unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_mbus_pixelfmt *fmt; - struct device *dev = icd->parent; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - int ret, formats = 0; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* no more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); - return 0; - } - - if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 || - code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - /* - * CH2 can output YUV420 which is a standard format in - * soc_mediabus.c - */ - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - /* Generic pass-trough */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - return formats; -} - -static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, - struct v4l2_mbus_framefmt *mf_in, - struct v4l2_pix_format *pix_out, bool apply) -{ - unsigned int num, den; - unsigned long m; - int i, dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - struct emma_prp_resize tmprsz; - unsigned char *s = tmprsz.s; - int len = 0; - int in, out; - - if (dir == RESIZE_DIR_H) { - in = mf_in->width; - out = pix_out->width; - } else { - in = mf_in->height; - out = pix_out->height; - } - - if (in < out) - return -EINVAL; - else if (in == out) - continue; - - /* Calculate ratio */ - m = gcd(in, out); - num = in / m; - den = out / m; - if (num > RESIZE_NUM_MAX) - return -EINVAL; - - if ((num >= 2 * den) && (den == 1) && - (num < 9) && (!(num & 0x01))) { - int sum = 0; - int j; - - /* Average scaling for >= 2:1 ratios */ - /* Support can be added for num >=9 and odd values */ - - tmprsz.algo = RESIZE_ALGO_AVERAGING; - len = num; - - for (i = 0; i < (len / 2); i++) - s[i] = 8; - - do { - for (i = 0; i < (len / 2); i++) { - s[i] = s[i] >> 1; - sum = 0; - for (j = 0; j < (len / 2); j++) - sum += s[j]; - if (sum == 4) - break; - } - } while (sum != 4); - - for (i = (len / 2); i < len; i++) - s[i] = s[len - i - 1]; - - s[len - 1] |= SZ_COEF; - } else { - /* bilinear scaling for < 2:1 ratios */ - int v; /* overflow counter */ - int coeff, nxt; /* table output */ - int in_pos_inc = 2 * den; - int out_pos = num; - int out_pos_inc = 2 * num; - int init_carry = num - den; - int carry = init_carry; - - tmprsz.algo = RESIZE_ALGO_BILINEAR; - v = den + in_pos_inc; - do { - coeff = v - out_pos; - out_pos += out_pos_inc; - carry += out_pos_inc; - for (nxt = 0; v < out_pos; nxt++) { - v += in_pos_inc; - carry -= in_pos_inc; - } - - if (len > RESIZE_NUM_MAX) - return -EINVAL; - - coeff = ((coeff << BC_COEF) + - (in_pos_inc >> 1)) / in_pos_inc; - - if (coeff >= (SZ_COEF - 1)) - coeff--; - - coeff |= SZ_COEF; - s[len] = (unsigned char)coeff; - len++; - - for (i = 1; i < nxt; i++) { - if (len >= RESIZE_NUM_MAX) - return -EINVAL; - s[len] = 0; - len++; - } - } while (carry != init_carry); - } - tmprsz.len = len; - if (dir == RESIZE_DIR_H) - mf_in->width = pix_out->width; - else - mf_in->height = pix_out->height; - - if (apply) - memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); - } - return 0; -} - -static int mx2_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - /* Store width and height returned by the sensor for resizing */ - pcdev->s_width = mf->width; - pcdev->s_height = mf->height; - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); - if ((mf->width != pix->width || mf->height != pix->height) && - pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->code != xlate->code) - return -EINVAL; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *emma_prp; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* - * limit to MX27 hardware capabilities: width must be a multiple of 8 as - * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). - */ - pix->width &= ~0x7; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - /* If the sensor does not support image size try PrP resizing */ - emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - if ((mf->width != pix->width || mf->height != pix->height) && - emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->field == V4L2_FIELD_ANY) - mf->field = V4L2_FIELD_NONE; - /* - * Driver supports interlaced images provided they have - * both fields so that they can be processed as if they - * were progressive. - */ - if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) { - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - return -EINVAL; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static struct soc_camera_host_ops mx2_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx2_camera_add_device, - .remove = mx2_camera_remove_device, - .clock_start = mx2_camera_clock_start, - .clock_stop = mx2_camera_clock_stop, - .set_fmt = mx2_camera_set_fmt, - .set_crop = mx2_camera_set_crop, - .get_formats = mx2_camera_get_formats, - .try_fmt = mx2_camera_try_fmt, - .init_videobuf2 = mx2_camera_init_videobuf, - .poll = mx2_camera_poll, - .querycap = mx2_camera_querycap, - .set_bus_param = mx2_camera_set_bus_param, -}; - -static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, bool err) -{ -#ifdef DEBUG - struct mx2_fmt_cfg *prp = pcdev->emma_prp; -#endif - struct mx2_buf_internal *ibuf; - struct mx2_buffer *buf; - struct vb2_buffer *vb; - struct vb2_v4l2_buffer *vbuf; - unsigned long phys; - - ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, - queue); - - BUG_ON(ibuf->bufnum != bufnum); - - if (ibuf->discard) { - /* - * Discard buffer must not be returned to user space. - * Just return it to the discard queue. - */ - list_move_tail(pcdev->active_bufs.next, &pcdev->discard); - } else { - buf = mx2_ibuf_to_buf(ibuf); - - vb = &buf->vb.vb2_buf; - vbuf = to_vb2_v4l2_buffer(vb); -#ifdef DEBUG - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - if (prp->cfg.channel == 1) { - if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + - 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum)); - } - } else { - if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum)); - } - } -#endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, - vb2_plane_vaddr(vb, 0), - vb2_get_plane_payload(vb, 0)); - - list_del_init(&buf->internal.queue); - vb->timestamp = ktime_get_ns(); - vbuf->sequence = pcdev->frame_count; - if (err) - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - else - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } - - pcdev->frame_count++; - - if (list_empty(&pcdev->capture)) { - if (list_empty(&pcdev->discard)) { - dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", - __func__); - return; - } - - ibuf = list_first_entry(&pcdev->discard, - struct mx2_buf_internal, queue); - ibuf->bufnum = bufnum; - - list_move_tail(pcdev->discard.next, &pcdev->active_bufs); - mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); - return; - } - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - - buf->internal.bufnum = bufnum; - - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, bufnum); -} - -static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) -{ - struct mx2_camera_dev *pcdev = data; - unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buf_internal *ibuf; - - spin_lock(&pcdev->lock); - - if (list_empty(&pcdev->active_bufs)) { - dev_warn(pcdev->dev, "%s: called while active list is empty\n", - __func__); - - if (!status) { - spin_unlock(&pcdev->lock); - return IRQ_NONE; - } - } - - if (status & (1 << 7)) { /* overflow */ - u32 cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), - pcdev->base_emma + PRP_CNTL); - writel(cntl, pcdev->base_emma + PRP_CNTL); - - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, - ibuf->bufnum, true); - - status &= ~(1 << 7); - } else if (((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) { - /* - * Both buffers have triggered, process the one we're expecting - * to first - */ - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); - status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ - } else if ((status & (1 << 6)) || (status & (1 << 4))) { - mx27_camera_frame_done_emma(pcdev, 0, false); - } else if ((status & (1 << 5)) || (status & (1 << 3))) { - mx27_camera_frame_done_emma(pcdev, 1, false); - } - - spin_unlock(&pcdev->lock); - writel(status, pcdev->base_emma + PRP_INTRSTATUS); - - return IRQ_HANDLED; -} - -static int mx27_camera_emma_init(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev); - struct resource *res_emma; - int irq_emma; - int err = 0; - - res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq_emma = platform_get_irq(pdev, 1); - if (!res_emma || !irq_emma) { - dev_err(pcdev->dev, "no EMMA resources\n"); - err = -ENODEV; - goto out; - } - - pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma); - if (IS_ERR(pcdev->base_emma)) { - err = PTR_ERR(pcdev->base_emma); - goto out; - } - - err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n"); - goto out; - } - - pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg"); - if (IS_ERR(pcdev->clk_emma_ipg)) { - err = PTR_ERR(pcdev->clk_emma_ipg); - goto out; - } - - clk_prepare_enable(pcdev->clk_emma_ipg); - - pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb"); - if (IS_ERR(pcdev->clk_emma_ahb)) { - err = PTR_ERR(pcdev->clk_emma_ahb); - goto exit_clk_emma_ipg; - } - - clk_prepare_enable(pcdev->clk_emma_ahb); - - err = mx27_camera_emma_prp_reset(pcdev); - if (err) - goto exit_clk_emma_ahb; - - return err; - -exit_clk_emma_ahb: - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit_clk_emma_ipg: - clk_disable_unprepare(pcdev->clk_emma_ipg); -out: - return err; -} - -static int mx2_camera_probe(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev; - struct resource *res_csi; - int irq_csi; - int err = 0; - - dev_dbg(&pdev->dev, "initialising\n"); - - res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq_csi = platform_get_irq(pdev, 0); - if (res_csi == NULL || irq_csi < 0) { - dev_err(&pdev->dev, "Missing platform resources data\n"); - err = -ENODEV; - goto exit; - } - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; - } - - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(pcdev->clk_csi_ahb)) { - dev_err(&pdev->dev, "Could not get csi ahb clock\n"); - err = PTR_ERR(pcdev->clk_csi_ahb); - goto exit; - } - - pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(pcdev->clk_csi_per)) { - dev_err(&pdev->dev, "Could not get csi per clock\n"); - err = PTR_ERR(pcdev->clk_csi_per); - goto exit; - } - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - long rate; - - pcdev->platform_flags = pcdev->pdata->flags; - - rate = clk_round_rate(pcdev->clk_csi_per, - pcdev->pdata->clk * 2); - if (rate <= 0) { - err = -ENODEV; - goto exit; - } - err = clk_set_rate(pcdev->clk_csi_per, rate); - if (err < 0) - goto exit; - } - - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - spin_lock_init(&pcdev->lock); - - pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi); - if (IS_ERR(pcdev->base_csi)) { - err = PTR_ERR(pcdev->base_csi); - goto exit; - } - - pcdev->dev = &pdev->dev; - platform_set_drvdata(pdev, pcdev); - - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - - /* - * We're done with drvdata here. Clear the pointer so that - * v4l2 core can start using drvdata on its purpose. - */ - platform_set_drvdata(pdev, NULL); - - pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, - pcdev->soc_host.ops = &mx2_soc_camera_host_ops, - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - err = PTR_ERR(pcdev->alloc_ctx); - goto eallocctx; - } - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - goto exit_free_emma; - - dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi_per)); - - return 0; - -exit_free_emma: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); -eallocctx: - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit: - return err; -} - -static int mx2_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx2_camera_dev *pcdev = container_of(soc_host, - struct mx2_camera_dev, soc_host); - - soc_camera_host_unregister(&pcdev->soc_host); - - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - - dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); - - return 0; -} - -static struct platform_driver mx2_camera_driver = { - .driver = { - .name = MX2_CAM_DRV_NAME, - }, - .id_table = mx2_camera_devtype, - .remove = mx2_camera_remove, -}; - -module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe); - -MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); -MODULE_AUTHOR("Sascha Hauer "); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MX2_CAM_VERSION); -- cgit v0.10.2 From c93cc61475ebbe6e66574bd4f55e29db06bb752f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 2 Jun 2016 04:07:49 -0300 Subject: [media] staging/media: remove deprecated mx3 driver Remove this deprecated old driver. Signed-off-by: Hans Verkuil Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index baf6fb3..c8adade 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,8 +27,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/mn88472/Kconfig" -source "drivers/staging/media/mx3/Kconfig" - source "drivers/staging/media/omap1/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 4ba7c6b..508e9ca 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ -obj-$(CONFIG_VIDEO_MX3) += mx3/ obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_DVB_MN88472) += mn88472/ diff --git a/drivers/staging/media/mx3/Kconfig b/drivers/staging/media/mx3/Kconfig deleted file mode 100644 index 595d5fe..0000000 --- a/drivers/staging/media/mx3/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config VIDEO_MX3 - tristate "i.MX3x Camera Sensor Interface driver" - depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA - depends on MX3_IPU || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX3x Camera Sensor Interface - - This driver is deprecated: it should become a stand-alone driver - instead of using the soc-camera framework. - - Unless someone is willing to take this on (unlikely with such - ancient hardware) it is going to be removed from the kernel - soon. diff --git a/drivers/staging/media/mx3/Makefile b/drivers/staging/media/mx3/Makefile deleted file mode 100644 index 6d91dcd..0000000 --- a/drivers/staging/media/mx3/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for i.MX3x Camera Sensor driver - -obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o diff --git a/drivers/staging/media/mx3/TODO b/drivers/staging/media/mx3/TODO deleted file mode 100644 index bc68fa4..0000000 --- a/drivers/staging/media/mx3/TODO +++ /dev/null @@ -1,10 +0,0 @@ -This driver is deprecated: it should become a stand-alone driver instead of -using the soc-camera framework. - -Unless someone is willing to take this on (unlikely with such ancient -hardware) it is going to be removed from the kernel soon. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/mx3/mx3_camera.c b/drivers/staging/media/mx3/mx3_camera.c deleted file mode 100644 index aa39e95..0000000 --- a/drivers/staging/media/mx3/mx3_camera.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* - * V4L2 Driver for i.MX3x camera host - * - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#define MX3_CAM_DRV_NAME "mx3-camera" - -/* CMOS Sensor Interface Registers */ -#define CSI_REG_START 0x60 - -#define CSI_SENS_CONF (0x60 - CSI_REG_START) -#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) -#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) -#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) -#define CSI_TST_CTRL (0x70 - CSI_REG_START) -#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) -#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) -#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) -#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) -#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) - -#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 -#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 -#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 - -#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) - -#define MAX_VIDEO_MEM 16 - -struct mx3_camera_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head queue; - - /* One descriptot per scatterlist (per frame) */ - struct dma_async_tx_descriptor *txd; - - /* We have to "build" a scatterlist ourselves - one element per frame */ - struct scatterlist sg; -}; - -/** - * struct mx3_camera_dev - i.MX3x camera (CSI) object - * @dev: camera device, to which the coherent buffer is attached - * @icd: currently attached camera sensor - * @clk: pointer to clock - * @base: remapped register base address - * @pdata: platform data - * @platform_flags: platform flags - * @mclk: master clock frequency in Hz - * @capture: list of capture videobuffers - * @lock: protects video buffer lists - * @active: active video buffer - * @idmac_channel: array of pointers to IPU DMAC DMA channels - * @soc_host: embedded soc_host object - */ -struct mx3_camera_dev { - /* - * i.MX3x is only supposed to handle one camera on its Camera Sensor - * Interface. If anyone ever builds hardware to enable more than one - * camera _simultaneously_, they will have to modify this driver too - */ - struct clk *clk; - - void __iomem *base; - - struct mx3_camera_pdata *pdata; - - unsigned long platform_flags; - unsigned long mclk; - u16 width_flags; /* max 15 bits */ - - struct list_head capture; - spinlock_t lock; /* Protects video buffer lists */ - struct mx3_camera_buffer *active; - size_t buf_total; - struct vb2_alloc_ctx *alloc_ctx; - enum v4l2_field field; - int sequence; - - /* IDMAC / dmaengine interface */ - struct idmac_channel *idmac_channel[1]; /* We need one channel */ - - struct soc_camera_host soc_host; -}; - -struct dma_chan_request { - struct mx3_camera_dev *mx3_cam; - enum ipu_channel id; -}; - -static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) -{ - return __raw_readl(mx3->base + reg); -} - -static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) -{ - __raw_writel(value, mx3->base + reg); -} - -static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb) -{ - return container_of(vb, struct mx3_camera_buffer, vb); -} - -/* Called from the IPU IDMAC ISR */ -static void mx3_cam_dma_done(void *arg) -{ - struct idmac_tx_desc *desc = to_tx_desc(arg); - struct dma_chan *chan = desc->txd.chan; - struct idmac_channel *ichannel = to_idmac_chan(chan); - struct mx3_camera_dev *mx3_cam = ichannel->client; - - dev_dbg(chan->device->dev, "callback cookie %d, active DMA %pad\n", - desc->txd.cookie, mx3_cam->active ? &sg_dma_address(&mx3_cam->active->sg) : NULL); - - spin_lock(&mx3_cam->lock); - if (mx3_cam->active) { - struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); - - list_del_init(&buf->queue); - vb->vb2_buf.timestamp = ktime_get_ns(); - vb->field = mx3_cam->field; - vb->sequence = mx3_cam->sequence++; - vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); - } - - if (list_empty(&mx3_cam->capture)) { - mx3_cam->active = NULL; - spin_unlock(&mx3_cam->lock); - - /* - * stop capture - without further buffers IPU_CHA_BUF0_RDY will - * not get updated - */ - return; - } - - mx3_cam->active = list_entry(mx3_cam->capture.next, - struct mx3_camera_buffer, queue); - spin_unlock(&mx3_cam->lock); -} - -/* - * Videobuf operations - */ - -/* - * Calculate the __buffer__ (not data) size and number of buffers. - */ -static int mx3_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - - if (!mx3_cam->idmac_channel[0]) - return -EINVAL; - - alloc_ctxs[0] = mx3_cam->alloc_ctx; - - if (!vq->num_buffers) - mx3_cam->sequence = 0; - - if (!*count) - *count = 2; - - /* Called from VIDIOC_REQBUFS or in compatibility mode */ - if (!*num_planes) - sizes[0] = icd->sizeimage; - else if (sizes[0] < icd->sizeimage) - return -EINVAL; - - /* If *num_planes != 0, we have already verified *count. */ - if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) / - sizes[0]; - - *num_planes = 1; - - return 0; -} - -static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) -{ - /* Add more formats as need arises and test possibilities appear... */ - switch (fourcc) { - case V4L2_PIX_FMT_RGB24: - return IPU_PIX_FMT_RGB24; - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_RGB565: - default: - return IPU_PIX_FMT_GENERIC; - } -} - -static void mx3_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct scatterlist *sg = &buf->sg; - struct dma_async_tx_descriptor *txd; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct idmac_video_param *video = &ichan->params.video; - const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; - dma_cookie_t cookie; - size_t new_size; - - new_size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < new_size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", - vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size); - goto error; - } - - if (!buf->txd) { - sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); - sg_dma_len(sg) = new_size; - - txd = dmaengine_prep_slave_sg( - &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!txd) - goto error; - - txd->callback_param = txd; - txd->callback = mx3_cam_dma_done; - - buf->txd = txd; - } else { - txd = buf->txd; - } - - vb2_set_plane_payload(vb, 0, new_size); - - /* This is the configuration of one sg-element */ - video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc); - - if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) { - /* - * If the IPU DMA channel is configured to transfer generic - * 8-bit data, we have to set up the geometry parameters - * correctly, according to the current pixel format. The DMA - * horizontal parameters in this case are expressed in bytes, - * not in pixels. - */ - video->out_width = icd->bytesperline; - video->out_height = icd->user_height; - video->out_stride = icd->bytesperline; - } else { - /* - * For IPU known formats the pixel unit will be managed - * successfully by the IPU code - */ - video->out_width = icd->user_width; - video->out_height = icd->user_height; - video->out_stride = icd->user_width; - } - -#ifdef DEBUG - /* helps to see what DMA actually has written */ - if (vb2_plane_vaddr(vb, 0)) - memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - spin_lock_irq(&mx3_cam->lock); - list_add_tail(&buf->queue, &mx3_cam->capture); - - if (!mx3_cam->active) - mx3_cam->active = buf; - - spin_unlock_irq(&mx3_cam->lock); - - cookie = txd->tx_submit(txd); - dev_dbg(icd->parent, "Submitted cookie %d DMA %pad\n", - cookie, &sg_dma_address(&buf->sg)); - - if (cookie >= 0) - return; - - spin_lock_irq(&mx3_cam->lock); - - /* Submit error */ - list_del_init(&buf->queue); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - spin_unlock_irq(&mx3_cam->lock); -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -static void mx3_videobuf_release(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct dma_async_tx_descriptor *txd = buf->txd; - unsigned long flags; - - dev_dbg(icd->parent, - "Release%s DMA %pad, queue %sempty\n", - mx3_cam->active == buf ? " active" : "", &sg_dma_address(&buf->sg), - list_empty(&buf->queue) ? "" : "not "); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - /* Doesn't hurt also if the list is empty */ - list_del_init(&buf->queue); - - if (txd) { - buf->txd = NULL; - if (mx3_cam->idmac_channel[0]) - async_tx_ack(txd); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); - - mx3_cam->buf_total -= vb2_plane_size(vb, 0); -} - -static int mx3_videobuf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - - if (!buf->txd) { - /* This is for locking debugging only */ - INIT_LIST_HEAD(&buf->queue); - sg_init_table(&buf->sg, 1); - - mx3_cam->buf_total += vb2_plane_size(vb, 0); - } - - return 0; -} - -static void mx3_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct mx3_camera_buffer *buf, *tmp; - unsigned long flags; - - if (ichan) - dmaengine_pause(&ichan->dma_chan); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - mx3_cam->active = NULL; - - list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { - list_del_init(&buf->queue); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); -} - -static struct vb2_ops mx3_videobuf_ops = { - .queue_setup = mx3_videobuf_setup, - .buf_queue = mx3_videobuf_queue, - .buf_cleanup = mx3_videobuf_release, - .buf_init = mx3_videobuf_init, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = mx3_stop_streaming, -}; - -static int mx3_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx3_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx3_camera_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ici->host_lock; - - return vb2_queue_init(q); -} - -/* First part of ipu_csi_init_interface() */ -static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam) -{ - u32 conf; - long rate; - - /* Set default size: ipu_csi_set_window_size() */ - csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); - /* ...and position to 0:0: ipu_csi_set_window_pos() */ - conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); - - /* We use only gated clock synchronisation mode so far */ - conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; - - /* Set generic data, platform-biggest bus-width */ - conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ - conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - - if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) - conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) - conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - - /* ipu_csi_init_interface() */ - csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); - - clk_prepare_enable(mx3_cam->clk); - rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); - if (rate) - clk_set_rate(mx3_cam->clk, rate); -} - -static int mx3_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx3_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", - icd->devnum); -} - -/* Called with .host_lock held */ -static int mx3_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - - mx3_camera_activate(mx3_cam); - - mx3_cam->buf_total = 0; - - return 0; -} - -/* Called with .host_lock held */ -static void mx3_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - - if (*ichan) { - dma_release_channel(&(*ichan)->dma_chan); - *ichan = NULL; - } - - clk_disable_unprepare(mx3_cam->clk); -} - -static int test_platform_param(struct mx3_camera_dev *mx3_cam, - unsigned char buswidth, unsigned long *flags) -{ - /* - * If requested data width is supported by the platform, use it or any - * possible lower value - i.MX31 is smart enough to shift bits - */ - if (buswidth > fls(mx3_cam->width_flags)) - return -EINVAL; - - /* - * Platform specified synchronization and pixel clock polarities are - * only a recommendation and are only used during probing. MX3x - * camera interface only works in master mode, i.e., uses HSYNC and - * VSYNC signals from the sensor - */ - *flags = V4L2_MBUS_MASTER | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW; - - return 0; -} - -static int mx3_camera_try_bus_param(struct soc_camera_device *icd, - const unsigned int depth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long bus_flags, common_flags; - int ret = test_platform_param(mx3_cam, depth, &bus_flags); - - dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } - - return 0; -} - -static bool chan_filter(struct dma_chan *chan, void *arg) -{ - struct dma_chan_request *rq = arg; - struct mx3_camera_pdata *pdata; - - if (!imx_dma_is_ipu(chan)) - return false; - - if (!rq) - return false; - - pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data; - - return rq->id == chan->chan_id && - pdata->dma_dev == chan->device->dev; -} - -static const struct soc_mbus_pixelfmt mx3_camera_formats[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer BGGR (sRGB) 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Monochrome 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}; - -/* This will be corrected as we get more formats */ -static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(icd->parent, - "Unsupported format code #%u: 0x%x\n", idx, code.code); - return 0; - } - - /* This also checks support for the requested bits-per-sample */ - ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[0]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[0].name, code.code); - } - break; - case MEDIA_BUS_FMT_Y10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[1]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[1].name, code.code); - } - break; - default: - if (!mx3_camera_packing_supported(fmt)) - return 0; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n", - (fmt->fourcc >> (0*8)) & 0xFF, - (fmt->fourcc >> (1*8)) & 0xFF, - (fmt->fourcc >> (2*8)) & 0xFF, - (fmt->fourcc >> (3*8)) & 0xFF); - xlate++; - } - - return formats; -} - -static void configure_geometry(struct mx3_camera_dev *mx3_cam, - unsigned int width, unsigned int height, - const struct soc_mbus_pixelfmt *fmt) -{ - u32 ctrl, width_field, height_field; - - if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) { - /* - * As the CSI will be configured to output BAYER, here - * the width parameter count the number of samples to - * capture to complete the whole image width. - */ - unsigned int num, den; - int ret = soc_mbus_samples_per_pixel(fmt, &num, &den); - BUG_ON(ret < 0); - width = width * num / den; - } - - /* Setup frame size - this cannot be changed on-the-fly... */ - width_field = width - 1; - height_field = height - 1; - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); - - csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); - csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); - - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); - - /* ...and position */ - ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - /* Sensor does the cropping */ - csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); -} - -static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - /* We have to use IDMAC_IC_7 for Bayer / generic data */ - struct dma_chan_request rq = {.mx3_cam = mx3_cam, - .id = IDMAC_IC_7}; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - chan = dma_request_channel(mask, chan_filter, &rq); - if (!chan) - return -EBUSY; - - *ichan = to_idmac_chan(chan); - (*ichan)->client = mx3_cam; - - return 0; -} - -/* - * FIXME: learn to use stride != width, then we can keep stride properly aligned - * and support arbitrary (even) widths. - */ -static inline void stride_align(__u32 *width) -{ - if (ALIGN(*width, 8) < 4096) - *width = ALIGN(*width, 8); - else - *width = *width & ~7; -} - -/* - * As long as we don't implement host-side cropping and scaling, we can use - * default g_crop and cropcap from soc_camera.c - */ -static int mx3_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output sizes */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->code != icd->current_fmt->code) - return -EINVAL; - - if (mf->width & 7) { - /* Ouch! We can only handle 8-byte aligned width... */ - stride_align(&mf->width); - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - } - - if (mf->width != icd->user_width || mf->height != icd->user_height) - configure_geometry(mx3_cam, mf->width, mf->height, - icd->current_fmt->host_fmt); - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx3_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - stride_align(&pix->width); - dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height); - - /* - * Might have to perform a complete interface initialisation like in - * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider - * mxc_v4l2_s_fmt() - */ - - configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt); - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0) - return ret; - - if (mf->code != xlate->code) - return -EINVAL; - - if (!mx3_cam->idmac_channel[0]) { - ret = acquire_dma_channel(mx3_cam); - if (ret < 0) - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - mx3_cam->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height); - - return ret; -} - -static int mx3_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* limit to MX3 hardware capabilities */ - if (pix->height > 4096) - pix->height = 4096; - if (pix->width > 4096) - pix->width = 4096; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->colorspace = mf->colorspace; - - switch (mf->field) { - case V4L2_FIELD_ANY: - pix->field = V4L2_FIELD_NONE; - break; - case V4L2_FIELD_NONE: - break; - default: - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - ret = -EINVAL; - } - - return ret; -} - -static int mx3_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - return 0; -} - -static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int mx3_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the firendly caller:-> */ - strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int mx3_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - unsigned long bus_flags, common_flags; - u32 dw, sens_conf; - const struct soc_mbus_pixelfmt *fmt; - int buswidth; - int ret; - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - - fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code); - if (!fmt) - return -EINVAL; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - buswidth = fmt->bits_per_sample; - ret = test_platform_param(mx3_cam, buswidth, &bus_flags); - - dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = bus_flags; - } - - dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n", - cfg.flags, bus_flags, common_flags); - - /* Make choices, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - /* - * So far only gated clock mode is supported. Add a line - * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | - * below and select the required mode when supporting other - * synchronisation protocols. - */ - sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & - ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | - (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | - (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | - (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); - - /* TODO: Support RGB and YUV formats */ - - /* This has been set in mx3_camera_activate(), but we clear it above */ - sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - - /* Just do what we're asked to do */ - switch (xlate->host_fmt->bits_per_sample) { - case 4: - dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 8: - dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 10: - dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - default: - /* - * Actually it can only be 15 now, default is just to silence - * compiler warnings - */ - case 15: - dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - } - - csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - - dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw); - - return 0; -} - -static struct soc_camera_host_ops mx3_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx3_camera_add_device, - .remove = mx3_camera_remove_device, - .clock_start = mx3_camera_clock_start, - .clock_stop = mx3_camera_clock_stop, - .set_crop = mx3_camera_set_crop, - .set_fmt = mx3_camera_set_fmt, - .try_fmt = mx3_camera_try_fmt, - .get_formats = mx3_camera_get_formats, - .init_videobuf2 = mx3_camera_init_videobuf, - .reqbufs = mx3_camera_reqbufs, - .poll = mx3_camera_poll, - .querycap = mx3_camera_querycap, - .set_bus_param = mx3_camera_set_bus_param, -}; - -static int mx3_camera_probe(struct platform_device *pdev) -{ - struct mx3_camera_pdata *pdata = pdev->dev.platform_data; - struct mx3_camera_dev *mx3_cam; - struct resource *res; - void __iomem *base; - int err = 0; - struct soc_camera_host *soc_host; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - if (!pdata) - return -EINVAL; - - mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); - if (!mx3_cam) { - dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); - return -ENOMEM; - } - - mx3_cam->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mx3_cam->clk)) - return PTR_ERR(mx3_cam->clk); - - mx3_cam->pdata = pdata; - mx3_cam->platform_flags = pdata->flags; - if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) { - /* - * Platform hasn't set available data widths. This is bad. - * Warn and use a default. - */ - dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " - "data widths, using default 8 bit\n"); - mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; - } - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4) - mx3_cam->width_flags = 1 << 3; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - mx3_cam->width_flags |= 1 << 7; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - mx3_cam->width_flags |= 1 << 9; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - mx3_cam->width_flags |= 1 << 14; - - mx3_cam->mclk = pdata->mclk_10khz * 10000; - if (!mx3_cam->mclk) { - dev_warn(&pdev->dev, - "mclk_10khz == 0! Please, fix your platform data. " - "Using default 20MHz\n"); - mx3_cam->mclk = 20000000; - } - - /* list of video-buffers */ - INIT_LIST_HEAD(&mx3_cam->capture); - spin_lock_init(&mx3_cam->lock); - - mx3_cam->base = base; - - soc_host = &mx3_cam->soc_host; - soc_host->drv_name = MX3_CAM_DRV_NAME; - soc_host->ops = &mx3_soc_camera_host_ops; - soc_host->priv = mx3_cam; - soc_host->v4l2_dev.dev = &pdev->dev; - soc_host->nr = pdev->id; - - mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(mx3_cam->alloc_ctx)) - return PTR_ERR(mx3_cam->alloc_ctx); - - if (pdata->asd_sizes) { - soc_host->asd = pdata->asd; - soc_host->asd_sizes = pdata->asd_sizes; - } - - err = soc_camera_host_register(soc_host); - if (err) - goto ecamhostreg; - - /* IDMAC interface */ - dmaengine_get(); - - return 0; - -ecamhostreg: - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - return err; -} - -static int mx3_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx3_camera_dev *mx3_cam = container_of(soc_host, - struct mx3_camera_dev, soc_host); - - soc_camera_host_unregister(soc_host); - - /* - * The channel has either not been allocated, - * or should have been released - */ - if (WARN_ON(mx3_cam->idmac_channel[0])) - dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); - - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - - dmaengine_put(); - - return 0; -} - -static struct platform_driver mx3_camera_driver = { - .driver = { - .name = MX3_CAM_DRV_NAME, - }, - .probe = mx3_camera_probe, - .remove = mx3_camera_remove, -}; - -module_platform_driver(mx3_camera_driver); - -MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2.3"); -MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME); -- cgit v0.10.2 From c021127527a7ab54c292c11a58aa30bd48d119de Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 2 Jun 2016 04:09:32 -0300 Subject: [media] staging/media: remove deprecated omap1 driver Remove this deprecated old driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index c8adade..c6878cd 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,8 +27,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/mn88472/Kconfig" -source "drivers/staging/media/omap1/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/timb/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 508e9ca..9c89b3e 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ -obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_DVB_MN88472) += mn88472/ obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/ diff --git a/drivers/staging/media/omap1/Kconfig b/drivers/staging/media/omap1/Kconfig deleted file mode 100644 index 6cfab3a..0000000 --- a/drivers/staging/media/omap1/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config VIDEO_OMAP1 - tristate "OMAP1 Camera Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_OMAP1 - depends on HAS_DMA - select VIDEOBUF_DMA_CONTIG - select VIDEOBUF_DMA_SG - ---help--- - This is a v4l2 driver for the TI OMAP1 camera interface - - This driver is deprecated and will be removed soon unless someone - will start the work to convert this driver to the vb2 framework - and remove the soc-camera dependency. diff --git a/drivers/staging/media/omap1/Makefile b/drivers/staging/media/omap1/Makefile deleted file mode 100644 index 2885622..0000000 --- a/drivers/staging/media/omap1/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Makefile for OMAP1 driver - -obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o diff --git a/drivers/staging/media/omap1/TODO b/drivers/staging/media/omap1/TODO deleted file mode 100644 index 1025f9f..0000000 --- a/drivers/staging/media/omap1/TODO +++ /dev/null @@ -1,8 +0,0 @@ -This driver is deprecated and will be removed soon unless someone will start -the work to convert this driver to the vb2 framework and remove the -soc-camera dependency. - -Note that trivial patches will not be accepted anymore, only a full conversion. - -If you want to convert this driver, please contact the linux-media mailinglist -(see http://linuxtv.org/lists.php). diff --git a/drivers/staging/media/omap1/omap1_camera.c b/drivers/staging/media/omap1/omap1_camera.c deleted file mode 100644 index 54b8dd2..0000000 --- a/drivers/staging/media/omap1/omap1_camera.c +++ /dev/null @@ -1,1702 +0,0 @@ -/* - * V4L2 SoC Camera driver for OMAP1 Camera Interface - * - * Copyright (C) 2010, Janusz Krzysztofik - * - * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host - * Copyright (C) 2008, Paulius Zaleckas - * Copyright (C) 2009, Darius Augulis - * - * Based on PXA SoC camera driver - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski - * - * Hardware specific bits initialy based on former work by Matt Callow - * drivers/media/platform/omap/omap1510cam.c - * Copyright (C) 2006 Matt Callow - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - - -#define DRIVER_NAME "omap1-camera" -#define DRIVER_VERSION "0.0.2" - -#define OMAP_DMA_CAMERA_IF_RX 20 - -/* - * --------------------------------------------------------------------------- - * OMAP1 Camera Interface registers - * --------------------------------------------------------------------------- - */ - -#define REG_CTRLCLOCK 0x00 -#define REG_IT_STATUS 0x04 -#define REG_MODE 0x08 -#define REG_STATUS 0x0C -#define REG_CAMDATA 0x10 -#define REG_GPIO 0x14 -#define REG_PEAK_COUNTER 0x18 - -/* CTRLCLOCK bit shifts */ -#define LCLK_EN BIT(7) -#define DPLL_EN BIT(6) -#define MCLK_EN BIT(5) -#define CAMEXCLK_EN BIT(4) -#define POLCLK BIT(3) -#define FOSCMOD_SHIFT 0 -#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT) -#define FOSCMOD_12MHz 0x0 -#define FOSCMOD_6MHz 0x2 -#define FOSCMOD_9_6MHz 0x4 -#define FOSCMOD_24MHz 0x5 -#define FOSCMOD_8MHz 0x6 - -/* IT_STATUS bit shifts */ -#define DATA_TRANSFER BIT(5) -#define FIFO_FULL BIT(4) -#define H_DOWN BIT(3) -#define H_UP BIT(2) -#define V_DOWN BIT(1) -#define V_UP BIT(0) - -/* MODE bit shifts */ -#define RAZ_FIFO BIT(18) -#define EN_FIFO_FULL BIT(17) -#define EN_NIRQ BIT(16) -#define THRESHOLD_SHIFT 9 -#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT) -#define DMA BIT(8) -#define EN_H_DOWN BIT(7) -#define EN_H_UP BIT(6) -#define EN_V_DOWN BIT(5) -#define EN_V_UP BIT(4) -#define ORDERCAMD BIT(3) - -#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \ - EN_NIRQ | EN_FIFO_FULL) - -/* STATUS bit shifts */ -#define HSTATUS BIT(1) -#define VSTATUS BIT(0) - -/* GPIO bit shifts */ -#define CAM_RST BIT(0) - -/* end of OMAP1 Camera Interface registers */ - - -#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - - -#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1) -#define FIFO_SHIFT __fls(FIFO_SIZE) - -#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4) -#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT) - -#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32 -#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT) - -#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1) -#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT - -#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \ - DMA_FRAME_SHIFT_CONTIG : \ - DMA_FRAME_SHIFT_SG) -#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x)) -#define DMA_SYNC OMAP_DMA_SYNC_FRAME -#define THRESHOLD_LEVEL DMA_FRAME_SIZE - - -#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */ - - -/* - * Structures - */ - -/* buffer for one video frame */ -struct omap1_cam_buf { - struct videobuf_buffer vb; - u32 code; - int inwork; - struct scatterlist *sgbuf; - int sgcount; - int bytes_left; - enum videobuf_state result; -}; - -struct omap1_cam_dev { - struct soc_camera_host soc_host; - struct clk *clk; - - unsigned int irq; - void __iomem *base; - - int dma_ch; - - struct omap1_cam_platform_data *pdata; - struct resource *res; - unsigned long pflags; - unsigned long camexclk; - - struct list_head capture; - - /* lock used to protect videobuf */ - spinlock_t lock; - - /* Pointers to DMA buffers */ - struct omap1_cam_buf *active; - struct omap1_cam_buf *ready; - - enum omap1_cam_vb_mode vb_mode; - int (*mmap_mapper)(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma); - - u32 reg_cache[0]; -}; - - -static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val) -{ - pcdev->reg_cache[reg / sizeof(u32)] = val; - __raw_writel(val, pcdev->base + reg); -} - -static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache) -{ - return !from_cache ? __raw_readl(pcdev->base + reg) : - pcdev->reg_cache[reg / sizeof(u32)]; -} - -#define CAM_READ(pcdev, reg) \ - cam_read(pcdev, REG_##reg, false) -#define CAM_WRITE(pcdev, reg, val) \ - cam_write(pcdev, REG_##reg, val) -#define CAM_READ_CACHE(pcdev, reg) \ - cam_read(pcdev, REG_##reg, true) - -/* - * Videobuf operations - */ -static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - *size = icd->sizeimage; - - if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode)) - *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode); - - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - - dev_dbg(icd->parent, - "%s: count=%d, size=%d\n", __func__, *count, *size); - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - struct videobuf_buffer *vb = &buf->vb; - - BUG_ON(in_interrupt()); - - videobuf_waiton(vq, vb, 0, 0); - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - videobuf_dma_contig_free(vq, vb); - } else { - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(dev, dma); - videobuf_dma_free(dma); - } - - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static int omap1_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - WARN_ON(!list_empty(&vb->queue)); - - BUG_ON(NULL == icd->current_fmt); - - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || vb->field != field || - vb->width != icd->user_width || - vb->height != icd->user_height) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = icd->sizeimage; - - if (vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; - } - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - buf->inwork = 0; - - return 0; -fail: - free_buffer(vq, buf, pcdev->vb_mode); -out: - buf->inwork = 0; - return ret; -} - -static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - dma_addr_t dma_addr; - unsigned int block_size; - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - dma_addr = videobuf_to_dma_contig(&buf->vb); - block_size = buf->vb.size; - } else { - if (WARN_ON(!buf->sgbuf)) { - buf->result = VIDEOBUF_ERROR; - return; - } - dma_addr = sg_dma_address(buf->sgbuf); - if (WARN_ON(!dma_addr)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - block_size = sg_dma_len(buf->sgbuf); - if (WARN_ON(!block_size)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - if (unlikely(buf->bytes_left < block_size)) - block_size = buf->bytes_left; - if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1))) { - dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE); - block_size &= ~(DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1); - } - buf->bytes_left -= block_size; - buf->sgcount++; - } - - omap_set_dma_dest_params(dma_ch, - OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); - omap_set_dma_transfer_params(dma_ch, - OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode), - block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT), - DMA_SYNC, 0, 0); -} - -static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf; - - /* - * If there is already a buffer pointed out by the pcdev->ready, - * (re)use it, otherwise try to fetch and configure a new one. - */ - buf = pcdev->ready; - if (!buf) { - if (list_empty(&pcdev->capture)) - return buf; - buf = list_entry(pcdev->capture.next, - struct omap1_cam_buf, vb.queue); - buf->vb.state = VIDEOBUF_ACTIVE; - pcdev->ready = buf; - list_del_init(&buf->vb.queue); - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we can safely enter next buffer parameters - * into the DMA programming register set after the DMA - * has already been activated on the previous buffer - */ - set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode); - } else { - /* - * In SG mode, the above is not safe since there are probably - * a bunch of sgbufs from previous sglist still pending. - * Instead, mark the sglist fresh for the upcoming - * try_next_sgbuf(). - */ - buf->sgbuf = NULL; - } - - return buf; -} - -static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf) -{ - struct scatterlist *sgbuf; - - if (likely(buf->sgbuf)) { - /* current sglist is active */ - if (unlikely(!buf->bytes_left)) { - /* indicate sglist complete */ - sgbuf = NULL; - } else { - /* process next sgbuf */ - sgbuf = sg_next(buf->sgbuf); - if (WARN_ON(!sgbuf)) { - buf->result = VIDEOBUF_ERROR; - } else if (WARN_ON(!sg_dma_len(sgbuf))) { - sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - } - } - buf->sgbuf = sgbuf; - } else { - /* sglist is fresh, initialize it before using */ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - - sgbuf = dma->sglist; - if (!(WARN_ON(!sgbuf))) { - buf->sgbuf = sgbuf; - buf->sgcount = 0; - buf->bytes_left = buf->vb.size; - buf->result = VIDEOBUF_DONE; - } - } - if (sgbuf) - /* - * Put our next sgbuf parameters (address, size) - * into the DMA programming register set. - */ - set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG); - - return sgbuf; -} - -static void start_capture(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf = pcdev->active; - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN; - - if (WARN_ON(!buf)) - return; - - /* - * Enable start of frame interrupt, which we will use for activating - * our end of frame watchdog when capture actually starts. - */ - mode |= EN_V_UP; - - if (unlikely(ctrlclock & LCLK_EN)) - /* stop pixel clock before FIFO reset */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - /* reset FIFO */ - CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO); - - omap_start_dma(pcdev->dma_ch); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, it's a good moment for fetching next sgbuf - * from the current sglist and, if available, already putting - * its parameters into the DMA programming register set. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - /* (re)enable pixel clock */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN); - /* release FIFO reset */ - CAM_WRITE(pcdev, MODE, mode); -} - -static void suspend_capture(struct omap1_cam_dev *pcdev) -{ - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - omap_stop_dma(pcdev->dma_ch); -} - -static void disable_capture(struct omap1_cam_dev *pcdev) -{ - u32 mode = CAM_READ_CACHE(pcdev, MODE); - - CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA)); -} - -static void omap1_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - struct omap1_cam_buf *buf; - u32 mode; - - list_add_tail(&vb->queue, &pcdev->capture); - vb->state = VIDEOBUF_QUEUED; - - if (pcdev->active) { - /* - * Capture in progress, so don't touch pcdev->ready even if - * empty. Since the transfer of the DMA programming register set - * content to the DMA working register set is done automatically - * by the DMA hardware, this can pretty well happen while we - * are keeping the lock here. Leave fetching it from the queue - * to be done when a next DMA interrupt occures instead. - */ - return; - } - - WARN_ON(pcdev->ready); - - buf = prepare_next_vb(pcdev); - if (WARN_ON(!buf)) - return; - - pcdev->active = buf; - pcdev->ready = NULL; - - dev_dbg(icd->parent, - "%s: capture not active, setup FIFO, start DMA\n", __func__); - mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK; - mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT; - CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, the above prepare_next_vb() didn't actually - * put anything into the DMA programming register set, - * so we have to do it now, before activating DMA. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - start_capture(pcdev); -} - -static void omap1_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct omap1_cam_buf *buf = - container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - - switch (vb->state) { - case VIDEOBUF_DONE: - dev_dbg(dev, "%s (done)\n", __func__); - break; - case VIDEOBUF_ACTIVE: - dev_dbg(dev, "%s (active)\n", __func__); - break; - case VIDEOBUF_QUEUED: - dev_dbg(dev, "%s (queued)\n", __func__); - break; - case VIDEOBUF_PREPARED: - dev_dbg(dev, "%s (prepared)\n", __func__); - break; - default: - dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state); - break; - } - - free_buffer(vq, buf, pcdev->vb_mode); -} - -static void videobuf_done(struct omap1_cam_dev *pcdev, - enum videobuf_state result) -{ - struct omap1_cam_buf *buf = pcdev->active; - struct videobuf_buffer *vb; - struct device *dev = pcdev->soc_host.icd->parent; - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - return; - } - - if (result == VIDEOBUF_ERROR) - suspend_capture(pcdev); - - vb = &buf->vb; - if (waitqueue_active(&vb->done)) { - if (!pcdev->ready && result != VIDEOBUF_ERROR) { - /* - * No next buffer has been entered into the DMA - * programming register set on time (could be done only - * while the previous DMA interurpt was processed, not - * later), so the last DMA block, be it a whole buffer - * if in CONTIG or its last sgbuf if in SG mode, is - * about to be reused by the just autoreinitialized DMA - * engine, and overwritten with next frame data. Best we - * can do is stopping the capture as soon as possible, - * hopefully before the next frame start. - */ - suspend_capture(pcdev); - } - vb->state = result; - v4l2_get_timestamp(&vb->ts); - if (result != VIDEOBUF_ERROR) - vb->field_count++; - wake_up(&vb->done); - - /* shift in next buffer */ - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - - if (!buf) { - /* - * No next buffer was ready on time (see above), so - * indicate error condition to force capture restart or - * stop, depending on next buffer already queued or not. - */ - result = VIDEOBUF_ERROR; - prepare_next_vb(pcdev); - - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - } - } else if (pcdev->ready) { - /* - * In both CONTIG and SG mode, the DMA engine has possibly - * been already autoreinitialized with the preprogrammed - * pcdev->ready buffer. We can either accept this fact - * and just swap the buffers, or provoke an error condition - * and restart capture. The former seems less intrusive. - */ - dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n", - __func__); - pcdev->active = pcdev->ready; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, we have to make sure that the buffer we - * are putting back into the pcdev->ready is marked - * fresh. - */ - buf->sgbuf = NULL; - } - pcdev->ready = buf; - - buf = pcdev->active; - } else { - /* - * No next buffer has been entered into - * the DMA programming register set on time. - */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the DMA engine has already been - * reinitialized with the current buffer. Best we can do - * is not touching it. - */ - dev_dbg(dev, - "%s: nobody waiting on videobuf, reuse it\n", - __func__); - } else { - /* - * In SG mode, the DMA engine has just been - * autoreinitialized with the last sgbuf from the - * current list. Restart capture in order to transfer - * next frame start into the first sgbuf, not the last - * one. - */ - if (result != VIDEOBUF_ERROR) { - suspend_capture(pcdev); - result = VIDEOBUF_ERROR; - } - } - } - - if (!buf) { - dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__); - disable_capture(pcdev); - return; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the current buffer parameters had already - * been entered into the DMA programming register set while the - * buffer was fetched with prepare_next_vb(), they may have also - * been transferred into the runtime set and already active if - * the DMA still running. - */ - } else { - /* In SG mode, extra steps are required */ - if (result == VIDEOBUF_ERROR) - /* make sure we (re)use sglist from start on error */ - buf->sgbuf = NULL; - - /* - * In any case, enter the next sgbuf parameters into the DMA - * programming register set. They will be used either during - * nearest DMA autoreinitialization or, in case of an error, - * on DMA startup below. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - if (result == VIDEOBUF_ERROR) { - dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n", - __func__); - start_capture(pcdev); - /* - * In SG mode, the above also resulted in the next sgbuf - * parameters being entered into the DMA programming register - * set, making them ready for next DMA autoreinitialization. - */ - } - - /* - * Finally, try fetching next buffer. - * In CONTIG mode, it will also enter it into the DMA programming - * register set, making it ready for next DMA autoreinitialization. - */ - prepare_next_vb(pcdev); -} - -static void dma_isr(int channel, unsigned short status, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct omap1_cam_buf *buf = pcdev->active; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, assume we have just managed to collect the - * whole frame, hopefully before our end of frame watchdog is - * triggered. Then, all we have to do is disabling the watchdog - * for this frame, and calling videobuf_done() with success - * indicated. - */ - CAM_WRITE(pcdev, MODE, - CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN); - videobuf_done(pcdev, VIDEOBUF_DONE); - } else { - /* - * In SG mode, we have to process every sgbuf from the current - * sglist, one after another. - */ - if (buf->sgbuf) { - /* - * Current sglist not completed yet, try fetching next - * sgbuf, hopefully putting it into the DMA programming - * register set, making it ready for next DMA - * autoreinitialization. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - if (buf->sgbuf) - goto out; - - /* - * No more sgbufs left in the current sglist. This - * doesn't mean that the whole videobuffer is already - * complete, but only that the last sgbuf from the - * current sglist is about to be filled. It will be - * ready on next DMA interrupt, signalled with the - * buf->sgbuf set back to NULL. - */ - if (buf->result != VIDEOBUF_ERROR) { - /* - * Video frame collected without errors so far, - * we can prepare for collecting a next one - * as soon as DMA gets autoreinitialized - * after the current (last) sgbuf is completed. - */ - buf = prepare_next_vb(pcdev); - if (!buf) - goto out; - - try_next_sgbuf(pcdev->dma_ch, buf); - goto out; - } - } - /* end of videobuf */ - videobuf_done(pcdev, buf->result); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t cam_isr(int irq, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct device *dev = pcdev->soc_host.icd->parent; - struct omap1_cam_buf *buf = pcdev->active; - u32 it_status; - unsigned long flags; - - it_status = CAM_READ(pcdev, IT_STATUS); - if (!it_status) - return IRQ_NONE; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (unlikely(it_status & FIFO_FULL)) { - dev_warn(dev, "%s: FIFO overflow\n", __func__); - - } else if (it_status & V_DOWN) { - /* end of video frame watchdog */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the watchdog is disabled with - * successful DMA end of block interrupt, and reenabled - * on next frame start. If we get here, there is nothing - * to check, we must be out of sync. - */ - } else { - if (buf->sgcount == 2) { - /* - * If exactly 2 sgbufs from the next sglist have - * been programmed into the DMA engine (the - * first one already transferred into the DMA - * runtime register set, the second one still - * in the programming set), then we are in sync. - */ - goto out; - } - } - dev_notice(dev, "%s: unexpected end of video frame\n", - __func__); - - } else if (it_status & V_UP) { - u32 mode; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we need this interrupt every frame - * in oredr to reenable our end of frame watchdog. - */ - mode = CAM_READ_CACHE(pcdev, MODE); - } else { - /* - * In SG mode, the below enabled end of frame watchdog - * is kept on permanently, so we can turn this one shot - * setup off. - */ - mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP; - } - - if (!(mode & EN_V_DOWN)) { - /* (re)enable end of frame watchdog interrupt */ - mode |= EN_V_DOWN; - } - CAM_WRITE(pcdev, MODE, mode); - goto out; - - } else { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - goto out; - } - - videobuf_done(pcdev, VIDEOBUF_ERROR); -out: - spin_unlock_irqrestore(&pcdev->lock, flags); - return IRQ_HANDLED; -} - -static struct videobuf_queue_ops omap1_videobuf_ops = { - .buf_setup = omap1_videobuf_setup, - .buf_prepare = omap1_videobuf_prepare, - .buf_queue = omap1_videobuf_queue, - .buf_release = omap1_videobuf_release, -}; - - -/* - * SOC Camera host operations - */ - -static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset) -{ - /* apply/release camera sensor reset if requested by platform data */ - if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH) - CAM_WRITE(pcdev, GPIO, reset); - else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW) - CAM_WRITE(pcdev, GPIO, !reset); -} - -static int omap1_cam_add_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void omap1_cam_remove_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, - "OMAP1 Camera driver detached from camera %d\n", icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on OMAP1 camera sensor interface - */ -static int omap1_cam_clock_start(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - clk_enable(pcdev->clk); - - /* setup sensor clock */ - ctrlclock = CAM_READ(pcdev, CTRLCLOCK); - ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock &= ~FOSCMOD_MASK; - switch (pcdev->camexclk) { - case 6000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz; - break; - case 8000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN; - break; - case 9600000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN; - break; - case 12000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz; - break; - case 24000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN; - default: - break; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN); - - /* enable internal clock */ - ctrlclock |= MCLK_EN; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - sensor_reset(pcdev, false); - - return 0; -} - -static void omap1_cam_clock_stop(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - suspend_capture(pcdev); - disable_capture(pcdev); - - sensor_reset(pcdev, true); - - /* disable and release system clocks */ - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); - - clk_disable(pcdev->clk); -} - -/* Duplicate standard formats based on host capability of byte swapping */ -static const struct soc_mbus_lookup omap1_cam_formats[] = { -{ - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -static int omap1_cam_get_formats(struct soc_camera_device *icd, - unsigned int idx, struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__, - idx, code.code); - return 0; - } - - /* Check support for the requested bits-per-sample */ - if (fmt->bits_per_sample != 8) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_BE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - formats++; - if (xlate) { - xlate->host_fmt = soc_mbus_find_fmtdesc(code.code, - omap1_cam_formats, - ARRAY_SIZE(omap1_cam_formats)); - xlate->code = code.code; - xlate++; - dev_dbg(dev, - "%s: providing format %s as byte swapped code #%d\n", - __func__, xlate->host_fmt->name, code.code); - } - default: - if (xlate) - dev_dbg(dev, - "%s: providing format %s in pass-through mode\n", - __func__, fmt->name); - } - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - - return formats; -} - -static bool is_dma_aligned(s32 bytes_per_line, unsigned int height, - enum omap1_cam_vb_mode vb_mode) -{ - int size = bytes_per_line * height; - - return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) && - IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE); -} - -static int dma_align(int *width, int *height, - const struct soc_mbus_pixelfmt *fmt, - enum omap1_cam_vb_mode vb_mode, bool enlarge) -{ - s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - - if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) { - unsigned int pxalign = __fls(bytes_per_line / *width); - unsigned int salign = DMA_FRAME_SHIFT(vb_mode) + - DMA_ELEMENT_SHIFT - pxalign; - unsigned int incr = enlarge << salign; - - v4l_bound_align_image(width, 1, *width + incr, 0, - height, 1, *height + incr, 0, salign); - return 0; - } - return 1; -} - -#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \ -({ \ - struct soc_camera_sense sense = { \ - .master_clock = pcdev->camexclk, \ - .pixel_clock_max = 0, \ - }; \ - int __ret; \ - \ - if (pcdev->pdata) \ - sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ - icd->sense = &sense; \ - __ret = v4l2_subdev_call(sd, op, function, ##args); \ - icd->sense = NULL; \ - \ - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ - if (sense.pixel_clock > sense.pixel_clock_max) { \ - dev_err(dev, \ - "%s: pixel clock %lu set by the camera too high!\n", \ - __func__, sense.pixel_clock); \ - __ret = -EINVAL; \ - } \ - } \ - __ret; \ -}) - -static int set_format(struct omap1_cam_dev *pcdev, struct device *dev, - struct soc_camera_device *icd, struct v4l2_subdev *sd, - struct v4l2_subdev_format *format, - const struct soc_camera_format_xlate *xlate) -{ - s32 bytes_per_line; - struct v4l2_mbus_framefmt *mf = &format->format; - int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format); - - if (ret < 0) { - dev_err(dev, "%s: set_fmt failed\n", __func__); - return ret; - } - - if (mf->code != xlate->code) { - dev_err(dev, "%s: unexpected pixel code change\n", __func__); - return -EINVAL; - } - - bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt); - if (bytes_per_line < 0) { - dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n", - __func__); - return bytes_per_line; - } - - if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) { - dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n", - __func__, mf->width, mf->height); - return -EINVAL; - } - return 0; -} - -static int omap1_cam_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *crop) -{ - const struct v4l2_rect *rect = &crop->c; - const struct soc_camera_format_xlate *xlate = icd->current_fmt; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop); - if (ret < 0) { - dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__, - rect->width, rect->height, rect->left, rect->top); - return ret; - } - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) { - dev_warn(dev, "%s: failed to fetch current format\n", __func__); - return ret; - } - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - false); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, mf->width, mf->height, - xlate->host_fmt->name); - return ret; - } - - if (!ret) { - /* sensor returned geometry not DMA aligned, trying to fix */ - ret = set_format(pcdev, dev, icd, sd, &fmt, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - } - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return 0; -} - -static int omap1_cam_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(dev, "%s: format %#x not found\n", __func__, - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - true); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, pix->width, pix->height, - xlate->host_fmt->name); - return ret; - } - - ret = set_format(pcdev, dev, icd, sd, &format, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - return 0; -} - -static int omap1_cam_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - /* TODO: limit to mx1 hardware capabilities */ - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %#x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - /* limit to sensor capabilities */ - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - return 0; -} - -static bool sg_mode; - -/* - * Local mmap_mapper wrapper, - * used for detecting videobuf-dma-contig buffer allocation failures - * and switching to videobuf-dma-sg automatically for future attempts. - */ -static int omap1_cam_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = q->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - ret = pcdev->mmap_mapper(q, buf, vma); - - if (ret == -ENOMEM) - sg_mode = true; - - return ret; -} - -static void omap1_cam_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - if (!sg_mode) - videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - else - videobuf_queue_sg_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - - /* use videobuf mode (auto)selected with the module parameter */ - pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; - - /* - * Ensure we substitute the videobuf-dma-contig version of the - * mmap_mapper() callback with our own wrapper, used for switching - * automatically to videobuf-dma-sg on buffer allocation failure. - */ - if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) { - pcdev->mmap_mapper = q->int_ops->mmap_mapper; - q->int_ops->mmap_mapper = omap1_cam_mmap_mapper; - } -} - -static int omap1_cam_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i], - struct omap1_cam_buf, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -static int omap1_cam_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int omap1_cam_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - const struct soc_camera_format_xlate *xlate; - const struct soc_mbus_pixelfmt *fmt; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - u32 ctrlclock, mode; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS); - if (!common_flags) { - dev_warn(dev, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, SOCAM_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = SOCAM_BUS_FLAGS; - } - - /* Make choices, possibly based on platform configuration */ - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (!pcdev->pdata || - pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) { - dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); - ctrlclock |= POLCLK; - } else { - dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n"); - ctrlclock &= ~POLCLK; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - /* select bus endianness */ - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - fmt = xlate->host_fmt; - - mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA); - if (fmt->order == SOC_MBUS_ORDER_LE) { - dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD); - } else { - dev_dbg(dev, "MODE_REG |= ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode | ORDERCAMD); - } - - return 0; -} - -static unsigned int omap1_cam_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct omap1_cam_buf *buf; - - buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf, - vb.stream); - - poll_wait(file, &buf->vb.done, pt); - - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - - return 0; -} - -static struct soc_camera_host_ops omap1_host_ops = { - .owner = THIS_MODULE, - .add = omap1_cam_add_device, - .remove = omap1_cam_remove_device, - .clock_start = omap1_cam_clock_start, - .clock_stop = omap1_cam_clock_stop, - .get_formats = omap1_cam_get_formats, - .set_crop = omap1_cam_set_crop, - .set_fmt = omap1_cam_set_fmt, - .try_fmt = omap1_cam_try_fmt, - .init_videobuf = omap1_cam_init_videobuf, - .reqbufs = omap1_cam_reqbufs, - .querycap = omap1_cam_querycap, - .set_bus_param = omap1_cam_set_bus_param, - .poll = omap1_cam_poll, -}; - -static int omap1_cam_probe(struct platform_device *pdev) -{ - struct omap1_cam_dev *pcdev; - struct resource *res; - struct clk *clk; - void __iomem *base; - unsigned int irq; - int err = 0; - - irq = platform_get_irq(pdev, 0); - if ((int)irq <= 0) { - err = -ENODEV; - goto exit; - } - - clk = devm_clk_get(&pdev->dev, "armper_ck"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev) + resource_size(res), - GFP_KERNEL); - if (!pcdev) - return -ENOMEM; - - pcdev->clk = clk; - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - pcdev->pflags = pcdev->pdata->flags; - pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000; - } - - switch (pcdev->camexclk) { - case 6000000: - case 8000000: - case 9600000: - case 12000000: - case 24000000: - break; - default: - /* pcdev->camexclk != 0 => pcdev->pdata != NULL */ - dev_warn(&pdev->dev, - "Incorrect sensor clock frequency %ld kHz, " - "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, " - "please correct your platform data\n", - pcdev->pdata->camexclk_khz); - pcdev->camexclk = 0; - case 0: - dev_info(&pdev->dev, "Not providing sensor clock\n"); - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - pcdev->irq = irq; - pcdev->base = base; - - sensor_reset(pcdev, true); - - err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME, - dma_isr, (void *)pcdev, &pcdev->dma_ch); - if (err < 0) { - dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n"); - return -EBUSY; - } - dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch); - - /* preconfigure DMA */ - omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA, - 0, 0); - omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4); - /* setup DMA autoinitialization */ - omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch); - - err = devm_request_irq(&pdev->dev, pcdev->irq, cam_isr, 0, DRIVER_NAME, - pcdev); - if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_free_dma; - } - - pcdev->soc_host.drv_name = DRIVER_NAME; - pcdev->soc_host.ops = &omap1_host_ops; - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - return err; - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n"); - - return 0; - -exit_free_dma: - omap_free_dma(pcdev->dma_ch); -exit: - return err; -} - -static int omap1_cam_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct omap1_cam_dev *pcdev = container_of(soc_host, - struct omap1_cam_dev, soc_host); - - omap_free_dma(pcdev->dma_ch); - - soc_camera_host_unregister(soc_host); - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n"); - - return 0; -} - -static struct platform_driver omap1_cam_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = omap1_cam_probe, - .remove = omap1_cam_remove, -}; - -module_platform_driver(omap1_cam_driver); - -module_param(sg_mode, bool, 0644); -MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg"); - -MODULE_DESCRIPTION("OMAP1 Camera Interface driver"); -MODULE_AUTHOR("Janusz Krzysztofik "); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v0.10.2 From 9c2ccd3c645806a1280c4f66705fb6dd55168cd6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 2 Jun 2016 04:10:58 -0300 Subject: [media] staging/media: remove deprecated timb driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove this deprecated old driver. Signed-off-by: Hans Verkuil Cc: Richard Röjfors Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index c6878cd..f1db900 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -29,8 +29,6 @@ source "drivers/staging/media/mn88472/Kconfig" 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 diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 9c89b3e..61c3973 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,5 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ 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/timb/Kconfig b/drivers/staging/media/timb/Kconfig deleted file mode 100644 index e413fec..0000000 --- a/drivers/staging/media/timb/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -config VIDEO_TIMBERDALE - tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST - select VIDEO_ADV7180 - select VIDEOBUF_DMA_CONTIG - ---help--- - Add support for the Video In peripherial of the timberdale FPGA. - - This driver is deprecated and will be removed soon unless someone - will start the work to convert this driver to the vb2 framework. diff --git a/drivers/staging/media/timb/Makefile b/drivers/staging/media/timb/Makefile deleted file mode 100644 index 4c989c2..0000000 --- a/drivers/staging/media/timb/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o diff --git a/drivers/staging/media/timb/timblogiw.c b/drivers/staging/media/timb/timblogiw.c deleted file mode 100644 index 113c9f3..0000000 --- a/drivers/staging/media/timb/timblogiw.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * timblogiw.c timberdale FPGA LogiWin Video In driver - * Copyright (c) 2009-2010 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA LogiWin Video In - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "timb-video" - -#define TIMBLOGIWIN_NAME "Timberdale Video-In" -#define TIMBLOGIW_VERSION_CODE 0x04 - -#define TIMBLOGIW_LINES_PER_DESC 44 -#define TIMBLOGIW_MAX_VIDEO_MEM 16 - -#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name) - - -struct timblogiw { - struct video_device video_dev; - struct v4l2_device v4l2_dev; /* mutual exclusion */ - struct mutex lock; - struct device *dev; - struct timb_video_platform_data pdata; - struct v4l2_subdev *sd_enc; /* encoder */ - bool opened; -}; - -struct timblogiw_tvnorm { - v4l2_std_id std; - u16 width; - u16 height; - u8 fps; -}; - -struct timblogiw_fh { - struct videobuf_queue vb_vidq; - struct timblogiw_tvnorm const *cur_norm; - struct list_head capture; - struct dma_chan *chan; - spinlock_t queue_lock; /* mutual exclusion */ - unsigned int frame_count; -}; - -struct timblogiw_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - struct scatterlist sg[16]; - dma_cookie_t cookie; - struct timblogiw_fh *fh; -}; - -static const struct timblogiw_tvnorm timblogiw_tvnorms[] = { - { - .std = V4L2_STD_PAL, - .width = 720, - .height = 576, - .fps = 25 - }, - { - .std = V4L2_STD_NTSC, - .width = 720, - .height = 480, - .fps = 30 - } -}; - -static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm) -{ - return norm->width * 2; -} - - -static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm) -{ - return norm->height * timblogiw_bytes_per_line(norm); -} - -static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std) -{ - int i; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - if (timblogiw_tvnorms[i].std & std) - return timblogiw_tvnorms + i; - - /* default to first element */ - return timblogiw_tvnorms; -} - -static void timblogiw_dma_cb(void *data) -{ - struct timblogiw_buffer *buf = data; - struct timblogiw_fh *fh = buf->fh; - struct videobuf_buffer *vb = &buf->vb; - - spin_lock(&fh->queue_lock); - - /* mark the transfer done */ - buf->cookie = -1; - - fh->frame_count++; - - if (vb->state != VIDEOBUF_ERROR) { - list_del(&vb->queue); - v4l2_get_timestamp(&vb->ts); - vb->field_count = fh->frame_count * 2; - vb->state = VIDEOBUF_DONE; - - wake_up(&vb->done); - } - - if (!list_empty(&fh->capture)) { - vb = list_entry(fh->capture.next, struct videobuf_buffer, - queue); - vb->state = VIDEOBUF_ACTIVE; - } - - spin_unlock(&fh->queue_lock); -} - -static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param) -{ - return chan->chan_id == (uintptr_t)filter_param; -} - -/* IOCTL functions */ - -static int timblogiw_g_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - mutex_lock(&lw->lock); - - format->fmt.pix.width = fh->cur_norm->width; - format->fmt.pix.height = fh->cur_norm->height; - format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm); - format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm); - format->fmt.pix.field = V4L2_FIELD_NONE; - - mutex_unlock(&lw->lock); - - return 0; -} - -static int timblogiw_try_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_pix_format *pix = &format->fmt.pix; - - dev_dbg(&vdev->dev, - "%s - width=%d, height=%d, pixelformat=%d, field=%d\n" - "bytes per line %d, size image: %d, colorspace: %d\n", - __func__, - pix->width, pix->height, pix->pixelformat, pix->field, - pix->bytesperline, pix->sizeimage, pix->colorspace); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - return 0; -} - -static int timblogiw_s_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - struct v4l2_pix_format *pix = &format->fmt.pix; - int err; - - mutex_lock(&lw->lock); - - err = timblogiw_try_fmt(file, priv, format); - if (err) - goto out; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dev_err(&vdev->dev, "%s queue busy\n", __func__); - err = -EBUSY; - goto out; - } - - pix->width = fh->cur_norm->width; - pix->height = fh->cur_norm->height; - -out: - mutex_unlock(&lw->lock); - return err; -} - -static int timblogiw_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1); - strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name); - 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 timblogiw_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index); - - if (fmt->index != 0) - return -EINVAL; - memset(fmt, 0, sizeof(*fmt)); - fmt->index = 0; - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strncpy(fmt->description, "4:2:2, packed, YUYV", - sizeof(fmt->description)-1); - fmt->pixelformat = V4L2_PIX_FMT_UYVY; - - return 0; -} - -static int timblogiw_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct timblogiw_fh *fh = priv; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = fh->cur_norm->fps; - - return 0; -} - -static int timblogiw_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_reqbufs(&fh->vb_vidq, rb); -} - -static int timblogiw_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_querybuf(&fh->vb_vidq, b); -} - -static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_qbuf(&fh->vb_vidq, b); -} - -static int timblogiw_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); -} - -static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - *std = fh->cur_norm->std; - return 0; -} - -static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - - if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, video, s_std, std); - - if (!err) - fh->cur_norm = timblogiw_get_norm(std); - - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_enuminput(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct video_device *vdev = video_devdata(file); - int i; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (inp->index != 0) - return -EINVAL; - - inp->index = 0; - - strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); - inp->type = V4L2_INPUT_TYPE_CAMERA; - - inp->std = 0; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - inp->std |= timblogiw_tvnorms[i].std; - - return 0; -} - -static int timblogiw_g_input(struct file *file, void *priv, - unsigned int *input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - *input = 0; - - return 0; -} - -static int timblogiw_s_input(struct file *file, void *priv, unsigned int input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (input != 0) - return -EINVAL; - return 0; -} - -static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(&vdev->dev, "%s - No capture device\n", __func__); - return -EINVAL; - } - - fh->frame_count = 0; - return videobuf_streamon(&fh->vb_vidq); -} - -static int timblogiw_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vb_vidq); -} - -static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (TIMBLOGIW_HAS_DECODER(lw)) - return v4l2_subdev_call(lw->sd_enc, video, querystd, std); - else { - *std = fh->cur_norm->std; - return 0; - } -} - -static int timblogiw_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__, - fsize->index, fsize->pixel_format); - - if ((fsize->index != 0) || - (fsize->pixel_format != V4L2_PIX_FMT_UYVY)) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fh->cur_norm->width; - fsize->discrete.height = fh->cur_norm->height; - - return 0; -} - -/* Video buffer functions */ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct timblogiw_fh *fh = vq->priv_data; - - *size = timblogiw_frame_size(fh->cur_norm); - - if (!*count) - *count = 32; - - while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024) - (*count)--; - - return 0; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - unsigned int data_size = timblogiw_frame_size(fh->cur_norm); - int err = 0; - - if (vb->baddr && vb->bsize < data_size) - /* User provided buffer, but it is too small */ - return -ENOMEM; - - vb->size = data_size; - vb->width = fh->cur_norm->width; - vb->height = fh->cur_norm->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int i; - unsigned int size; - unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - dma_addr_t addr; - - sg_init_table(buf->sg, ARRAY_SIZE(buf->sg)); - - err = videobuf_iolock(vq, vb, NULL); - if (err) - goto err; - - addr = videobuf_to_dma_contig(vb); - for (i = 0, size = 0; size < data_size; i++) { - sg_dma_address(buf->sg + i) = addr + size; - size += bytes_per_desc; - sg_dma_len(buf->sg + i) = (size > data_size) ? - (bytes_per_desc - (size - data_size)) : - bytes_per_desc; - } - - vb->state = VIDEOBUF_PREPARED; - buf->cookie = -1; - buf->fh = fh; - } - - return 0; - -err: - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; - return err; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - struct dma_async_tx_descriptor *desc; - int sg_elems; - int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - - sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc; - sg_elems += - (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0; - - if (list_empty(&fh->capture)) - vb->state = VIDEOBUF_ACTIVE; - else - vb->state = VIDEOBUF_QUEUED; - - list_add_tail(&vb->queue, &fh->capture); - - spin_unlock_irq(&fh->queue_lock); - - desc = dmaengine_prep_slave_sg(fh->chan, - buf->sg, sg_elems, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!desc) { - spin_lock_irq(&fh->queue_lock); - list_del_init(&vb->queue); - vb->state = VIDEOBUF_PREPARED; - return; - } - - desc->callback_param = buf; - desc->callback = timblogiw_dma_cb; - - buf->cookie = desc->tx_submit(desc); - - spin_lock_irq(&fh->queue_lock); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - - videobuf_waiton(vq, vb, 0, 0); - if (buf->cookie >= 0) - dma_sync_wait(fh->chan, buf->cookie); - - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops timblogiw_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* Device Operations functions */ - -static int timblogiw_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh; - v4l2_std_id std; - dma_cap_mask_t mask; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - if (lw->opened) { - err = -EBUSY; - goto out; - } - - if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) { - struct i2c_adapter *adapt; - - /* find the video decoder */ - adapt = i2c_get_adapter(lw->pdata.i2c_adapter); - if (!adapt) { - dev_err(&vdev->dev, "No I2C bus #%d\n", - lw->pdata.i2c_adapter); - err = -ENODEV; - goto out; - } - - /* now find the encoder */ - lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt, - lw->pdata.encoder.info, NULL); - - i2c_put_adapter(adapt); - - if (!lw->sd_enc) { - dev_err(&vdev->dev, "Failed to get encoder: %s\n", - lw->pdata.encoder.module_name); - err = -ENODEV; - goto out; - } - } - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) { - err = -ENOMEM; - goto out; - } - - fh->cur_norm = timblogiw_tvnorms; - timblogiw_querystd(file, fh, &std); - fh->cur_norm = timblogiw_get_norm(std); - - INIT_LIST_HEAD(&fh->capture); - spin_lock_init(&fh->queue_lock); - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - - /* find the DMA channel */ - fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn, - (void *)(uintptr_t)lw->pdata.dma_channel); - if (!fh->chan) { - dev_err(&vdev->dev, "Failed to get DMA channel\n"); - kfree(fh); - err = -ENODEV; - goto out; - } - - file->private_data = fh; - videobuf_queue_dma_contig_init(&fh->vb_vidq, - &timblogiw_video_qops, lw->dev, &fh->queue_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct timblogiw_buffer), fh, NULL); - - lw->opened = true; -out: - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_close(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - dma_release_channel(fh->chan); - - kfree(fh); - - mutex_lock(&lw->lock); - lw->opened = false; - mutex_unlock(&lw->lock); - return 0; -} - -static ssize_t timblogiw_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int timblogiw_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_poll_stream(file, &fh->vb_vidq, wait); -} - -static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_mmap_mapper(&fh->vb_vidq, vma); -} - -/* Platform device functions */ - -static struct v4l2_ioctl_ops timblogiw_ioctl_ops = { - .vidioc_querycap = timblogiw_querycap, - .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt, - .vidioc_g_fmt_vid_cap = timblogiw_g_fmt, - .vidioc_try_fmt_vid_cap = timblogiw_try_fmt, - .vidioc_s_fmt_vid_cap = timblogiw_s_fmt, - .vidioc_g_parm = timblogiw_g_parm, - .vidioc_reqbufs = timblogiw_reqbufs, - .vidioc_querybuf = timblogiw_querybuf, - .vidioc_qbuf = timblogiw_qbuf, - .vidioc_dqbuf = timblogiw_dqbuf, - .vidioc_g_std = timblogiw_g_std, - .vidioc_s_std = timblogiw_s_std, - .vidioc_enum_input = timblogiw_enuminput, - .vidioc_g_input = timblogiw_g_input, - .vidioc_s_input = timblogiw_s_input, - .vidioc_streamon = timblogiw_streamon, - .vidioc_streamoff = timblogiw_streamoff, - .vidioc_querystd = timblogiw_querystd, - .vidioc_enum_framesizes = timblogiw_enum_framesizes, -}; - -static struct v4l2_file_operations timblogiw_fops = { - .owner = THIS_MODULE, - .open = timblogiw_open, - .release = timblogiw_close, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = timblogiw_mmap, - .read = timblogiw_read, - .poll = timblogiw_poll, -}; - -static struct video_device timblogiw_template = { - .name = TIMBLOGIWIN_NAME, - .fops = &timblogiw_fops, - .ioctl_ops = &timblogiw_ioctl_ops, - .release = video_device_release_empty, - .minor = -1, - .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC -}; - -static int timblogiw_probe(struct platform_device *pdev) -{ - int err; - struct timblogiw *lw = NULL; - struct timb_video_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "No platform data\n"); - err = -EINVAL; - goto err; - } - - if (!pdata->encoder.module_name) - dev_info(&pdev->dev, "Running without decoder\n"); - - lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL); - if (!lw) { - err = -ENOMEM; - goto err; - } - - if (pdev->dev.parent) - lw->dev = pdev->dev.parent; - else - lw->dev = &pdev->dev; - - memcpy(&lw->pdata, pdata, sizeof(lw->pdata)); - - mutex_init(&lw->lock); - - lw->video_dev = timblogiw_template; - - strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name)); - err = v4l2_device_register(NULL, &lw->v4l2_dev); - if (err) - goto err; - - lw->video_dev.v4l2_dev = &lw->v4l2_dev; - - platform_set_drvdata(pdev, lw); - video_set_drvdata(&lw->video_dev, lw); - - err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0); - if (err) { - dev_err(&pdev->dev, "Error reg video: %d\n", err); - goto err_request; - } - - return 0; - -err_request: - v4l2_device_unregister(&lw->v4l2_dev); -err: - dev_err(&pdev->dev, "Failed to register: %d\n", err); - - return err; -} - -static int timblogiw_remove(struct platform_device *pdev) -{ - struct timblogiw *lw = platform_get_drvdata(pdev); - - video_unregister_device(&lw->video_dev); - - v4l2_device_unregister(&lw->v4l2_dev); - - return 0; -} - -static struct platform_driver timblogiw_platform_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = timblogiw_probe, - .remove = timblogiw_remove, -}; - -module_platform_driver(timblogiw_platform_driver); - -MODULE_DESCRIPTION(TIMBLOGIWIN_NAME); -MODULE_AUTHOR("Pelagicore AB "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:"DRIVER_NAME); -- cgit v0.10.2 From 37cf9b2d5b0fdeb61642e15ad30251f1371e25d5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 14 May 2016 02:29:21 -0300 Subject: [media] mn88473: fix error path on probe() Latest, 3rd, regmap instance should be freed on error case. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 6c5d5921..e6933d4 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -536,7 +536,7 @@ static int mn88473_probe(struct i2c_client *client, /* Sleep because chip is active by default */ ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) - goto err_client_2_i2c_unregister_device; + goto err_regmap_2_regmap_exit; /* Create dvb frontend */ memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); @@ -547,7 +547,8 @@ static int mn88473_probe(struct i2c_client *client, dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); return 0; - +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: -- cgit v0.10.2 From c5ee19c8663750456cf4121a1f1c7e24b23ed972 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 17 May 2016 11:38:41 -0300 Subject: [media] mn88473: fix typo firmare -> firmware Signed-off-by: Julia Lawall Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index e6933d4..451974a 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -330,7 +330,7 @@ static int mn88473_init(struct dvb_frontend *fe) /* Request the firmware, this will block and timeout */ ret = request_firmware(&fw, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", name); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } -- cgit v0.10.2 From 734fd634a67e51cb58331d552ba410f8b4cc0049 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 13 May 2016 12:08:45 -0300 Subject: [media] rtl28xxu: increase failed I2C msg repeat count to 3 1 and 2 wasn't enough for mn88472 chip on Astrometa device, so increase it to 3. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index eb7af8c..6643762 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -624,7 +624,7 @@ static int rtl28xxu_identify_state(struct dvb_usb_device *d, const char **name) dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id); /* Retry failed I2C messages */ - d->i2c_adap.retries = 1; + d->i2c_adap.retries = 3; d->i2c_adap.timeout = msecs_to_jiffies(10); return WARM; -- cgit v0.10.2 From eb7e61b62db36e6987d2b502aea0ff27f00b00fc Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 17 May 2016 11:38:42 -0300 Subject: [media] mn88472: fix typo firmare -> firmware Signed-off-by: Julia Lawall Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index 7ea749c..5cfa22a 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -312,7 +312,7 @@ static int mn88472_init(struct dvb_frontend *fe) /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, &client->dev); if (ret) { - dev_err(&client->dev, "firmare file '%s' not found\n", + dev_err(&client->dev, "firmware file '%s' not found\n", fw_file); goto err; } -- cgit v0.10.2 From 8d1f38fcb768cded9b957f3f87e87dd1965c655c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 13 May 2016 12:19:19 -0300 Subject: [media] mn88472: finalize driver Finalize driver in order to move out of staging. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h index 095294d..3236325 100644 --- a/drivers/media/dvb-frontends/mn88472.h +++ b/drivers/media/dvb-frontends/mn88472.h @@ -19,23 +19,33 @@ #include -enum ts_clock { - VARIABLE_TS_CLOCK, - FIXED_TS_CLOCK, -}; +/** + * struct mn88472_config - Platform data for the mn88472 driver + * @xtal: Clock frequency. + * @ts_mode: TS mode. + * @ts_clock: TS clock config. + * @i2c_wr_max: Max number of bytes driver writes to I2C at once. + * @get_dvb_frontend: Get DVB frontend. + */ -enum ts_mode { - SERIAL_TS_MODE, - PARALLEL_TS_MODE, -}; +/* Define old names for backward compatibility */ +#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE +#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED +#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL +#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL struct mn88472_config { - /* - * Max num of bytes given I2C adapter could write at once. - * Default: none - */ - u16 i2c_wr_max; + unsigned int xtal; + +#define MN88472_TS_MODE_SERIAL 0 +#define MN88472_TS_MODE_PARALLEL 1 + int ts_mode; +#define MN88472_TS_CLK_FIXED 0 +#define MN88472_TS_CLK_VARIABLE 1 + int ts_clock; + + u16 i2c_wr_max; /* Everything after that is returned by the driver. */ @@ -43,14 +53,7 @@ struct mn88472_config { * DVB frontend. */ struct dvb_frontend **fe; - - /* - * Xtal frequency. - * Hz - */ - u32 xtal; - int ts_mode; - int ts_clock; + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); }; #endif diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index 5cfa22a..18fb2df 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -17,28 +17,90 @@ #include "mn88472_priv.h" static int mn88472_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *s) + struct dvb_frontend_tune_settings *s) { - s->min_delay_ms = 800; + s->min_delay_ms = 1000; return 0; } +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x7f, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x92, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x84, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x08) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static int mn88472_set_frontend(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; - u32 if_frequency = 0; - u64 tmp; - u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; + u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; + u8 reg_bank0_d6_val; dev_dbg(&client->dev, - "delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n", - c->delivery_system, c->modulation, - c->frequency, c->symbol_rate, c->inversion); + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); - if (!dev->warm) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) switch (c->delivery_system) { case SYS_DVBT: delivery_system_val = 0x02; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x1f; + reg_bank0_d4_val = 0x0a; + reg_bank0_d6_val = 0x48; break; case SYS_DVBT2: delivery_system_val = 0x03; + reg_bank0_b4_val = 0xf6; + reg_bank0_cd_val = 0x01; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x46; break; case SYS_DVBC_ANNEX_A: delivery_system_val = 0x04; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x17; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x48; break; default: ret = -EINVAL; goto err; } - if (c->bandwidth_hz <= 5000000) { - memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7); - bw_val2 = 0x03; - } else if (c->bandwidth_hz <= 6000000) { - /* IF 3570000 Hz, BW 6000000 Hz */ - memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); - bw_val2 = 0x02; - } else if (c->bandwidth_hz <= 7000000) { - /* IF 4570000 Hz, BW 7000000 Hz */ - memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); - bw_val2 = 0x01; - } else if (c->bandwidth_hz <= 8000000) { - /* IF 4570000 Hz, BW 8000000 Hz */ - memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); - bw_val2 = 0x00; - } else { - ret = -EINVAL; - goto err; + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 5000000: + bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; + bandwidth_val = 0x03; + break; + case 6000000: + bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; + bandwidth_val = 0x02; + break; + case 7000000: + bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; + bandwidth_val = 0x01; + break; + case 8000000: + bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; + bandwidth_val = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + bandwidth_vals_ptr = NULL; + bandwidth_val = 0x00; + break; + default: + break; } - /* program tuner */ + /* Program tuner */ if (fe->ops.tuner_ops.set_params) { ret = fe->ops.tuner_ops.set_params(fe); if (ret) @@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) goto err; dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); - } - - /* Calculate IF registers ( (1<<24)*IF / Xtal ) */ - tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), - dev->xtal); - if_val[0] = (tmp >> 16) & 0xff; - if_val[1] = (tmp >> 8) & 0xff; - if_val[2] = (tmp >> 0) & 0xff; - - ret = regmap_write(dev->regmap[2], 0xfb, 0x13); - ret = regmap_write(dev->regmap[2], 0xef, 0x13); - ret = regmap_write(dev->regmap[2], 0xf9, 0x13); - if (ret) + } else { + ret = -EINVAL; goto err; + } ret = regmap_write(dev->regmap[2], 0x00, 0x66); if (ret) @@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x04, bw_val2); + ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); if (ret) goto err; - for (i = 0; i < sizeof(if_val); i++) { - ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); + /* IF */ + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + for (i = 0; i < 3; i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); if (ret) goto err; } - for (i = 0; i < sizeof(bw_val); i++) { - ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]); - if (ret) - goto err; + /* Bandwidth */ + if (bandwidth_vals_ptr) { + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + bandwidth_vals_ptr[i]); + if (ret) + goto err; + } } + ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); + if (ret) + goto err; + switch (c->delivery_system) { case SYS_DVBT: ret = regmap_write(dev->regmap[0], 0x07, 0x26); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x1f); - ret = regmap_write(dev->regmap[0], 0xd4, 0x0a); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x00, 0xba); + if (ret) + goto err; ret = regmap_write(dev->regmap[0], 0x01, 0x13); if (ret) goto err; break; case SYS_DVBT2: ret = regmap_write(dev->regmap[2], 0x2b, 0x13); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x4f, 0x05); + if (ret) + goto err; ret = regmap_write(dev->regmap[1], 0xf6, 0x05); - ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); - ret = regmap_write(dev->regmap[0], 0xb4, 0xf6); - ret = regmap_write(dev->regmap[0], 0xcd, 0x01); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x46); - ret = regmap_write(dev->regmap[2], 0x30, 0x80); - ret = regmap_write(dev->regmap[2], 0x32, 0x00); if (ret) goto err; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_write(dev->regmap[0], 0xb0, 0x0b); - ret = regmap_write(dev->regmap[0], 0xb4, 0x00); - ret = regmap_write(dev->regmap[0], 0xcd, 0x17); - ret = regmap_write(dev->regmap[0], 0xd4, 0x09); - ret = regmap_write(dev->regmap[0], 0xd6, 0x48); - ret = regmap_write(dev->regmap[1], 0x00, 0xb0); + ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); if (ret) goto err; break; - default: - ret = -EINVAL; - goto err; - } - - ret = regmap_write(dev->regmap[0], 0x46, 0x00); - ret = regmap_write(dev->regmap[0], 0xae, 0x00); - - switch (dev->ts_mode) { - case SERIAL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x1d); - break; - case PARALLEL_TS_MODE: - ret = regmap_write(dev->regmap[2], 0x08, 0x00); + case SYS_DVBC_ANNEX_A: break; default: - dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode); - ret = -EINVAL; - goto err; - } - - switch (dev->ts_clock) { - case VARIABLE_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); break; - case FIXED_TS_CLOCK: - ret = regmap_write(dev->regmap[0], 0xd9, 0xe1); - break; - default: - dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock); - ret = -EINVAL; - goto err; } - /* Reset demod */ + /* Reset FSM */ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); if (ret) goto err; - dev->delivery_system = c->delivery_system; - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - unsigned int utmp; - int lock = 0; - - *status = 0; - - if (!dev->warm) { - ret = -EAGAIN; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - ret = regmap_read(dev->regmap[0], 0x7F, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x09) - lock = 1; - break; - case SYS_DVBT2: - ret = regmap_read(dev->regmap[2], 0x92, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x07) - *status |= FE_HAS_SIGNAL; - if ((utmp & 0xF) >= 0x0a) - *status |= FE_HAS_CARRIER; - if ((utmp & 0xF) >= 0x0d) - *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_read(dev->regmap[1], 0x84, &utmp); - if (ret) - goto err; - if ((utmp & 0xF) >= 0x08) - lock = 1; - break; - default: - ret = -EINVAL; - goto err; - } - - if (lock) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_LOCK; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe) { struct i2c_client *client = fe->demodulator_priv; struct mn88472_dev *dev = i2c_get_clientdata(client); - int ret, len, remaining; - const struct firmware *fw = NULL; - u8 *fw_file = MN88472_FIRMWARE; - unsigned int tmp; + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name = MN88472_FIRMWARE; dev_dbg(&client->dev, "\n"); - /* set cold state by default */ - dev->warm = false; - - /* power on */ + /* Power up */ ret = regmap_write(dev->regmap[2], 0x05, 0x00); if (ret) goto err; - - ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2); + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); if (ret) goto err; - - /* check if firmware is already running */ - ret = regmap_read(dev->regmap[0], 0xf5, &tmp); + ret = regmap_write(dev->regmap[2], 0x0c, 0x00); if (ret) goto err; - if (!(tmp & 0x1)) { - dev_info(&client->dev, "firmware already running\n"); - dev->warm = true; - return 0; - } + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &utmp); + if (ret) + goto err; + if (!(utmp & 0x01)) + goto warm; - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &client->dev); + ret = request_firmware(&firmware, name, &client->dev); if (ret) { - dev_err(&client->dev, "firmware file '%s' not found\n", - fw_file); + dev_err(&client->dev, "firmware file '%s' not found\n", name); goto err; } - dev_info(&client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); ret = regmap_write(dev->regmap[0], 0xf5, 0x03); if (ret) - goto firmware_release; - - for (remaining = fw->size; remaining > 0; - remaining -= (dev->i2c_wr_max - 1)) { - len = remaining; - if (len > (dev->i2c_wr_max - 1)) - len = dev->i2c_wr_max - 1; + goto err_release_firmware; + for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { + len = min(dev->i2c_write_max - 1, rem); ret = regmap_bulk_write(dev->regmap[0], 0xf6, - &fw->data[fw->size - remaining], len); + &firmware->data[firmware->size - rem], + len); if (ret) { - dev_err(&client->dev, - "firmware download failed=%d\n", ret); - goto firmware_release; + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; } } - /* parity check of firmware */ - ret = regmap_read(dev->regmap[0], 0xf8, &tmp); - if (ret) { - dev_err(&client->dev, - "parity reg read failed=%d\n", ret); - goto firmware_release; - } - if (tmp & 0x10) { - dev_err(&client->dev, - "firmware parity check failed=0x%x\n", tmp); - goto firmware_release; + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &utmp); + if (ret) + goto err_release_firmware; + if (utmp & 0x10) { + ret = -EINVAL; + dev_err(&client->dev, "firmware did not run\n"); + goto err_release_firmware; } - dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp); ret = regmap_write(dev->regmap[0], 0xf5, 0x00); if (ret) - goto firmware_release; + goto err_release_firmware; + + release_firmware(firmware); +warm: + /* TS config */ + switch (dev->ts_mode) { + case SERIAL_TS_MODE: + utmp = 0x1d; + break; + case PARALLEL_TS_MODE: + utmp = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[2], 0x08, utmp); + if (ret) + goto err; - release_firmware(fw); - fw = NULL; + switch (dev->ts_clk) { + case VARIABLE_TS_CLOCK: + utmp = 0xe3; + break; + case FIXED_TS_CLOCK: + utmp = 0xe1; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[0], 0xd9, utmp); + if (ret) + goto err; - /* warm state */ - dev->warm = true; + dev->active = true; return 0; -firmware_release: - release_firmware(fw); +err_release_firmware: + release_firmware(firmware); err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; @@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); - /* power off */ + /* Power down */ + ret = regmap_write(dev->regmap[2], 0x0c, 0x30); + if (ret) + goto err; ret = regmap_write(dev->regmap[2], 0x0b, 0x30); - if (ret) goto err; - ret = regmap_write(dev->regmap[2], 0x05, 0x3e); if (ret) goto err; - dev->delivery_system = SYS_UNDEFINED; - return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = { .read_status = mn88472_read_status, }; +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + static int mn88472_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { - struct mn88472_config *config = client->dev.platform_data; + struct mn88472_config *pdata = client->dev.platform_data; struct mn88472_dev *dev; int ret; unsigned int utmp; @@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client, dev_dbg(&client->dev, "\n"); - /* Caller really need to provide pointer for frontend we create. */ - if (config->fe == NULL) { - dev_err(&client->dev, "frontend pointer not defined\n"); - ret = -EINVAL; - goto err; - } - dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err; } - dev->i2c_wr_max = config->i2c_wr_max; - dev->xtal = config->xtal; - dev->ts_mode = config->ts_mode; - dev->ts_clock = config->ts_clock; + dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; + dev->clk = pdata->xtal; + dev->ts_mode = pdata->ts_mode; + dev->ts_clk = pdata->ts_clock; dev->client[0] = client; dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); if (IS_ERR(dev->regmap[0])) { @@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client, goto err_kfree; } - /* check demod answers to I2C */ - ret = regmap_read(dev->regmap[0], 0x00, &utmp); + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[0], 0xff, &utmp); if (ret) goto err_regmap_0_regmap_exit; + dev_dbg(&client->dev, "chip id=%02x\n", utmp); + + if (utmp != 0x02) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + /* - * Chip has three I2C addresses for different register pages. Used + * Chip has three I2C addresses for different register banks. Used * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, - * 0x1a and 0x1c, in order to get own I2C client for each register page. + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. */ dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); if (!dev->client[1]) { @@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client, } i2c_set_clientdata(dev->client[2], dev); - /* create dvb_frontend */ + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->fe = &dev->fe; + *pdata->fe = &dev->fe; i2c_set_clientdata(client, dev); - dev_info(&client->dev, "Panasonic MN88472 successfully attached\n"); - return 0; + /* Setup callbacks */ + pdata->get_dvb_frontend = mn88472_get_dvb_frontend; + dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); err_client_2_i2c_unregister_device: i2c_unregister_device(dev->client[2]); err_regmap_1_regmap_exit: @@ -561,11 +597,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table); static struct i2c_driver mn88472_driver = { .driver = { - .name = "mn88472", + .name = "mn88472", + .suppress_bind_attrs = true, }, - .probe = mn88472_probe, - .remove = mn88472_remove, - .id_table = mn88472_id_table, + .probe = mn88472_probe, + .remove = mn88472_remove, + .id_table = mn88472_id_table, }; module_i2c_driver(mn88472_driver); diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h index 1a0de9e..cdf2597 100644 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ b/drivers/staging/media/mn88472/mn88472_priv.h @@ -28,12 +28,11 @@ struct mn88472_dev { struct i2c_client *client[3]; struct regmap *regmap[3]; struct dvb_frontend fe; - u16 i2c_wr_max; - enum fe_delivery_system delivery_system; - bool warm; /* FW running */ - u32 xtal; - int ts_mode; - int ts_clock; + u16 i2c_write_max; + unsigned int clk; + unsigned int active:1; + unsigned int ts_mode:1; + unsigned int ts_clk:1; }; #endif -- cgit v0.10.2 From 94d0eaa419871a6e2783f8c131b1d76d5f2a5524 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 14 May 2016 08:08:15 +0300 Subject: [media] mn88472: move out of staging to media Move mn88472 DVB-T/T2/C demod driver out of staging to media. v2: Fix build error reported by kbuild test robot: drivers/staging/media/mn88472/Makefile: No such file or directory Reported-by: kbuild test robot Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 1446778..45fad6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7610,10 +7610,8 @@ L: linux-media@vger.kernel.org W: https://linuxtv.org W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ -T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/mn88472/ -F: drivers/media/dvb-frontends/mn88472.h +F: drivers/media/dvb-frontends/mn88472* MN88473 MEDIA DRIVER M: Antti Palosaari diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 8decfc3..c645aa8 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -73,6 +73,14 @@ config DVB_SI2165 Say Y when you want to support this frontend. +config DVB_MN88472 + tristate "Panasonic MN88472" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + config DVB_MN88473 tristate "Panasonic MN88473" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 7fc5605..e90165a 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_MN88472) += mn88472.o obj-$(CONFIG_DVB_MN88473) += mn88473.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c new file mode 100644 index 0000000..18fb2df --- /dev/null +++ b/drivers/media/dvb-frontends/mn88472.c @@ -0,0 +1,613 @@ +/* + * Panasonic MN88472 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mn88472_priv.h" + +static int mn88472_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x7f, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x92, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else if ((utmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((utmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + *status = 0; + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x84, &utmp); + if (ret) + goto err; + if ((utmp & 0x0f) >= 0x08) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + else + *status = 0; + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int utmp; + u32 if_frequency; + u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; + u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; + u8 reg_bank0_d6_val; + + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system_val = 0x02; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x1f; + reg_bank0_d4_val = 0x0a; + reg_bank0_d6_val = 0x48; + break; + case SYS_DVBT2: + delivery_system_val = 0x03; + reg_bank0_b4_val = 0xf6; + reg_bank0_cd_val = 0x01; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x46; + break; + case SYS_DVBC_ANNEX_A: + delivery_system_val = 0x04; + reg_bank0_b4_val = 0x00; + reg_bank0_cd_val = 0x17; + reg_bank0_d4_val = 0x09; + reg_bank0_d6_val = 0x48; + break; + default: + ret = -EINVAL; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 5000000: + bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; + bandwidth_val = 0x03; + break; + case 6000000: + bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; + bandwidth_val = 0x02; + break; + case 7000000: + bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; + bandwidth_val = 0x01; + break; + case 8000000: + bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; + bandwidth_val = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + bandwidth_vals_ptr = NULL; + bandwidth_val = 0x00; + break; + default: + break; + } + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto err; + + dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); + } else { + ret = -EINVAL; + goto err; + } + + ret = regmap_write(dev->regmap[2], 0x00, 0x66); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x01, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x02, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); + if (ret) + goto err; + + /* IF */ + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + for (i = 0; i < 3; i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); + if (ret) + goto err; + } + + /* Bandwidth */ + if (bandwidth_vals_ptr) { + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + bandwidth_vals_ptr[i]); + if (ret) + goto err; + } + } + + ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); + if (ret) + goto err; + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_write(dev->regmap[0], 0x07, 0x26); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x00, 0xba); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x01, 0x13); + if (ret) + goto err; + break; + case SYS_DVBT2: + ret = regmap_write(dev->regmap[2], 0x2b, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x4f, 0x05); + if (ret) + goto err; + ret = regmap_write(dev->regmap[1], 0xf6, 0x05); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); + if (ret) + goto err; + break; + case SYS_DVBC_ANNEX_A: + break; + default: + break; + } + + /* Reset FSM */ + ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + int ret, len, rem; + unsigned int utmp; + const struct firmware *firmware; + const char *name = MN88472_FIRMWARE; + + dev_dbg(&client->dev, "\n"); + + /* Power up */ + ret = regmap_write(dev->regmap[2], 0x05, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0c, 0x00); + if (ret) + goto err; + + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &utmp); + if (ret) + goto err; + if (!(utmp & 0x01)) + goto warm; + + ret = request_firmware(&firmware, name, &client->dev); + if (ret) { + dev_err(&client->dev, "firmware file '%s' not found\n", name); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); + + ret = regmap_write(dev->regmap[0], 0xf5, 0x03); + if (ret) + goto err_release_firmware; + + for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { + len = min(dev->i2c_write_max - 1, rem); + ret = regmap_bulk_write(dev->regmap[0], 0xf6, + &firmware->data[firmware->size - rem], + len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &utmp); + if (ret) + goto err_release_firmware; + if (utmp & 0x10) { + ret = -EINVAL; + dev_err(&client->dev, "firmware did not run\n"); + goto err_release_firmware; + } + + ret = regmap_write(dev->regmap[0], 0xf5, 0x00); + if (ret) + goto err_release_firmware; + + release_firmware(firmware); +warm: + /* TS config */ + switch (dev->ts_mode) { + case SERIAL_TS_MODE: + utmp = 0x1d; + break; + case PARALLEL_TS_MODE: + utmp = 0x00; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[2], 0x08, utmp); + if (ret) + goto err; + + switch (dev->ts_clk) { + case VARIABLE_TS_CLOCK: + utmp = 0xe3; + break; + case FIXED_TS_CLOCK: + utmp = 0xe1; + break; + default: + ret = -EINVAL; + goto err; + } + ret = regmap_write(dev->regmap[0], 0xd9, utmp); + if (ret) + goto err; + + dev->active = true; + + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88472_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "\n"); + + /* Power down */ + ret = regmap_write(dev->regmap[2], 0x0c, 0x30); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x30); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static struct dvb_frontend_ops mn88472_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Panasonic MN88472", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM + }, + + .get_tune_settings = mn88472_get_tune_settings, + + .init = mn88472_init, + .sleep = mn88472_sleep, + + .set_frontend = mn88472_set_frontend, + + .read_status = mn88472_read_status, +}; + +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static int mn88472_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mn88472_config *pdata = client->dev.platform_data; + struct mn88472_dev *dev; + int ret; + unsigned int utmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev_dbg(&client->dev, "\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; + dev->clk = pdata->xtal; + dev->ts_mode = pdata->ts_mode; + dev->ts_clk = pdata->ts_clock; + dev->client[0] = client; + dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); + if (IS_ERR(dev->regmap[0])) { + ret = PTR_ERR(dev->regmap[0]); + goto err_kfree; + } + + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[0], 0xff, &utmp); + if (ret) + goto err_regmap_0_regmap_exit; + + dev_dbg(&client->dev, "chip id=%02x\n", utmp); + + if (utmp != 0x02) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + + /* + * Chip has three I2C addresses for different register banks. Used + * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. + */ + dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); + if (!dev->client[1]) { + ret = -ENODEV; + dev_err(&client->dev, "I2C registration failed\n"); + if (ret) + goto err_regmap_0_regmap_exit; + } + dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); + if (IS_ERR(dev->regmap[1])) { + ret = PTR_ERR(dev->regmap[1]); + goto err_client_1_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[1], dev); + + dev->client[2] = i2c_new_dummy(client->adapter, 0x1c); + if (!dev->client[2]) { + ret = -ENODEV; + dev_err(&client->dev, "2nd I2C registration failed\n"); + if (ret) + goto err_regmap_1_regmap_exit; + } + dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); + if (IS_ERR(dev->regmap[2])) { + ret = PTR_ERR(dev->regmap[2]); + goto err_client_2_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[2], dev); + + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_regmap_2_regmap_exit; + + /* Create dvb frontend */ + memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = client; + *pdata->fe = &dev->fe; + i2c_set_clientdata(client, dev); + + /* Setup callbacks */ + pdata->get_dvb_frontend = mn88472_get_dvb_frontend; + + dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); + + return 0; +err_regmap_2_regmap_exit: + regmap_exit(dev->regmap[2]); +err_client_2_i2c_unregister_device: + i2c_unregister_device(dev->client[2]); +err_regmap_1_regmap_exit: + regmap_exit(dev->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(dev->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(dev->regmap[0]); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88472_remove(struct i2c_client *client) +{ + struct mn88472_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + regmap_exit(dev->regmap[2]); + i2c_unregister_device(dev->client[2]); + + regmap_exit(dev->regmap[1]); + i2c_unregister_device(dev->client[1]); + + regmap_exit(dev->regmap[0]); + + kfree(dev); + + return 0; +} + +static const struct i2c_device_id mn88472_id_table[] = { + {"mn88472", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88472_id_table); + +static struct i2c_driver mn88472_driver = { + .driver = { + .name = "mn88472", + .suppress_bind_attrs = true, + }, + .probe = mn88472_probe, + .remove = mn88472_remove, + .id_table = mn88472_id_table, +}; + +module_i2c_driver(mn88472_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Panasonic MN88472 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(MN88472_FIRMWARE); diff --git a/drivers/media/dvb-frontends/mn88472_priv.h b/drivers/media/dvb-frontends/mn88472_priv.h new file mode 100644 index 0000000..cdf2597 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88472_priv.h @@ -0,0 +1,38 @@ +/* + * Panasonic MN88472 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MN88472_PRIV_H +#define MN88472_PRIV_H + +#include "dvb_frontend.h" +#include "mn88472.h" +#include +#include + +#define MN88472_FIRMWARE "dvb-demod-mn88472-02.fw" + +struct mn88472_dev { + struct i2c_client *client[3]; + struct regmap *regmap[3]; + struct dvb_frontend fe; + u16 i2c_write_max; + unsigned int clk; + unsigned int active:1; + unsigned int ts_mode:1; + unsigned int ts_clk:1; +}; + +#endif diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index f1db900..ee91868 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,8 +25,6 @@ source "drivers/staging/media/cxd2099/Kconfig" source "drivers/staging/media/davinci_vpfe/Kconfig" -source "drivers/staging/media/mn88472/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/tw686x-kh/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 61c3973..8c05d0a 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -3,5 +3,4 @@ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ -obj-$(CONFIG_DVB_MN88472) += mn88472/ obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh/ diff --git a/drivers/staging/media/mn88472/Kconfig b/drivers/staging/media/mn88472/Kconfig deleted file mode 100644 index a85c90a..0000000 --- a/drivers/staging/media/mn88472/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config DVB_MN88472 - tristate "Panasonic MN88472" - depends on DVB_CORE && I2C - select REGMAP_I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y when you want to support this frontend. diff --git a/drivers/staging/media/mn88472/Makefile b/drivers/staging/media/mn88472/Makefile deleted file mode 100644 index 5987b7e..0000000 --- a/drivers/staging/media/mn88472/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_DVB_MN88472) += mn88472.o - -ccflags-y += -Idrivers/media/dvb-core/ -ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/tuners/ diff --git a/drivers/staging/media/mn88472/TODO b/drivers/staging/media/mn88472/TODO deleted file mode 100644 index b90a14b..0000000 --- a/drivers/staging/media/mn88472/TODO +++ /dev/null @@ -1,21 +0,0 @@ -Driver general quality is not good enough for mainline. Also, other -device drivers (USB-bridge, tuner) needed for Astrometa receiver in -question could need some changes. However, if that driver is mainlined -due to some other device than Astrometa, unrelated TODOs could be -skipped. In that case rtl28xxu driver needs module parameter to prevent -driver loading. - -Required TODOs: -* missing lock flags -* I2C errors -* tuner sensitivity - -*Do not* send any patch fixing checkpatch.pl issues. Currently it passes -checkpatch.pl tests. I don't want waste my time to review this kind of -trivial stuff. *Do not* add missing register I/O error checks. Those are -missing for the reason it is much easier to compare I2C data sniffs when -there is less lines. Those error checks are about the last thing to be added. - -Patches should be submitted to: -linux-media@vger.kernel.org and Antti Palosaari - diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c deleted file mode 100644 index 18fb2df..0000000 --- a/drivers/staging/media/mn88472/mn88472.c +++ /dev/null @@ -1,613 +0,0 @@ -/* - * Panasonic MN88472 DVB-T/T2/C demodulator driver - * - * Copyright (C) 2013 Antti Palosaari - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "mn88472_priv.h" - -static int mn88472_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *s) -{ - s->min_delay_ms = 1000; - return 0; -} - -static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - unsigned int utmp; - - if (!dev->active) { - ret = -EAGAIN; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - ret = regmap_read(dev->regmap[0], 0x7f, &utmp); - if (ret) - goto err; - if ((utmp & 0x0f) >= 0x09) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - else - *status = 0; - break; - case SYS_DVBT2: - ret = regmap_read(dev->regmap[2], 0x92, &utmp); - if (ret) - goto err; - if ((utmp & 0x0f) >= 0x0d) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - else if ((utmp & 0x0f) >= 0x0a) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI; - else if ((utmp & 0x0f) >= 0x07) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - else - *status = 0; - break; - case SYS_DVBC_ANNEX_A: - ret = regmap_read(dev->regmap[1], 0x84, &utmp); - if (ret) - goto err; - if ((utmp & 0x0f) >= 0x08) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - else - *status = 0; - break; - default: - ret = -EINVAL; - goto err; - } - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_set_frontend(struct dvb_frontend *fe) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i; - unsigned int utmp; - u32 if_frequency; - u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; - u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; - u8 reg_bank0_d6_val; - - dev_dbg(&client->dev, - "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", - c->delivery_system, c->modulation, c->frequency, - c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); - - if (!dev->active) { - ret = -EAGAIN; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - delivery_system_val = 0x02; - reg_bank0_b4_val = 0x00; - reg_bank0_cd_val = 0x1f; - reg_bank0_d4_val = 0x0a; - reg_bank0_d6_val = 0x48; - break; - case SYS_DVBT2: - delivery_system_val = 0x03; - reg_bank0_b4_val = 0xf6; - reg_bank0_cd_val = 0x01; - reg_bank0_d4_val = 0x09; - reg_bank0_d6_val = 0x46; - break; - case SYS_DVBC_ANNEX_A: - delivery_system_val = 0x04; - reg_bank0_b4_val = 0x00; - reg_bank0_cd_val = 0x17; - reg_bank0_d4_val = 0x09; - reg_bank0_d6_val = 0x48; - break; - default: - ret = -EINVAL; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBT: - case SYS_DVBT2: - switch (c->bandwidth_hz) { - case 5000000: - bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; - bandwidth_val = 0x03; - break; - case 6000000: - bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; - bandwidth_val = 0x02; - break; - case 7000000: - bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; - bandwidth_val = 0x01; - break; - case 8000000: - bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; - bandwidth_val = 0x00; - break; - default: - ret = -EINVAL; - goto err; - } - break; - case SYS_DVBC_ANNEX_A: - bandwidth_vals_ptr = NULL; - bandwidth_val = 0x00; - break; - default: - break; - } - - /* Program tuner */ - if (fe->ops.tuner_ops.set_params) { - ret = fe->ops.tuner_ops.set_params(fe); - if (ret) - goto err; - } - - if (fe->ops.tuner_ops.get_if_frequency) { - ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); - if (ret) - goto err; - - dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); - } else { - ret = -EINVAL; - goto err; - } - - ret = regmap_write(dev->regmap[2], 0x00, 0x66); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x01, 0x00); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x02, 0x01); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); - if (ret) - goto err; - - /* IF */ - utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); - buf[0] = (utmp >> 16) & 0xff; - buf[1] = (utmp >> 8) & 0xff; - buf[2] = (utmp >> 0) & 0xff; - for (i = 0; i < 3; i++) { - ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); - if (ret) - goto err; - } - - /* Bandwidth */ - if (bandwidth_vals_ptr) { - for (i = 0; i < 7; i++) { - ret = regmap_write(dev->regmap[2], 0x13 + i, - bandwidth_vals_ptr[i]); - if (ret) - goto err; - } - } - - ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); - if (ret) - goto err; - ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); - if (ret) - goto err; - ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); - if (ret) - goto err; - ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); - if (ret) - goto err; - - switch (c->delivery_system) { - case SYS_DVBT: - ret = regmap_write(dev->regmap[0], 0x07, 0x26); - if (ret) - goto err; - ret = regmap_write(dev->regmap[0], 0x00, 0xba); - if (ret) - goto err; - ret = regmap_write(dev->regmap[0], 0x01, 0x13); - if (ret) - goto err; - break; - case SYS_DVBT2: - ret = regmap_write(dev->regmap[2], 0x2b, 0x13); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x4f, 0x05); - if (ret) - goto err; - ret = regmap_write(dev->regmap[1], 0xf6, 0x05); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); - if (ret) - goto err; - break; - case SYS_DVBC_ANNEX_A: - break; - default: - break; - } - - /* Reset FSM */ - ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_init(struct dvb_frontend *fe) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - int ret, len, rem; - unsigned int utmp; - const struct firmware *firmware; - const char *name = MN88472_FIRMWARE; - - dev_dbg(&client->dev, "\n"); - - /* Power up */ - ret = regmap_write(dev->regmap[2], 0x05, 0x00); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x0b, 0x00); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x0c, 0x00); - if (ret) - goto err; - - /* Check if firmware is already running */ - ret = regmap_read(dev->regmap[0], 0xf5, &utmp); - if (ret) - goto err; - if (!(utmp & 0x01)) - goto warm; - - ret = request_firmware(&firmware, name, &client->dev); - if (ret) { - dev_err(&client->dev, "firmware file '%s' not found\n", name); - goto err; - } - - dev_info(&client->dev, "downloading firmware from file '%s'\n", name); - - ret = regmap_write(dev->regmap[0], 0xf5, 0x03); - if (ret) - goto err_release_firmware; - - for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { - len = min(dev->i2c_write_max - 1, rem); - ret = regmap_bulk_write(dev->regmap[0], 0xf6, - &firmware->data[firmware->size - rem], - len); - if (ret) { - dev_err(&client->dev, "firmware download failed %d\n", - ret); - goto err_release_firmware; - } - } - - /* Parity check of firmware */ - ret = regmap_read(dev->regmap[0], 0xf8, &utmp); - if (ret) - goto err_release_firmware; - if (utmp & 0x10) { - ret = -EINVAL; - dev_err(&client->dev, "firmware did not run\n"); - goto err_release_firmware; - } - - ret = regmap_write(dev->regmap[0], 0xf5, 0x00); - if (ret) - goto err_release_firmware; - - release_firmware(firmware); -warm: - /* TS config */ - switch (dev->ts_mode) { - case SERIAL_TS_MODE: - utmp = 0x1d; - break; - case PARALLEL_TS_MODE: - utmp = 0x00; - break; - default: - ret = -EINVAL; - goto err; - } - ret = regmap_write(dev->regmap[2], 0x08, utmp); - if (ret) - goto err; - - switch (dev->ts_clk) { - case VARIABLE_TS_CLOCK: - utmp = 0xe3; - break; - case FIXED_TS_CLOCK: - utmp = 0xe1; - break; - default: - ret = -EINVAL; - goto err; - } - ret = regmap_write(dev->regmap[0], 0xd9, utmp); - if (ret) - goto err; - - dev->active = true; - - return 0; -err_release_firmware: - release_firmware(firmware); -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_sleep(struct dvb_frontend *fe) -{ - struct i2c_client *client = fe->demodulator_priv; - struct mn88472_dev *dev = i2c_get_clientdata(client); - int ret; - - dev_dbg(&client->dev, "\n"); - - /* Power down */ - ret = regmap_write(dev->regmap[2], 0x0c, 0x30); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x0b, 0x30); - if (ret) - goto err; - ret = regmap_write(dev->regmap[2], 0x05, 0x3e); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static struct dvb_frontend_ops mn88472_ops = { - .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, - .info = { - .name = "Panasonic MN88472", - .symbol_rate_min = 1000000, - .symbol_rate_max = 7200000, - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_QAM_16 | - FE_CAN_QAM_32 | - FE_CAN_QAM_64 | - FE_CAN_QAM_128 | - FE_CAN_QAM_256 | - FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | - FE_CAN_MUTE_TS | - FE_CAN_2G_MODULATION | - FE_CAN_MULTISTREAM - }, - - .get_tune_settings = mn88472_get_tune_settings, - - .init = mn88472_init, - .sleep = mn88472_sleep, - - .set_frontend = mn88472_set_frontend, - - .read_status = mn88472_read_status, -}; - -static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) -{ - struct mn88472_dev *dev = i2c_get_clientdata(client); - - dev_dbg(&client->dev, "\n"); - - return &dev->fe; -} - -static int mn88472_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct mn88472_config *pdata = client->dev.platform_data; - struct mn88472_dev *dev; - int ret; - unsigned int utmp; - static const struct regmap_config regmap_config = { - .reg_bits = 8, - .val_bits = 8, - }; - - dev_dbg(&client->dev, "\n"); - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - ret = -ENOMEM; - goto err; - } - - dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; - dev->clk = pdata->xtal; - dev->ts_mode = pdata->ts_mode; - dev->ts_clk = pdata->ts_clock; - dev->client[0] = client; - dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); - if (IS_ERR(dev->regmap[0])) { - ret = PTR_ERR(dev->regmap[0]); - goto err_kfree; - } - - /* Check demod answers with correct chip id */ - ret = regmap_read(dev->regmap[0], 0xff, &utmp); - if (ret) - goto err_regmap_0_regmap_exit; - - dev_dbg(&client->dev, "chip id=%02x\n", utmp); - - if (utmp != 0x02) { - ret = -ENODEV; - goto err_regmap_0_regmap_exit; - } - - /* - * Chip has three I2C addresses for different register banks. Used - * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, - * 0x1a and 0x1c, in order to get own I2C client for each register bank. - * - * Also, register bank 2 do not support sequential I/O. Only single - * register write or read is allowed to that bank. - */ - dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); - if (!dev->client[1]) { - ret = -ENODEV; - dev_err(&client->dev, "I2C registration failed\n"); - if (ret) - goto err_regmap_0_regmap_exit; - } - dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); - if (IS_ERR(dev->regmap[1])) { - ret = PTR_ERR(dev->regmap[1]); - goto err_client_1_i2c_unregister_device; - } - i2c_set_clientdata(dev->client[1], dev); - - dev->client[2] = i2c_new_dummy(client->adapter, 0x1c); - if (!dev->client[2]) { - ret = -ENODEV; - dev_err(&client->dev, "2nd I2C registration failed\n"); - if (ret) - goto err_regmap_1_regmap_exit; - } - dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); - if (IS_ERR(dev->regmap[2])) { - ret = PTR_ERR(dev->regmap[2]); - goto err_client_2_i2c_unregister_device; - } - i2c_set_clientdata(dev->client[2], dev); - - /* Sleep because chip is active by default */ - ret = regmap_write(dev->regmap[2], 0x05, 0x3e); - if (ret) - goto err_regmap_2_regmap_exit; - - /* Create dvb frontend */ - memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); - dev->fe.demodulator_priv = client; - *pdata->fe = &dev->fe; - i2c_set_clientdata(client, dev); - - /* Setup callbacks */ - pdata->get_dvb_frontend = mn88472_get_dvb_frontend; - - dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); - - return 0; -err_regmap_2_regmap_exit: - regmap_exit(dev->regmap[2]); -err_client_2_i2c_unregister_device: - i2c_unregister_device(dev->client[2]); -err_regmap_1_regmap_exit: - regmap_exit(dev->regmap[1]); -err_client_1_i2c_unregister_device: - i2c_unregister_device(dev->client[1]); -err_regmap_0_regmap_exit: - regmap_exit(dev->regmap[0]); -err_kfree: - kfree(dev); -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int mn88472_remove(struct i2c_client *client) -{ - struct mn88472_dev *dev = i2c_get_clientdata(client); - - dev_dbg(&client->dev, "\n"); - - regmap_exit(dev->regmap[2]); - i2c_unregister_device(dev->client[2]); - - regmap_exit(dev->regmap[1]); - i2c_unregister_device(dev->client[1]); - - regmap_exit(dev->regmap[0]); - - kfree(dev); - - return 0; -} - -static const struct i2c_device_id mn88472_id_table[] = { - {"mn88472", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, mn88472_id_table); - -static struct i2c_driver mn88472_driver = { - .driver = { - .name = "mn88472", - .suppress_bind_attrs = true, - }, - .probe = mn88472_probe, - .remove = mn88472_remove, - .id_table = mn88472_id_table, -}; - -module_i2c_driver(mn88472_driver); - -MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Panasonic MN88472 DVB-T/T2/C demodulator driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(MN88472_FIRMWARE); diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h deleted file mode 100644 index cdf2597..0000000 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Panasonic MN88472 DVB-T/T2/C demodulator driver - * - * Copyright (C) 2013 Antti Palosaari - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef MN88472_PRIV_H -#define MN88472_PRIV_H - -#include "dvb_frontend.h" -#include "mn88472.h" -#include -#include - -#define MN88472_FIRMWARE "dvb-demod-mn88472-02.fw" - -struct mn88472_dev { - struct i2c_client *client[3]; - struct regmap *regmap[3]; - struct dvb_frontend fe; - u16 i2c_write_max; - unsigned int clk; - unsigned int active:1; - unsigned int ts_mode:1; - unsigned int ts_clk:1; -}; - -#endif -- cgit v0.10.2 From d287a4ef927cd31611e19525b26ddb7a9c5bfa97 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 22 May 2016 13:12:06 -0300 Subject: [media] rtl28xxu: auto-select more DVB-frontends and tuners This adds the missing auto-select bits for DVB-frontends and tuners (if MEDIA_SUBDRV_AUTOSELECT is enabled) which are used by the various rtl28xxu devices. The driver itself probes for three more tuners, but it's not actually using any of them: - MEDIA_TUNER_MT2063 - MEDIA_TUNER_MT2266 - MEDIA_TUNER_MXL5007T Signed-off-by: Martin Blumenstingl Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 3dc8ef0..fcecbf7 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -127,9 +127,12 @@ config DVB_USB_MXL111SF config DVB_USB_RTL28XXU tristate "Realtek RTL28xxU DVB USB support" depends on DVB_USB_V2 && I2C_MUX + select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MN88473 if MEDIA_SUBDRV_AUTOSELECT select DVB_RTL2830 select DVB_RTL2832 select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT) + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT @@ -138,6 +141,8 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. -- cgit v0.10.2 From 1f846620e7bfbd7fc2520e79c3ae900785fcb0aa Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 22 May 2016 13:12:07 -0300 Subject: [media] rtl28xxu: sort the config symbols which are auto-selected No functional changes. Signed-off-by: Martin Blumenstingl Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index fcecbf7..524533d 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -133,13 +133,13 @@ config DVB_USB_RTL28XXU select DVB_RTL2832 select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT) select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT -- cgit v0.10.2 From 155af08b15dac04033aa7b14c96d9cd67f0fd581 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 7 Jun 2016 12:26:01 -0300 Subject: [media] fix semicolon.cocci warnings drivers/media/dvb-frontends/helene.c:750:2-3: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci CC: Abylay Ospan Signed-off-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 693b5bf..97a8982 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -747,7 +747,7 @@ static int helene_set_params(struct dvb_frontend *fe) data[6] = 0x78; data[7] = 0x08; data[8] = 0x30; - }; + } helene_write_regs(priv, 0x5E, data, 9); /* LT_AMP_EN should be 0 */ -- cgit v0.10.2 From 212fa081262e17affb1aa3ed8fd7a5b2fffbfc00 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 16 Mar 2016 08:04:51 -0300 Subject: [media] ds3000: return error if invalid symbol rate is set Return -EINVAL if ds3000_set_frontend is called with invalid parameters. v1 of the patch series got incorrect subject lines. Signed-off-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index addffc3..447b518 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -959,6 +959,15 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* enable ac coupling */ ds3000_writereg(state, 0x25, 0x8a); + if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) || + (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) { + dprintk("%s() symbol_rate %u out of range (%u ... %u)\n", + __func__, c->symbol_rate, + ds3000_ops.info.symbol_rate_min, + ds3000_ops.info.symbol_rate_max); + return -EINVAL; + } + /* enhance symbol rate performance */ if ((c->symbol_rate / 1000) <= 5000) { value = 29777 / (c->symbol_rate / 1000) + 1; -- cgit v0.10.2 From bd3df3c53e8e5527fc98d0adb4672f3c96e5d69f Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 21 Mar 2016 10:30:17 -0300 Subject: [media] drivers/media/dvb-core/en50221: move code to dvb_ca_private_free() Prepare for postponing the call until all file handles have been closed. [mchehab@osg.samsung.com: make checkpatch happy] Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index f82cd1f..b1e3a26 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -161,6 +161,18 @@ struct dvb_ca_private { struct mutex ioctl_mutex; }; +static void dvb_ca_private_free(struct dvb_ca_private *ca) +{ + unsigned int i; + + dvb_unregister_device(ca->dvbdev); + for (i = 0; i < ca->slot_count; i++) + vfree(ca->slot_info[i].rx_buffer.data); + + kfree(ca->slot_info); + kfree(ca); +} + static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); @@ -1759,10 +1771,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) for (i = 0; i < ca->slot_count; i++) { dvb_ca_en50221_slot_shutdown(ca, i); - vfree(ca->slot_info[i].rx_buffer.data); } - kfree(ca->slot_info); - dvb_unregister_device(ca->dvbdev); - kfree(ca); + dvb_ca_private_free(ca); pubca->private = NULL; } -- cgit v0.10.2 From 9f5a802b1d51dc80a27d828a5f7dcc8ec4a72f03 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 8 Jun 2016 03:51:53 -0300 Subject: [media] of: reserved_mem: restore old behavior when no region is defined Change return value back to -ENODEV when no region is defined for given device. This restores old behavior of this function, as some drivers rely on such error code. Fixes: 59ce4039727ef40 ("of: reserved_mem: add support for using more than one region for given device") Reported-by: Liviu Dudau Signed-off-by: Marek Szyprowski Reviewed-by: Sylwester Nawrocki Reviewed-by: Liviu Dudau Acked-by: Rob Herring Reviewed-by: Sumit Semwal diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 04e4fe5..2b2291b 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -327,7 +327,7 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, target = of_parse_phandle(np, "memory-region", idx); if (!target) - return -EINVAL; + return -ENODEV; rmem = __find_rmem(target); of_node_put(target); -- cgit v0.10.2 From 57f1c0533850c0d68d114353b3b3a61148498698 Mon Sep 17 00:00:00 2001 From: Alessandro Radicati Date: Tue, 5 Apr 2016 19:23:43 -0300 Subject: [media] af9035: I2C combined write + read transaction fix This patch will modify the af9035 driver to use the register address fields of the I2C read command for the combined write/read transaction case. Without this change, the firmware issues just a I2C read transaction without the preceding write transaction to select the register. Signed-off-by: Alessandro Radicati Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 2638e32..09a549b 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -367,10 +367,25 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, memcpy(&buf[3], msg[0].buf, msg[0].len); } else { buf[1] = msg[0].addr << 1; - buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ - memcpy(&buf[5], msg[0].buf, msg[0].len); + + /* Keep prev behavior for write req len > 2*/ + if (msg[0].len > 2) { + buf[2] = 0x00; /* reg addr len */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + + /* Use reg addr fields if write req len <= 2 */ + } else { + req.wlen = 5; + buf[2] = msg[0].len; + if (msg[0].len == 2) { + buf[3] = msg[0].buf[0]; + buf[4] = msg[0].buf[1]; + } else if (msg[0].len == 1) { + buf[4] = msg[0].buf[0]; + } + } } ret = af9035_ctrl_msg(d, &req); } -- cgit v0.10.2 From 1882f59f93066cb4ae922d68526bd0f33d2277cb Mon Sep 17 00:00:00 2001 From: Alessandro Radicati Date: Thu, 12 May 2016 07:47:12 -0300 Subject: [media] af9035: fix for MXL5007T devices with I2C read issues The MXL5007T tuner will lock-up on some devices after an I2C read transaction. This patch works around this issue by inhibiting such operations and emulating a 0x00 response. The workaround is only applied to USB devices known to exhibit this flaw. Signed-off-by: Alessandro Radicati Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 09a549b..a8ab592 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -348,6 +348,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); + } else if (state->no_read) { + memset(msg[1].buf, 0, msg[1].len); + ret = 0; } else { /* I2C write + read */ u8 buf[MAX_XFER_SIZE]; @@ -436,6 +439,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; + } else if (state->no_read) { + memset(msg[0].buf, 0, msg[0].len); + ret = 0; } else { /* I2C read */ u8 buf[5]; @@ -977,6 +983,21 @@ skip_eeprom: state->af9033_config[i].clock = clock_lut_af9035[tmp]; } + state->no_read = false; + /* Some MXL5007T devices cannot properly handle tuner I2C read ops. */ + if (state->af9033_config[0].tuner == AF9033_TUNER_MXL5007T && + le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) + + switch (le16_to_cpu(d->udev->descriptor.idProduct)) { + case USB_PID_AVERMEDIA_A867: + case USB_PID_AVERMEDIA_TWINSTAR: + dev_info(&d->udev->dev, + "%s: Device may have issues with I2C read operations. Enabling fix.\n", + KBUILD_MODNAME); + state->no_read = true; + break; + } + return 0; err: diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 89e629a..c91d1a3 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -62,6 +62,7 @@ struct state { u8 chip_version; u16 chip_type; u8 dual_mode:1; + u8 no_read:1; u16 eeprom_addr; u8 af9033_i2c_addr[2]; struct af9033_config af9033_config[2]; -- cgit v0.10.2 From 2b987ea78b8d129145fe565bfe8f3b75c6f3474d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 1 Jun 2016 08:25:05 -0300 Subject: [media] af9035: fix logging Remove __func__ and KBUILD_MODNAME from logging formatters and pass USB interface device instead, so logging can be done correctly. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index a8ab592..eabede4 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -49,6 +49,7 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) #define CHECKSUM_LEN 2 #define USB_TIMEOUT 2000 struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, wlen, rlen; u16 checksum, tmp_checksum; @@ -57,8 +58,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) /* buffer overflow check */ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { - dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n", - KBUILD_MODNAME, req->wlen, req->rlen); + dev_err(&intf->dev, "too much data wlen=%d rlen=%d\n", + req->wlen, req->rlen); ret = -EINVAL; goto exit; } @@ -94,10 +95,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) checksum = af9035_checksum(state->buf, rlen - 2); tmp_checksum = (state->buf[rlen - 2] << 8) | state->buf[rlen - 1]; if (tmp_checksum != checksum) { - dev_err(&d->udev->dev, - "%s: command=%02x checksum mismatch (%04x != %04x)\n", - KBUILD_MODNAME, req->cmd, tmp_checksum, - checksum); + dev_err(&intf->dev, "command=%02x checksum mismatch (%04x != %04x)\n", + req->cmd, tmp_checksum, checksum); ret = -EIO; goto exit; } @@ -110,8 +109,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) goto exit; } - dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n", - __func__, req->cmd, state->buf[2]); + dev_dbg(&intf->dev, "command=%02x failed fw error=%d\n", + req->cmd, state->buf[2]); ret = -EIO; goto exit; } @@ -122,20 +121,20 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) exit: mutex_unlock(&d->usb_mutex); if (ret < 0) - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } /* write multiple registers */ static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) { + struct usb_interface *intf = d->intf; u8 wbuf[MAX_XFER_SIZE]; u8 mbox = (reg >> 16) & 0xff; struct usb_req req = { CMD_MEM_WR, mbox, 6 + len, wbuf, 0, NULL }; if (6 + len > sizeof(wbuf)) { - dev_warn(&d->udev->dev, "%s: i2c wr: len=%d is too big!\n", - KBUILD_MODNAME, len); + dev_warn(&intf->dev, "i2c wr: len=%d is too big!\n", len); return -EOPNOTSUPP; } @@ -198,6 +197,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, { int ret, num; struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; struct i2c_client *client; struct i2c_board_info board_info = { .addr = addr, @@ -212,11 +212,10 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, break; } - dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + dev_dbg(&intf->dev, "num=%d\n", num); if (num == AF9035_I2C_CLIENT_MAX) { - dev_err(&d->udev->dev, "%s: I2C client out of index\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "I2C client out of index\n"); ret = -ENODEV; goto err; } @@ -240,7 +239,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, state->i2c_client[num] = client; return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -248,6 +247,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) { int num; struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; struct i2c_client *client; /* find last used client */ @@ -257,11 +257,10 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) break; } - dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num); + dev_dbg(&intf->dev, "num=%d\n", num); if (num == -1) { - dev_err(&d->udev->dev, "%s: I2C client out of index\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "I2C client out of index\n"); goto err; } @@ -276,7 +275,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d) state->i2c_client[num] = NULL; return; err: - dev_dbg(&d->udev->dev, "%s: failed\n", __func__); + dev_dbg(&intf->dev, "failed\n"); } static int af9035_i2c_master_xfer(struct i2c_adapter *adap, @@ -496,6 +495,7 @@ static struct i2c_algorithm af9035_i2c_algo = { static int af9035_identify_state(struct dvb_usb_device *d, const char **name) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; @@ -513,10 +513,8 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) if (ret < 0) goto err; - dev_info(&d->udev->dev, - "%s: prechip_version=%02x chip_version=%02x chip_type=%04x\n", - KBUILD_MODNAME, state->prechip_version, - state->chip_version, state->chip_type); + dev_info(&intf->dev, "prechip_version=%02x chip_version=%02x chip_type=%04x\n", + state->prechip_version, state->chip_version, state->chip_type); if (state->chip_type == 0x9135) { if (state->chip_version == 0x02) @@ -536,7 +534,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: reply=%*ph\n", __func__, 4, rbuf); + dev_dbg(&intf->dev, "reply=%*ph\n", 4, rbuf); if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) ret = WARM; else @@ -545,7 +543,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -553,6 +551,7 @@ err: static int af9035_download_firmware_old(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; int ret, i, j, len; u8 wbuf[1]; struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; @@ -583,14 +582,12 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d, hdr_checksum = fw->data[fw->size - i + 5] << 8; hdr_checksum |= fw->data[fw->size - i + 6] << 0; - dev_dbg(&d->udev->dev, - "%s: core=%d addr=%04x data_len=%d checksum=%04x\n", - __func__, hdr_core, hdr_addr, hdr_data_len, - hdr_checksum); + dev_dbg(&intf->dev, "core=%d addr=%04x data_len=%d checksum=%04x\n", + hdr_core, hdr_addr, hdr_data_len, hdr_checksum); if (((hdr_core != 1) && (hdr_core != 2)) || (hdr_data_len > i)) { - dev_dbg(&d->udev->dev, "%s: bad firmware\n", __func__); + dev_dbg(&intf->dev, "bad firmware\n"); break; } @@ -621,18 +618,17 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d, i -= hdr_data_len + HDR_SIZE; - dev_dbg(&d->udev->dev, "%s: data uploaded=%zu\n", - __func__, fw->size - i); + dev_dbg(&intf->dev, "data uploaded=%zu\n", fw->size - i); } /* print warn if firmware is bad, continue and see what happens */ if (i) - dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); + dev_warn(&intf->dev, "bad firmware\n"); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -640,6 +636,7 @@ err: static int af9035_download_firmware_new(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; int ret, i, i_prev; struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; #define HDR_SIZE 7 @@ -669,15 +666,14 @@ static int af9035_download_firmware_new(struct dvb_usb_device *d, if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: data uploaded=%d\n", - __func__, i); + dev_dbg(&intf->dev, "data uploaded=%d\n", i); } } return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -685,6 +681,7 @@ err: static int af9035_download_firmware(struct dvb_usb_device *d, const struct firmware *fw) { + struct usb_interface *intf = d->intf; struct state *state = d_to_priv(d); int ret; u8 wbuf[1]; @@ -693,7 +690,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d, struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf }; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&intf->dev, "\n"); /* * In case of dual tuner configuration we need to do some extra @@ -773,25 +770,25 @@ static int af9035_download_firmware(struct dvb_usb_device *d, goto err; if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { - dev_err(&d->udev->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); + dev_err(&intf->dev, "firmware did not run\n"); ret = -ENODEV; goto err; } - dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d", - KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + dev_info(&intf->dev, "firmware version=%d.%d.%d.%d", + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } static int af9035_read_config(struct dvb_usb_device *d) { + struct usb_interface *intf = d->intf; struct state *state = d_to_priv(d); int ret, i; u8 tmp; @@ -826,7 +823,7 @@ static int af9035_read_config(struct dvb_usb_device *d) goto err; if (tmp == 0x00) { - dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__); + dev_dbg(&intf->dev, "no eeprom\n"); goto skip_eeprom; } } else if (state->chip_type == 0x9306) { @@ -847,8 +844,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (tmp == 1 || tmp == 3 || tmp == 5) state->dual_mode = true; - dev_dbg(&d->udev->dev, "%s: ts mode=%d dual mode=%d\n", __func__, - tmp, state->dual_mode); + dev_dbg(&intf->dev, "ts mode=%d dual mode=%d\n", tmp, state->dual_mode); if (state->dual_mode) { /* read 2nd demodulator I2C address */ @@ -861,8 +857,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (tmp) state->af9033_i2c_addr[1] = tmp; - dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", - __func__, tmp); + dev_dbg(&intf->dev, "2nd demod I2C addr=%02x\n", tmp); } addr = state->eeprom_addr; @@ -873,8 +868,7 @@ static int af9035_read_config(struct dvb_usb_device *d) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n", - __func__, i, tmp); + dev_dbg(&intf->dev, "[%d]tuner=%02x\n", i, tmp); /* tuner sanity check */ if (state->chip_type == 0x9135) { @@ -903,10 +897,8 @@ static int af9035_read_config(struct dvb_usb_device *d) } if (state->af9033_config[i].tuner != tmp) { - dev_info(&d->udev->dev, - "%s: [%d] overriding tuner from %02x to %02x\n", - KBUILD_MODNAME, i, tmp, - state->af9033_config[i].tuner); + dev_info(&intf->dev, "[%d] overriding tuner from %02x to %02x\n", + i, tmp, state->af9033_config[i].tuner); } switch (state->af9033_config[i].tuner) { @@ -926,9 +918,8 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_IT9135_62: break; default: - dev_warn(&d->udev->dev, - "%s: tuner id=%02x not supported, please report!", - KBUILD_MODNAME, tmp); + dev_warn(&intf->dev, "tuner id=%02x not supported, please report!", + tmp); } /* disable dual mode if driver does not support it */ @@ -945,9 +936,7 @@ static int af9035_read_config(struct dvb_usb_device *d) break; default: state->dual_mode = false; - dev_info(&d->udev->dev, - "%s: driver does not support 2nd tuner and will disable it", - KBUILD_MODNAME); + dev_info(&intf->dev, "driver does not support 2nd tuner and will disable it"); } /* tuner IF frequency */ @@ -963,7 +952,7 @@ static int af9035_read_config(struct dvb_usb_device *d) tmp16 |= tmp << 8; - dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16); + dev_dbg(&intf->dev, "[%d]IF=%d\n", i, tmp16); addr += 0x10; /* shift for the 2nd tuner params */ } @@ -991,9 +980,8 @@ skip_eeprom: switch (le16_to_cpu(d->udev->descriptor.idProduct)) { case USB_PID_AVERMEDIA_A867: case USB_PID_AVERMEDIA_TWINSTAR: - dev_info(&d->udev->dev, - "%s: Device may have issues with I2C read operations. Enabling fix.\n", - KBUILD_MODNAME); + dev_info(&intf->dev, + "Device may have issues with I2C read operations. Enabling fix.\n"); state->no_read = true; break; } @@ -1001,7 +989,7 @@ skip_eeprom: return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1009,10 +997,11 @@ err: static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { + struct usb_interface *intf = d->intf; int ret; u8 val; - dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + dev_dbg(&intf->dev, "cmd=%d arg=%d\n", cmd, arg); /* * CEN always enabled by hardware wiring @@ -1046,7 +1035,7 @@ static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d, return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1055,6 +1044,7 @@ err: static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { + struct usb_interface *intf = d->intf; int ret; switch (cmd) { @@ -1112,7 +1102,7 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1138,9 +1128,10 @@ static int af9035_frontend_callback(void *adapter_priv, int component, { struct i2c_adapter *adap = adapter_priv; struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n", - __func__, component, cmd, arg); + dev_dbg(&intf->dev, "component=%d cmd=%d arg=%d\n", + component, cmd, arg); switch (component) { case DVB_FRONTEND_COMPONENT_TUNER: @@ -1163,9 +1154,10 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); if (!state->af9033_config[adap->id].tuner) { /* unsupported tuner */ @@ -1192,7 +1184,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1201,11 +1193,12 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct si2168_config si2168_config; struct i2c_adapter *adapter; - dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &adapter; @@ -1228,7 +1221,7 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1237,9 +1230,10 @@ static int af9035_frontend_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int demod2; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* * For dual tuner devices we have to resolve 2nd demod client, as there @@ -1315,12 +1309,13 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct dvb_frontend *fe; struct i2c_msg msg[1]; u8 tuner_addr; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] @@ -1558,7 +1553,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1567,10 +1562,11 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; int ret; struct si2157_config si2157_config; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); /* I2C master bus 2 clock speed 300k */ ret = af9035_wr_reg(d, 0x00f6a7, 0x07); @@ -1626,7 +1622,7 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1636,8 +1632,9 @@ static int it930x_tuner_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); if (adap->id == 1) { if (state->i2c_client[3]) @@ -1655,8 +1652,9 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); + dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: @@ -1682,6 +1680,7 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap) static int af9035_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, i; u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4; u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; @@ -1706,9 +1705,8 @@ static int af9035_init(struct dvb_usb_device *d) { 0x80f9a4, 0x00, 0x01 }, }; - dev_dbg(&d->udev->dev, - "%s: USB speed=%d frame_size=%04x packet_size=%02x\n", - __func__, d->udev->speed, frame_size, packet_size); + dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n", + d->udev->speed, frame_size, packet_size); /* init endpoints */ for (i = 0; i < ARRAY_SIZE(tab); i++) { @@ -1721,7 +1719,7 @@ static int af9035_init(struct dvb_usb_device *d) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1729,6 +1727,7 @@ err: static int it930x_init(struct dvb_usb_device *d) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret, i; u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 816) * 188 / 4; u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4; @@ -1788,9 +1787,8 @@ static int it930x_init(struct dvb_usb_device *d) { 0x00da5a, 0x1f, 0xff }, /* ts_fail_ignore */ }; - dev_dbg(&d->udev->dev, - "%s: USB speed=%d frame_size=%04x packet_size=%02x\n", - __func__, d->udev->speed, frame_size, packet_size); + dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n", + d->udev->speed, frame_size, packet_size); /* init endpoints */ for (i = 0; i < ARRAY_SIZE(tab); i++) { @@ -1803,7 +1801,7 @@ static int it930x_init(struct dvb_usb_device *d) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1812,6 +1810,7 @@ err: #if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { + struct usb_interface *intf = d->intf; int ret; u32 key; u8 buf[4]; @@ -1837,14 +1836,14 @@ static int af9035_rc_query(struct dvb_usb_device *d) buf[2] << 8 | buf[3]); } - dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf); + dev_dbg(&intf->dev, "%*ph\n", 4, buf); rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1852,6 +1851,7 @@ err: static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { struct state *state = d_to_priv(d); + struct usb_interface *intf = d->intf; int ret; u8 tmp; @@ -1859,7 +1859,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: ir_mode=%02x\n", __func__, tmp); + dev_dbg(&intf->dev, "ir_mode=%02x\n", tmp); /* don't activate rc if in HID mode or if not available */ if (tmp == 5) { @@ -1868,7 +1868,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (ret < 0) goto err; - dev_dbg(&d->udev->dev, "%s: ir_type=%02x\n", __func__, tmp); + dev_dbg(&intf->dev, "ir_type=%02x\n", tmp); switch (tmp) { case 0: /* NEC */ @@ -1891,7 +1891,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -1903,8 +1903,9 @@ static int af9035_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, struct usb_data_stream_properties *stream) { struct dvb_usb_device *d = fe_to_d(fe); + struct usb_interface *intf = d->intf; - dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id); + dev_dbg(&intf->dev, "adap=%d\n", fe_to_adap(fe)->id); if (d->udev->speed == USB_SPEED_FULL) stream->u.bulk.buffersize = 5 * 188; @@ -1956,7 +1957,7 @@ static int af9035_probe(struct usb_interface *intf, if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) && (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) { if (!strcmp("Afatech", manufacturer)) { - dev_dbg(&udev->dev, "%s: rejecting device\n", __func__); + dev_dbg(&udev->dev, "rejecting device\n"); return -ENODEV; } } -- cgit v0.10.2 From d10f89cc36c3a302165d9d800ef97449793b9e6a Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 7 Jun 2016 18:31:47 -0300 Subject: [media] rtl2832: add support for slave ts pid filter The rtl2832 demod has 2 sets of PID filters. This patch enables the filter support when using a slave demod. Signed-off-by: Benjamin Larsson Signed-off-by: Martin Blumenstingl Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index bfb6bee..c16c69e 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -947,6 +947,8 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) goto err; } + dev->slave_ts = enable; + return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -960,7 +962,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) int ret; u8 u8tmp; - dev_dbg(&client->dev, "onoff=%d\n", onoff); + dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts); /* enable / disable PID filter */ if (onoff) @@ -968,7 +970,10 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) else u8tmp = 0x00; - ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); + if (dev->slave_ts) + ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp); + else + ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); if (ret) goto err; @@ -986,8 +991,8 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int ret; u8 buf[4]; - dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", - index, pid, onoff); + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n", + index, pid, onoff, dev->slave_ts); /* skip invalid PIDs (0x2000) */ if (pid > 0x1fff || index > 32) @@ -1003,14 +1008,22 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, buf[1] = (dev->filters >> 8) & 0xff; buf[2] = (dev->filters >> 16) & 0xff; buf[3] = (dev->filters >> 24) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4); + else + ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); if (ret) goto err; /* add PID */ buf[0] = (pid >> 8) & 0xff; buf[1] = (pid >> 0) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); + + if (dev->slave_ts) + ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2); + else + ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); if (ret) goto err; diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index c1a8a69..9a6d01a 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -44,6 +44,7 @@ struct rtl2832_dev { bool sleeping; struct delayed_work i2c_gate_work; unsigned long filters; /* PID filter */ + bool slave_ts; }; struct rtl2832_reg_entry { -- cgit v0.10.2 From 917d11a4e0c8c9e3d375a7baa945fe0da6b15f88 Mon Sep 17 00:00:00 2001 From: Zhaoxiu Zeng Date: Wed, 27 Apr 2016 04:07:08 -0300 Subject: [media] mt2063: use lib gcd This patch removes the local MT2063_gcd function, uses lib gcd instead Signed-off-by: Zhaoxiu Zeng Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 6457ac9..7f0b9d5 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "mt2063.h" @@ -665,27 +666,6 @@ static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info) } /** - * gcd() - Uses Euclid's algorithm - * - * @u, @v: Unsigned values whose GCD is desired. - * - * Returns THE greatest common divisor of u and v, if either value is 0, - * the other value is returned as the result. - */ -static u32 MT2063_gcd(u32 u, u32 v) -{ - u32 r; - - while (v != 0) { - r = u % v; - u = v; - v = r; - } - - return u; -} - -/** * IsSpurInBand() - Checks to see if a spur will be present within the IF's * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) * @@ -731,12 +711,12 @@ static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info, ** of f_LO1, f_LO2 and the edge value. Use the larger of this ** gcd-based scale factor or f_Scale. */ - lo_gcd = MT2063_gcd(f_LO1, f_LO2); - gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale); + lo_gcd = gcd(f_LO1, f_LO2); + gd_Scale = max((u32) gcd(lo_gcd, d), f_Scale); hgds = gd_Scale / 2; - gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale); + gc_Scale = max((u32) gcd(lo_gcd, c), f_Scale); hgcs = gc_Scale / 2; - gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale); + gf_Scale = max((u32) gcd(lo_gcd, f), f_Scale); hgfs = gf_Scale / 2; n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2); -- cgit v0.10.2 From e44c153b30c9a0580fc2b5a93f3c6d593def2278 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 May 2016 05:22:55 -0300 Subject: [media] em28xx-i2c: rt_mutex_trylock() returns zero on failure The code is checking for negative returns but it should be checking for zero. Fixes: aab3125c43d8 ('[media] em28xx: add support for registering multiple i2c buses') Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a19b5c8..1a9e1e5 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, if (dev->disconnected) return -ENODEV; - rc = rt_mutex_trylock(&dev->i2c_bus_lock); - if (rc < 0) - return rc; + if (!rt_mutex_trylock(&dev->i2c_bus_lock)) + return -EAGAIN; /* Switch I2C bus if needed */ if (bus != dev->cur_i2c_bus && -- cgit v0.10.2 From cac1c639bc336a9de90ecd7fa97180d3f4acc3f3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 10 May 2016 02:40:22 -0300 Subject: [media] m88rs2000: initialize status to zero status is not initialized so it can contain garbage. The check for status containing the FE_HAS_LOCK bit may randomly pass or fail if the read of register 0x8c fails to set status after 25 read attempts. Fix this by initializing status to 0. Issue found with CoverityScan, CID#986738 Signed-off-by: Colin Ian King Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index a09b123..ef79a4e 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -609,7 +609,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - enum fe_status status; + enum fe_status status = 0; int i, ret = 0; u32 tuner_freq; s16 offset = 0; -- cgit v0.10.2 From dc19ed1571dd3882b35e12fdaf50acbcc9b69714 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Jun 2016 14:48:50 -0300 Subject: Update my main e-mails at the Kernel tree For the third time in three years, I'm changing my e-mail at Samsung. That's bad, as it may stop communications with me for a while. So, this time, I'll also the mchehab@kernel.org e-mail, as it remains stable since ever. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/.mailmap b/.mailmap index 08b8042..4a293be 100644 --- a/.mailmap +++ b/.mailmap @@ -89,6 +89,7 @@ Leonid I Ananiev Linas Vepstas Mark Brown Matthieu CASTET +Mauro Carvalho Chehab Mayuresh Janorkar Michael Buesch Michel Dänzer diff --git a/CREDITS b/CREDITS index 0f0bf22..2a3fbcd 100644 --- a/CREDITS +++ b/CREDITS @@ -649,6 +649,7 @@ D: Configure, Menuconfig, xconfig N: Mauro Carvalho Chehab E: m.chehab@samsung.org +E: mchehab@osg.samsung.com E: mchehab@infradead.org D: Media subsystem (V4L/DVB) drivers and core D: EDAC drivers and EDAC 3.0 core rework diff --git a/MAINTAINERS b/MAINTAINERS index 45fad6c..02299fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2242,7 +2242,8 @@ F: include/net/ax25.h F: net/ax25/ AZ6007 DVB DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -2709,7 +2710,8 @@ F: Documentation/filesystems/btrfs.txt F: fs/btrfs/ BTTV VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -3343,7 +3345,8 @@ S: Maintained F: drivers/media/dvb-frontends/cx24120* CX88 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -4290,7 +4293,8 @@ F: fs/ecryptfs/ EDAC-CORE M: Doug Thompson M: Borislav Petkov -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next @@ -4335,7 +4339,8 @@ S: Maintained F: drivers/edac/e7xxx_edac.c EDAC-GHES -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/ghes_edac.c @@ -4359,19 +4364,22 @@ S: Maintained F: drivers/edac/i5000_edac.c EDAC-I5400 -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/i5400_edac.c EDAC-I7300 -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/i7300_edac.c EDAC-I7CORE -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/i7core_edac.c @@ -4408,7 +4416,8 @@ S: Maintained F: drivers/edac/r82600_edac.c EDAC-SBRIDGE -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/sb_edac.c @@ -4467,7 +4476,8 @@ S: Maintained F: drivers/net/ethernet/ibm/ehea/ EM28XX VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -7371,7 +7381,8 @@ S: Supported F: drivers/media/pci/netup_unidvb/* MEDIA INPUT INFRASTRUCTURE (V4L/DVB) -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab P: LinuxTV.org Project L: linux-media@vger.kernel.org W: https://linuxtv.org @@ -9863,7 +9874,8 @@ S: Odd Fixes F: drivers/media/i2c/saa6588* SAA7134 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -10382,7 +10394,8 @@ S: Maintained F: drivers/media/radio/si4713/radio-usb-si4713.c SIANO DVB DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -11148,7 +11161,8 @@ S: Maintained F: drivers/media/i2c/tda9840* TEA5761 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -11156,7 +11170,8 @@ S: Odd fixes F: drivers/media/tuners/tea5761.* TEA5767 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -11543,7 +11558,8 @@ F: include/linux/shmem_fs.h F: mm/shmem.c TM6000 VIDEO4LINUX DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git @@ -12529,7 +12545,8 @@ S: Maintained F: arch/x86/entry/vdso/ XC2028/3028 TUNER DRIVER -M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab +M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index ca94bde..8bef433 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -1,7 +1,7 @@ /* * Media Controller ancillary functions * - * Copyright (c) 2016 Mauro Carvalho Chehab + * Copyright (c) 2016 Mauro Carvalho Chehab * Copyright (C) 2016 Shuah Khan * Copyright (C) 2006-2010 Nokia Corporation * Copyright (c) 2016 Intel Corporation. diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index 98a938a..7a8d603 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -1,7 +1,7 @@ /* * v4l2-mc.h - Media Controller V4L2 types and prototypes * - * Copyright (C) 2016 Mauro Carvalho Chehab + * Copyright (C) 2016 Mauro Carvalho Chehab * Copyright (C) 2006-2010 Nokia Corporation * Copyright (c) 2016 Intel Corporation. * -- cgit v0.10.2 From 163f1e93e995048b894c5fc86a6034d16beed740 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Mar 2016 11:22:57 -0300 Subject: [media] media-devnode: fix namespace mess Along all media controller code, "mdev" is used to represent a pointer to struct media_device, and "devnode" for a pointer to struct media_devnode. However, inside media-devnode.[ch], "mdev" is used to represent a pointer to struct media_devnode. This is very confusing and may lead to development errors. So, let's change all occurrences at media-devnode.[ch] to also use "devnode" for such pointers. This patch doesn't make any functional changes. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index b66dc9d..7481c96 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -59,21 +59,21 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); /* Called when the last user of the media device exits. */ static void media_devnode_release(struct device *cd) { - struct media_devnode *mdev = to_media_devnode(cd); + struct media_devnode *devnode = to_media_devnode(cd); mutex_lock(&media_devnode_lock); /* Delete the cdev on this minor as well */ - cdev_del(&mdev->cdev); + cdev_del(&devnode->cdev); /* Mark device node number as free */ - clear_bit(mdev->minor, media_devnode_nums); + clear_bit(devnode->minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); /* Release media_devnode and perform other cleanups as needed. */ - if (mdev->release) - mdev->release(mdev); + if (devnode->release) + devnode->release(devnode); } static struct bus_type media_bus_type = { @@ -83,37 +83,37 @@ static struct bus_type media_bus_type = { static ssize_t media_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->read) + if (!devnode->fops->read) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->read(filp, buf, sz, off); + return devnode->fops->read(filp, buf, sz, off); } static ssize_t media_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!mdev->fops->write) + if (!devnode->fops->write) return -EINVAL; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; - return mdev->fops->write(filp, buf, sz, off); + return devnode->fops->write(filp, buf, sz, off); } static unsigned int media_poll(struct file *filp, struct poll_table_struct *poll) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return POLLERR | POLLHUP; - if (!mdev->fops->poll) + if (!devnode->fops->poll) return DEFAULT_POLLMASK; - return mdev->fops->poll(filp, poll); + return devnode->fops->poll(filp, poll); } static long @@ -121,12 +121,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, long (*ioctl_func)(struct file *filp, unsigned int cmd, unsigned long arg)) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); if (!ioctl_func) return -ENOTTY; - if (!media_devnode_is_registered(mdev)) + if (!media_devnode_is_registered(devnode)) return -EIO; return ioctl_func(filp, cmd, arg); @@ -134,9 +134,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); } #ifdef CONFIG_COMPAT @@ -144,9 +144,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static long media_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); + return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); } #endif /* CONFIG_COMPAT */ @@ -154,7 +154,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd, /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { - struct media_devnode *mdev; + struct media_devnode *devnode; int ret; /* Check if the media device is available. This needs to be done with @@ -164,23 +164,23 @@ static int media_open(struct inode *inode, struct file *filp) * a crash. */ mutex_lock(&media_devnode_lock); - mdev = container_of(inode->i_cdev, struct media_devnode, cdev); + devnode = container_of(inode->i_cdev, struct media_devnode, cdev); /* return ENXIO if the media device has been removed already or if it is not registered anymore. */ - if (!media_devnode_is_registered(mdev)) { + if (!media_devnode_is_registered(devnode)) { mutex_unlock(&media_devnode_lock); return -ENXIO; } /* and increase the device refcount */ - get_device(&mdev->dev); + get_device(&devnode->dev); mutex_unlock(&media_devnode_lock); - filp->private_data = mdev; + filp->private_data = devnode; - if (mdev->fops->open) { - ret = mdev->fops->open(filp); + if (devnode->fops->open) { + ret = devnode->fops->open(filp); if (ret) { - put_device(&mdev->dev); + put_device(&devnode->dev); filp->private_data = NULL; return ret; } @@ -192,16 +192,16 @@ static int media_open(struct inode *inode, struct file *filp) /* Override for the release function */ static int media_release(struct inode *inode, struct file *filp) { - struct media_devnode *mdev = media_devnode_data(filp); + struct media_devnode *devnode = media_devnode_data(filp); - if (mdev->fops->release) - mdev->fops->release(filp); + if (devnode->fops->release) + devnode->fops->release(filp); filp->private_data = NULL; /* decrease the refcount unconditionally since the release() return value is ignored. */ - put_device(&mdev->dev); + put_device(&devnode->dev); return 0; } @@ -219,7 +219,7 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -int __must_check media_devnode_register(struct media_devnode *mdev, +int __must_check media_devnode_register(struct media_devnode *devnode, struct module *owner) { int minor; @@ -237,55 +237,55 @@ int __must_check media_devnode_register(struct media_devnode *mdev, set_bit(minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); - mdev->minor = minor; + devnode->minor = minor; /* Part 2: Initialize and register the character device */ - cdev_init(&mdev->cdev, &media_devnode_fops); - mdev->cdev.owner = owner; + cdev_init(&devnode->cdev, &media_devnode_fops); + devnode->cdev.owner = owner; - ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); + ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); if (ret < 0) { pr_err("%s: cdev_add failed\n", __func__); goto error; } /* Part 3: Register the media device */ - mdev->dev.bus = &media_bus_type; - mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); - mdev->dev.release = media_devnode_release; - if (mdev->parent) - mdev->dev.parent = mdev->parent; - dev_set_name(&mdev->dev, "media%d", mdev->minor); - ret = device_register(&mdev->dev); + devnode->dev.bus = &media_bus_type; + devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); + devnode->dev.release = media_devnode_release; + if (devnode->parent) + devnode->dev.parent = devnode->parent; + dev_set_name(&devnode->dev, "media%d", devnode->minor); + ret = device_register(&devnode->dev); if (ret < 0) { pr_err("%s: device_register failed\n", __func__); goto error; } /* Part 4: Activate this minor. The char device can now be used. */ - set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); return 0; error: mutex_lock(&media_devnode_lock); - cdev_del(&mdev->cdev); - clear_bit(mdev->minor, media_devnode_nums); + cdev_del(&devnode->cdev); + clear_bit(devnode->minor, media_devnode_nums); mutex_unlock(&media_devnode_lock); return ret; } -void media_devnode_unregister(struct media_devnode *mdev) +void media_devnode_unregister(struct media_devnode *devnode) { - /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(mdev)) + /* Check if devnode was ever registered at all */ + if (!media_devnode_is_registered(devnode)) return; mutex_lock(&media_devnode_lock); - clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); mutex_unlock(&media_devnode_lock); - device_unregister(&mdev->dev); + device_unregister(&devnode->dev); } /* diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index fe42f08..e1d5af0 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -94,7 +94,7 @@ struct media_devnode { unsigned long flags; /* Use bitops to access flags */ /* callbacks */ - void (*release)(struct media_devnode *mdev); + void (*release)(struct media_devnode *devnode); }; /* dev to media_devnode */ @@ -103,7 +103,7 @@ struct media_devnode { /** * media_devnode_register - register a media device node * - * @mdev: media device node structure we want to register + * @devnode: media device node structure we want to register * @owner: should be filled with %THIS_MODULE * * The registration code assigns minor numbers and registers the new device node @@ -116,12 +116,12 @@ struct media_devnode { * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ -int __must_check media_devnode_register(struct media_devnode *mdev, +int __must_check media_devnode_register(struct media_devnode *devnode, struct module *owner); /** * media_devnode_unregister - unregister a media device node - * @mdev: the device node to unregister + * @devnode: the device node to unregister * * This unregisters the passed device. Future open calls will be met with * errors. @@ -129,7 +129,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev, * This function can safely be called if the device node has never been * registered or has already been unregistered. */ -void media_devnode_unregister(struct media_devnode *mdev); +void media_devnode_unregister(struct media_devnode *devnode); /** * media_devnode_data - returns a pointer to the &media_devnode @@ -145,11 +145,11 @@ static inline struct media_devnode *media_devnode_data(struct file *filp) * media_devnode_is_registered - returns true if &media_devnode is registered; * false otherwise. * - * @mdev: pointer to struct &media_devnode. + * @devnode: pointer to struct &media_devnode. */ -static inline int media_devnode_is_registered(struct media_devnode *mdev) +static inline int media_devnode_is_registered(struct media_devnode *devnode) { - return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); + return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); } #endif /* _MEDIA_DEVNODE_H */ -- cgit v0.10.2 From a087ce704b802becbb4b0f2a20f2cb3f6911802e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 27 Apr 2016 19:28:26 -0300 Subject: [media] media-device: dynamically allocate struct media_devnode struct media_devnode is currently embedded at struct media_device. While this works fine during normal usage, it leads to a race condition during devnode unregister. the problem is that drivers assume that, after calling media_device_unregister(), the struct that contains media_device can be freed. This is not true, as it can't be freed until userspace closes all opened /dev/media devnodes. In other words, if the media devnode is still open, and media_device gets freed, any call to an ioctl will make the core to try to access struct media_device, with will cause an use-after-free and even GPF. Fix this by dynamically allocating the struct media_devnode and only freeing it when it is safe. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index a1cd50f..e61fa66 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -423,7 +423,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; mutex_lock(&dev->graph_mutex); @@ -495,7 +495,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); - struct media_device *dev = to_media_device(devnode); + struct media_device *dev = devnode->media_dev; long ret; switch (cmd) { @@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = { static ssize_t show_model(struct device *cd, struct device_attribute *attr, char *buf) { - struct media_device *mdev = to_media_device(to_media_devnode(cd)); + struct media_devnode *devnode = to_media_devnode(cd); + struct media_device *mdev = devnode->media_dev; return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); } @@ -704,23 +705,34 @@ EXPORT_SYMBOL_GPL(media_device_cleanup); int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { + struct media_devnode *devnode; int ret; + devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); + if (!devnode) + return -ENOMEM; + /* Register the device node. */ - mdev->devnode.fops = &media_device_fops; - mdev->devnode.parent = mdev->dev; - mdev->devnode.release = media_device_release; + mdev->devnode = devnode; + devnode->fops = &media_device_fops; + devnode->parent = mdev->dev; + devnode->release = media_device_release; /* Set version 0 to indicate user-space that the graph is static */ mdev->topology_version = 0; - ret = media_devnode_register(&mdev->devnode, owner); - if (ret < 0) + ret = media_devnode_register(mdev, devnode, owner); + if (ret < 0) { + mdev->devnode = NULL; + kfree(devnode); return ret; + } - ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); + ret = device_create_file(&devnode->dev, &dev_attr_model); if (ret < 0) { - media_devnode_unregister(&mdev->devnode); + mdev->devnode = NULL; + media_devnode_unregister(devnode); + kfree(devnode); return ret; } @@ -771,7 +783,7 @@ void media_device_unregister(struct media_device *mdev) mutex_lock(&mdev->graph_mutex); /* Check if mdev was ever registered at all */ - if (!media_devnode_is_registered(&mdev->devnode)) { + if (!media_devnode_is_registered(mdev->devnode)) { mutex_unlock(&mdev->graph_mutex); return; } @@ -794,9 +806,13 @@ 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"); + + /* Check if mdev devnode was registered */ + if (media_devnode_is_registered(mdev->devnode)) { + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); + } } EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 7481c96..ecdc02d 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -44,6 +44,7 @@ #include #include +#include #define MEDIA_NUM_DEVICES 256 #define MEDIA_NAME "media" @@ -74,6 +75,8 @@ static void media_devnode_release(struct device *cd) /* Release media_devnode and perform other cleanups as needed. */ if (devnode->release) devnode->release(devnode); + + kfree(devnode); } static struct bus_type media_bus_type = { @@ -219,7 +222,8 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -int __must_check media_devnode_register(struct media_devnode *devnode, +int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, struct module *owner) { int minor; @@ -238,6 +242,7 @@ int __must_check media_devnode_register(struct media_devnode *devnode, mutex_unlock(&media_devnode_lock); devnode->minor = minor; + devnode->media_dev = mdev; /* Part 2: Initialize and register the character device */ cdev_init(&devnode->cdev, &media_devnode_fops); diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 321ea5c..bf53553 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -142,7 +142,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev) struct media_device *mdev = dev->media_dev; struct media_entity_notify *notify, *nextp; - if (!mdev || !media_devnode_is_registered(&mdev->devnode)) + if (!mdev || !media_devnode_is_registered(mdev->devnode)) return; /* Remove au0828 entity_notify callbacks */ @@ -482,7 +482,7 @@ static int au0828_media_device_register(struct au0828_dev *dev, if (!dev->media_dev) return 0; - if (!media_devnode_is_registered(&dev->media_dev->devnode)) { + if (!media_devnode_is_registered(dev->media_dev->devnode)) { /* register media device */ ret = media_device_register(dev->media_dev); diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 451e84e9..302e284 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1674,7 +1674,7 @@ static void uvc_delete(struct uvc_device *dev) if (dev->vdev.dev) v4l2_device_unregister(&dev->vdev); #ifdef CONFIG_MEDIA_CONTROLLER - if (media_devnode_is_registered(&dev->mdev.devnode)) + if (media_devnode_is_registered(dev->mdev.devnode)) media_device_unregister(&dev->mdev); media_device_cleanup(&dev->mdev); #endif diff --git a/include/media/media-device.h b/include/media/media-device.h index a9b33c4..f743ae2 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -347,7 +347,7 @@ struct media_entity_notify { struct media_device { /* dev->driver_data points to this struct. */ struct device *dev; - struct media_devnode devnode; + struct media_devnode *devnode; char model[32]; char driver_name[32]; @@ -393,9 +393,6 @@ struct usb_device; #define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0 #define MEDIA_DEV_NOTIFY_POST_LINK_CH 1 -/* media_devnode to media_device */ -#define to_media_device(node) container_of(node, struct media_device, devnode) - /** * media_entity_enum_init - Initialise an entity enumeration * diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index e1d5af0..5bb3b0e 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -33,6 +33,8 @@ #include #include +struct media_device; + /* * Flag to mark the media_devnode struct as registered. Drivers must not touch * this flag directly, it will be set and cleared by media_devnode_register and @@ -81,6 +83,8 @@ struct media_file_operations { * before registering the node. */ struct media_devnode { + struct media_device *media_dev; + /* device ops */ const struct media_file_operations *fops; @@ -103,6 +107,7 @@ struct media_devnode { /** * media_devnode_register - register a media device node * + * @media_dev: struct media_device we want to register a device node * @devnode: media device node structure we want to register * @owner: should be filled with %THIS_MODULE * @@ -116,7 +121,8 @@ struct media_devnode { * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ -int __must_check media_devnode_register(struct media_devnode *devnode, +int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, struct module *owner); /** @@ -146,9 +152,14 @@ static inline struct media_devnode *media_devnode_data(struct file *filp) * false otherwise. * * @devnode: pointer to struct &media_devnode. + * + * Note: If mdev is NULL, it also returns false. */ static inline int media_devnode_is_registered(struct media_devnode *devnode) { + if (!devnode) + return false; + return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); } -- cgit v0.10.2 From 5b28dde51d0ccc54cee70756e1800d70bed7114a Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 4 May 2016 16:48:28 -0300 Subject: [media] media: fix use-after-free in cdev_put() when app exits after driver unbind When driver unbinds while media_ioctl is in progress, cdev_put() fails with when app exits after driver unbinds. Add devnode struct device kobj as the cdev parent kobject. cdev_add() gets a reference to it and releases it in cdev_del() ensuring that the devnode is not deallocated as long as the application has the device file open. media_devnode_register() initializes the struct device kobj before calling cdev_add(). media_devnode_unregister() does cdev_del() and then deletes the device. devnode is released when the last reference to the struct device is gone. This problem is found on uvcvideo, em28xx, and au0828 drivers and fix has been tested on all three. kernel: [ 193.599736] BUG: KASAN: use-after-free in cdev_put+0x4e/0x50 kernel: [ 193.599745] Read of size 8 by task media_device_te/1851 kernel: [ 193.599792] INFO: Allocated in __media_device_register+0x54 kernel: [ 193.599951] INFO: Freed in media_devnode_release+0xa4/0xc0 kernel: [ 193.601083] Call Trace: kernel: [ 193.601093] [] dump_stack+0x67/0x94 kernel: [ 193.601102] [] print_trailer+0x112/0x1a0 kernel: [ 193.601111] [] object_err+0x34/0x40 kernel: [ 193.601119] [] kasan_report_error+0x224/0x530 kernel: [ 193.601128] [] ? kzfree+0x2d/0x40 kernel: [ 193.601137] [] ? kfree+0x1d2/0x1f0 kernel: [ 193.601154] [] ? cdev_put+0x4e/0x50 kernel: [ 193.601162] [] cdev_put+0x4e/0x50 kernel: [ 193.601170] [] __fput+0x52b/0x6c0 kernel: [ 193.601179] [] ? switch_task_namespaces+0x2a kernel: [ 193.601188] [] ____fput+0xe/0x10 kernel: [ 193.601196] [] task_work_run+0x133/0x1f0 kernel: [ 193.601204] [] ? switch_task_namespaces+0x5e kernel: [ 193.601213] [] do_exit+0x72c/0x2c20 kernel: [ 193.601224] [] ? release_task+0x1250/0x1250 - - - kernel: [ 193.601360] [] ? exit_to_usermode_loop+0xe7 kernel: [ 193.601368] [] exit_to_usermode_loop+0x120 kernel: [ 193.601376] [] syscall_return_slowpath+0x16a kernel: [ 193.601386] [] entry_SYSCALL_64_fastpath+0xa6 Signed-off-by: Shuah Khan Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index e61fa66..33a9952 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -723,16 +723,16 @@ int __must_check __media_device_register(struct media_device *mdev, ret = media_devnode_register(mdev, devnode, owner); if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ mdev->devnode = NULL; - kfree(devnode); return ret; } ret = device_create_file(&devnode->dev, &dev_attr_model); if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ mdev->devnode = NULL; media_devnode_unregister(devnode); - kfree(devnode); return ret; } @@ -812,6 +812,8 @@ void media_device_unregister(struct media_device *mdev) if (media_devnode_is_registered(mdev->devnode)) { device_remove_file(&mdev->devnode->dev, &dev_attr_model); media_devnode_unregister(mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; } } EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index ecdc02d..5b605ff 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -63,13 +63,8 @@ static void media_devnode_release(struct device *cd) struct media_devnode *devnode = to_media_devnode(cd); mutex_lock(&media_devnode_lock); - - /* Delete the cdev on this minor as well */ - cdev_del(&devnode->cdev); - /* Mark device node number as free */ clear_bit(devnode->minor, media_devnode_nums); - mutex_unlock(&media_devnode_lock); /* Release media_devnode and perform other cleanups as needed. */ @@ -77,6 +72,7 @@ static void media_devnode_release(struct device *cd) devnode->release(devnode); kfree(devnode); + pr_debug("%s: Media Devnode Deallocated\n", __func__); } static struct bus_type media_bus_type = { @@ -205,6 +201,8 @@ static int media_release(struct inode *inode, struct file *filp) /* decrease the refcount unconditionally since the release() return value is ignored. */ put_device(&devnode->dev); + + pr_debug("%s: Media Release\n", __func__); return 0; } @@ -235,6 +233,7 @@ int __must_check media_devnode_register(struct media_device *mdev, if (minor == MEDIA_NUM_DEVICES) { mutex_unlock(&media_devnode_lock); pr_err("could not get a free minor\n"); + kfree(devnode); return -ENFILE; } @@ -244,27 +243,31 @@ int __must_check media_devnode_register(struct media_device *mdev, devnode->minor = minor; devnode->media_dev = mdev; + /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ + devnode->dev.bus = &media_bus_type; + devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); + devnode->dev.release = media_devnode_release; + if (devnode->parent) + devnode->dev.parent = devnode->parent; + dev_set_name(&devnode->dev, "media%d", devnode->minor); + device_initialize(&devnode->dev); + /* Part 2: Initialize and register the character device */ cdev_init(&devnode->cdev, &media_devnode_fops); devnode->cdev.owner = owner; + devnode->cdev.kobj.parent = &devnode->dev.kobj; ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); if (ret < 0) { pr_err("%s: cdev_add failed\n", __func__); - goto error; + goto cdev_add_error; } - /* Part 3: Register the media device */ - devnode->dev.bus = &media_bus_type; - devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); - devnode->dev.release = media_devnode_release; - if (devnode->parent) - devnode->dev.parent = devnode->parent; - dev_set_name(&devnode->dev, "media%d", devnode->minor); - ret = device_register(&devnode->dev); + /* Part 3: Add the media device */ + ret = device_add(&devnode->dev); if (ret < 0) { - pr_err("%s: device_register failed\n", __func__); - goto error; + pr_err("%s: device_add failed\n", __func__); + goto device_add_error; } /* Part 4: Activate this minor. The char device can now be used. */ @@ -272,12 +275,15 @@ int __must_check media_devnode_register(struct media_device *mdev, return 0; -error: - mutex_lock(&media_devnode_lock); +device_add_error: cdev_del(&devnode->cdev); +cdev_add_error: + mutex_lock(&media_devnode_lock); clear_bit(devnode->minor, media_devnode_nums); + devnode->media_dev = NULL; mutex_unlock(&media_devnode_lock); + put_device(&devnode->dev); return ret; } @@ -289,8 +295,12 @@ void media_devnode_unregister(struct media_devnode *devnode) mutex_lock(&media_devnode_lock); clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + /* Delete the cdev on this minor as well */ + cdev_del(&devnode->cdev); mutex_unlock(&media_devnode_lock); - device_unregister(&devnode->dev); + device_del(&devnode->dev); + devnode->media_dev = NULL; + put_device(&devnode->dev); } /* -- cgit v0.10.2 From 6f0dd24a084a17f9984dd49dffbf7055bf123993 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 10 Jun 2016 14:37:23 -0300 Subject: [media] media: fix media devnode ioctl/syscall and unregister race Media devnode open/ioctl could be in progress when media device unregister is initiated. System calls and ioctls check media device registered status at the beginning, however, there is a window where unregister could be in progress without changing the media devnode status to unregistered. process 1 process 2 fd = open(/dev/media0) media_devnode_is_registered() (returns true here) media_device_unregister() (unregister is in progress and devnode isn't unregistered yet) ... ioctl(fd, ...) __media_ioctl() media_devnode_is_registered() (returns true here) ... media_devnode_unregister() ... (driver releases the media device memory) media_device_ioctl() (By this point devnode->media_dev does not point to allocated memory. use-after free in in mutex_lock_nested) BUG: KASAN: use-after-free in mutex_lock_nested+0x79c/0x800 at addr ffff8801ebe914f0 Fix it by clearing register bit when unregister starts to avoid the race. process 1 process 2 fd = open(/dev/media0) media_devnode_is_registered() (could return true here) media_device_unregister() (clear the register bit, then start unregister.) ... ioctl(fd, ...) __media_ioctl() media_devnode_is_registered() (return false here, ioctl returns I/O error, and will not access media device memory) ... media_devnode_unregister() ... (driver releases the media device memory) Signed-off-by: Shuah Khan Suggested-by: Sakari Ailus Reported-by: Mauro Carvalho Chehab Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 33a9952..1795abe 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -732,6 +732,7 @@ int __must_check __media_device_register(struct media_device *mdev, if (ret < 0) { /* devnode free is handled in media_devnode_*() */ mdev->devnode = NULL; + media_devnode_unregister_prepare(devnode); media_devnode_unregister(devnode); return ret; } @@ -788,6 +789,9 @@ void media_device_unregister(struct media_device *mdev) return; } + /* Clear the devnode register bit to avoid races with media dev open */ + media_devnode_unregister_prepare(mdev->devnode); + /* Remove all entities from the media device */ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) __media_device_unregister_entity(entity); @@ -808,13 +812,10 @@ void media_device_unregister(struct media_device *mdev) dev_dbg(mdev->dev, "Media device unregistered\n"); - /* Check if mdev devnode was registered */ - if (media_devnode_is_registered(mdev->devnode)) { - device_remove_file(&mdev->devnode->dev, &dev_attr_model); - media_devnode_unregister(mdev->devnode); - /* devnode free is handled in media_devnode_*() */ - mdev->devnode = NULL; - } + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; } EXPORT_SYMBOL_GPL(media_device_unregister); diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 5b605ff..f2772ba 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -287,7 +287,7 @@ cdev_add_error: return ret; } -void media_devnode_unregister(struct media_devnode *devnode) +void media_devnode_unregister_prepare(struct media_devnode *devnode) { /* Check if devnode was ever registered at all */ if (!media_devnode_is_registered(devnode)) @@ -295,6 +295,12 @@ void media_devnode_unregister(struct media_devnode *devnode) mutex_lock(&media_devnode_lock); clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + mutex_unlock(&media_devnode_lock); +} + +void media_devnode_unregister(struct media_devnode *devnode) +{ + mutex_lock(&media_devnode_lock); /* Delete the cdev on this minor as well */ cdev_del(&devnode->cdev); mutex_unlock(&media_devnode_lock); diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 5bb3b0e..f0b7dd7 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -126,14 +126,26 @@ int __must_check media_devnode_register(struct media_device *mdev, struct module *owner); /** + * media_devnode_unregister_prepare - clear the media device node register bit + * @devnode: the device node to prepare for unregister + * + * This clears the passed device register bit. Future open calls will be met + * with errors. Should be called before media_devnode_unregister() to avoid + * races with unregister and device file open calls. + * + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +void media_devnode_unregister_prepare(struct media_devnode *devnode); + +/** * media_devnode_unregister - unregister a media device node * @devnode: the device node to unregister * * This unregisters the passed device. Future open calls will be met with * errors. * - * This function can safely be called if the device node has never been - * registered or has already been unregistered. + * Should be called after media_devnode_unregister_prepare() */ void media_devnode_unregister(struct media_devnode *devnode); -- cgit v0.10.2 From 126f40298446a82116e1f92a1aaf72b8c8228fae Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 May 2016 18:44:32 -0300 Subject: [media] vb2: core: Skip planes array verification if pb is NULL An earlier patch fixing an input validation issue introduced another issue: vb2_core_dqbuf() is called with pb argument value NULL in some cases, causing a NULL pointer dereference. Fix this by skipping the verification as there's nothing to verify. Fixes: e7e0c3e26587 ("[media] videobuf2-core: Check user space planes array in dqbuf") Signed-off-by: David R Signed-off-by: Sakari Ailus Reviewed-by: Hans Verkuil Cc: stable@vger.kernel.org # for v4.4 and later Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9fbcb67..633fc1a 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1648,7 +1648,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, void *pb, int nonblocking) { unsigned long flags; - int ret; + int ret = 0; /* * Wait for at least one buffer to become available on the done_list. @@ -1664,10 +1664,12 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, spin_lock_irqsave(&q->done_lock, flags); *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); /* - * Only remove the buffer from done_list if v4l2_buffer can handle all - * the planes. + * Only remove the buffer from done_list if all planes can be + * handled. Some cases such as V4L2 file I/O and DVB have pb + * == NULL; skip the check then as there's nothing to verify. */ - ret = call_bufop(q, verify_planes_array, *vb, pb); + if (pb) + ret = call_bufop(q, verify_planes_array, *vb, pb); if (!ret) list_del(&(*vb)->done_entry); spin_unlock_irqrestore(&q->done_lock, flags); -- cgit v0.10.2 From 83934b75c368f529d084815c463a7ef781dc9751 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 3 Apr 2016 16:31:03 -0300 Subject: [media] videobuf2-v4l2: Verify planes array in buffer dequeueing When a buffer is being dequeued using VIDIOC_DQBUF IOCTL, the exact buffer which will be dequeued is not known until the buffer has been removed from the queue. The number of planes is specific to a buffer, not to the queue. This does lead to the situation where multi-plane buffers may be requested and queued with n planes, but VIDIOC_DQBUF IOCTL may be passed an argument struct with fewer planes. __fill_v4l2_buffer() however uses the number of planes from the dequeued videobuf2 buffer, overwriting kernel memory (the m.planes array allocated in video_usercopy() in v4l2-ioctl.c) if the user provided fewer planes than the dequeued buffer had. Oops! Fixes: b0e0e1f83de3 ("[media] media: videobuf2: Prepare to divide videobuf2") Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Cc: stable@vger.kernel.org # for v4.4 and later Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-v4l2.c b/drivers/media/v4l2-core/videobuf2-v4l2.c index 0b1b8c7..7f366f1 100644 --- a/drivers/media/v4l2-core/videobuf2-v4l2.c +++ b/drivers/media/v4l2-core/videobuf2-v4l2.c @@ -74,6 +74,11 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer return 0; } +static int __verify_planes_array_core(struct vb2_buffer *vb, const void *pb) +{ + return __verify_planes_array(vb, pb); +} + /** * __verify_length() - Verify that the bytesused value for each plane fits in * the plane length and that the data offset doesn't exceed the bytesused value. @@ -437,6 +442,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, } static const struct vb2_buf_ops v4l2_buf_ops = { + .verify_planes_array = __verify_planes_array_core, .fill_user_buffer = __fill_v4l2_buffer, .fill_vb2_buffer = __fill_vb2_buffer, .copy_timestamp = __copy_timestamp, -- cgit v0.10.2 From f00add9655e17032617da602f7c636ad055761e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=83=C2=B6derlund?= Date: Tue, 26 Apr 2016 10:22:19 -0300 Subject: [media] rcar-vin: add Renesas R-Car VIN driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A V4L2 driver for Renesas R-Car VIN driver that do not depend on soc_camera. The driver is heavily based on its predecessor and aims to replace it. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 84e041c..382f393 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -110,6 +110,7 @@ source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" +source "drivers/media/platform/rcar-vin/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index bbb7bd1..99cf315 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -55,4 +55,6 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig new file mode 100644 index 0000000..b2ff2d4 --- /dev/null +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_RCAR_VIN + tristate "R-Car Video Input (VIN) Driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + ---help--- + Support for Renesas R-Car Video Input (VIN) driver. + Supports R-Car Gen2 SoCs. + + To compile this driver as a module, choose M here: the + module will be called rcar-vin. diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile new file mode 100644 index 0000000..48c5632 --- /dev/null +++ b/drivers/media/platform/rcar-vin/Makefile @@ -0,0 +1,3 @@ +rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o + +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c new file mode 100644 index 0000000..341c081 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -0,0 +1,337 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * Async notifier + */ + +#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) + +static int rvin_mbus_supported(struct rvin_dev *vin) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + sd = vin_to_source(vin); + + code.index = 0; + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + code.index++; + switch (code.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + vin->source.code = code.code; + vin_dbg(vin, "Found supported media bus format: %d\n", + vin->source.code); + return true; + default: + break; + } + } + + return false; +} + +static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct v4l2_subdev *sd; + struct rvin_dev *vin = notifier_to_vin(notifier); + int ret; + + sd = vin_to_source(vin); + + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); + if (ret < 0) { + vin_err(vin, "Failed to register subdev nodes\n"); + return ret; + } + + if (!rvin_mbus_supported(vin)) { + vin_err(vin, "No supported mediabus format found\n"); + return -EINVAL; + } + + return rvin_v4l2_probe(vin); +} + +static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + rvin_v4l2_remove(vin); +} + +static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + vin_dbg(vin, "subdev %s bound\n", subdev->name); + + vin->entity.entity = &subdev->entity; + vin->entity.subdev = subdev; + + return 0; +} + +static int rvin_graph_parse(struct rvin_dev *vin, + struct device_node *node) +{ + struct device_node *remote; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + while (1) { + next = of_graph_get_next_endpoint(node, ep); + if (!next) + break; + + of_node_put(ep); + ep = next; + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + ret = -EINVAL; + break; + } + + /* Skip entities that we have already processed. */ + if (remote == vin->dev->of_node) { + of_node_put(remote); + continue; + } + + /* Remote node to connect */ + if (!vin->entity.node) { + vin->entity.node = remote; + vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; + vin->entity.asd.match.of.node = remote; + ret++; + } + } + + of_node_put(ep); + + return ret; +} + +static int rvin_graph_init(struct rvin_dev *vin) +{ + struct v4l2_async_subdev **subdevs = NULL; + int ret; + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = rvin_graph_parse(vin, vin->dev->of_node); + if (ret < 0) { + vin_err(vin, "Graph parsing failed\n"); + goto done; + } + + if (!ret) { + vin_err(vin, "No subdev found in graph\n"); + goto done; + } + + if (ret != 1) { + vin_err(vin, "More then one subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); + if (subdevs == NULL) { + ret = -ENOMEM; + goto done; + } + + subdevs[0] = &vin->entity.asd; + + vin->notifier.subdevs = subdevs; + vin->notifier.num_subdevs = 1; + vin->notifier.bound = rvin_graph_notify_bound; + vin->notifier.unbind = rvin_graph_notify_unbind; + vin->notifier.complete = rvin_graph_notify_complete; + + ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + if (ret < 0) { + vin_err(vin, "Notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) { + v4l2_async_notifier_unregister(&vin->notifier); + of_node_put(vin->entity.node); + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static const struct of_device_id rvin_of_id_table[] = { + { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, + { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rvin_of_id_table); + +static int rvin_parse_dt(struct rvin_dev *vin) +{ + const struct of_device_id *match; + struct v4l2_of_endpoint ep; + struct device_node *np; + int ret; + + match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev); + if (!match) + return -ENODEV; + + vin->chip = (enum chip_id)match->data; + + np = of_graph_get_next_endpoint(vin->dev->of_node, NULL); + if (!np) { + vin_err(vin, "Could not find endpoint\n"); + return -EINVAL; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + vin_err(vin, "Could not parse endpoint\n"); + return ret; + } + + of_node_put(np); + + vin->mbus_cfg.type = ep.bus_type; + + switch (vin->mbus_cfg.type) { + case V4L2_MBUS_PARALLEL: + vin->mbus_cfg.flags = ep.bus.parallel.flags; + break; + case V4L2_MBUS_BT656: + vin->mbus_cfg.flags = 0; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_vin_probe(struct platform_device *pdev) +{ + struct rvin_dev *vin; + struct resource *mem; + int irq, ret; + + vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL); + if (!vin) + return -ENOMEM; + + vin->dev = &pdev->dev; + + ret = rvin_parse_dt(vin); + if (ret) + return ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + vin->base = devm_ioremap_resource(vin->dev, mem); + if (IS_ERR(vin->base)) + return PTR_ERR(vin->base); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return ret; + + ret = rvin_dma_probe(vin, irq); + if (ret) + return ret; + + ret = rvin_graph_init(vin); + if (ret < 0) + goto error; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, vin); + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct rvin_dev *vin = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&vin->notifier); + + rvin_dma_remove(vin); + + return 0; +} + +static struct platform_driver rcar_vin_driver = { + .driver = { + .name = "rcar-vin", + .of_match_table = rvin_of_id_table, + }, + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_AUTHOR("Niklas Söderlund "); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c new file mode 100644 index 0000000..dad3b03 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -0,0 +1,1196 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include + +#include + +#include "rcar-vin.h" + +/* ----------------------------------------------------------------------------- + * HW Functions + */ + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ +#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ +#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ +#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ +#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ +#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ +#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ +#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ +#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ +#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ +#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ +#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ +#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ +#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ +#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ +#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ +#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ +#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ +#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ +#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ +#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ +#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ +#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ +#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ +#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ + + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV10_BT656 (2 << 16) +#define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB1555 (1 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) +#define VNDMR2_VLV(n) ((n & 0xf) << 12) + +static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset) +{ + iowrite32(value, vin->base + offset); +} + +static u32 rvin_read(struct rvin_dev *vin, u32 offset) +{ + return ioread32(vin->base + offset); +} + +static int rvin_setup(struct rvin_dev *vin) +{ + u32 vnmc, dmr, dmr2, interrupts; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (vin->continuous) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* + * Input interface + */ + switch (vin->source.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; + break; + case MEDIA_BUS_FMT_YUYV10_2X10: + /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; + break; + default: + break; + } + + /* Enable VSYNC Field Toogle mode after one VSYNC input */ + dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); + + /* Hsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; + + /* + * Output format + */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV16: + rvin_write(vin, + ALIGN(vin->format.width * vin->format.height, 0x80), + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_XRGB555: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_XBGR32: + if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) { + dmr = VNDMR_EXRGB; + break; + } + /* fall through */ + default: + vin_err(vin, "Invalid pixelformat (0x%x)\n", + vin->format.pixelformat); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (input_is_yuv == output_is_yuv) + vnmc |= VNMC_BPS; + + /* Progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE : VNIE_EFE; + + /* Ack interrupts */ + rvin_write(vin, interrupts, VNINTS_REG); + /* Enable interrupts */ + rvin_write(vin, interrupts, VNIE_REG); + /* Start capturing */ + rvin_write(vin, dmr, VNDMR_REG); + rvin_write(vin, dmr2, VNDMR2_REG); + + /* Enable module */ + rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); + + return 0; +} + +static void rvin_capture_on(struct rvin_dev *vin) +{ + vin_dbg(vin, "Capture on in %s mode\n", + vin->continuous ? "continuous" : "single"); + + if (vin->continuous) + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); + else + /* Single Frame Capture Mode */ + rvin_write(vin, VNFC_S_FRAME, VNFC_REG); +} + +static void rvin_capture_off(struct rvin_dev *vin) +{ + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); +} + +static int rvin_capture_start(struct rvin_dev *vin) +{ + int ret; + + rvin_crop_scale_comp(vin); + + ret = rvin_setup(vin); + if (ret) + return ret; + + rvin_capture_on(vin); + + return 0; +} + +static void rvin_capture_stop(struct rvin_dev *vin) +{ + rvin_capture_off(vin); + + /* Disable module */ + rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); +} + +static void rvin_disable_interrupts(struct rvin_dev *vin) +{ + rvin_write(vin, 0, VNIE_REG); +} + +static u32 rvin_get_interrupt_status(struct rvin_dev *vin) +{ + return rvin_read(vin, VNINTS_REG); +} + +static void rvin_ack_interrupt(struct rvin_dev *vin) +{ + rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); +} + +static bool rvin_capture_active(struct rvin_dev *vin) +{ + return rvin_read(vin, VNMS_REG) & VNMS_CA; +} + +static int rvin_get_active_slot(struct rvin_dev *vin) +{ + if (vin->continuous) + return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK) + >> VNMS_FBS_SHIFT; + + return 0; +} + +static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) +{ + const struct rvin_video_format *fmt; + int offsetx, offsety; + dma_addr_t offset; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + + /* + * There is no HW support for composition do the beast we can + * by modifying the buffer offset + */ + offsetx = vin->compose.left * fmt->bpp; + offsety = vin->compose.top * vin->format.bytesperline; + offset = addr + offsetx + offsety; + + /* + * The address needs to be 128 bytes aligned. Driver should never accept + * settings that do not satisfy this in the first place... + */ + if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) + return; + + rvin_write(vin, offset, VNMB_REG(slot)); +} + +/* ----------------------------------------------------------------------------- + * Crop and Scaling Gen2 + */ + +struct vin_coeff { + unsigned short xs_value; + u32 coeff_set[24]; +}; + +static const struct vin_coeff vin_coeff_set[] = { + { 0x0000, { + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 }, + }, + { 0x1000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x000003f8, 0x00000403, 0x3de0d9f0, + 0x001fffed, 0x00000804, 0x3cc1f9c3, + 0x001003de, 0x00000c01, 0x3cb34d7f, + 0x002003d2, 0x00000c00, 0x3d24a92d, + 0x00200bca, 0x00000bff, 0x3df600d2, + 0x002013cc, 0x000007ff, 0x3ed70c7e, + 0x00100fde, 0x00000000, 0x3f87c036 }, + }, + { 0x1200, { + 0x002ffff1, 0x002ffff1, 0x02a0a9c8, + 0x002003e7, 0x001ffffa, 0x000185bc, + 0x002007dc, 0x000003ff, 0x3e52859c, + 0x00200bd4, 0x00000002, 0x3d53996b, + 0x00100fd0, 0x00000403, 0x3d04ad2d, + 0x00000bd5, 0x00000403, 0x3d35ace7, + 0x3ff003e4, 0x00000801, 0x3dc674a1, + 0x3fffe800, 0x00000800, 0x3e76f461 }, + }, + { 0x1400, { + 0x00100be3, 0x00100be3, 0x04d1359a, + 0x00000fdb, 0x002003ed, 0x0211fd93, + 0x00000fd6, 0x002003f4, 0x0002d97b, + 0x000007d6, 0x002ffffb, 0x3e93b956, + 0x3ff003da, 0x001003ff, 0x3db49926, + 0x3fffefe9, 0x00100001, 0x3d655cee, + 0x3fffd400, 0x00000003, 0x3d65f4b6, + 0x000fb421, 0x00000402, 0x3dc6547e }, + }, + { 0x1600, { + 0x00000bdd, 0x00000bdd, 0x06519578, + 0x3ff007da, 0x00000be3, 0x03c24973, + 0x3ff003d9, 0x00000be9, 0x01b30d5f, + 0x3ffff7df, 0x001003f1, 0x0003c542, + 0x000fdfec, 0x001003f7, 0x3ec4711d, + 0x000fc400, 0x002ffffd, 0x3df504f1, + 0x001fa81a, 0x002ffc00, 0x3d957cc2, + 0x002f8c3c, 0x00100000, 0x3db5c891 }, + }, + { 0x1800, { + 0x3ff003dc, 0x3ff003dc, 0x0791e558, + 0x000ff7dd, 0x3ff007de, 0x05328554, + 0x000fe7e3, 0x3ff00be2, 0x03232546, + 0x000fd7ee, 0x000007e9, 0x0143bd30, + 0x001fb800, 0x000007ee, 0x00044511, + 0x002fa015, 0x000007f4, 0x3ef4bcee, + 0x002f8832, 0x001003f9, 0x3e4514c7, + 0x001f7853, 0x001003fd, 0x3de54c9f }, + }, + { 0x1a00, { + 0x000fefe0, 0x000fefe0, 0x08721d3c, + 0x001fdbe7, 0x000ffbde, 0x0652a139, + 0x001fcbf0, 0x000003df, 0x0463292e, + 0x002fb3ff, 0x3ff007e3, 0x0293a91d, + 0x002f9c12, 0x3ff00be7, 0x01241905, + 0x001f8c29, 0x000007ed, 0x3fe470eb, + 0x000f7c46, 0x000007f2, 0x3f04b8ca, + 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, + }, + { 0x1c00, { + 0x001fd3e9, 0x001fd3e9, 0x08f23d26, + 0x002fbff3, 0x001fe3e4, 0x0712ad23, + 0x002fa800, 0x000ff3e0, 0x05631d1b, + 0x001f9810, 0x000ffbe1, 0x03b3890d, + 0x000f8c23, 0x000003e3, 0x0233e8fa, + 0x3fef843b, 0x000003e7, 0x00f430e4, + 0x3fbf8456, 0x3ff00bea, 0x00046cc8, + 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, + }, + { 0x1e00, { + 0x001fbbf4, 0x001fbbf4, 0x09425112, + 0x001fa800, 0x002fc7ed, 0x0792b110, + 0x000f980e, 0x001fdbe6, 0x0613110a, + 0x3fff8c20, 0x001fe7e3, 0x04a368fd, + 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, + 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, + 0x3f5f9c61, 0x000003e6, 0x00e428c5, + 0x3f1fb07b, 0x000003eb, 0x3fe440af }, + }, + { 0x2000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x3fff980c, 0x001fb7f5, 0x0812b0ff, + 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, + 0x3faf902d, 0x001fd3e8, 0x055348f1, + 0x3f7f983f, 0x001fe3e5, 0x04038ce3, + 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, + 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, + 0x3ecfd880, 0x000fffe6, 0x00c404ac }, + }, + { 0x2200, { + 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, + 0x3fbf9818, 0x3fffa400, 0x0842a8f1, + 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, + 0x3f5fa037, 0x000fc3ef, 0x05d330e4, + 0x3f2fac49, 0x001fcfea, 0x04a364d9, + 0x3effc05c, 0x001fdbe7, 0x038394ca, + 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, + 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, + }, + { 0x2400, { + 0x3f9fa014, 0x3f9fa014, 0x098260e6, + 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, + 0x3f4fa431, 0x3fefa400, 0x0742d8e1, + 0x3f1fb440, 0x3fffb3f8, 0x062310d9, + 0x3eefc850, 0x000fbbf2, 0x050340d0, + 0x3ecfe062, 0x000fcbec, 0x041364c2, + 0x3ea00073, 0x001fd3ea, 0x03037cb5, + 0x3e902086, 0x001fdfe8, 0x022388a5 }, + }, + { 0x2600, { + 0x3f5fa81e, 0x3f5fa81e, 0x096258da, + 0x3f3fac2b, 0x3f8fa412, 0x088290d8, + 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, + 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, + 0x3ecfe456, 0x3fefaffa, 0x05531cc6, + 0x3eb00066, 0x3fffbbf3, 0x047334bb, + 0x3ea01c77, 0x000fc7ee, 0x039348ae, + 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, + }, + { 0x2800, { + 0x3f2fb426, 0x3f2fb426, 0x094250ce, + 0x3f0fc032, 0x3f4fac1b, 0x086284cd, + 0x3eefd040, 0x3f7fa811, 0x0782acc9, + 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, + 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, + 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, + 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, + 0x3ec06884, 0x000fbff2, 0x03031c9e }, + }, + { 0x2a00, { + 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, + 0x3eefd439, 0x3f2fb822, 0x08526cc2, + 0x3edfe845, 0x3f4fb018, 0x078294bf, + 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, + 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, + 0x3ec0386b, 0x3fafac00, 0x0502e8ac, + 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, + 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, + }, + { 0x2c00, { + 0x3eefdc31, 0x3eefdc31, 0x08e238b8, + 0x3edfec3d, 0x3f0fc828, 0x082258b9, + 0x3ed00049, 0x3f1fc01e, 0x077278b6, + 0x3ed01455, 0x3f3fb815, 0x06c294b2, + 0x3ed03460, 0x3f5fb40d, 0x0602acac, + 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, + 0x3f107476, 0x3f9fb400, 0x0472c89d, + 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, + }, + { 0x2e00, { + 0x3eefec37, 0x3eefec37, 0x088220b0, + 0x3ee00041, 0x3effdc2d, 0x07f244ae, + 0x3ee0144c, 0x3f0fd023, 0x07625cad, + 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, + 0x3f004861, 0x3f3fbc13, 0x060288a6, + 0x3f20686b, 0x3f5fb80c, 0x05529c9e, + 0x3f408c74, 0x3f6fb805, 0x04b2ac96, + 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, + }, + { 0x3000, { + 0x3ef0003a, 0x3ef0003a, 0x084210a6, + 0x3ef01045, 0x3effec32, 0x07b228a7, + 0x3f00284e, 0x3f0fdc29, 0x073244a4, + 0x3f104058, 0x3f0fd420, 0x06a258a2, + 0x3f305c62, 0x3f2fc818, 0x0612689d, + 0x3f508069, 0x3f3fc011, 0x05728496, + 0x3f80a072, 0x3f4fc00a, 0x04d28c90, + 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, + }, + { 0x3200, { + 0x3f00103e, 0x3f00103e, 0x07f1fc9e, + 0x3f102447, 0x3f000035, 0x0782149d, + 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, + 0x3f405458, 0x3f0fe424, 0x06924099, + 0x3f607061, 0x3f1fd41d, 0x06024c97, + 0x3f909068, 0x3f2fcc16, 0x05726490, + 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, + 0x0000d077, 0x3f4fc409, 0x04627484 }, + }, + { 0x3400, { + 0x3f202040, 0x3f202040, 0x07a1e898, + 0x3f303449, 0x3f100c38, 0x0741fc98, + 0x3f504c50, 0x3f10002f, 0x06e21495, + 0x3f706459, 0x3f1ff028, 0x06722492, + 0x3fa08060, 0x3f1fe421, 0x05f2348f, + 0x3fd09c67, 0x3f1fdc19, 0x05824c89, + 0x0000bc6e, 0x3f2fd014, 0x04f25086, + 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, + }, + { 0x3600, { + 0x3f403042, 0x3f403042, 0x0761d890, + 0x3f504848, 0x3f301c3b, 0x0701f090, + 0x3f805c50, 0x3f200c33, 0x06a2008f, + 0x3fa07458, 0x3f10002b, 0x06520c8d, + 0x3fd0905e, 0x3f1ff424, 0x05e22089, + 0x0000ac65, 0x3f1fe81d, 0x05823483, + 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, + 0x0080e871, 0x3f2fd412, 0x0482407c }, + }, + { 0x3800, { + 0x3f604043, 0x3f604043, 0x0721c88a, + 0x3f80544a, 0x3f502c3c, 0x06d1d88a, + 0x3fb06851, 0x3f301c35, 0x0681e889, + 0x3fd08456, 0x3f30082f, 0x0611fc88, + 0x00009c5d, 0x3f200027, 0x05d20884, + 0x0030b863, 0x3f2ff421, 0x05621880, + 0x0070d468, 0x3f2fe81b, 0x0502247c, + 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, + }, + { 0x3a00, { + 0x3f904c44, 0x3f904c44, 0x06e1b884, + 0x3fb0604a, 0x3f70383e, 0x0691c885, + 0x3fe07451, 0x3f502c36, 0x0661d483, + 0x00009055, 0x3f401831, 0x0601ec81, + 0x0030a85b, 0x3f300c2a, 0x05b1f480, + 0x0070c061, 0x3f300024, 0x0562047a, + 0x00b0d867, 0x3f3ff41e, 0x05020c77, + 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, + }, + { 0x3c00, { + 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, + 0x3fe06c4b, 0x3f902c3f, 0x0681c081, + 0x0000844f, 0x3f703838, 0x0631cc7d, + 0x00309855, 0x3f602433, 0x05d1d47e, + 0x0060b459, 0x3f50142e, 0x0581e47b, + 0x00a0c85f, 0x3f400828, 0x0531f078, + 0x00e0e064, 0x3f300021, 0x0501fc73, + 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, + }, + { 0x3e00, { + 0x3fe06444, 0x3fe06444, 0x0681a07a, + 0x00007849, 0x3fc0503f, 0x0641b07a, + 0x0020904d, 0x3fa0403a, 0x05f1c07a, + 0x0060a453, 0x3f803034, 0x05c1c878, + 0x0090b858, 0x3f70202f, 0x0571d477, + 0x00d0d05d, 0x3f501829, 0x0531e073, + 0x0110e462, 0x3f500825, 0x04e1e471, + 0x01510065, 0x3f40001f, 0x04a1f06d }, + }, + { 0x4000, { + 0x00007044, 0x00007044, 0x06519476, + 0x00208448, 0x3fe05c3f, 0x0621a476, + 0x0050984d, 0x3fc04c3a, 0x05e1b075, + 0x0080ac52, 0x3fa03c35, 0x05a1b875, + 0x00c0c056, 0x3f803030, 0x0561c473, + 0x0100d45b, 0x3f70202b, 0x0521d46f, + 0x0140e860, 0x3f601427, 0x04d1d46e, + 0x01810064, 0x3f500822, 0x0491dc6b }, + }, + { 0x5000, { + 0x0110a442, 0x0110a442, 0x0551545e, + 0x0140b045, 0x00e0983f, 0x0531585f, + 0x0160c047, 0x00c08c3c, 0x0511645e, + 0x0190cc4a, 0x00908039, 0x04f1685f, + 0x01c0dc4c, 0x00707436, 0x04d1705e, + 0x0200e850, 0x00506833, 0x04b1785b, + 0x0230f453, 0x00305c30, 0x0491805a, + 0x02710056, 0x0010542d, 0x04718059 }, + }, + { 0x6000, { + 0x01c0bc40, 0x01c0bc40, 0x04c13052, + 0x01e0c841, 0x01a0b43d, 0x04c13851, + 0x0210cc44, 0x0180a83c, 0x04a13453, + 0x0230d845, 0x0160a03a, 0x04913c52, + 0x0260e047, 0x01409838, 0x04714052, + 0x0280ec49, 0x01208c37, 0x04514c50, + 0x02b0f44b, 0x01008435, 0x04414c50, + 0x02d1004c, 0x00e07c33, 0x0431544f }, + }, + { 0x7000, { + 0x0230c83e, 0x0230c83e, 0x04711c4c, + 0x0250d03f, 0x0210c43c, 0x0471204b, + 0x0270d840, 0x0200b83c, 0x0451244b, + 0x0290dc42, 0x01e0b43a, 0x0441244c, + 0x02b0e443, 0x01c0b038, 0x0441284b, + 0x02d0ec44, 0x01b0a438, 0x0421304a, + 0x02f0f445, 0x0190a036, 0x04213449, + 0x0310f847, 0x01709c34, 0x04213848 }, + }, + { 0x8000, { + 0x0280d03d, 0x0280d03d, 0x04310c48, + 0x02a0d43e, 0x0270c83c, 0x04311047, + 0x02b0dc3e, 0x0250c83a, 0x04311447, + 0x02d0e040, 0x0240c03a, 0x04211446, + 0x02e0e840, 0x0220bc39, 0x04111847, + 0x0300e842, 0x0210b438, 0x04012445, + 0x0310f043, 0x0200b037, 0x04012045, + 0x0330f444, 0x01e0ac36, 0x03f12445 }, + }, + { 0xefff, { + 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, + 0x0340e03a, 0x0330e039, 0x03c0f03e, + 0x0350e03b, 0x0330dc39, 0x03c0ec3e, + 0x0350e43a, 0x0320dc38, 0x03c0f43e, + 0x0360e43b, 0x0320d839, 0x03b0f03e, + 0x0360e83b, 0x0310d838, 0x03c0fc3b, + 0x0370e83b, 0x0310d439, 0x03a0f83d, + 0x0370e83c, 0x0300d438, 0x03b0fc3c }, + } +}; + +static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) +{ + int i; + const struct vin_coeff *p_prev_set = NULL; + const struct vin_coeff *p_set = NULL; + + /* Look for suitable coefficient values */ + for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { + p_prev_set = p_set; + p_set = &vin_coeff_set[i]; + + if (xs < p_set->xs_value) + break; + } + + /* Use previous value if its XS value is closer */ + if (p_prev_set && p_set && + xs - p_prev_set->xs_value < p_set->xs_value - xs) + p_set = p_prev_set; + + /* Set coefficient registers */ + rvin_write(vin, p_set->coeff_set[0], VNC1A_REG); + rvin_write(vin, p_set->coeff_set[1], VNC1B_REG); + rvin_write(vin, p_set->coeff_set[2], VNC1C_REG); + + rvin_write(vin, p_set->coeff_set[3], VNC2A_REG); + rvin_write(vin, p_set->coeff_set[4], VNC2B_REG); + rvin_write(vin, p_set->coeff_set[5], VNC2C_REG); + + rvin_write(vin, p_set->coeff_set[6], VNC3A_REG); + rvin_write(vin, p_set->coeff_set[7], VNC3B_REG); + rvin_write(vin, p_set->coeff_set[8], VNC3C_REG); + + rvin_write(vin, p_set->coeff_set[9], VNC4A_REG); + rvin_write(vin, p_set->coeff_set[10], VNC4B_REG); + rvin_write(vin, p_set->coeff_set[11], VNC4C_REG); + + rvin_write(vin, p_set->coeff_set[12], VNC5A_REG); + rvin_write(vin, p_set->coeff_set[13], VNC5B_REG); + rvin_write(vin, p_set->coeff_set[14], VNC5C_REG); + + rvin_write(vin, p_set->coeff_set[15], VNC6A_REG); + rvin_write(vin, p_set->coeff_set[16], VNC6B_REG); + rvin_write(vin, p_set->coeff_set[17], VNC6C_REG); + + rvin_write(vin, p_set->coeff_set[18], VNC7A_REG); + rvin_write(vin, p_set->coeff_set[19], VNC7B_REG); + rvin_write(vin, p_set->coeff_set[20], VNC7C_REG); + + rvin_write(vin, p_set->coeff_set[21], VNC8A_REG); + rvin_write(vin, p_set->coeff_set[22], VNC8B_REG); + rvin_write(vin, p_set->coeff_set[23], VNC8C_REG); +} + +void rvin_crop_scale_comp(struct rvin_dev *vin) +{ + u32 xs, ys; + + /* Set Start/End Pixel/Line Pre-Clip */ + rvin_write(vin, vin->crop.left, VNSPPRC_REG); + rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); + rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, + VNELPRC_REG); + break; + default: + rvin_write(vin, vin->crop.top, VNSLPRC_REG); + rvin_write(vin, vin->crop.top + vin->crop.height - 1, + VNELPRC_REG); + break; + } + + /* Set scaling coefficient */ + ys = 0; + if (vin->crop.height != vin->compose.height) + ys = (4096 * vin->crop.height) / vin->compose.height; + rvin_write(vin, ys, VNYS_REG); + + xs = 0; + if (vin->crop.width != vin->compose.width) + xs = (4096 * vin->crop.width) / vin->compose.width; + + /* Horizontal upscaling is up to double size */ + if (xs > 0 && xs < 2048) + xs = 2048; + + rvin_write(vin, xs, VNXS_REG); + + /* Horizontal upscaling is done out by scaling down from double size */ + if (xs < 4096) + xs *= 2; + + rvin_set_coeff(vin, xs); + + /* Set Start/End Pixel/Line Post-Clip */ + rvin_write(vin, 0, VNSPPOC_REG); + rvin_write(vin, 0, VNSLPOC_REG); + rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); + break; + default: + rvin_write(vin, vin->format.height - 1, VNELPOC_REG); + break; + } + + if (vin->format.pixelformat == V4L2_PIX_FMT_NV16) + rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG); + else + rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG); + + vin_dbg(vin, + "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", + vin->crop.width, vin->crop.height, vin->crop.left, + vin->crop.top, ys, xs, vin->format.width, vin->format.height, + 0, 0); +} + +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height) +{ + /* All VIN channels on Gen2 have scalers */ + pix->width = width; + pix->height = height; +} + +/* ----------------------------------------------------------------------------- + * DMA Functions + */ + +#define RVIN_TIMEOUT_MS 100 +#define RVIN_RETRIES 10 + +struct rvin_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rvin_buffer, \ + vb)->list) + +/* Moves a buffer from the queue to the HW slots */ +static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +{ + struct rvin_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr_top; + + if (vin->queue_buf[slot] != NULL) + return true; + + if (list_empty(&vin->buf_list)) + return false; + + vin_dbg(vin, "Filling HW slot: %d\n", slot); + + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rvin_set_slot_addr(vin, slot, phys_addr_top); + + return true; +} + +static bool rvin_fill_hw(struct rvin_dev *vin) +{ + int slot, limit; + + limit = vin->continuous ? HW_BUFFER_NUM : 1; + + for (slot = 0; slot < limit; slot++) + if (!rvin_fill_hw_slot(vin, slot)) + return false; + return true; +} + +static irqreturn_t rvin_irq(int irq, void *data) +{ + struct rvin_dev *vin = data; + u32 int_status; + int slot; + unsigned int sequence, handled = 0; + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + int_status = rvin_get_interrupt_status(vin); + if (!int_status) + goto done; + + rvin_ack_interrupt(vin); + handled = 1; + + /* Nothing to do if capture status is 'STOPPED' */ + if (vin->state == STOPPED) { + vin_dbg(vin, "IRQ while state stopped\n"); + goto done; + } + + /* Nothing to do if capture status is 'STOPPING' */ + if (vin->state == STOPPING) { + vin_dbg(vin, "IRQ while state stopping\n"); + goto done; + } + + /* Prepare for capture and update state */ + slot = rvin_get_active_slot(vin); + sequence = vin->sequence++; + + vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", + sequence, slot, + slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0', + slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0', + slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0', + !list_empty(&vin->buf_list)); + + /* HW have written to a slot that is not prepared we are in trouble */ + if (WARN_ON((vin->queue_buf[slot] == NULL))) + goto done; + + /* Capture frame */ + vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->sequence = sequence; + vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); + vin->queue_buf[slot] = NULL; + + /* Prepare for next frame */ + if (!rvin_fill_hw(vin)) { + + /* + * Can't supply HW with new buffers fast enough. Halt + * capture until more buffers are available. + */ + vin->state = STALLED; + + /* + * The continuous capturing requires an explicit stop + * operation when there is no buffer to be set into + * the VnMBm registers. + */ + if (vin->continuous) { + rvin_capture_off(vin); + vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence); + } + } else { + /* + * The single capturing requires an explicit capture + * operation to fetch the next frame. + */ + if (!vin->continuous) + rvin_capture_on(vin); + } +done: + spin_unlock_irqrestore(&vin->qlock, flags); + + return IRQ_RETVAL(handled); +} + +/* Need to hold qlock before calling */ +static void return_all_buffers(struct rvin_dev *vin, + enum vb2_buffer_state state) +{ + struct rvin_buffer *buf, *node; + int i; + + for (i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->queue_buf[i]) { + vb2_buffer_done(&vin->queue_buf[i]->vb2_buf, + state); + vin->queue_buf[i] = NULL; + } + } + + list_for_each_entry_safe(buf, node, &vin->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } +} + +static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + void *alloc_ctxs[]) + +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + + alloc_ctxs[0] = vin->alloc_ctx; + /* Make sure the image size is large enough. */ + if (*nplanes) + return sizes[0] < vin->format.sizeimage ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = vin->format.sizeimage; + + return 0; +}; + +static int rvin_buffer_prepare(struct vb2_buffer *vb) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = vin->format.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + vin_err(vin, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void rvin_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&vin->qlock, flags); + + list_add_tail(to_buf_list(vbuf), &vin->buf_list); + + /* + * If capture is stalled add buffer to HW and restart + * capturing if HW is ready to continue. + */ + if (vin->state == STALLED) + if (rvin_fill_hw(vin)) + rvin_capture_on(vin); + + spin_unlock_irqrestore(&vin->qlock, flags); +} + +static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int ret; + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 1); + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = RUNNING; + vin->sequence = 0; + + /* Continuous capture requires more buffers then there are HW slots */ + vin->continuous = count > HW_BUFFER_NUM; + + /* + * This should never happen but if we don't have enough + * buffers for HW bail out + */ + if (!rvin_fill_hw(vin)) { + vin_err(vin, "HW not ready to start, not enough buffers available\n"); + ret = -EINVAL; + goto out; + } + + ret = rvin_capture_start(vin); +out: + /* Return all buffers if something went wrong */ + if (ret) { + return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + v4l2_subdev_call(sd, video, s_stream, 0); + } + + spin_unlock_irqrestore(&vin->qlock, flags); + + return ret; +} + +static void rvin_stop_streaming(struct vb2_queue *vq) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + struct v4l2_subdev *sd; + unsigned long flags; + int retries = 0; + + spin_lock_irqsave(&vin->qlock, flags); + + vin->state = STOPPING; + + /* Wait for streaming to stop */ + while (retries++ < RVIN_RETRIES) { + + rvin_capture_stop(vin); + + /* Check if HW is stopped */ + if (!rvin_capture_active(vin)) { + vin->state = STOPPED; + break; + } + + spin_unlock_irqrestore(&vin->qlock, flags); + msleep(RVIN_TIMEOUT_MS); + spin_lock_irqsave(&vin->qlock, flags); + } + + if (vin->state != STOPPED) { + /* + * If this happens something have gone horribly wrong. + * Set state to stopped to prevent the interrupt handler + * to make things worse... + */ + vin_err(vin, "Failed stop HW, something is seriously broken\n"); + vin->state = STOPPED; + } + + /* Release all active buffers */ + return_all_buffers(vin, VB2_BUF_STATE_ERROR); + + spin_unlock_irqrestore(&vin->qlock, flags); + + sd = vin_to_source(vin); + v4l2_subdev_call(sd, video, s_stream, 0); + + /* disable interrupts */ + rvin_disable_interrupts(vin); +} + +static struct vb2_ops rvin_qops = { + .queue_setup = rvin_queue_setup, + .buf_prepare = rvin_buffer_prepare, + .buf_queue = rvin_buffer_queue, + .start_streaming = rvin_start_streaming, + .stop_streaming = rvin_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +void rvin_dma_remove(struct rvin_dev *vin) +{ + if (!IS_ERR_OR_NULL(vin->alloc_ctx)) + vb2_dma_contig_cleanup_ctx(vin->alloc_ctx); + + mutex_destroy(&vin->lock); + + v4l2_device_unregister(&vin->v4l2_dev); +} + +int rvin_dma_probe(struct rvin_dev *vin, int irq) +{ + struct vb2_queue *q = &vin->queue; + int i, ret; + + /* Initialize the top-level structure */ + ret = v4l2_device_register(vin->dev, &vin->v4l2_dev); + if (ret) + return ret; + + mutex_init(&vin->lock); + INIT_LIST_HEAD(&vin->buf_list); + + spin_lock_init(&vin->qlock); + + vin->state = STOPPED; + + for (i = 0; i < HW_BUFFER_NUM; i++) + vin->queue_buf[i] = NULL; + + /* buffer queue */ + vin->alloc_ctx = vb2_dma_contig_init_ctx(vin->dev); + if (IS_ERR(vin->alloc_ctx)) { + ret = PTR_ERR(vin->alloc_ctx); + goto error; + } + + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + q->lock = &vin->lock; + q->drv_priv = vin; + q->buf_struct_size = sizeof(struct rvin_buffer); + q->ops = &rvin_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + + ret = vb2_queue_init(q); + if (ret < 0) { + vin_err(vin, "failed to initialize VB2 queue\n"); + goto error; + } + + /* irq */ + ret = devm_request_irq(vin->dev, irq, rvin_irq, IRQF_SHARED, + KBUILD_MODNAME, vin); + if (ret) { + vin_err(vin, "failed to request irq\n"); + goto error; + } + + return 0; +error: + rvin_dma_remove(vin); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c new file mode 100644 index 0000000..0bc4487 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -0,0 +1,768 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include +#include +#include + +#include "rcar-vin.h" + +#define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV +#define RVIN_MAX_WIDTH 2048 +#define RVIN_MAX_HEIGHT 2048 + +/* ----------------------------------------------------------------------------- + * Format Conversions + */ + +static const struct rvin_video_format rvin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB555, + .bpp = 2, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .bpp = 4, + }, +}; + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) + if (rvin_formats[i].fourcc == pixelformat) + return rvin_formats + i; + + return NULL; +} + +static u32 rvin_format_bytesperline(struct v4l2_pix_format *pix) +{ + const struct rvin_video_format *fmt; + + fmt = rvin_format_from_pixel(pix->pixelformat); + + if (WARN_ON(!fmt)) + return -EINVAL; + + return pix->width * fmt->bpp; +} + +static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) +{ + if (pix->pixelformat == V4L2_PIX_FMT_NV16) + return pix->bytesperline * pix->height * 2; + + return pix->bytesperline * pix->height; +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +static int __rvin_try_format_source(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + struct v4l2_subdev *sd; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = which, + }; + int ret; + + sd = vin_to_source(vin); + + v4l2_fill_mbus_format(&format.format, pix, vin->source.code); + + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt, + &pad_cfg, &format); + if (ret < 0) + return ret; + + v4l2_fill_pix_format(pix, &format.format); + + source->width = pix->width; + source->height = pix->height; + + vin_dbg(vin, "Source resolution: %ux%u\n", source->width, + source->height); + + return 0; +} + +static int __rvin_try_format(struct rvin_dev *vin, + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) +{ + const struct rvin_video_format *info; + u32 rwidth, rheight, walign; + + /* Requested */ + rwidth = pix->width; + rheight = pix->height; + + /* + * Retrieve format information and select the current format if the + * requested format isn't supported. + */ + info = rvin_format_from_pixel(pix->pixelformat); + if (!info) { + vin_dbg(vin, "Format %x not found, keeping %x\n", + pix->pixelformat, vin->format.pixelformat); + *pix = vin->format; + pix->width = rwidth; + pix->height = rheight; + } + + /* Always recalculate */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* Limit to source capabilities */ + __rvin_try_format_source(vin, which, pix, source); + + /* If source can't match format try if VIN can scale */ + if (source->width != rwidth || source->height != rheight) + rvin_scale_try(vin, pix, rwidth, rheight); + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, + &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + default: + pix->field = V4L2_FIELD_NONE; + break; + } + + pix->bytesperline = max_t(u32, pix->bytesperline, + rvin_format_bytesperline(pix)); + pix->sizeimage = max_t(u32, pix->sizeimage, + rvin_format_sizeimage(pix)); + + vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", + rwidth, rheight, pix->width, pix->height, + pix->bytesperline, pix->sizeimage); + + return 0; +} + +static int rvin_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rvin_dev *vin = video_drvdata(file); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(vin->dev)); + return 0; +} + +static int rvin_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + + return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, + &source); +} + +static int rvin_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + struct rvin_source_fmt source; + int ret; + + if (vb2_is_busy(&vin->queue)) + return -EBUSY; + + ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, + &source); + if (ret) + return ret; + + vin->source.width = source.width; + vin->source.height = source.height; + + vin->format = f->fmt.pix; + + return 0; +} + +static int rvin_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + + f->fmt.pix = vin->format; + + return 0; +} + +static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(rvin_formats)) + return -EINVAL; + + f->pixelformat = rvin_formats[f->index].fourcc; + + return 0; +} + +static int rvin_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->source.width; + s->r.height = vin->source.height; + break; + case V4L2_SEL_TGT_CROP: + s->r = vin->crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vin->format.width; + s->r.height = vin->format.height; + break; + case V4L2_SEL_TGT_COMPOSE: + s->r = vin->compose; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rvin_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct rvin_dev *vin = video_drvdata(file); + const struct rvin_video_format *fmt; + struct v4l2_rect r = s->r; + struct v4l2_rect max_rect; + struct v4l2_rect min_rect = { + .width = 6, + .height = 2, + }; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_rect_set_min_size(&r, &min_rect); + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Can't crop outside of source input */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->source.width; + max_rect.height = vin->source.height; + v4l2_rect_map_inside(&r, &max_rect); + + v4l_bound_align_image(&r.width, 2, vin->source.width, 1, + &r.height, 4, vin->source.height, 2, 0); + + r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); + r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); + + vin->crop = s->r = r; + + vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", + r.width, r.height, r.left, r.top, + vin->source.width, vin->source.height); + break; + case V4L2_SEL_TGT_COMPOSE: + /* Make sure compose rect fits inside output format */ + max_rect.top = max_rect.left = 0; + max_rect.width = vin->format.width; + max_rect.height = vin->format.height; + v4l2_rect_map_inside(&r, &max_rect); + + /* + * Composing is done by adding a offset to the buffer address, + * the HW wants this address to be aligned to HW_BUFFER_MASK. + * Make sure the top and left values meets this requirement. + */ + while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK) + r.top--; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + while ((r.left * fmt->bpp) & HW_BUFFER_MASK) + r.left--; + + vin->compose = s->r = r; + + vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", + r.width, r.height, r.left, r.top, + vin->format.width, vin->format.height); + break; + default: + return -EINVAL; + } + + /* HW supports modifying configuration while running */ + rvin_crop_scale_comp(vin); + + return 0; +} + +static int rvin_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(sd, video, cropcap, crop); +} + +static int rvin_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int ret; + + if (i->index != 0) + return -EINVAL; + + ret = v4l2_subdev_call(sd, video, g_input_status, &i->status); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = vin->vdev.tvnorms; + strlcpy(i->name, "Camera", sizeof(i->name)); + + return 0; +} + +static int rvin_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int rvin_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, querystd, a); +} + +static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret = v4l2_subdev_call(sd, video, s_std, a); + + if (ret < 0) + return ret; + + /* Changing the standard will change the width/height */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + vin->format.width = mf->width; + vin->format.height = mf->height; + + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + return 0; +} + +static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + + return v4l2_subdev_call(sd, video, g_std, a); +} + +static int rvin_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops rvin_ioctl_ops = { + .vidioc_querycap = rvin_querycap, + .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, + + .vidioc_g_selection = rvin_g_selection, + .vidioc_s_selection = rvin_s_selection, + + .vidioc_cropcap = rvin_cropcap, + + .vidioc_enum_input = rvin_enum_input, + .vidioc_g_input = rvin_g_input, + .vidioc_s_input = rvin_s_input, + + .vidioc_querystd = rvin_querystd, + .vidioc_g_std = rvin_g_std, + .vidioc_s_std = rvin_s_std, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = rvin_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- + * File Operations + */ + +static int rvin_power_on(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + pm_runtime_get_sync(vin->v4l2_dev.dev); + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + return 0; +} + +static int rvin_power_off(struct rvin_dev *vin) +{ + int ret; + struct v4l2_subdev *sd = vin_to_source(vin); + + ret = v4l2_subdev_call(sd, core, s_power, 0); + + pm_runtime_put(vin->v4l2_dev.dev); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int rvin_initialize_device(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = vin->format.width, + .height = vin->format.height, + .field = vin->format.field, + .colorspace = vin->format.colorspace, + .pixelformat = vin->format.pixelformat, + }, + }; + + ret = rvin_power_on(vin); + if (ret < 0) + return ret; + + pm_runtime_enable(&vin->vdev.dev); + ret = pm_runtime_resume(&vin->vdev.dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + + /* + * Try to configure with default parameters. Notice: this is the + * very first open, so, we cannot race against other calls, + * apart from someone else calling open() simultaneously, but + * .host_lock is protecting us against it. + */ + ret = rvin_s_fmt_vid_cap(file, NULL, &f); + if (ret < 0) + goto esfmt; + + v4l2_ctrl_handler_setup(&vin->ctrl_handler); + + return 0; +esfmt: + pm_runtime_disable(&vin->vdev.dev); +eresume: + rvin_power_off(vin); + + return ret; +} + +static int rvin_open(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + mutex_lock(&vin->lock); + + file->private_data = vin; + + ret = v4l2_fh_open(file); + if (ret) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (rvin_initialize_device(file)) { + v4l2_fh_release(file); + ret = -ENODEV; + } + +unlock: + mutex_unlock(&vin->lock); + return ret; +} + +static int rvin_release(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + bool fh_singular; + int ret; + + mutex_lock(&vin->lock); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + + /* + * If this was the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) { + pm_runtime_suspend(&vin->vdev.dev); + pm_runtime_disable(&vin->vdev.dev); + rvin_power_off(vin); + } + + mutex_unlock(&vin->lock); + + return ret; +} + +static const struct v4l2_file_operations rvin_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = rvin_open, + .release = rvin_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +void rvin_v4l2_remove(struct rvin_dev *vin) +{ + v4l2_info(&vin->v4l2_dev, "Removing %s\n", + video_device_node_name(&vin->vdev)); + + /* Checks internaly if handlers have been init or not */ + v4l2_ctrl_handler_free(&vin->ctrl_handler); + + /* Checks internaly if vdev have been init or not */ + video_unregister_device(&vin->vdev); +} + +static void rvin_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct rvin_dev *vin = + container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + v4l2_event_queue(&vin->vdev, arg); + break; + default: + break; + } +} + +int rvin_v4l2_probe(struct rvin_dev *vin) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct video_device *vdev = &vin->vdev; + struct v4l2_subdev *sd = vin_to_source(vin); + int ret; + + v4l2_set_subdev_hostdata(sd, vin); + + vin->v4l2_dev.notify = rvin_notify; + + ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (vin->vdev.tvnorms == 0) { + /* Disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); + } + + /* Add the controls */ + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); + if (ret < 0) + return ret; + + ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); + if (ret < 0) + return ret; + + /* video node */ + vdev->fops = &rvin_fops; + vdev->v4l2_dev = &vin->v4l2_dev; + vdev->queue = &vin->queue; + strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &rvin_ioctl_ops; + vdev->lock = &vin->lock; + vdev->ctrl_handler = &vin->ctrl_handler; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + + /* Try to improve our guess of a reasonable window format */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret) { + vin_err(vin, "Failed to get initial format\n"); + return ret; + } + + /* Set default format */ + vin->format.width = mf->width; + vin->format.height = mf->height; + vin->format.colorspace = mf->colorspace; + vin->format.field = mf->field; + vin->format.pixelformat = RVIN_DEFAULT_FORMAT; + + + /* Set initial crop and compose */ + vin->crop.top = vin->crop.left = 0; + vin->crop.width = mf->width; + vin->crop.height = mf->height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = mf->width; + vin->compose.height = mf->height; + + ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + vin_err(vin, "Failed to register video device\n"); + return ret; + } + + video_set_drvdata(&vin->vdev, vin); + + v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", + video_device_node_name(&vin->vdev)); + + return ret; +} diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h new file mode 100644 index 0000000..544a3b3 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -0,0 +1,163 @@ +/* + * Driver for Renesas R-Car VIN + * + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * Copyright (C) 2008 Magnus Damm + * + * Based on the soc-camera rcar_vin driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __RCAR_VIN__ +#define __RCAR_VIN__ + +#include +#include +#include +#include +#include + +/* Number of HW buffers */ +#define HW_BUFFER_NUM 3 + +/* Address alignment mask for HW buffers */ +#define HW_BUFFER_MASK 0x7f + +enum chip_id { + RCAR_GEN2, + RCAR_H1, + RCAR_M1, +}; + +/** + * STOPPED - No operation in progress + * RUNNING - Operation in progress have buffers + * STALLED - No operation in progress have no buffers + * STOPPING - Stopping operation + */ +enum rvin_dma_state { + STOPPED = 0, + RUNNING, + STALLED, + STOPPING, +}; + +/** + * struct rvin_source_fmt - Source information + * @code: Media bus format from source + * @width: Width from source + * @height: Height from source + */ +struct rvin_source_fmt { + u32 code; + u32 width; + u32 height; +}; + +/** + * struct rvin_video_format - Data format stored in memory + * @fourcc: Pixelformat + * @bpp: Bytes per pixel + */ +struct rvin_video_format { + u32 fourcc; + u8 bpp; +}; + +struct rvin_graph_entity { + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; +}; + +/** + * struct rvin_dev - Renesas VIN device structure + * @dev: (OF) device + * @base: device I/O register space remapped to virtual memory + * @chip: type of VIN chip + * @mbus_cfg media bus configuration + * + * @vdev: V4L2 video device associated with VIN + * @v4l2_dev: V4L2 device + * @ctrl_handler: V4L2 control handler + * @notifier: V4L2 asynchronous subdevs notifier + * @entity: entity in the DT for subdevice + * + * @lock: protects @queue + * @queue: vb2 buffers queue + * @alloc_ctx: allocation context for the vb2 @queue + * + * @qlock: protects @queue_buf, @buf_list, @continuous, @sequence + * @state + * @queue_buf: Keeps track of buffers given to HW slot + * @buf_list: list of queued buffers + * @continuous: tracks if active operation is continuous or single mode + * @sequence: V4L2 buffers sequence number + * @state: keeps track of operation state + * + * @source: active format from the video source + * @format: active V4L2 pixel format + * + * @crop: active cropping + * @compose: active composing + */ +struct rvin_dev { + struct device *dev; + void __iomem *base; + enum chip_id chip; + struct v4l2_mbus_config mbus_cfg; + + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + struct rvin_graph_entity entity; + + struct mutex lock; + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + + spinlock_t qlock; + struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; + struct list_head buf_list; + bool continuous; + unsigned int sequence; + enum rvin_dma_state state; + + struct rvin_source_fmt source; + struct v4l2_pix_format format; + + struct v4l2_rect crop; + struct v4l2_rect compose; +}; + +#define vin_to_source(vin) vin->entity.subdev + +/* Debug */ +#define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) +#define vin_info(d, fmt, arg...) dev_info(d->dev, fmt, ##arg) +#define vin_warn(d, fmt, arg...) dev_warn(d->dev, fmt, ##arg) +#define vin_err(d, fmt, arg...) dev_err(d->dev, fmt, ##arg) + +int rvin_dma_probe(struct rvin_dev *vin, int irq); +void rvin_dma_remove(struct rvin_dev *vin); + +int rvin_v4l2_probe(struct rvin_dev *vin); +void rvin_v4l2_remove(struct rvin_dev *vin); + +const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat); + +/* Cropping, composing and scaling */ +void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, + u32 width, u32 height); +void rvin_crop_scale_comp(struct rvin_dev *vin); + +#endif diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 83029a4..39f6641 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -25,8 +25,8 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface -config VIDEO_RCAR_VIN - tristate "R-Car Video Input (VIN) support" +config VIDEO_RCAR_VIN_OLD + tristate "R-Car Video Input (VIN) support (DEPRECATED)" depends on VIDEO_DEV && SOC_CAMERA depends on ARCH_RENESAS || COMPILE_TEST depends on HAS_DMA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7ee71ae..7703cb7 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o -obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o +obj-$(CONFIG_VIDEO_RCAR_VIN_OLD) += rcar_vin.o -- cgit v0.10.2 From 66fa12003678512a599fb6f7f753e3f4f512a100 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Jun 2016 07:44:42 -0300 Subject: [media] rcar-vin: get rid of an unused var drivers/media/platform/rcar-vin/rcar-core.c: In function 'rvin_graph_notify_complete': drivers/media/platform/rcar-vin/rcar-core.c:65:22: warning: variable 'sd' set but not used [-Wunused-but-set-variable] struct v4l2_subdev *sd; ^ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 341c081..4b2007b 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -62,12 +62,9 @@ static int rvin_mbus_supported(struct rvin_dev *vin) static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) { - struct v4l2_subdev *sd; struct rvin_dev *vin = notifier_to_vin(notifier); int ret; - sd = vin_to_source(vin); - ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); if (ret < 0) { vin_err(vin, "Failed to register subdev nodes\n"); -- cgit v0.10.2 From 0db5c79989de2c68d5abb7ba891bfdb3cd3b7e05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Jun 2016 08:04:40 -0300 Subject: [media] media-devnode.h: Fix documentation Two parameters were documented with a wrong name, and a struct device pointer description was missing. That caused the following warnings, when building documentation: include/media/media-devnode.h:102: warning: No description found for parameter 'media_dev' include/media/media-devnode.h:126: warning: No description found for parameter 'mdev' include/media/media-devnode.h:126: warning: Excess function parameter 'media_dev' description in 'media_devnode_register' Rename the description, to match the function parameter and fix Documentation. No funcional changes. Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index f0b7dd7..37d4948 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -69,8 +69,9 @@ struct media_file_operations { /** * struct media_devnode - Media device node + * @media_dev: pointer to struct &media_device * @fops: pointer to struct &media_file_operations with media device ops - * @dev: struct device pointer for the media controller device + * @dev: pointer to struct &device containing the media controller device * @cdev: struct cdev pointer character device * @parent: parent device * @minor: device node minor number @@ -107,7 +108,7 @@ struct media_devnode { /** * media_devnode_register - register a media device node * - * @media_dev: struct media_device we want to register a device node + * @mdev: struct media_device we want to register a device node * @devnode: media device node structure we want to register * @owner: should be filled with %THIS_MODULE * -- cgit v0.10.2 From 60135db8d7acaff289d10568068469b57b730b6b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Apr 2016 20:37:44 -0300 Subject: [media] dt-bindings: Add Renesas R-Car FCP DT bindings The FCP is a companion module of video processing modules in the Renesas R-Car Gen3 SoCs. It provides data compression and decompression, data caching, and conversion of AXI transactions in order to reduce the memory bandwidth. Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt new file mode 100644 index 0000000..6a12960 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fcp.txt @@ -0,0 +1,32 @@ +Renesas R-Car Frame Compression Processor (FCP) +----------------------------------------------- + +The FCP is a companion module of video processing modules in the Renesas R-Car +Gen3 SoCs. It provides data compression and decompression, data caching, and +conversion of AXI transactions in order to reduce the memory bandwidth. + +There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP +for FDP (FCPF). Their configuration and behaviour depend on the module they +are paired with. These DT bindings currently support the FCPV only. + + - compatible: Must be one or more of the following + + - "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP' + - "renesas,fcpv" for generic compatible 'FCP for VSP' + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first, followed by the + family-specific and/or generic versions. + + - reg: the register base and size for the device registers + - clocks: Reference to the functional clock + + +Device node example +------------------- + + fcpvd1: fcp@fea2f000 { + compatible = "renesas,r8a7795-fcpv", "renesas,fcpv"; + reg = <0 0xfea2f000 0 0x200>; + clocks = <&cpg CPG_MOD 602>; + }; -- cgit v0.10.2 From 7b49235e83b2347caf4bc66dd0154cb799bd7405 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 11 Feb 2016 22:29:58 -0200 Subject: [media] v4l: Add Renesas R-Car FCP driver The FCP is a companion module of video processing modules in the Renesas R-Car Gen3 SoCs. It provides data compression and decompression, data caching, and conversion of AXI transactions in order to reduce the memory bandwidth. The driver is not meant to be used standalone but provides an API to the video processing modules to control the FCP. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 02299fd..949a920 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7312,6 +7312,16 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/potentiometer/mcp4531.c +MEDIA DRIVERS FOR RENESAS - FCP +M: Laurent Pinchart +L: linux-media@vger.kernel.org +L: linux-renesas-soc@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/renesas,fcp.txt +F: drivers/media/platform/rcar-fcp.c +F: include/media/rcar-fcp.h + MEDIA DRIVERS FOR RENESAS - VSP1 M: Laurent Pinchart L: linux-media@vger.kernel.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 382f393..d6fe4fe 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -248,6 +248,19 @@ config VIDEO_RENESAS_JPU To compile this driver as a module, choose M here: the module will be called rcar_jpu. +config VIDEO_RENESAS_FCP + tristate "Renesas Frame Compression Processor" + depends on ARCH_RENESAS || COMPILE_TEST + depends on OF + ---help--- + This is a driver for the Renesas Frame Compression Processor (FCP). + The FCP is a companion module of video processing modules in the + Renesas R-Car Gen3 SoCs. It handles memory access for the codec, + VSP and FDP modules. + + To compile this driver as a module, choose M here: the module + will be called rcar-fcp. + config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 99cf315..c83800a 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c new file mode 100644 index 0000000..6a7bcc3 --- /dev/null +++ b/drivers/media/platform/rcar-fcp.c @@ -0,0 +1,181 @@ +/* + * rcar-fcp.c -- R-Car Frame Compression Processor Driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct rcar_fcp_device { + struct list_head list; + struct device *dev; +}; + +static LIST_HEAD(fcp_devices); +static DEFINE_MUTEX(fcp_lock); + +/* ----------------------------------------------------------------------------- + * Public API + */ + +/** + * rcar_fcp_get - Find and acquire a reference to an FCP instance + * @np: Device node of the FCP instance + * + * Search the list of registered FCP instances for the instance corresponding to + * the given device node. + * + * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be + * found. + */ +struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) +{ + struct rcar_fcp_device *fcp; + + mutex_lock(&fcp_lock); + + list_for_each_entry(fcp, &fcp_devices, list) { + if (fcp->dev->of_node != np) + continue; + + /* + * Make sure the module won't be unloaded behind our back. This + * is a poor man's safety net, the module should really not be + * unloaded while FCP users can be active. + */ + if (!try_module_get(fcp->dev->driver->owner)) + fcp = NULL; + + goto done; + } + + fcp = ERR_PTR(-EPROBE_DEFER); + +done: + mutex_unlock(&fcp_lock); + return fcp; +} +EXPORT_SYMBOL_GPL(rcar_fcp_get); + +/** + * rcar_fcp_put - Release a reference to an FCP instance + * @fcp: The FCP instance + * + * Release the FCP instance acquired by a call to rcar_fcp_get(). + */ +void rcar_fcp_put(struct rcar_fcp_device *fcp) +{ + if (fcp) + module_put(fcp->dev->driver->owner); +} +EXPORT_SYMBOL_GPL(rcar_fcp_put); + +/** + * rcar_fcp_enable - Enable an FCP + * @fcp: The FCP instance + * + * Before any memory access through an FCP is performed by a module, the FCP + * must be enabled by a call to this function. The enable calls are reference + * counted, each successful call must be followed by one rcar_fcp_disable() + * call when no more memory transfer can occur through the FCP. + * + * Return 0 on success or a negative error code if an error occurs. The enable + * reference count isn't increased when this function returns an error. + */ +int rcar_fcp_enable(struct rcar_fcp_device *fcp) +{ + if (!fcp) + return 0; + + return pm_runtime_get_sync(fcp->dev); +} +EXPORT_SYMBOL_GPL(rcar_fcp_enable); + +/** + * rcar_fcp_disable - Disable an FCP + * @fcp: The FCP instance + * + * This function is the counterpart of rcar_fcp_enable(). As enable calls are + * reference counted a disable call may not disable the FCP synchronously. + */ +void rcar_fcp_disable(struct rcar_fcp_device *fcp) +{ + if (fcp) + pm_runtime_put(fcp->dev); +} +EXPORT_SYMBOL_GPL(rcar_fcp_disable); + +/* ----------------------------------------------------------------------------- + * Platform Driver + */ + +static int rcar_fcp_probe(struct platform_device *pdev) +{ + struct rcar_fcp_device *fcp; + + fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL); + if (fcp == NULL) + return -ENOMEM; + + fcp->dev = &pdev->dev; + + pm_runtime_enable(&pdev->dev); + + mutex_lock(&fcp_lock); + list_add_tail(&fcp->list, &fcp_devices); + mutex_unlock(&fcp_lock); + + platform_set_drvdata(pdev, fcp); + + return 0; +} + +static int rcar_fcp_remove(struct platform_device *pdev) +{ + struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); + + mutex_lock(&fcp_lock); + list_del(&fcp->list); + mutex_unlock(&fcp_lock); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id rcar_fcp_of_match[] = { + { .compatible = "renesas,fcpv" }, + { }, +}; + +static struct platform_driver rcar_fcp_platform_driver = { + .probe = rcar_fcp_probe, + .remove = rcar_fcp_remove, + .driver = { + .name = "rcar-fcp", + .of_match_table = rcar_fcp_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(rcar_fcp_platform_driver); + +MODULE_ALIAS("rcar-fcp"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Renesas FCP Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/media/rcar-fcp.h b/include/media/rcar-fcp.h new file mode 100644 index 0000000..4c7fc77 --- /dev/null +++ b/include/media/rcar-fcp.h @@ -0,0 +1,37 @@ +/* + * rcar-fcp.h -- R-Car Frame Compression Processor Driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __MEDIA_RCAR_FCP_H__ +#define __MEDIA_RCAR_FCP_H__ + +struct device_node; +struct rcar_fcp_device; + +#if IS_ENABLED(CONFIG_VIDEO_RENESAS_FCP) +struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np); +void rcar_fcp_put(struct rcar_fcp_device *fcp); +int rcar_fcp_enable(struct rcar_fcp_device *fcp); +void rcar_fcp_disable(struct rcar_fcp_device *fcp); +#else +static inline struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) +{ + return ERR_PTR(-ENOENT); +} +static inline void rcar_fcp_put(struct rcar_fcp_device *fcp) { } +static inline int rcar_fcp_enable(struct rcar_fcp_device *fcp) +{ + return -ENOSYS; +} +static inline void rcar_fcp_disable(struct rcar_fcp_device *fcp) { } +#endif + +#endif /* __MEDIA_RCAR_FCP_H__ */ -- cgit v0.10.2 From 1e6af546ee66b2870c5c21f4430910a27c26b5bb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Feb 2016 11:49:39 -0200 Subject: [media] v4l: vsp1: Implement runtime PM support Replace the manual refcount and clock management code by runtime PM. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 46738b6..9e09bce 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -64,9 +64,6 @@ struct vsp1_device { void __iomem *mmio; struct clk *clock; - struct mutex lock; - int ref_count; - struct vsp1_bru *bru; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index e2d779f..d6abc7f 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -462,35 +463,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1) /* * vsp1_device_get - Acquire the VSP1 device * - * Increment the VSP1 reference count and initialize the device if the first - * reference is taken. + * Make sure the device is not suspended and initialize it if needed. * * Return 0 on success or a negative error code otherwise. */ int vsp1_device_get(struct vsp1_device *vsp1) { - int ret = 0; - - mutex_lock(&vsp1->lock); - if (vsp1->ref_count > 0) - goto done; - - ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) - goto done; - - ret = vsp1_device_init(vsp1); - if (ret < 0) { - clk_disable_unprepare(vsp1->clock); - goto done; - } - -done: - if (!ret) - vsp1->ref_count++; + int ret; - mutex_unlock(&vsp1->lock); - return ret; + ret = pm_runtime_get_sync(vsp1->dev); + return ret < 0 ? ret : 0; } /* @@ -501,12 +483,7 @@ done: */ void vsp1_device_put(struct vsp1_device *vsp1) { - mutex_lock(&vsp1->lock); - - if (--vsp1->ref_count == 0) - clk_disable_unprepare(vsp1->clock); - - mutex_unlock(&vsp1->lock); + pm_runtime_put_sync(vsp1->dev); } /* ----------------------------------------------------------------------------- @@ -518,37 +495,55 @@ static int vsp1_pm_suspend(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - WARN_ON(mutex_is_locked(&vsp1->lock)); + vsp1_pipelines_suspend(vsp1); + pm_runtime_force_suspend(vsp1->dev); - if (vsp1->ref_count == 0) - return 0; + return 0; +} - vsp1_pipelines_suspend(vsp1); +static int vsp1_pm_resume(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); - clk_disable_unprepare(vsp1->clock); + pm_runtime_force_resume(vsp1->dev); + vsp1_pipelines_resume(vsp1); return 0; } +#endif -static int vsp1_pm_resume(struct device *dev) +static int vsp1_pm_runtime_suspend(struct device *dev) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - WARN_ON(mutex_is_locked(&vsp1->lock)); + clk_disable_unprepare(vsp1->clock); - if (vsp1->ref_count == 0) - return 0; + return 0; +} - clk_prepare_enable(vsp1->clock); +static int vsp1_pm_runtime_resume(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + int ret; - vsp1_pipelines_resume(vsp1); + ret = clk_prepare_enable(vsp1->clock); + if (ret < 0) + return ret; + + if (vsp1->info) { + ret = vsp1_device_init(vsp1); + if (ret < 0) { + clk_disable_unprepare(vsp1->clock); + return ret; + } + } return 0; } -#endif static const struct dev_pm_ops vsp1_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume) + SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL) }; /* ----------------------------------------------------------------------------- @@ -640,10 +635,11 @@ static int vsp1_probe(struct platform_device *pdev) return -ENOMEM; vsp1->dev = &pdev->dev; - mutex_init(&vsp1->lock); INIT_LIST_HEAD(&vsp1->entities); INIT_LIST_HEAD(&vsp1->videos); + platform_set_drvdata(pdev, vsp1); + /* I/O, IRQ and clock resources */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); @@ -670,12 +666,14 @@ static int vsp1_probe(struct platform_device *pdev) } /* Configure device parameters based on the version register. */ - ret = clk_prepare_enable(vsp1->clock); + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - return ret; + goto done; version = vsp1_read(vsp1, VI6_IP_VERSION); - clk_disable_unprepare(vsp1->clock); + pm_runtime_put_sync(&pdev->dev); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { if ((version & VI6_IP_VERSION_MODEL_MASK) == @@ -687,7 +685,8 @@ static int vsp1_probe(struct platform_device *pdev) if (!vsp1->info) { dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); - return -ENXIO; + ret = -ENXIO; + goto done; } dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); @@ -696,12 +695,14 @@ static int vsp1_probe(struct platform_device *pdev) ret = vsp1_create_entities(vsp1); if (ret < 0) { dev_err(&pdev->dev, "failed to create entities\n"); - return ret; + goto done; } - platform_set_drvdata(pdev, vsp1); +done: + if (ret) + pm_runtime_disable(&pdev->dev); - return 0; + return ret; } static int vsp1_remove(struct platform_device *pdev) @@ -710,6 +711,8 @@ static int vsp1_remove(struct platform_device *pdev) vsp1_destroy_entities(vsp1); + pm_runtime_disable(&pdev->dev); + return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 4f3b4a1..0c1dc80 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -383,7 +383,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1) { unsigned int i; - /* Resume pipeline all running pipelines. */ + /* Resume all running pipelines. */ for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; -- cgit v0.10.2 From c7b12cfddf7b4065a881b5890521a8a1a375924a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Feb 2016 11:49:39 -0200 Subject: [media] v4l: vsp1: Don't handle clocks manually The power domain performs functional clock handling when using runtime PM, there's no need to enable and disable the clock manually. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 9e09bce..37cc05e 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -62,7 +62,6 @@ struct vsp1_device { const struct vsp1_device_info *info; void __iomem *mmio; - struct clk *clock; struct vsp1_bru *bru; struct vsp1_hsit *hsi; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index d6abc7f..13907d4 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -514,10 +514,6 @@ static int vsp1_pm_resume(struct device *dev) static int vsp1_pm_runtime_suspend(struct device *dev) { - struct vsp1_device *vsp1 = dev_get_drvdata(dev); - - clk_disable_unprepare(vsp1->clock); - return 0; } @@ -526,16 +522,10 @@ static int vsp1_pm_runtime_resume(struct device *dev) struct vsp1_device *vsp1 = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) - return ret; - if (vsp1->info) { ret = vsp1_device_init(vsp1); - if (ret < 0) { - clk_disable_unprepare(vsp1->clock); + if (ret < 0) return ret; - } } return 0; @@ -640,18 +630,12 @@ static int vsp1_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vsp1); - /* I/O, IRQ and clock resources */ + /* I/O and IRQ resources (clock managed by the clock PM domain) */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); if (IS_ERR(vsp1->mmio)) return PTR_ERR(vsp1->mmio); - vsp1->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(vsp1->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return PTR_ERR(vsp1->clock); - } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "missing IRQ\n"); -- cgit v0.10.2 From 94fcdf829793b141dc93e20a2bbd9eeaa44ea25f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 11 Feb 2016 22:49:14 -0200 Subject: [media] v4l: vsp1: Add FCP support On some platforms the VSP performs memory accesses through an FCP. When that's the case get a reference to the FCP from the VSP DT node and enable/disable it at runtime as needed. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt index 627405ab..9b695bc 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.txt @@ -14,6 +14,11 @@ Required properties: - interrupts: VSP interrupt specifier. - clocks: A phandle + clock-specifier pair for the VSP functional clock. +Optional properties: + + - renesas,fcp: A phandle referencing the FCP that handles memory accesses + for the VSP. Not needed on Gen2, mandatory on Gen3. + Example: R8A7790 (R-Car H2) VSP1-S node diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index d6fe4fe..9d0a3ff 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -265,6 +265,7 @@ config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA depends on (ARCH_RENESAS && OF) || COMPILE_TEST + depends on !ARM64 || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 37cc05e..7cb0f5e 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -25,6 +25,7 @@ struct clk; struct device; +struct rcar_fcp_device; struct vsp1_drm; struct vsp1_entity; @@ -62,6 +63,7 @@ struct vsp1_device { const struct vsp1_device_info *info; void __iomem *mmio; + struct rcar_fcp_device *fcp; struct vsp1_bru *bru; struct vsp1_hsit *hsi; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 13907d4..e655639 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -22,6 +22,7 @@ #include #include +#include #include #include "vsp1.h" @@ -514,6 +515,10 @@ static int vsp1_pm_resume(struct device *dev) static int vsp1_pm_runtime_suspend(struct device *dev) { + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + rcar_fcp_disable(vsp1->fcp); + return 0; } @@ -528,7 +533,7 @@ static int vsp1_pm_runtime_resume(struct device *dev) return ret; } - return 0; + return rcar_fcp_enable(vsp1->fcp); } static const struct dev_pm_ops vsp1_pm_ops = { @@ -614,6 +619,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; + struct device_node *fcp_node; struct resource *irq; struct resource *io; unsigned int i; @@ -649,6 +655,18 @@ static int vsp1_probe(struct platform_device *pdev) return ret; } + /* FCP (optional) */ + fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); + if (fcp_node) { + vsp1->fcp = rcar_fcp_get(fcp_node); + of_node_put(fcp_node); + if (IS_ERR(vsp1->fcp)) { + dev_dbg(&pdev->dev, "FCP not found (%ld)\n", + PTR_ERR(vsp1->fcp)); + return PTR_ERR(vsp1->fcp); + } + } + /* Configure device parameters based on the version register. */ pm_runtime_enable(&pdev->dev); @@ -694,6 +712,7 @@ static int vsp1_remove(struct platform_device *pdev) struct vsp1_device *vsp1 = platform_get_drvdata(pdev); vsp1_destroy_entities(vsp1); + rcar_fcp_put(vsp1->fcp); pm_runtime_disable(&pdev->dev); -- cgit v0.10.2 From 44f4619857766289c99e5d61a87ba1621e8ddef6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 24 Feb 2016 19:10:04 -0300 Subject: [media] v4l: vsp1: Add output node value to routing table The output node value indicates the value to be used in a sampling point register to use the node as the source of histogram data. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 3d070bc..6a96ea7 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -274,28 +274,44 @@ int vsp1_entity_link_setup(struct media_entity *entity, * Initialization */ +#define VSP1_ENTITY_ROUTE(ent) \ + { VSP1_ENTITY_##ent, 0, VI6_DPR_##ent##_ROUTE, \ + { VI6_DPR_NODE_##ent }, VI6_DPR_NODE_##ent } + +#define VSP1_ENTITY_ROUTE_RPF(idx) \ + { VSP1_ENTITY_RPF, idx, VI6_DPR_RPF_ROUTE(idx), \ + { 0, }, VI6_DPR_NODE_RPF(idx) } + +#define VSP1_ENTITY_ROUTE_UDS(idx) \ + { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \ + { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) } + +#define VSP1_ENTITY_ROUTE_WPF(idx) \ + { VSP1_ENTITY_WPF, idx, 0, \ + { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } + static const struct vsp1_route vsp1_routes[] = { { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), - VI6_DPR_NODE_BRU_IN(4) } }, - { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, - { 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), { 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), } }, - { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } }, - { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } }, - { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } }, - { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } }, - { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } }, + VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT }, + VSP1_ENTITY_ROUTE(HSI), + VSP1_ENTITY_ROUTE(HST), + { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF }, + VSP1_ENTITY_ROUTE(LUT), + VSP1_ENTITY_ROUTE_RPF(0), + VSP1_ENTITY_ROUTE_RPF(1), + VSP1_ENTITY_ROUTE_RPF(2), + VSP1_ENTITY_ROUTE_RPF(3), + VSP1_ENTITY_ROUTE_RPF(4), + VSP1_ENTITY_ROUTE(SRU), + VSP1_ENTITY_ROUTE_UDS(0), + VSP1_ENTITY_ROUTE_UDS(1), + VSP1_ENTITY_ROUTE_UDS(2), + VSP1_ENTITY_ROUTE_WPF(0), + VSP1_ENTITY_ROUTE_WPF(1), + VSP1_ENTITY_ROUTE_WPF(2), + VSP1_ENTITY_ROUTE_WPF(3), }; int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 69eff4e..aaab05f 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -42,17 +42,21 @@ enum vsp1_entity_type { * @index: Entity index this routing entry is associated with * @reg: Output routing configuration register * @inputs: Target node value for each input + * @output: Target node value for entity output * * Each $vsp1_route entry describes routing configuration for the entity * specified by the entry's @type and @index. @reg indicates the register that * holds output routing configuration for the entity, and the @inputs array - * store the target node value for each input of the entity. + * store the target node value for each input of the entity. The @output field + * stores the target node value of the entity output when used as a source for + * histogram generation. */ struct vsp1_route { enum vsp1_entity_type type; unsigned int index; unsigned int reg; unsigned int inputs[VSP1_ENTITY_MAX_INPUTS]; + unsigned int output; }; /** -- cgit v0.10.2 From 7cf0f123c7354aa9b11cad5e3fdd9a5435cde4f0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 20:06:22 -0300 Subject: [media] v4l: vsp1: Replace container_of() with dedicated macro Add a macro to cast from a struct media_entity to a struct vsp1_entity to replace the manual implementations. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 6a96ea7..f60d792 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -22,6 +22,12 @@ #include "vsp1_dl.h" #include "vsp1_entity.h" +static inline struct vsp1_entity * +media_entity_to_vsp1_entity(struct media_entity *entity) +{ + return container_of(entity, struct vsp1_entity, subdev.entity); +} + void vsp1_entity_route_setup(struct vsp1_entity *source, struct vsp1_dl_list *dl) { @@ -30,7 +36,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *source, if (source->route->reg == 0) return; - sink = container_of(source->sink, struct vsp1_entity, subdev.entity); + sink = media_entity_to_vsp1_entity(source->sink); vsp1_dl_list_write(dl, source->route->reg, sink->route->inputs[source->sink_pad]); } @@ -252,7 +258,7 @@ int vsp1_entity_link_setup(struct media_entity *entity, if (!(local->flags & MEDIA_PAD_FL_SOURCE)) return 0; - source = container_of(local->entity, struct vsp1_entity, subdev.entity); + source = media_entity_to_vsp1_entity(local->entity); if (!source->route) return 0; -- cgit v0.10.2 From ccd3d95a93de25e971b23d2230b5c0a2e15d5cb7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Mar 2016 20:17:49 -0300 Subject: [media] v4l: vsp1: Make vsp1_entity_get_pad_compose() more generic Turn the helper into a function that can retrieve crop and compose selection rectangles. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index f60d792..fd20c0d 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -87,12 +87,30 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); } +/** + * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity + * @entity: the entity + * @cfg: the configuration storage + * @pad: the pad number + * @target: the selection target + * + * Return the selection rectangle stored in the given configuration for an + * entity's pad. The configuration can be an ACTIVE or TRY configuration. The + * selection target can be COMPOSE or CROP. + */ struct v4l2_rect * -vsp1_entity_get_pad_compose(struct vsp1_entity *entity, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +vsp1_entity_get_pad_selection(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int target) { - return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); + switch (target) { + case V4L2_SEL_TGT_COMPOSE: + return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); + case V4L2_SEL_TGT_CROP: + return v4l2_subdev_get_try_crop(&entity->subdev, cfg, pad); + default: + return NULL; + } } /* diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index aaab05f..a240fc1 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -122,9 +122,9 @@ 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); +vsp1_entity_get_pad_selection(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int target); 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 49168db..64dfbdd 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -130,9 +130,10 @@ static void rpf_configure(struct vsp1_entity *entity, if (pipe->bru) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_compose(pipe->bru, - pipe->bru->config, - rpf->bru_input); + compose = vsp1_entity_get_pad_selection(pipe->bru, + pipe->bru->config, + rpf->bru_input, + V4L2_SEL_TGT_COMPOSE); left = compose->left; top = compose->top; } -- cgit v0.10.2 From 0c1a41b50805464f397a334a6b2dd95ca6415f32 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 10 Apr 2016 02:59:04 -0300 Subject: [media] v4l: vsp1: Move frame sequence number from video node to pipeline The frame sequence number is global to the pipeline, there's no need to store copies in each video node. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 0c1dc80..be47c8a 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -286,6 +286,8 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) if (pipe->frame_end) pipe->frame_end(pipe); + + pipe->sequence++; } /* diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 7b561135..febc62f 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -67,6 +67,7 @@ enum vsp1_pipeline_state { * @kref: pipeline reference count * @stream_count: number of streaming video nodes * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available + * @sequence: frame sequence number * @num_inputs: number of RPFs * @inputs: array of RPFs in the pipeline (indexed by RPF index) * @output: WPF at the output of the pipeline @@ -90,6 +91,7 @@ struct vsp1_pipeline { struct kref kref; unsigned int stream_count; unsigned int buffers_ready; + unsigned int sequence; unsigned int num_inputs; struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a9aec5c..34aa642 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -219,7 +219,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video) spin_unlock_irqrestore(&video->irqlock, flags); - done->buf.sequence = video->sequence++; + done->buf.sequence = pipe->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, @@ -805,8 +805,6 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (video->queue.owner && video->queue.owner != file->private_data) return -EBUSY; - video->sequence = 0; - /* 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. diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 867b008..1595fd5 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -49,7 +49,6 @@ struct vsp1_video { void *alloc_ctx; spinlock_t irqlock; struct list_head irqqueue; - unsigned int sequence; }; static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) -- cgit v0.10.2 From c6b013ab517b0ae09036d50f6b1684d9dbedf596 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Apr 2016 19:08:59 -0300 Subject: [media] v4l: vsp1: Group DRM RPF parameters in a structure The vsp1_du_atomic_update_ext() function takes 7 RPF configuration parameters, and more will likely be added later. This makes the code difficult to read and error-prone as multiple parameters have the same type. Make the API safer and easier to extend in the future by grouping all parameters in a structure. Use macro magic to ease the transition to the new function by allowing the old and new functions to be called using the same name. The macros and static inline wrapper will be removed as soon as the caller is updated. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index fc4bbc4..fef53ec 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -230,42 +230,33 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline * @dev: the VSP device * @rpf_index: index of the RPF to setup (0-based) - * @pixelformat: V4L2 pixel format for the RPF memory input - * @pitch: number of bytes per line in the image stored in memory - * @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 + * @cfg: the RPF configuration * - * Configure the VSP to perform composition of the image referenced by @mem - * through RPF @rpf_index, using the @src crop rectangle and the @dst + * Configure the VSP to perform image composition through RPF @rpf_index as + * described by the @cfg configuration. The image to compose is referenced by + * @cfg.mem and composed using the @cfg.src crop rectangle and the @cfg.dst * 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 - * @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the + * If the @cfg configuration is NULL, the RPF will be disabled. Calling the * function on a disabled RPF is allowed. * - * The memory pitch is configurable to allow for padding at end of lines, or - * simple for images that extend beyond the crop rectangle boundaries. The - * @pitch value is expressed in bytes and applies to all planes for multiplanar - * formats. + * Image format as stored in memory is expressed as a V4L2 @cfg.pixelformat + * value. The memory pitch is configurable to allow for padding at end of lines, + * or simply for images that extend beyond the crop rectangle boundaries. The + * @cfg.pitch value is expressed in bytes and applies to all planes for + * multiplanar formats. * * The source memory buffer is referenced by the DMA address of its planes in - * the @mem array. Up to two planes are supported. The second plane DMA address - * is ignored for formats using a single plane. + * the @cfg.mem array. Up to two planes are supported. The second plane DMA + * address is ignored for formats using a single plane. * * This function isn't reentrant, the caller needs to serialize calls. * * Return 0 on success or a negative error code on failure. */ -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 alpha, - unsigned int zpos) +int __vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, + const struct vsp1_du_atomic_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); const struct vsp1_format_info *fmtinfo; @@ -276,7 +267,7 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, rpf = vsp1->rpf[rpf_index]; - if (pixelformat == 0) { + if (!cfg) { dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__, rpf_index); @@ -287,38 +278,39 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index, dev_dbg(vsp1->dev, "%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], zpos); + cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, + cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, + cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], + cfg->zpos); /* 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); + fmtinfo = vsp1_get_format_info(cfg->pixelformat); if (!fmtinfo) { dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", - pixelformat); + cfg->pixelformat); return -EINVAL; } rpf->fmtinfo = fmtinfo; rpf->format.num_planes = fmtinfo->planes; - rpf->format.plane_fmt[0].bytesperline = pitch; - rpf->format.plane_fmt[1].bytesperline = pitch; - rpf->alpha = alpha; + rpf->format.plane_fmt[0].bytesperline = cfg->pitch; + rpf->format.plane_fmt[1].bytesperline = cfg->pitch; + rpf->alpha = cfg->alpha; - rpf->mem.addr[0] = mem[0]; - rpf->mem.addr[1] = mem[1]; + rpf->mem.addr[0] = cfg->mem[0]; + rpf->mem.addr[1] = cfg->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].crop = cfg->src; + vsp1->drm->inputs[rpf_index].compose = cfg->dst; + vsp1->drm->inputs[rpf_index].zpos = cfg->zpos; vsp1->drm->inputs[rpf_index].enabled = true; return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext); +EXPORT_SYMBOL_GPL(__vsp1_du_atomic_update); static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf, unsigned int bru_input) diff --git a/include/media/vsp1.h b/include/media/vsp1.h index 3e654a0..ea8ad75 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -14,31 +14,52 @@ #define __MEDIA_VSP1_H__ #include +#include struct device; -struct v4l2_rect; int vsp1_du_init(struct device *dev); int vsp1_du_setup_lif(struct device *dev, unsigned int width, unsigned int height); +struct vsp1_du_atomic_config { + u32 pixelformat; + unsigned int pitch; + dma_addr_t mem[2]; + struct v4l2_rect src; + struct v4l2_rect dst; + unsigned int alpha; + unsigned int zpos; +}; + 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 alpha, - unsigned int zpos); +int __vsp1_du_atomic_update(struct device *dev, unsigned int rpf, + const struct vsp1_du_atomic_config *cfg); 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) +static inline int vsp1_du_atomic_update_old(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) { - return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch, - mem, src, dst, 255, 0); + struct vsp1_du_atomic_config cfg = { + .pixelformat = pixelformat, + .pitch = pitch, + .mem[0] = mem[0], + .mem[1] = mem[1], + .src = *src, + .dst = *dst, + .alpha = 255, + .zpos = 0, + }; + + return __vsp1_du_atomic_update(dev, rpf, &cfg); } +#define _vsp1_du_atomic_update(_1, _2, _3, _4, _5, _6, _7, f, ...) f +#define vsp1_du_atomic_update(...) \ + _vsp1_du_atomic_update(__VA_ARGS__, vsp1_du_atomic_update_old, 0, 0, \ + 0, __vsp1_du_atomic_update)(__VA_ARGS__) + #endif /* __MEDIA_VSP1_H__ */ -- cgit v0.10.2 From 41740824559d67bf1251a148ccf4f427bb76fee9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 24 Mar 2016 05:15:59 -0300 Subject: [media] drm: rcar-du: Add alpha support for VSP planes Make the global alpha multiplier of VSP planes configurable through the alpha property, exactly as for the native DU planes. Signed-off-by: Laurent Pinchart Acked-by: Dave Airlie Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index e671a7c..f892cf1 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -148,40 +148,41 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) struct rcar_du_vsp_plane_state *state = to_rcar_vsp_plane_state(plane->plane.state); struct drm_framebuffer *fb = plane->plane.state->fb; - struct v4l2_rect src; - struct v4l2_rect dst; - dma_addr_t paddr[2] = { 0, }; - u32 pixelformat = 0; + struct vsp1_du_atomic_config cfg = { + .pixelformat = 0, + .pitch = fb->pitches[0], + .alpha = state->alpha, + .zpos = 0, + }; unsigned int i; - src.left = state->state.src_x >> 16; - src.top = state->state.src_y >> 16; - src.width = state->state.src_w >> 16; - src.height = state->state.src_h >> 16; + cfg.src.left = state->state.src_x >> 16; + cfg.src.top = state->state.src_y >> 16; + cfg.src.width = state->state.src_w >> 16; + cfg.src.height = state->state.src_h >> 16; - dst.left = state->state.crtc_x; - dst.top = state->state.crtc_y; - dst.width = state->state.crtc_w; - dst.height = state->state.crtc_h; + cfg.dst.left = state->state.crtc_x; + cfg.dst.top = state->state.crtc_y; + cfg.dst.width = state->state.crtc_w; + cfg.dst.height = state->state.crtc_h; for (i = 0; i < state->format->planes; ++i) { struct drm_gem_cma_object *gem; gem = drm_fb_cma_get_gem_obj(fb, i); - paddr[i] = gem->paddr + fb->offsets[i]; + cfg.mem[i] = gem->paddr + fb->offsets[i]; } for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) { if (formats_kms[i] == state->format->fourcc) { - pixelformat = formats_v4l2[i]; + cfg.pixelformat = formats_v4l2[i]; break; } } - WARN_ON(!pixelformat); + WARN_ON(!cfg.pixelformat); - vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat, - fb->pitches[0], paddr, &src, &dst); + vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg); } static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, @@ -220,8 +221,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane, if (plane->state->crtc) rcar_du_vsp_plane_setup(rplane); else - vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0, - NULL, NULL); + vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL); } static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = { -- cgit v0.10.2 From 85e0a168f51b5ef9836288fa0f14dcadb8401181 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 24 Mar 2016 05:15:59 -0300 Subject: [media] drm: rcar-du: Add Z-order support for VSP planes Make the Z-order of VSP planes configurable through the zpos property, exactly as for the native DU planes. Signed-off-by: Laurent Pinchart Acked-by: Dave Airlie Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index f892cf1..6ac717f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -152,7 +152,7 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) .pixelformat = 0, .pitch = fb->pitches[0], .alpha = state->alpha, - .zpos = 0, + .zpos = state->zpos, }; unsigned int i; @@ -180,8 +180,6 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) } } - WARN_ON(!cfg.pixelformat); - vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg); } @@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane) return; state->alpha = 255; + state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; plane->state = &state->state; plane->state->plane = plane; @@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane, if (property == rcdu->props.alpha) rstate->alpha = val; + else if (property == rcdu->props.zpos) + rstate->zpos = val; else return -EINVAL; @@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane, if (property == rcdu->props.alpha) *val = rstate->alpha; + else if (property == rcdu->props.zpos) + *val = rstate->zpos; else return -EINVAL; @@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp) drm_object_attach_property(&plane->plane.base, rcdu->props.alpha, 255); + drm_object_attach_property(&plane->plane.base, + rcdu->props.zpos, 1); } return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index df3bf38..510dcc9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p) * @state: base DRM plane state * @format: information about the pixel format used by the plane * @alpha: value of the plane alpha property + * @zpos: value of the plane zpos property */ struct rcar_du_vsp_plane_state { struct drm_plane_state state; @@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state { const struct rcar_du_format_info *format; unsigned int alpha; + unsigned int zpos; }; static inline struct rcar_du_vsp_plane_state * -- cgit v0.10.2 From c3f34a4bdd596127000666c17bbf8ba1c3d2d332 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Apr 2016 20:11:59 -0300 Subject: [media] v4l: vsp1: Remove deprecated DRM API The DRM driver has switched to the new API, remove the deprecated macros and inline wrapper. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index fef53ec..1473011 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -255,8 +255,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); * * Return 0 on success or a negative error code on failure. */ -int __vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, - const struct vsp1_du_atomic_config *cfg) +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, + const struct vsp1_du_atomic_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); const struct vsp1_format_info *fmtinfo; @@ -310,7 +310,7 @@ int __vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, return 0; } -EXPORT_SYMBOL_GPL(__vsp1_du_atomic_update); +EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf, unsigned int bru_input) diff --git a/include/media/vsp1.h b/include/media/vsp1.h index ea8ad75..9322d977 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -34,32 +34,8 @@ struct vsp1_du_atomic_config { }; void vsp1_du_atomic_begin(struct device *dev); -int __vsp1_du_atomic_update(struct device *dev, unsigned int rpf, - const struct vsp1_du_atomic_config *cfg); +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf, + const struct vsp1_du_atomic_config *cfg); void vsp1_du_atomic_flush(struct device *dev); -static inline int vsp1_du_atomic_update_old(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) -{ - struct vsp1_du_atomic_config cfg = { - .pixelformat = pixelformat, - .pitch = pitch, - .mem[0] = mem[0], - .mem[1] = mem[1], - .src = *src, - .dst = *dst, - .alpha = 255, - .zpos = 0, - }; - - return __vsp1_du_atomic_update(dev, rpf, &cfg); -} - -#define _vsp1_du_atomic_update(_1, _2, _3, _4, _5, _6, _7, f, ...) f -#define vsp1_du_atomic_update(...) \ - _vsp1_du_atomic_update(__VA_ARGS__, vsp1_du_atomic_update_old, 0, 0, \ - 0, __vsp1_du_atomic_update)(__VA_ARGS__) - #endif /* __MEDIA_VSP1_H__ */ -- cgit v0.10.2 From 04d983fc4d675949e04417c39ce91bd862d2714a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 May 2016 18:15:47 -0300 Subject: [media] v4l: vsp1: Fix typo in register field names The VI6_RPF_ALPH_SEL ALPHA0 and ALPHA1 fields are inverted, swap them. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 927b5fb..7657545 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -154,10 +154,10 @@ #define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18) #define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18) #define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18) -#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8) -#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8 -#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0) -#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0 +#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 8) +#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 8 +#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 0) +#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 0 #define VI6_RPF_VRTCOL_SET 0x0318 #define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24) -- cgit v0.10.2 From 398e3d4f1e0baecb926315673a6740f2573b07ae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 May 2016 04:23:33 -0300 Subject: [media] v4l: vsp1: Fix descriptions of Gen2 VSP instances The number of UDS and WPF are set to incorrect values, fix them. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index e655639..70e7a81 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -560,7 +560,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_SRU, .rpf_count = 5, - .uds_count = 1, + .uds_count = 3, .wpf_count = 4, .num_bru_inputs = 4, .uapi = true, @@ -570,7 +570,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, .rpf_count = 4, .uds_count = 1, - .wpf_count = 4, + .wpf_count = 1, .num_bru_inputs = 4, .uapi = true, }, { @@ -578,7 +578,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, .rpf_count = 5, - .uds_count = 3, + .uds_count = 1, .wpf_count = 4, .num_bru_inputs = 4, .uapi = true, -- cgit v0.10.2 From d69e40fade97b6b19837c1772efa516bc28cc870 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 18 May 2016 20:01:21 -0300 Subject: [media] v4l: vsp1: Fix crash when resetting pipeline The vsp1_pipeline_reset() function loops over pipeline inputs and output and resets them. When doing so it assumes both that the pipeline has been correctly configured with an output, and that inputs are are stored in the pipe inputs array at positions 0 to num_inputs-1. Both the assumptions are incorrect. The pipeline might need to be reset after a failed attempts to configure it, without any output specified. Furthermore, inputs are stored in a positiong equal to their RPF index, possibly creating holes in the inputs array if the RPFs are not used in sequence. Fix both issues by looping over the whole inputs array and skipping unused entries, and ignoring the output when not set. Fixes: ff7e97c94d9f ("[media] v4l: vsp1: Store pipeline pointer in rwpf") Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index be47c8a..3c6f623 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -172,13 +172,17 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) bru->inputs[i].rpf = NULL; } - for (i = 0; i < pipe->num_inputs; ++i) { - pipe->inputs[i]->pipe = NULL; - pipe->inputs[i] = NULL; + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + if (pipe->inputs[i]) { + pipe->inputs[i]->pipe = NULL; + pipe->inputs[i] = NULL; + } } - pipe->output->pipe = NULL; - pipe->output = NULL; + if (pipe->output) { + pipe->output->pipe = NULL; + pipe->output = NULL; + } INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; -- cgit v0.10.2 From e98c59dd717fddde28193777c4602196b1240e66 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 19:13:51 -0300 Subject: [media] v4l: vsp1: pipe: Fix typo in comment The vsp1_pipeline wq field is a wait queue, not a work queue. Fix the comment accordingly. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index febc62f..fc9825d 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -61,7 +61,7 @@ enum vsp1_pipeline_state { * @pipe: the media pipeline * @irqlock: protects the pipeline state * @state: current state - * @wq: work queue to wait for state change completion + * @wq: wait queue to wait for state change completion * @frame_end: frame end interrupt handler * @lock: protects the pipeline use count and stream count * @kref: pipeline reference count -- cgit v0.10.2 From eb9163d3bd2700ea3c6570a07e7548f678211dac Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Jun 2016 21:11:26 -0300 Subject: [media] v4l: vsp1: Constify operation structures The structures are never modified, make them const. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index b1068c0..ed36d33 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -249,7 +249,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops bru_pad_ops = { +static const 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, @@ -259,7 +259,7 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = { .set_selection = bru_set_selection, }; -static struct v4l2_subdev_ops bru_ops = { +static const struct v4l2_subdev_ops bru_ops = { .pad = &bru_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 68b8567..ec90af1 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -107,7 +107,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops hsit_pad_ops = { +static const 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, @@ -115,7 +115,7 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = { .set_fmt = hsit_set_format, }; -static struct v4l2_subdev_ops hsit_ops = { +static const struct v4l2_subdev_ops hsit_ops = { .pad = &hsit_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 0217393..e554b31 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -104,7 +104,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops lif_pad_ops = { +static const 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, @@ -112,7 +112,7 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = { .set_fmt = lif_set_format, }; -static struct v4l2_subdev_ops lif_ops = { +static const struct v4l2_subdev_ops lif_ops = { .pad = &lif_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index aa09e59..a543deb 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -147,11 +147,11 @@ static int lut_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_core_ops lut_core_ops = { +static const struct v4l2_subdev_core_ops lut_core_ops = { .ioctl = lut_ioctl, }; -static struct v4l2_subdev_pad_ops lut_pad_ops = { +static const 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, @@ -159,7 +159,7 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = { .set_fmt = lut_set_format, }; -static struct v4l2_subdev_ops lut_ops = { +static const struct v4l2_subdev_ops lut_ops = { .core = &lut_core_ops, .pad = &lut_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 64dfbdd..4b397ba 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -38,7 +38,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_ops rpf_ops = { +static const struct v4l2_subdev_ops rpf_ops = { .pad = &vsp1_rwpf_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 97ef997..6f8cf98 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -239,7 +239,7 @@ static int sru_set_format(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_subdev_pad_ops sru_pad_ops = { +static const 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, @@ -247,7 +247,7 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = { .set_fmt = sru_set_format, }; -static struct v4l2_subdev_ops sru_ops = { +static const struct v4l2_subdev_ops sru_ops = { .pad = &sru_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 1875e29..5d508e7 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -226,7 +226,7 @@ static int uds_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static struct v4l2_subdev_pad_ops uds_pad_ops = { +static const 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, @@ -234,7 +234,7 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = { .set_fmt = uds_set_format, }; -static struct v4l2_subdev_ops uds_ops = { +static const struct v4l2_subdev_ops uds_ops = { .pad = &uds_pad_ops, }; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 34aa642..a899b15 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -696,7 +696,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&video->irqlock, flags); } -static struct vb2_ops vsp1_video_queue_qops = { +static const struct vb2_ops vsp1_video_queue_qops = { .queue_setup = vsp1_video_queue_setup, .buf_prepare = vsp1_video_buffer_prepare, .buf_queue = vsp1_video_buffer_queue, @@ -913,7 +913,7 @@ static int vsp1_video_release(struct file *file) return 0; } -static struct v4l2_file_operations vsp1_video_fops = { +static const struct v4l2_file_operations vsp1_video_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, .open = vsp1_video_open, diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6c91eaa..59bdb45 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -62,11 +62,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) * V4L2 Subdevice Operations */ -static struct v4l2_subdev_video_ops wpf_video_ops = { +static const struct v4l2_subdev_video_ops wpf_video_ops = { .s_stream = wpf_s_stream, }; -static struct v4l2_subdev_ops wpf_ops = { +static const struct v4l2_subdev_ops wpf_ops = { .video = &wpf_video_ops, .pad = &vsp1_rwpf_pad_ops, }; -- cgit v0.10.2 From b4dfb9b35a192f555d76fc4f670a447657bcc183 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Jun 2016 21:03:29 -0300 Subject: [media] v4l: vsp1: Stop the pipeline upon the first STREAMOFF The device is stopped when STREAMOFF is called on the last video node in the pipeline. This results in possible memory corruption and/or crashes, as userspace could free buffers while the hardware is still writing to them, and the frame completion interrupt handler could try to access buffers that don't exist anymore. Fix this by stopping the pipeline upon the first STREAMOFF call, not the last. Reported-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a899b15..f6208b9 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -674,7 +674,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) int ret; mutex_lock(&pipe->lock); - if (--pipe->stream_count == 0) { + if (--pipe->stream_count == pipe->num_inputs) { /* Stop the pipeline. */ ret = vsp1_pipeline_stop(pipe); if (ret == -ETIMEDOUT) -- cgit v0.10.2 From 67a706f3ebde90416b45257cf4b23f3d9db97670 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 16 Jun 2016 19:33:43 -0300 Subject: [media] v4l: vsp1: sru: Fix intensity control ID The intensity control reused the V4L2_CID_CONTRAST control ID by mistake. Fix it by using an ID from the device-specific IDs range. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 6f8cf98..035c269 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -37,7 +37,7 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl, * Controls */ -#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1) +#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE | 0x1001) struct vsp1_sru_param { u32 ctrl0; -- cgit v0.10.2 From 1179aab13db3c6251484afe492c8dbd869ca8b05 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 15 Feb 2016 22:00:30 -0200 Subject: [media] media: Add video processing entity functions Add composer, pixel formatter, pixel encoding converter and scaler functions. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml index 5e3f20f..60fe841 100644 --- a/Documentation/DocBook/media/v4l/media-types.xml +++ b/Documentation/DocBook/media/v4l/media-types.xml @@ -121,6 +121,61 @@ MEDIA_ENT_F_AUDIO_MIXER Audio Mixer Function Entity. + + MEDIA_ENT_F_PROC_VIDEO_COMPOSER + Video composer (blender). An entity capable of video + composing must have at least two sink pads and one source + pad, and composes input video frames onto output video + frames. Composition can be performed using alpha blending, + color keying, raster operations (ROP), stitching or any other + means. + + + + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER + Video pixel formatter. An entity capable of pixel formatting + must have at least one sink pad and one source pad. Read + pixel formatters read pixels from memory and perform a subset + of unpacking, cropping, color keying, alpha multiplication + and pixel encoding conversion. Write pixel formatters perform + a subset of dithering, pixel encoding conversion and packing + and write pixels to memory. + + + + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV + Video pixel encoding converter. An entity capable of pixel + enconding conversion must have at least one sink pad and one + source pad, and convert the encoding of pixels received on + its sink pad(s) to a different encoding output on its source + pad(s). Pixel encoding conversion includes but isn't limited + to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB + conversions. + + + + MEDIA_ENT_F_PROC_VIDEO_LUT + Video look-up table. An entity capable of video lookup table + processing must have one sink pad and one source pad. It uses + the values of the pixels received on its sink pad to look up + entries in internal tables and output them on its source pad. + The lookup processing can be performed on all components + separately or combine them for multi-dimensional table + lookups. + + + + MEDIA_ENT_F_PROC_VIDEO_SCALER + Video scaler. An entity capable of video scaling must have + at least one sink pad and one source pad, and scale the + video frame(s) received on its sink pad(s) to a different + resolution output on its source pad(s). The range of + supported scaling ratios is entity-specific and can differ + between the horizontal and vertical directions (in particular + scaling can be supported in one direction only). Binning and + skipping are considered as scaling. + + diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index df59ede..3136686 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -95,6 +95,15 @@ struct media_device_info { #define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003) /* + * Processing entities + */ +#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER (MEDIA_ENT_F_BASE + 0x4001) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002) +#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003) +#define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) +#define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) + +/* * Connectors */ /* It is a responsibility of the entity drivers to add connectors and links */ -- cgit v0.10.2 From eaa0b96bbb6586a95e66d3dedd974313d7b0d94f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 1 Mar 2016 21:12:27 -0300 Subject: [media] media: Add video statistics computation functions The video statistics function describes entities such as video histogram engines or 3A statistics engines. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml index 60fe841..95aa1f9 100644 --- a/Documentation/DocBook/media/v4l/media-types.xml +++ b/Documentation/DocBook/media/v4l/media-types.xml @@ -176,6 +176,15 @@ skipping are considered as scaling. + + MEDIA_ENT_F_PROC_VIDEO_STATISTICS + Video statistics computation (histogram, 3A, ...). An entity + capable of statistics computation must have one sink pad and + one source pad. It computes statistics over the frames + received on its sink pad and outputs the statistics data on + its source pad. + + diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 3136686..7acf0f6 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -102,6 +102,7 @@ struct media_device_info { #define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003) #define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) #define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) +#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) /* * Connectors -- cgit v0.10.2 From 078e04993aef1bc7388e99dba99f6793e246c659 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 13:04:51 -0300 Subject: [media] v4l: vsp1: Base link creation on availability of entities Check the entity pointer instead of the feature flag to see if the entity is available before creating related links. The two methods are currently equivalent, but will differ in the future as we implement support for ignoring some of the entities present in the hardware. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 70e7a81..7e3b9da 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -147,7 +147,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) return ret; } - if (vsp1->info->features & VSP1_HAS_LIF) { + if (vsp1->lif) { ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, -- cgit v0.10.2 From aa0bad337ff231882dc8849f2234138281b5c414 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 13:09:25 -0300 Subject: [media] v4l: vsp1: Don't register media device when userspace API is disabled The media device doesn't need to be exposed to userspace when the VSP is fully controlled by the DU driver. Don't register it in that case. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 7e3b9da..71fd614 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -206,7 +206,8 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) } v4l2_device_unregister(&vsp1->v4l2_dev); - media_device_unregister(&vsp1->media_dev); + if (vsp1->info->uapi) + media_device_unregister(&vsp1->media_dev); media_device_cleanup(&vsp1->media_dev); if (!vsp1->info->uapi) @@ -381,14 +382,15 @@ 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) + if (vsp1->info->uapi) { ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); - else - ret = vsp1_drm_init(vsp1); - if (ret < 0) - goto done; + if (ret < 0) + goto done; - ret = media_device_register(mdev); + ret = media_device_register(mdev); + } else { + ret = vsp1_drm_init(vsp1); + } done: if (ret < 0) -- cgit v0.10.2 From 8d954abe30fcf1e979473abb0f3cf2f18c0d13e0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 13:10:57 -0300 Subject: [media] v4l: vsp1: Don't create LIF entity when the userspace API is enabled The LIF is only used when feeding the VSP output to the DU. The only way to do so is by controlling the VSP directly from the DU driver and disabling the VSP userspace API. There is thus no need to create a LIF entity when the userspace API is enabled, as it can't be used in that case. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 71fd614..0b0e736 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -170,19 +170,15 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) for (i = 0; i < vsp1->info->wpf_count; ++i) { /* Connect the video device to the WPF. All connections are - * immutable except for the WPF0 source link if a LIF is - * present. + * immutable. */ struct vsp1_rwpf *wpf = vsp1->wpf[i]; - unsigned int flags = MEDIA_LNK_FL_ENABLED; - - if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0) - flags |= MEDIA_LNK_FL_IMMUTABLE; ret = media_create_pad_link(&wpf->entity.subdev.entity, RWPF_PAD_SOURCE, &wpf->video->video.entity, 0, - flags); + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); if (ret < 0) return ret; } @@ -271,7 +267,11 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->info->features & VSP1_HAS_LIF) { + /* The LIF is only supported when used in conjunction with the DU, in + * which case the userspace API is disabled. If the userspace API is + * enabled skip the LIF, even when present. + */ + if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { ret = PTR_ERR(vsp1->lif); -- cgit v0.10.2 From 6a8e07b215a91be310dac358fdccd7709dd2458f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 15 Feb 2016 22:10:26 -0200 Subject: [media] v4l: vsp1: Set entities functions Initialize the function field of all subdev entities instantiated by the driver. This gets rids of multiple warnings printed by the media controller core. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index ed36d33..cae9117 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -390,7 +390,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) bru->entity.type = VSP1_ENTITY_BRU; ret = vsp1_entity_init(vsp1, &bru->entity, "bru", - vsp1->info->num_bru_inputs + 1, &bru_ops); + vsp1->info->num_bru_inputs + 1, &bru_ops, + MEDIA_ENT_F_PROC_VIDEO_COMPOSER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index fd20c0d..d72dbcc 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -340,7 +340,7 @@ static const struct vsp1_route vsp1_routes[] = { int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, - const struct v4l2_subdev_ops *ops) + const struct v4l2_subdev_ops *ops, u32 function) { struct v4l2_subdev *subdev; unsigned int i; @@ -381,6 +381,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, subdev = &entity->subdev; v4l2_subdev_init(subdev, ops); + subdev->entity.function = function; subdev->entity.ops = &vsp1->media_ops; subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index a240fc1..bcdea4e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -104,7 +104,7 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, - const struct v4l2_subdev_ops *ops); + const struct v4l2_subdev_ops *ops, u32 function); void vsp1_entity_destroy(struct vsp1_entity *entity); extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index ec90af1..ab3cae3 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -161,8 +161,9 @@ 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, inverse ? "hsi" : "hst", 2, - &hsit_ops); + ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", + 2, &hsit_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index e554b31..e006f0d 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -165,7 +165,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) lif->entity.ops = &lif_entity_ops; lif->entity.type = VSP1_ENTITY_LIF; - ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops); + /* The LIF is never exposed to userspace, but media entity registration + * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to + * avoid triggering a WARN_ON(), the value won't be seen anywhere. + */ + ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index a543deb..855e483 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -204,7 +204,8 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; - ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops); + ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops, + MEDIA_ENT_F_PROC_VIDEO_LUT); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 4b397ba..47b1714 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -237,7 +237,8 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.index = index; sprintf(name, "rpf.%u", index); - ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops); + ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 035c269..e13afd5 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -308,7 +308,8 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) sru->entity.ops = &sru_entity_ops; sru->entity.type = VSP1_ENTITY_SRU; - ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops); + ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops, + MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 5d508e7..f229451 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -314,7 +314,8 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) uds->entity.index = index; sprintf(name, "uds.%u", index); - ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops); + ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops, + MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret < 0) return ERR_PTR(ret); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 59bdb45..70fb979 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -216,7 +216,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->entity.index = index; sprintf(name, "wpf.%u", index); - ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops); + ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret); -- cgit v0.10.2 From 9489a8ff0a13fc0f62e556a31341d3bbaef9da6b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 19:17:02 -0300 Subject: [media] v4l: vsp1: dl: Don't free fragments with interrupts disabled Freeing a fragment requires freeing DMA coherent memory, which can be performed with interrupts disabled as per the DMA mapping API contract. The fragments can't thus be freed synchronously when a display list is recycled. Instead, move the fragments to a garbage list and use a work queue to run the garbage collection. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index e238d9b..37c3518 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "vsp1.h" #include "vsp1_dl.h" @@ -92,11 +93,13 @@ enum vsp1_dl_mode { * @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 + * @lock: protects the free, active, queued, pending and gc_fragments 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 + * @gc_work: fragments garbage collector work struct + * @gc_fragments: array of display list fragments waiting to be freed */ struct vsp1_dl_manager { unsigned int index; @@ -108,6 +111,9 @@ struct vsp1_dl_manager { struct vsp1_dl_list *active; struct vsp1_dl_list *queued; struct vsp1_dl_list *pending; + + struct work_struct gc_work; + struct list_head gc_fragments; }; /* ----------------------------------------------------------------------------- @@ -262,21 +268,10 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) 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) { vsp1_dl_body_cleanup(&dl->body0); - vsp1_dl_list_free_fragments(dl); + list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); kfree(dl); } @@ -311,7 +306,16 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) if (!dl) return; - vsp1_dl_list_free_fragments(dl); + /* We can't free fragments here as DMA memory can only be freed in + * interruptible context. Move all fragments to the display list + * manager's list of fragments to be freed, they will be + * garbage-collected by the work queue. + */ + if (!list_empty(&dl->fragments)) { + list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); + schedule_work(&dl->dlm->gc_work); + } + dl->body0.num_entries = 0; list_add_tail(&dl->list, &dl->dlm->free); @@ -550,6 +554,40 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } +/* + * Free all fragments awaiting to be garbage-collected. + * + * This function must be called without the display list manager lock held. + */ +static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm) +{ + unsigned long flags; + + spin_lock_irqsave(&dlm->lock, flags); + + while (!list_empty(&dlm->gc_fragments)) { + struct vsp1_dl_body *dlb; + + dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body, + list); + list_del(&dlb->list); + + spin_unlock_irqrestore(&dlm->lock, flags); + vsp1_dl_fragment_free(dlb); + spin_lock_irqsave(&dlm->lock, flags); + } + + spin_unlock_irqrestore(&dlm->lock, flags); +} + +static void vsp1_dlm_garbage_collect(struct work_struct *work) +{ + struct vsp1_dl_manager *dlm = + container_of(work, struct vsp1_dl_manager, gc_work); + + vsp1_dlm_fragments_free(dlm); +} + struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int index, unsigned int prealloc) @@ -568,6 +606,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, spin_lock_init(&dlm->lock); INIT_LIST_HEAD(&dlm->free); + INIT_LIST_HEAD(&dlm->gc_fragments); + INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect); for (i = 0; i < prealloc; ++i) { struct vsp1_dl_list *dl; @@ -589,8 +629,12 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) if (!dlm) return; + cancel_work_sync(&dlm->gc_work); + list_for_each_entry_safe(dl, next, &dlm->free, list) { list_del(&dl->list); vsp1_dl_list_free(dl); } + + vsp1_dlm_fragments_free(dlm); } -- cgit v0.10.2 From 0220990f63668852c8a2a8f03e3afb422780ef9d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 18:15:59 -0300 Subject: [media] v4l: vsp1: lut: Initialize the mutex The LUT mutex isn't initialized when creating the LUT, fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 855e483..4a47249 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -201,6 +201,8 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (lut == NULL) return ERR_PTR(-ENOMEM); + mutex_init(&lut->lock); + lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; -- cgit v0.10.2 From 42e89bed23000c0bf985a741422ef943df0ee1e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 May 2016 13:52:11 -0300 Subject: [media] v4l: vsp1: lut: Expose configuration through a control Replace the custom ioctl with a V4L2 control in order to standardize the API. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 4a47249..db8f01d 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -13,7 +13,6 @@ #include #include -#include #include @@ -35,43 +34,60 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, } /* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations + * Controls */ -static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config) +#define V4L2_CID_VSP1_LUT_TABLE (V4L2_CID_USER_BASE | 0x1001) + +static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) { struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut)); + dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256); if (!dlb) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(config->lut); ++i) + for (i = 0; i < 256; ++i) vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, - config->lut[i]); + ctrl->p_new.p_u32[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) +static int lut_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vsp1_lut *lut = to_lut(subdev); - - switch (cmd) { - case VIDIOC_VSP1_LUT_CONFIG: - return lut_set_table(lut, arg); + struct vsp1_lut *lut = + container_of(ctrl->handler, struct vsp1_lut, ctrls); - default: - return -ENOIOCTLCMD; + switch (ctrl->id) { + case V4L2_CID_VSP1_LUT_TABLE: + lut_set_table(lut, ctrl); + break; } + + return 0; } +static const struct v4l2_ctrl_ops lut_ctrl_ops = { + .s_ctrl = lut_s_ctrl, +}; + +static const struct v4l2_ctrl_config lut_table_control = { + .ops = &lut_ctrl_ops, + .id = V4L2_CID_VSP1_LUT_TABLE, + .name = "Look-Up Table", + .type = V4L2_CTRL_TYPE_U32, + .min = 0x00000000, + .max = 0x00ffffff, + .step = 1, + .def = 0, + .dims = { 256}, +}; + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ @@ -147,10 +163,6 @@ static int lut_set_format(struct v4l2_subdev *subdev, * V4L2 Subdevice Operations */ -static const struct v4l2_subdev_core_ops lut_core_ops = { - .ioctl = lut_ioctl, -}; - static const struct v4l2_subdev_pad_ops lut_pad_ops = { .init_cfg = vsp1_entity_init_cfg, .enum_mbus_code = lut_enum_mbus_code, @@ -160,7 +172,6 @@ static const struct v4l2_subdev_pad_ops lut_pad_ops = { }; static const struct v4l2_subdev_ops lut_ops = { - .core = &lut_core_ops, .pad = &lut_pad_ops, }; @@ -176,12 +187,14 @@ static void lut_configure(struct vsp1_entity *entity, vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - mutex_lock(&lut->lock); + mutex_lock(lut->ctrls.lock); + if (lut->lut) { vsp1_dl_list_add_fragment(dl, lut->lut); lut->lut = NULL; } - mutex_unlock(&lut->lock); + + mutex_unlock(lut->ctrls.lock); } static const struct vsp1_entity_operations lut_entity_ops = { @@ -201,8 +214,6 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (lut == NULL) return ERR_PTR(-ENOMEM); - mutex_init(&lut->lock); - lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; @@ -211,5 +222,20 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (ret < 0) return ERR_PTR(ret); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&lut->ctrls, 1); + v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); + + lut->entity.subdev.ctrl_handler = &lut->ctrls; + + if (lut->ctrls.error) { + dev_err(vsp1->dev, "lut: failed to initialize controls\n"); + ret = lut->ctrls.error; + vsp1_entity_destroy(&lut->entity); + return ERR_PTR(ret); + } + + v4l2_ctrl_handler_setup(&lut->ctrls); + return lut; } diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index cef874f..021898f 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -13,9 +13,8 @@ #ifndef __VSP1_LUT_H__ #define __VSP1_LUT_H__ -#include - #include +#include #include #include "vsp1_entity.h" @@ -28,7 +27,8 @@ struct vsp1_device; struct vsp1_lut { struct vsp1_entity entity; - struct mutex lock; + struct v4l2_ctrl_handler ctrls; + struct vsp1_dl_body *lut; }; diff --git a/include/uapi/linux/vsp1.h b/include/uapi/linux/vsp1.h deleted file mode 100644 index 9a82369..0000000 --- a/include/uapi/linux/vsp1.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * vsp1.h - * - * Renesas R-Car VSP1 - User-space API - * - * Copyright (C) 2013 Renesas Corporation - * - * Contacts: Laurent Pinchart - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __VSP1_USER_H__ -#define __VSP1_USER_H__ - -#include -#include - -/* - * Private IOCTLs - * - * VIDIOC_VSP1_LUT_CONFIG - Configure the lookup table - */ - -#define VIDIOC_VSP1_LUT_CONFIG \ - _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config) - -struct vsp1_lut_config { - __u32 lut[256]; -}; - -#endif /* __VSP1_USER_H__ */ -- cgit v0.10.2 From 1fd87bf2f3a76200fe2b57f5b744b1b341cd7690 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 11 Nov 2015 23:04:44 -0200 Subject: [media] v4l: vsp1: Add Cubic Look Up Table (CLU) support The CLU processing block is a 2D/3D lookup table that converts the input three color component data into desired three color components using a lookup table. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 95b3ac2..1328e1b 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,7 +1,8 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o -vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o +vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o +vsp1-y += vsp1_lif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 7cb0f5e..8713a43 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -31,6 +31,7 @@ struct vsp1_drm; struct vsp1_entity; struct vsp1_platform_data; struct vsp1_bru; +struct vsp1_clu; struct vsp1_hsit; struct vsp1_lif; struct vsp1_lut; @@ -46,6 +47,7 @@ struct vsp1_uds; #define VSP1_HAS_LUT (1 << 1) #define VSP1_HAS_SRU (1 << 2) #define VSP1_HAS_BRU (1 << 3) +#define VSP1_HAS_CLU (1 << 4) struct vsp1_device_info { u32 version; @@ -66,6 +68,7 @@ struct vsp1_device { struct rcar_fcp_device *fcp; struct vsp1_bru *bru; + struct vsp1_clu *clu; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; struct vsp1_lif *lif; diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c new file mode 100644 index 0000000..cea86e7 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -0,0 +1,278 @@ +/* + * vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table + * + * Copyright (C) 2015-2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "vsp1.h" +#include "vsp1_clu.h" +#include "vsp1_dl.h" + +#define CLU_MIN_SIZE 4U +#define CLU_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl, + u32 reg, u32 data) +{ + vsp1_dl_list_write(dl, reg, data); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +#define V4L2_CID_VSP1_CLU_TABLE (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_VSP1_CLU_MODE (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_VSP1_CLU_MODE_2D 0 +#define V4L2_CID_VSP1_CLU_MODE_3D 1 + +static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) +{ + struct vsp1_dl_body *dlb; + unsigned int i; + + dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17); + if (!dlb) + return -ENOMEM; + + vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0); + for (i = 0; i < 17 * 17 * 17; ++i) + vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); + + swap(clu->clu, dlb); + + vsp1_dl_fragment_free(dlb); + return 0; +} + +static int clu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_clu *clu = + container_of(ctrl->handler, struct vsp1_clu, ctrls); + + switch (ctrl->id) { + case V4L2_CID_VSP1_CLU_TABLE: + clu_set_table(clu, ctrl); + break; + + case V4L2_CID_VSP1_CLU_MODE: + clu->mode = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops clu_ctrl_ops = { + .s_ctrl = clu_s_ctrl, +}; + +static const struct v4l2_ctrl_config clu_table_control = { + .ops = &clu_ctrl_ops, + .id = V4L2_CID_VSP1_CLU_TABLE, + .name = "Look-Up Table", + .type = V4L2_CTRL_TYPE_U32, + .min = 0x00000000, + .max = 0x00ffffff, + .step = 1, + .def = 0, + .dims = { 17, 17, 17 }, +}; + +static const char * const clu_mode_menu[] = { + "2D", + "3D", + NULL, +}; + +static const struct v4l2_ctrl_config clu_mode_control = { + .ops = &clu_ctrl_ops, + .id = V4L2_CID_VSP1_CLU_MODE, + .name = "Mode", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = 1, + .def = 1, + .qmenu = clu_mode_menu, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int clu_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, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, + }; + + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, + ARRAY_SIZE(codes)); +} + +static int clu_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, CLU_MIN_SIZE, + CLU_MIN_SIZE, CLU_MAX_SIZE, + CLU_MAX_SIZE); +} + +static int clu_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_clu *clu = to_clu(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + config = vsp1_entity_get_pad_config(&clu->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(&clu->entity, config, fmt->pad); + + if (fmt->pad == CLU_PAD_SOURCE) { + /* The CLU output format can't be modified. */ + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + CLU_MIN_SIZE, CLU_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + CLU_MIN_SIZE, CLU_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&clu->entity, config, + CLU_PAD_SOURCE); + *format = fmt->format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const struct v4l2_subdev_pad_ops clu_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = clu_enum_mbus_code, + .enum_frame_size = clu_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = clu_set_format, +}; + +static const struct v4l2_subdev_ops clu_ops = { + .pad = &clu_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void clu_configure(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + struct v4l2_mbus_framefmt *format; + u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; + + format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.config, + CLU_PAD_SINK); + + mutex_lock(clu->ctrls.lock); + + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && + format->code == MEDIA_BUS_FMT_AYUV8_1X32) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + + if (clu->clu) { + vsp1_dl_list_add_fragment(dl, clu->clu); + clu->clu = NULL; + } + + mutex_unlock(clu->ctrls.lock); + + vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); +} + +static const struct vsp1_entity_operations clu_entity_ops = { + .configure = clu_configure, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) +{ + struct vsp1_clu *clu; + int ret; + + clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL); + if (clu == NULL) + return ERR_PTR(-ENOMEM); + + clu->entity.ops = &clu_entity_ops; + clu->entity.type = VSP1_ENTITY_CLU; + + ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops, + MEDIA_ENT_F_PROC_VIDEO_LUT); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&clu->ctrls, 2); + v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); + v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL); + + clu->entity.subdev.ctrl_handler = &clu->ctrls; + + if (clu->ctrls.error) { + dev_err(vsp1->dev, "clu: failed to initialize controls\n"); + ret = clu->ctrls.error; + vsp1_entity_destroy(&clu->entity); + return ERR_PTR(ret); + } + + v4l2_ctrl_handler_setup(&clu->ctrls); + + return clu; +} diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h new file mode 100644 index 0000000..33a6902 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_clu.h @@ -0,0 +1,44 @@ +/* + * vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table + * + * Copyright (C) 2015 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_CLU_H__ +#define __VSP1_CLU_H__ + +#include +#include +#include + +#include "vsp1_entity.h" + +struct vsp1_device; +struct vsp1_dl_body; + +#define CLU_PAD_SINK 0 +#define CLU_PAD_SOURCE 1 + +struct vsp1_clu { + struct vsp1_entity entity; + + struct v4l2_ctrl_handler ctrls; + + unsigned int mode; + struct vsp1_dl_body *clu; +}; + +static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_clu, entity.subdev); +} + +struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_CLU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 0b0e736..769b19e 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -27,6 +27,7 @@ #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_clu.h" #include "vsp1_dl.h" #include "vsp1_drm.h" #include "vsp1_hsit.h" @@ -251,6 +252,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); } + if (vsp1->info->features & VSP1_HAS_CLU) { + vsp1->clu = vsp1_clu_create(vsp1); + if (IS_ERR(vsp1->clu)) { + ret = PTR_ERR(vsp1->clu); + goto done; + } + + list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities); + } + vsp1->hsi = vsp1_hsit_create(vsp1, true); if (IS_ERR(vsp1->hsi)) { ret = PTR_ERR(vsp1->hsi); @@ -551,7 +562,8 @@ 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, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -578,7 +590,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPS_M2, .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU, .rpf_count = 5, .uds_count = 1, .wpf_count = 4, @@ -587,7 +600,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, .gen = 3, - .features = VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU, .rpf_count = 1, .uds_count = 1, .wpf_count = 1, @@ -603,7 +616,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT, .rpf_count = 5, .wpf_count = 1, .num_bru_inputs = 5, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index d72dbcc..4cf6cc7 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -319,6 +319,7 @@ static const struct vsp1_route vsp1_routes[] = { { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT }, + VSP1_ENTITY_ROUTE(CLU), VSP1_ENTITY_ROUTE(HSI), VSP1_ENTITY_ROUTE(HST), { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF }, diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index bcdea4e..f289ed2 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -24,6 +24,7 @@ struct vsp1_pipeline; enum vsp1_entity_type { VSP1_ENTITY_BRU, + VSP1_ENTITY_CLU, VSP1_ENTITY_HSI, VSP1_ENTITY_HST, VSP1_ENTITY_LIF, diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 7657545..dea0bc4 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -444,6 +444,15 @@ */ #define VI6_CLU_CTRL 0x2900 +#define VI6_CLU_CTRL_AAI (1 << 28) +#define VI6_CLU_CTRL_MVS (1 << 24) +#define VI6_CLU_CTRL_AX1I_2D (3 << 14) +#define VI6_CLU_CTRL_AX2I_2D (1 << 12) +#define VI6_CLU_CTRL_OS0_2D (3 << 8) +#define VI6_CLU_CTRL_OS1_2D (1 << 6) +#define VI6_CLU_CTRL_OS2_2D (3 << 4) +#define VI6_CLU_CTRL_M2D (1 << 1) +#define VI6_CLU_CTRL_EN (1 << 0) /* ----------------------------------------------------------------------------- * HST Control Registers -- cgit v0.10.2 From fc845e520baf00af12f6c39708c5e9e9a6eec661 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 11 Jun 2016 04:07:56 -0300 Subject: [media] v4l: vsp1: Support runtime modification of controls Controls are applied to the hardware in the configure operation of the VSP entities, which is only called when starting the video stream. To enable runtime modification of controls we need to call the configure operations for every frame. Doing so is currently not safe, as most parameters shouldn't be modified during streaming. Furthermore the configure operation can sleep, preventing it from being called from the frame completion interrupt handler for the next frame. Fix this by adding an argument to the configure operation to tell entities whether to perform a full configuration (as done now) or a partial runtime configuration. In the latter case the operation will only configure the subset of parameters related to runtime-configurable controls, and won't be allowed to sleep when doing so. Because partial reconfiguration can depend on parameters computed when performing a full configuration, the core guarantees that the configure operation will always be called with full and partial modes in that order at stream start. Entities thus don't have to duplicate configuration steps in the full and partial code paths. This change affects the VSP driver core only, all entities return immediately from the configure operation when called for a partial runtime configuration. Entities will be modified one by one in further commits. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index cae9117..8268b87 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -269,13 +269,16 @@ static const struct v4l2_subdev_ops bru_ops = { static void bru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_bru *bru = to_bru(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; + if (!full) + return; + format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, bru->entity.source_pad); diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index cea86e7..dd0cf20 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -205,12 +205,15 @@ static const struct v4l2_subdev_ops clu_ops = { static void clu_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_clu *clu = to_clu(&entity->subdev); struct v4l2_mbus_framefmt *format; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; + if (!full) + return; + format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.config, CLU_PAD_SINK); diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 1473011..fe9665e 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -491,8 +491,10 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity, pipe->dl); - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl); + if (entity->ops->configure) { + entity->ops->configure(entity, pipe, pipe->dl, true); + entity->ops->configure(entity, pipe, pipe->dl, false); + } /* The memory buffer address must be applied after configuring * the RPF to make sure the crop offset are computed. diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index f289ed2..b43457f 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -73,7 +73,7 @@ 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_pipeline *, - struct vsp1_dl_list *); + struct vsp1_dl_list *, bool); }; struct vsp1_entity { diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index ab3cae3..6e5077b 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -125,10 +125,13 @@ static const struct v4l2_subdev_ops hsit_ops = { static void hsit_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); + if (!full) + return; + if (hsit->inverse) vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); else diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index e006f0d..a720063 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -122,7 +122,7 @@ static const struct v4l2_subdev_ops lif_ops = { static void lif_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -130,6 +130,9 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; + if (!full) + return; + format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, LIF_PAD_SOURCE); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index db8f01d..9619b9a 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -181,10 +181,13 @@ static const struct v4l2_subdev_ops lut_ops = { static void lut_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_lut *lut = to_lut(&entity->subdev); + if (!full) + return; + vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); mutex_lock(lut->ctrls.lock); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 47b1714..390040a 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -60,7 +60,7 @@ static void rpf_set_memory(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_dl_list *dl, bool full) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; @@ -73,6 +73,9 @@ static void rpf_configure(struct vsp1_entity *entity, u32 pstride; u32 infmt; + if (!full) + return; + /* Source size, stride and crop offsets. * * The crop offsets correspond to the location of the crop rectangle top diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index e13afd5..47f5e0c 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -257,7 +257,7 @@ static const struct v4l2_subdev_ops sru_ops = { static void sru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -265,6 +265,9 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; + if (!full) + return; + 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, diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index f229451..5d5720f 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -244,7 +244,7 @@ static const struct v4l2_subdev_ops uds_ops = { static void uds_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_list *dl, bool full) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -253,6 +253,9 @@ static void uds_configure(struct vsp1_entity *entity, unsigned int vscale; bool multitap; + if (!full) + return; + 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, diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index f6208b9..0165423 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -251,11 +251,17 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; unsigned int i; if (!pipe->dl) pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->ops->configure) + entity->ops->configure(entity, pipe, pipe->dl, false); + } + for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rwpf = pipe->inputs[i]; @@ -632,7 +638,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, pipe->dl); + entity->ops->configure(entity, pipe, pipe->dl, true); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 70fb979..474feac 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -93,7 +93,7 @@ static void wpf_set_memory(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_dl_list *dl, bool full) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; @@ -104,6 +104,9 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; + if (!full) + return; + /* Cropping */ crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); -- cgit v0.10.2 From f4e37fb73f3037a4a4da976c7d5b28c221fe8a1a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 11 Jun 2016 04:11:27 -0300 Subject: [media] v4l: vsp1: lut: Support runtime modification of controls Allow reconfiguration of the look-up table at runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 9619b9a..dc31de9 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -52,7 +52,9 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); + spin_lock_irq(&lut->lock); swap(lut->lut, dlb); + spin_unlock_irq(&lut->lock); vsp1_dl_fragment_free(dlb); return 0; @@ -184,20 +186,21 @@ static void lut_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl, bool full) { struct vsp1_lut *lut = to_lut(&entity->subdev); + struct vsp1_dl_body *dlb; + unsigned long flags; - if (!full) + if (full) { + vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); return; - - vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - - mutex_lock(lut->ctrls.lock); - - if (lut->lut) { - vsp1_dl_list_add_fragment(dl, lut->lut); - lut->lut = NULL; } - mutex_unlock(lut->ctrls.lock); + spin_lock_irqsave(&lut->lock, flags); + dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); + + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); } static const struct vsp1_entity_operations lut_entity_ops = { @@ -217,6 +220,8 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (lut == NULL) return ERR_PTR(-ENOMEM); + spin_lock_init(&lut->lock); + lut->entity.ops = &lut_entity_ops; lut->entity.type = VSP1_ENTITY_LUT; diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index 021898f..f8c4e8f 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 #include @@ -29,6 +31,7 @@ struct vsp1_lut { struct v4l2_ctrl_handler ctrls; + spinlock_t lock; struct vsp1_dl_body *lut; }; -- cgit v0.10.2 From 20fab5e086aa141df5a94fd61706b18dfaadd090 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 11 Jun 2016 04:11:27 -0300 Subject: [media] v4l: vsp1: clu: Support runtime modification of controls Allow reconfiguration of the look-up table and processing mode at runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index dd0cf20..b63d2db 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -55,7 +55,9 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) for (i = 0; i < 17 * 17 * 17; ++i) vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); + spin_lock_irq(&clu->lock); swap(clu->clu, dlb); + spin_unlock_irq(&clu->lock); vsp1_dl_fragment_free(dlb); return 0; @@ -208,32 +210,39 @@ static void clu_configure(struct vsp1_entity *entity, struct vsp1_dl_list *dl, bool full) { struct vsp1_clu *clu = to_clu(&entity->subdev); - struct v4l2_mbus_framefmt *format; + struct vsp1_dl_body *dlb; + unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; - if (!full) + /* The format can't be changed during streaming, only verify it at + * stream start and store the information internally for future partial + * reconfiguration calls. + */ + if (full) { + struct v4l2_mbus_framefmt *format; + + format = vsp1_entity_get_pad_format(&clu->entity, + clu->entity.config, + CLU_PAD_SINK); + clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; return; - - format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.config, - CLU_PAD_SINK); - - mutex_lock(clu->ctrls.lock); + } /* 2D mode can only be used with the YCbCr pixel encoding. */ - if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && - format->code == MEDIA_BUS_FMT_AYUV8_1X32) + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; - if (clu->clu) { - vsp1_dl_list_add_fragment(dl, clu->clu); - clu->clu = NULL; - } + vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); - mutex_unlock(clu->ctrls.lock); + spin_lock_irqsave(&clu->lock, flags); + dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); - vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); } static const struct vsp1_entity_operations clu_entity_ops = { @@ -253,6 +262,8 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) if (clu == NULL) return ERR_PTR(-ENOMEM); + spin_lock_init(&clu->lock); + clu->entity.ops = &clu_entity_ops; clu->entity.type = VSP1_ENTITY_CLU; diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h index 33a6902..036e0a2 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.h +++ b/drivers/media/platform/vsp1/vsp1_clu.h @@ -13,6 +13,8 @@ #ifndef __VSP1_CLU_H__ #define __VSP1_CLU_H__ +#include + #include #include #include @@ -30,6 +32,8 @@ struct vsp1_clu { struct v4l2_ctrl_handler ctrls; + bool yuv_mode; + spinlock_t lock; unsigned int mode; struct vsp1_dl_body *clu; }; -- cgit v0.10.2 From 07a23c611778ab009b46b7b44b98e96157bb075f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Jun 2016 23:19:43 -0300 Subject: [media] v4l: vsp1: Simplify alpha propagation We don't need to walk the pipeline when propagating the alpha value as all the information needed for propagation is already available from the pipeline structure. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 3c6f623..3e75fb3 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -301,42 +301,20 @@ 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, - struct vsp1_dl_list *dl, - unsigned int alpha) + struct vsp1_dl_list *dl, unsigned int alpha) { - struct vsp1_entity *entity; - struct media_pad *pad; - - pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); - - while (pad) { - if (!is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); - - /* The BRU background color has a fixed alpha value set to 255, - * the output alpha value is thus always equal to 255. - */ - if (entity->type == VSP1_ENTITY_BRU) - alpha = 255; - - if (entity->type == VSP1_ENTITY_UDS) { - struct vsp1_uds *uds = to_uds(&entity->subdev); + if (!pipe->uds) + return; - vsp1_uds_set_alpha(uds, dl, alpha); - break; - } + /* The BRU background color has a fixed alpha value set to 255, the + * output alpha value is thus always equal to 255. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) + alpha = 255; - pad = &entity->pads[entity->source_pad]; - pad = media_entity_remote_pad(pad); - } + vsp1_uds_set_alpha(pipe->uds, dl, alpha); } void vsp1_pipelines_suspend(struct vsp1_device *vsp1) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index fc9825d..d20d997 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -117,9 +117,7 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); 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_dl_list *dl, unsigned int alpha); void vsp1_pipelines_suspend(struct vsp1_device *vsp1); void vsp1_pipelines_resume(struct vsp1_device *vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 390040a..b8e801e 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -206,7 +206,7 @@ static void rpf_configure(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult); } - vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha); + vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 5d5720f..652dcd8 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -40,9 +40,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl, * Scaling Computation */ -void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl, unsigned int alpha) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); } diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 5c8cbfca..7bf3cdc 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -35,7 +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, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl, unsigned int alpha); #endif /* __VSP1_UDS_H__ */ -- cgit v0.10.2 From d05a331029d31836053a934365056616b0142898 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 20 Jun 2016 05:04:38 -0300 Subject: [media] v4l: vsp1: rwpf: Support runtime modification of controls Allow reconfiguration of the alpha value at runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index b8e801e..4258c72 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -73,8 +73,15 @@ static void rpf_configure(struct vsp1_entity *entity, u32 pstride; u32 infmt; - if (!full) + if (!full) { + vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, + rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | + (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); + + vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); return; + } /* Source size, stride and crop offsets. * @@ -171,9 +178,6 @@ static void rpf_configure(struct vsp1_entity *entity, (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); - 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; @@ -191,8 +195,7 @@ static void rpf_configure(struct vsp1_entity *entity, 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); + VI6_RPF_MULT_ALPHA_P_MMD_NONE); } else { /* When the input doesn't contain an alpha channel the * global alpha value is applied in the unpacking unit, @@ -203,11 +206,9 @@ static void rpf_configure(struct vsp1_entity *entity, | VI6_RPF_MULT_ALPHA_P_MMD_NONE; } - vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult); + rpf->mult_alpha = mult; } - vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); - vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); @@ -253,6 +254,8 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; } + v4l2_ctrl_handler_setup(&rpf->ctrls); + return rpf; error: diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 3b6e032..cd3562d 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -243,8 +243,6 @@ static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { 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); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 9ff7c78..801cacc 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -49,6 +49,9 @@ struct vsp1_rwpf { unsigned int alpha; + u32 mult_alpha; + u32 outfmt; + unsigned int offsets[2]; struct vsp1_rwpf_memory mem; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 474feac..9385bc7 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -104,8 +104,11 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; - if (!full) + if (!full) { + vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, wpf->outfmt | + (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT)); return; + } /* Cropping */ crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); @@ -151,8 +154,7 @@ static void wpf_configure(struct vsp1_entity *entity, if (sink_format->code != source_format->code) outfmt |= VI6_WPF_OUTFMT_CSC; - outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; - vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); + wpf->outfmt = outfmt; vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -239,6 +241,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; } + v4l2_ctrl_handler_setup(&wpf->ctrls); + return wpf; error: -- cgit v0.10.2 From 894dde5c5d1c6d33c4bd3d4384c6cf0aff3f8015 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 26 May 2016 05:14:22 -0300 Subject: [media] v4l: vsp1: wpf: Add flipping support Vertical flipping is available on both Gen2 and Gen3, while horizontal flipping is only available on Gen3. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 8713a43..06a2ec7 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -48,6 +48,8 @@ struct vsp1_uds; #define VSP1_HAS_SRU (1 << 2) #define VSP1_HAS_BRU (1 << 3) #define VSP1_HAS_CLU (1 << 4) +#define VSP1_HAS_WPF_VFLIP (1 << 5) +#define VSP1_HAS_WPF_HFLIP (1 << 6) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 769b19e..e1377ff 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -563,7 +563,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPS_H2, .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT - | VSP1_HAS_SRU, + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -572,7 +572,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPR_H2, .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_SRU, + .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -591,7 +591,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPS_M2, .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT - | VSP1_HAS_SRU, + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 1, .wpf_count = 4, @@ -600,7 +600,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, .gen = 3, - .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU + | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, .rpf_count = 1, .uds_count = 1, .wpf_count = 1, @@ -608,7 +609,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, .gen = 3, - .features = VSP1_HAS_BRU, + .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 1, .num_bru_inputs = 5, @@ -616,7 +617,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 1, .num_bru_inputs = 5, @@ -624,7 +626,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_LIF, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .wpf_count = 2, .num_bru_inputs = 5, diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index dea0bc4..3b03007 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -255,6 +255,8 @@ #define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24) #define VI6_WPF_OUTFMT_PDV_SHIFT 24 #define VI6_WPF_OUTFMT_PXA (1 << 23) +#define VI6_WPF_OUTFMT_ROT (1 << 18) +#define VI6_WPF_OUTFMT_HFLP (1 << 17) #define VI6_WPF_OUTFMT_FLP (1 << 16) #define VI6_WPF_OUTFMT_SPYCS (1 << 15) #define VI6_WPF_OUTFMT_SPUVS (1 << 14) @@ -289,6 +291,11 @@ #define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12) #define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12) +#define VI6_WPF_ROT_CTRL 0x1018 +#define VI6_WPF_ROT_CTRL_LN16 (1 << 17) +#define VI6_WPF_ROT_CTRL_LMEM_WD_MASK (0x1fff << 0) +#define VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT 0 + #define VI6_WPF_DSTM_STRIDE_Y 0x101c #define VI6_WPF_DSTM_STRIDE_C 0x1020 #define VI6_WPF_DSTM_ADDR_Y 0x1024 diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 4258c72..3888389 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -247,7 +247,7 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) return ERR_PTR(ret); /* Initialize the control handler. */ - ret = vsp1_rwpf_init_ctrls(rpf); + ret = vsp1_rwpf_init_ctrls(rpf, 0); if (ret < 0) { dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", index); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index cd3562d..8d461b3 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -241,9 +241,9 @@ 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) +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) { - v4l2_ctrl_handler_init(&rwpf->ctrls, 1); + v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 801cacc..cb20484 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -13,6 +13,8 @@ #ifndef __VSP1_RWPF_H__ #define __VSP1_RWPF_H__ +#include + #include #include #include @@ -52,6 +54,13 @@ struct vsp1_rwpf { u32 mult_alpha; u32 outfmt; + struct { + spinlock_t lock; + struct v4l2_ctrl *ctrls[2]; + unsigned int pending; + unsigned int active; + } flip; + unsigned int offsets[2]; struct vsp1_rwpf_memory mem; @@ -71,7 +80,7 @@ static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *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); -int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf); +int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 9385bc7..3198316 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -37,6 +37,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, } /* ----------------------------------------------------------------------------- + * Controls + */ + +enum wpf_flip_ctrl { + WPF_CTRL_VFLIP = 0, + WPF_CTRL_HFLIP = 1, + WPF_CTRL_MAX, +}; + +static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *wpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + unsigned int i; + u32 flip = 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + for (i = 0; i < WPF_CTRL_MAX; ++i) { + if (wpf->flip.ctrls[i]) + flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0; + } + + spin_lock_irq(&wpf->flip.lock); + wpf->flip.pending = flip; + spin_unlock_irq(&wpf->flip.lock); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = { + .s_ctrl = vsp1_wpf_s_ctrl, +}; + +static int wpf_init_controls(struct vsp1_rwpf *wpf) +{ + struct vsp1_device *vsp1 = wpf->entity.vsp1; + unsigned int num_flip_ctrls; + + spin_lock_init(&wpf->flip.lock); + + if (wpf->entity.index != 0) { + /* Only WPF0 supports flipping. */ + num_flip_ctrls = 0; + } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { + /* When horizontal flip is supported the WPF implements two + * controls (horizontal flip and vertical flip). + */ + num_flip_ctrls = 2; + } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { + /* When only vertical flip is supported the WPF implements a + * single control (vertical flip). + */ + num_flip_ctrls = 1; + } else { + /* Otherwise flipping is not supported. */ + num_flip_ctrls = 0; + } + + vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + + if (num_flip_ctrls >= 1) { + wpf->flip.ctrls[WPF_CTRL_VFLIP] = + v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } + + if (num_flip_ctrls == 2) { + wpf->flip.ctrls[WPF_CTRL_HFLIP] = + v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_cluster(2, wpf->flip.ctrls); + } + + if (wpf->ctrls.error) { + dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", + wpf->entity.index); + return wpf->ctrls.error; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -85,10 +176,32 @@ static void vsp1_wpf_destroy(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); + const struct v4l2_pix_format_mplane *format = &wpf->format; + struct vsp1_rwpf_memory mem = wpf->mem; + unsigned int flip = wpf->flip.active; + unsigned int offset; + + /* Update the memory offsets based on flipping configuration. The + * destination addresses point to the locations where the VSP starts + * writing to memory, which can be different corners of the image + * depending on vertical flipping. Horizontal flipping is handled + * through a line buffer and doesn't modify the start address. + */ + if (flip & BIT(WPF_CTRL_VFLIP)) { + mem.addr[0] += (format->height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (format->height / wpf->fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } - 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]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); } static void wpf_configure(struct vsp1_entity *entity, @@ -105,8 +218,22 @@ static void wpf_configure(struct vsp1_entity *entity, u32 srcrpf = 0; if (!full) { - vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, wpf->outfmt | - (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT)); + const unsigned int mask = BIT(WPF_CTRL_VFLIP) + | BIT(WPF_CTRL_HFLIP); + + spin_lock(&wpf->flip.lock); + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + spin_unlock(&wpf->flip.lock); + + outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; + + if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) + outfmt |= VI6_WPF_OUTFMT_FLP; + if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) + outfmt |= VI6_WPF_OUTFMT_HFLP; + + vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); return; } @@ -149,6 +276,12 @@ static void wpf_configure(struct vsp1_entity *entity, format->plane_fmt[1].bytesperline); vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); + + if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && + wpf->entity.index == 0) + vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, + VI6_WPF_ROT_CTRL_LN16 | + (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } if (sink_format->code != source_format->code) @@ -234,7 +367,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) } /* Initialize the control handler. */ - ret = vsp1_rwpf_init_ctrls(wpf); + ret = wpf_init_controls(wpf); if (ret < 0) { dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", index); -- cgit v0.10.2