diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 22:51:35 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 22:51:35 +0100 |
commit | 182966e1cd74ec0e326cd376de241803ee79741b (patch) | |
tree | 70a09aa4a7b4923446f3f2ba29d87600bbdc4d55 /drivers | |
parent | Merge tag 'for-5.18/fbdev-1' of git://git.kernel.org/pub/scm/linux/kernel/git... (diff) | |
parent | media: nxp: Restrict VIDEO_IMX_MIPI_CSIS to ARCH_MXC or COMPILE_TEST (diff) | |
download | linux-182966e1cd74ec0e326cd376de241803ee79741b.tar.xz linux-182966e1cd74ec0e326cd376de241803ee79741b.zip |
Merge tag 'media/v5.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- a major reorg at platform Kconfig/Makefile files, organizing them per
vendor. The other media Kconfig/Makefile files also sorted
- New sensor drivers: hi847, isl7998x, ov08d10
- New Amphion vpu decoder stateful driver
- New Atmel microchip csi2dc driver
- tegra-vde driver promoted from staging
- atomisp: some fixes for it to work on BYT
- imx7-mipi-csis driver promoted from staging and renamed
- camss driver got initial support for VFE hardware version Titan 480
- mtk-vcodec has gained support for MT8192
- lots of driver changes, fixes and improvements
* tag 'media/v5.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (417 commits)
media: nxp: Restrict VIDEO_IMX_MIPI_CSIS to ARCH_MXC or COMPILE_TEST
media: amphion: cleanup media device if register it fail
media: amphion: fix some issues to improve robust
media: amphion: fix some error related with undefined reference to __divdi3
media: amphion: fix an issue that using pm_runtime_get_sync incorrectly
media: vidtv: use vfree() for memory allocated with vzalloc()
media: m5mols/m5mols.h: document new reset field
media: pixfmt-yuv-planar.rst: fix PIX_FMT labels
media: platform: Remove unnecessary print function dev_err()
media: amphion: Add missing of_node_put() in vpu_core_parse_dt()
media: mtk-vcodec: Add missing of_node_put() in mtk_vdec_hw_prob_done()
media: platform: amphion: Fix build error without MAILBOX
media: spi: Kconfig: Place SPI drivers on a single menu
media: i2c: Kconfig: move camera drivers to the top
media: atomisp: fix bad usage at error handling logic
media: platform: rename mediatek/mtk-jpeg/ to mediatek/jpeg/
media: media/*/Kconfig: sort entries
media: Kconfig: cleanup VIDEO_DEV dependencies
media: platform/*/Kconfig: make manufacturer menus more uniform
media: platform: Create vendor/{Makefile,Kconfig} files
...
Diffstat (limited to 'drivers')
824 files changed, 32393 insertions, 8133 deletions
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index 2146299e5f52..17cd9b932298 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -11,6 +11,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/soc/mediatek/mtk-cmdq.h> #include "mtk_disp_drv.h" @@ -414,9 +415,13 @@ static int mtk_disp_ovl_probe(struct platform_device *pdev) return ret; } + pm_runtime_enable(dev); + ret = component_add(dev, &mtk_disp_ovl_component_ops); - if (ret) + if (ret) { + pm_runtime_disable(dev); dev_err(dev, "Failed to add component: %d\n", ret); + } return ret; } @@ -424,6 +429,7 @@ static int mtk_disp_ovl_probe(struct platform_device *pdev) static int mtk_disp_ovl_remove(struct platform_device *pdev) { component_del(&pdev->dev, &mtk_disp_ovl_component_ops); + pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index d41a3970b944..662e91d9d45f 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -9,6 +9,7 @@ #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/soc/mediatek/mtk-cmdq.h> #include "mtk_disp_drv.h" @@ -327,9 +328,13 @@ static int mtk_disp_rdma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); + pm_runtime_enable(dev); + ret = component_add(dev, &mtk_disp_rdma_component_ops); - if (ret) + if (ret) { + pm_runtime_disable(dev); dev_err(dev, "Failed to add component: %d\n", ret); + } return ret; } @@ -338,6 +343,8 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev) { component_del(&pdev->dev, &mtk_disp_rdma_component_ops); + pm_runtime_disable(&pdev->dev); + return 0; } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index d661edf7e0fe..188c94cc08a5 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -12,7 +12,6 @@ #include <linux/soc/mediatek/mtk-mutex.h> #include <asm/barrier.h> -#include <soc/mediatek/smi.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -661,15 +660,15 @@ static void mtk_drm_crtc_atomic_enable(struct drm_crtc *crtc, DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id); - ret = mtk_smi_larb_get(comp->larb_dev); - if (ret) { - DRM_ERROR("Failed to get larb: %d\n", ret); + ret = pm_runtime_resume_and_get(comp->dev); + if (ret < 0) { + DRM_DEV_ERROR(comp->dev, "Failed to enable power domain: %d\n", ret); return; } ret = mtk_crtc_ddp_hw_init(mtk_crtc); if (ret) { - mtk_smi_larb_put(comp->larb_dev); + pm_runtime_put(comp->dev); return; } @@ -682,7 +681,7 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc, { struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0]; - int i; + int i, ret; DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id); if (!mtk_crtc->enabled) @@ -705,7 +704,9 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc, drm_crtc_vblank_off(crtc); mtk_crtc_ddp_hw_fini(mtk_crtc); - mtk_smi_larb_put(comp->larb_dev); + ret = pm_runtime_put(comp->dev); + if (ret < 0) + DRM_DEV_ERROR(comp->dev, "Failed to disable power domain: %d\n", ret); mtk_crtc->enabled = false; } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c index b4b682bc1991..2e99aee13dfe 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -447,37 +447,15 @@ unsigned int mtk_drm_find_possible_crtc_by_comp(struct drm_device *drm, return ret; } -static int mtk_ddp_get_larb_dev(struct device_node *node, struct mtk_ddp_comp *comp, - struct device *dev) -{ - struct device_node *larb_node; - struct platform_device *larb_pdev; - - larb_node = of_parse_phandle(node, "mediatek,larb", 0); - if (!larb_node) { - dev_err(dev, "Missing mediadek,larb phandle in %pOF node\n", node); - return -EINVAL; - } - - larb_pdev = of_find_device_by_node(larb_node); - if (!larb_pdev) { - dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); - of_node_put(larb_node); - return -EPROBE_DEFER; - } - of_node_put(larb_node); - comp->larb_dev = &larb_pdev->dev; - - return 0; -} - int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id) { struct platform_device *comp_pdev; enum mtk_ddp_comp_type type; struct mtk_ddp_comp_dev *priv; +#if IS_REACHABLE(CONFIG_MTK_CMDQ) int ret; +#endif if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX) return -EINVAL; @@ -493,16 +471,6 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp, } comp->dev = &comp_pdev->dev; - /* Only DMA capable components need the LARB property */ - if (type == MTK_DISP_OVL || - type == MTK_DISP_OVL_2L || - type == MTK_DISP_RDMA || - type == MTK_DISP_WDMA) { - ret = mtk_ddp_get_larb_dev(node, comp, comp->dev); - if (ret) - return ret; - } - if (type == MTK_DISP_AAL || type == MTK_DISP_BLS || type == MTK_DISP_CCORR || diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h index 4c6a98662305..ad267bb8fc9b 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h @@ -71,7 +71,6 @@ struct mtk_ddp_comp_funcs { struct mtk_ddp_comp { struct device *dev; int irq; - struct device *larb_dev; enum mtk_ddp_comp_id id; const struct mtk_ddp_comp_funcs *funcs; }; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 56ff8c57ef8f..b147797177c6 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -645,11 +645,8 @@ err_pm: pm_runtime_disable(dev); err_node: of_node_put(private->mutex_node); - for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) { + for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) of_node_put(private->comp_node[i]); - if (private->ddp_comp[i].larb_dev) - put_device(private->ddp_comp[i].larb_dev); - } return ret; } diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index a9639d098893..778bc26d3ba5 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -357,11 +357,11 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, switch (mbus_cfg->type) { case V4L2_MBUS_PARALLEL: csicfg->ext_vsync = 1; - csicfg->vsync_pol = (mbus_cfg->flags & + csicfg->vsync_pol = (mbus_cfg->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; - csicfg->hsync_pol = (mbus_cfg->flags & + csicfg->hsync_pol = (mbus_cfg->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; - csicfg->pixclk_pol = (mbus_cfg->flags & + csicfg->pixclk_pol = (mbus_cfg->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; break; diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 16119f760d11..c0163b983ce6 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -110,7 +110,7 @@ config RMI4_F3A config RMI4_F54 bool "RMI4 Function 54 (Analog diagnostics)" - depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) + depends on VIDEO_DEV=y || (RMI4_CORE=m && VIDEO_DEV=m) select VIDEOBUF2_VMALLOC select RMI4_F55 help diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2f6adfb7b938..ff7794cecf69 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -131,7 +131,7 @@ config TOUCHSCREEN_ATMEL_MXT config TOUCHSCREEN_ATMEL_MXT_T37 bool "Support T37 Diagnostic Data" depends on TOUCHSCREEN_ATMEL_MXT - depends on VIDEO_V4L2=y || (TOUCHSCREEN_ATMEL_MXT=m && VIDEO_V4L2=m) + depends on VIDEO_DEV=y || (TOUCHSCREEN_ATMEL_MXT=m && VIDEO_DEV=m) select VIDEOBUF2_VMALLOC help Say Y here if you want support to output data from the T37 @@ -1252,7 +1252,7 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" depends on USB && MEDIA_USB_SUPPORT && HAS_DMA - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_DMA_SG help Say Y here if you want support for the Samsung SUR40 touchscreen diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 25b834104790..77df61092be3 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -562,22 +562,52 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct mtk_iommu_data *data; + struct device_link *link; + struct device *larbdev; + unsigned int larbid, larbidx, i; if (!fwspec || fwspec->ops != &mtk_iommu_ops) return ERR_PTR(-ENODEV); /* Not a iommu client device */ data = dev_iommu_priv_get(dev); + /* + * Link the consumer device with the smi-larb device(supplier). + * The device that connects with each a larb is a independent HW. + * All the ports in each a device should be in the same larbs. + */ + larbid = MTK_M4U_TO_LARB(fwspec->ids[0]); + for (i = 1; i < fwspec->num_ids; i++) { + larbidx = MTK_M4U_TO_LARB(fwspec->ids[i]); + if (larbid != larbidx) { + dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n", + larbid, larbidx); + return ERR_PTR(-EINVAL); + } + } + larbdev = data->larb_imu[larbid].dev; + link = device_link_add(dev, larbdev, + DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); + if (!link) + dev_err(dev, "Unable to link %s\n", dev_name(larbdev)); return &data->iommu; } static void mtk_iommu_release_device(struct device *dev) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct mtk_iommu_data *data; + struct device *larbdev; + unsigned int larbid; if (!fwspec || fwspec->ops != &mtk_iommu_ops) return; + data = dev_iommu_priv_get(dev); + larbid = MTK_M4U_TO_LARB(fwspec->ids[0]); + larbdev = data->larb_imu[larbid].dev; + device_link_remove(dev, larbdev); + iommu_fwspec_free(dev); } @@ -848,6 +878,10 @@ static int mtk_iommu_probe(struct platform_device *pdev) plarbdev = of_find_device_by_node(larbnode); if (!plarbdev) { of_node_put(larbnode); + return -ENODEV; + } + if (!plarbdev->dev.driver) { + of_node_put(larbnode); return -EPROBE_DEFER; } data->larb_imu[id].dev = &plarbdev->dev; diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index be22fcf988ce..4052aad75a81 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -423,7 +423,18 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev) struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct of_phandle_args iommu_spec; struct mtk_iommu_data *data; - int err, idx = 0; + int err, idx = 0, larbid, larbidx; + struct device_link *link; + struct device *larbdev; + + /* + * In the deferred case, free the existed fwspec. + * Always initialize the fwspec internally. + */ + if (fwspec) { + iommu_fwspec_free(dev); + fwspec = dev_iommu_fwspec_get(dev); + } while (!of_parse_phandle_with_args(dev->of_node, "iommus", "#iommu-cells", @@ -444,6 +455,23 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev) data = dev_iommu_priv_get(dev); + /* Link the consumer device with the smi-larb device(supplier) */ + larbid = mt2701_m4u_to_larb(fwspec->ids[0]); + for (idx = 1; idx < fwspec->num_ids; idx++) { + larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]); + if (larbid != larbidx) { + dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n", + larbid, larbidx); + return ERR_PTR(-EINVAL); + } + } + + larbdev = data->larb_imu[larbid].dev; + link = device_link_add(dev, larbdev, + DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); + if (!link) + dev_err(dev, "Unable to link %s\n", dev_name(larbdev)); + return &data->iommu; } @@ -464,10 +492,18 @@ static void mtk_iommu_probe_finalize(struct device *dev) static void mtk_iommu_release_device(struct device *dev) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct mtk_iommu_data *data; + struct device *larbdev; + unsigned int larbid; if (!fwspec || fwspec->ops != &mtk_iommu_ops) return; + data = dev_iommu_priv_get(dev); + larbid = mt2701_m4u_to_larb(fwspec->ids[0]); + larbdev = data->larb_imu[larbid].dev; + device_link_remove(dev, larbdev); + iommu_fwspec_free(dev); } @@ -595,6 +631,10 @@ static int mtk_iommu_probe(struct platform_device *pdev) plarbdev = of_find_device_by_node(larbnode); if (!plarbdev) { of_node_put(larbnode); + return -ENODEV; + } + if (!plarbdev->dev.driver) { + of_node_put(larbnode); return -EPROBE_DEFER; } data->larb_imu[i].dev = &plarbdev->dev; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index f3f24c63536b..ba6592b3dab2 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -160,6 +160,9 @@ menu "Media core support" config VIDEO_DEV tristate "Video4Linux core" default MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT || MEDIA_PLATFORM_SUPPORT || MEDIA_TEST_SUPPORT + depends on (I2C || I2C=n) + select RATIONAL + select VIDEOBUF2_V4L2 if VIDEOBUF2_CORE help Enables the V4L2 API, used by cameras, analog TV, video grabbers, radio devices and by some input devices. @@ -216,13 +219,12 @@ menu "Media drivers" comment "Drivers filtered as selected at 'Filter media drivers'" depends on MEDIA_SUPPORT_FILTER +comment "Media drivers" + source "drivers/media/usb/Kconfig" source "drivers/media/pci/Kconfig" source "drivers/media/radio/Kconfig" -# Common driver options -source "drivers/media/common/Kconfig" - if MEDIA_PLATFORM_SUPPORT source "drivers/media/platform/Kconfig" source "drivers/media/mmc/Kconfig" @@ -234,6 +236,9 @@ endif source "drivers/media/firewire/Kconfig" +# Common driver options +source "drivers/media/common/Kconfig" + endmenu # diff --git a/drivers/media/Makefile b/drivers/media/Makefile index d18357bf1346..20fac24e4f0f 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -8,7 +8,7 @@ # when compiled as builtin drivers # obj-y += i2c/ tuners/ -obj-$(CONFIG_DVB_CORE) += dvb-frontends/ +obj-$(CONFIG_DVB_CORE) += dvb-frontends/ # # Now, let's link-in the media controller core @@ -18,7 +18,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y) endif obj-$(CONFIG_VIDEO_DEV) += v4l2-core/ -obj-$(CONFIG_DVB_CORE) += dvb-core/ +obj-$(CONFIG_DVB_CORE) += dvb-core/ # There are both core and drivers at RC subtree - merge before drivers obj-y += rc/ diff --git a/drivers/media/cec/platform/Makefile b/drivers/media/cec/platform/Makefile index ea6f8ee8161c..26d2bc778394 100644 --- a/drivers/media/cec/platform/Makefile +++ b/drivers/media/cec/platform/Makefile @@ -4,12 +4,12 @@ # # Please keep it in alphabetic order -obj-$(CONFIG_CEC_CROS_EC) += cros-ec/ -obj-$(CONFIG_CEC_GPIO) += cec-gpio/ -obj-$(CONFIG_CEC_MESON_AO) += meson/ -obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p/ -obj-$(CONFIG_CEC_SECO) += seco/ -obj-$(CONFIG_CEC_STI) += sti/ -obj-$(CONFIG_CEC_STM32) += stm32/ -obj-$(CONFIG_CEC_TEGRA) += tegra/ +obj-$(CONFIG_CEC_CROS_EC) += cros-ec/ +obj-$(CONFIG_CEC_GPIO) += cec-gpio/ +obj-$(CONFIG_CEC_MESON_AO) += meson/ +obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p/ +obj-$(CONFIG_CEC_SECO) += seco/ +obj-$(CONFIG_CEC_STI) += sti/ +obj-$(CONFIG_CEC_STM32) += stm32/ +obj-$(CONFIG_CEC_TEGRA) += tegra/ diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 2d95e16cd248..8c8d8fc5e63e 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -215,6 +215,8 @@ struct cec_dmi_match { static const struct cec_dmi_match cec_dmi_match_table[] = { /* Google Fizz */ { "Google", "Fizz", "0000:00:02.0", "Port B" }, + /* Google Brask */ + { "Google", "Brask", "0000:00:02.0", "Port B" }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index ae138cc253fd..51a6fcfd077d 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/dmi.h> #include <linux/gpio/consumer.h> -#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/platform_device.h> @@ -129,7 +128,7 @@ static int secocec_adap_enable(struct cec_adapter *adap, bool enable) if (status) goto err; - dev_dbg(dev, "Device enabled"); + dev_dbg(dev, "Device enabled\n"); } else { /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); @@ -141,7 +140,7 @@ static int secocec_adap_enable(struct cec_adapter *adap, bool enable) ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR); - dev_dbg(dev, "Device disabled"); + dev_dbg(dev, "Device disabled\n"); } return 0; @@ -264,12 +263,12 @@ static void secocec_rx_done(struct cec_adapter *adap, u16 status_val) if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) { /* NOTE: Untested, it also might not be necessary */ - dev_warn(dev, "Received more than 16 bytes. Discarding"); + dev_warn(dev, "Received more than 16 bytes. Discarding\n"); flag_overflow = true; } if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) { - dev_warn(dev, "Message received with errors. Discarding"); + dev_warn(dev, "Message received with errors. Discarding\n"); status = -EIO; goto rxerr; } @@ -390,12 +389,12 @@ static int secocec_ir_probe(void *priv) if (status != 0) goto err; - dev_dbg(dev, "IR enabled"); + dev_dbg(dev, "IR enabled\n"); status = devm_rc_register_device(dev, cec->ir); if (status) { - dev_err(dev, "Failed to prepare input device"); + dev_err(dev, "Failed to prepare input device\n"); cec->ir = NULL; goto err; } @@ -408,7 +407,7 @@ err: smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR); - dev_dbg(dev, "IR disabled"); + dev_dbg(dev, "IR disabled\n"); return status; } @@ -431,13 +430,13 @@ static int secocec_ir_rx(struct secocec_data *priv) rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle); - dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x", key, + dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x\n", key, addr, toggle); return 0; err: - dev_err(dev, "IR Receive message failed (%d)", status); + dev_err(dev, "IR Receive message failed (%d)\n", status); return -EIO; } #else @@ -497,7 +496,7 @@ static irqreturn_t secocec_irq_handler(int irq, void *priv) return IRQ_HANDLED; err: - dev_err_once(dev, "IRQ: R/W SMBus operation failed (%d)", status); + dev_err_once(dev, "IRQ: R/W SMBus operation failed %d\n", status); /* Reset status register */ val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR; @@ -551,18 +550,18 @@ static int secocec_acpi_probe(struct secocec_data *sdev) struct gpio_desc *gpio; int irq = 0; - gpio = devm_gpiod_get(dev, NULL, GPIOF_IN); + gpio = devm_gpiod_get(dev, NULL, GPIOD_IN); if (IS_ERR(gpio)) { - dev_err(dev, "Cannot request interrupt gpio"); + dev_err(dev, "Cannot request interrupt gpio\n"); return PTR_ERR(gpio); } irq = gpiod_to_irq(gpio); if (irq < 0) { - dev_err(dev, "Cannot find valid irq"); + dev_err(dev, "Cannot find valid irq\n"); return -ENODEV; } - dev_dbg(dev, "irq-gpio is bound to IRQ %d", irq); + dev_dbg(dev, "irq-gpio is bound to IRQ %d\n", irq); sdev->irq = irq; @@ -590,7 +589,7 @@ static int secocec_probe(struct platform_device *pdev) /* Request SMBus regions */ if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) { - dev_err(dev, "Request memory region failed"); + dev_err(dev, "Request memory region failed\n"); return -ENXIO; } @@ -598,14 +597,14 @@ static int secocec_probe(struct platform_device *pdev) secocec->dev = dev; if (!has_acpi_companion(dev)) { - dev_dbg(dev, "Cannot find any ACPI companion"); + dev_dbg(dev, "Cannot find any ACPI companion\n"); ret = -ENODEV; goto err; } ret = secocec_acpi_probe(secocec); if (ret) { - dev_err(dev, "Cannot assign gpio to IRQ"); + dev_err(dev, "Cannot assign gpio to IRQ\n"); ret = -ENODEV; goto err; } @@ -613,11 +612,11 @@ static int secocec_probe(struct platform_device *pdev) /* Firmware version check */ ret = smb_rd16(SECOCEC_VERSION, &val); if (ret) { - dev_err(dev, "Cannot check fw version"); + dev_err(dev, "Cannot check fw version\n"); goto err; } if (val < SECOCEC_LATEST_FW) { - dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x", + dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x\n", val, SECOCEC_LATEST_FW); ret = -EINVAL; goto err; @@ -631,7 +630,7 @@ static int secocec_probe(struct platform_device *pdev) dev_name(&pdev->dev), secocec); if (ret) { - dev_err(dev, "Cannot request IRQ %d", secocec->irq); + dev_err(dev, "Cannot request IRQ %d\n", secocec->irq); ret = -EIO; goto err; } @@ -666,7 +665,7 @@ static int secocec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, secocec); - dev_dbg(dev, "Device registered"); + dev_dbg(dev, "Device registered\n"); return ret; @@ -691,14 +690,14 @@ static int secocec_remove(struct platform_device *pdev) smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR); - dev_dbg(&pdev->dev, "IR disabled"); + dev_dbg(&pdev->dev, "IR disabled\n"); } cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); cec_unregister_adapter(secocec->cec_adap); release_region(BRA_SMB_BASE_ADDR, 7); - dev_dbg(&pdev->dev, "CEC device removed"); + dev_dbg(&pdev->dev, "CEC device removed\n"); return 0; } @@ -709,7 +708,7 @@ static int secocec_suspend(struct device *dev) int status; u16 val; - dev_dbg(dev, "Device going to suspend, disabling"); + dev_dbg(dev, "Device going to suspend, disabling\n"); /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); @@ -733,7 +732,7 @@ static int secocec_suspend(struct device *dev) return 0; err: - dev_err(dev, "Suspend failed (err: %d)", status); + dev_err(dev, "Suspend failed: %d\n", status); return status; } @@ -742,7 +741,7 @@ static int secocec_resume(struct device *dev) int status; u16 val; - dev_dbg(dev, "Resuming device from suspend"); + dev_dbg(dev, "Resuming device from suspend\n"); /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); @@ -762,12 +761,12 @@ static int secocec_resume(struct device *dev) if (status) goto err; - dev_dbg(dev, "Device resumed from suspend"); + dev_dbg(dev, "Device resumed from suspend\n"); return 0; err: - dev_err(dev, "Resume failed (err: %d)", status); + dev_err(dev, "Resume failed: %d\n", status); return status; } diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 0f6bde0f793e..a2ae71270054 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -6,23 +6,23 @@ config MEDIA_COMMON_OPTIONS comment "common driver options" depends on MEDIA_COMMON_OPTIONS -config VIDEO_CX2341X - tristate - -config VIDEO_TVEEPROM +config CYPRESS_FIRMWARE tristate - depends on I2C + depends on USB config TTPCI_EEPROM tristate depends on I2C -config CYPRESS_FIRMWARE +config VIDEO_CX2341X tristate - depends on USB -source "drivers/media/common/videobuf2/Kconfig" +config VIDEO_TVEEPROM + tristate + depends on I2C + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" source "drivers/media/common/v4l2-tpg/Kconfig" +source "drivers/media/common/videobuf2/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 55b5a1900124..ad0b1e95fb12 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/ -obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o + +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o +obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig index 3e85c0c3fd9a..a0aa155e5d85 100644 --- a/drivers/media/common/saa7146/Kconfig +++ b/drivers/media/common/saa7146/Kconfig @@ -5,6 +5,6 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF_DMA_SG select VIDEO_SAA7146 diff --git a/drivers/media/common/videobuf2/Makefile b/drivers/media/common/videobuf2/Makefile index 54306f8d096c..a6fe3f304685 100644 --- a/drivers/media/common/videobuf2/Makefile +++ b/drivers/media/common/videobuf2/Makefile @@ -6,10 +6,12 @@ ifeq ($(CONFIG_TRACEPOINTS),y) videobuf2-common-objs += vb2-trace.o endif +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-common.o -obj-$(CONFIG_VIDEOBUF2_V4L2) += videobuf2-v4l2.o -obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o -obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o obj-$(CONFIG_VIDEOBUF2_DVB) += videobuf2-dvb.o +obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o +obj-$(CONFIG_VIDEOBUF2_V4L2) += videobuf2-v4l2.o +obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 7c4096e62173..0e3f264122af 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -132,12 +132,12 @@ static void vb2_dc_prepare(void *buf_priv) if (!buf->non_coherent_mem) return; - /* For both USERPTR and non-coherent MMAP */ - dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); - /* Non-coherent MMAP only */ if (buf->vaddr) flush_kernel_vmap_range(buf->vaddr, buf->size); + + /* For both USERPTR and non-coherent MMAP */ + dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); } static void vb2_dc_finish(void *buf_priv) @@ -152,12 +152,12 @@ static void vb2_dc_finish(void *buf_priv) if (!buf->non_coherent_mem) return; - /* For both USERPTR and non-coherent MMAP */ - dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); - /* Non-coherent MMAP only */ if (buf->vaddr) invalidate_kernel_vmap_range(buf->vaddr, buf->size); + + /* For both USERPTR and non-coherent MMAP */ + dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); } /*********************************************/ diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig index 6ffac618417b..8b3f2d53cd62 100644 --- a/drivers/media/dvb-core/Kconfig +++ b/drivers/media/dvb-core/Kconfig @@ -6,7 +6,7 @@ config DVB_MMAP bool "Enable DVB memory-mapped API (EXPERIMENTAL)" depends on DVB_CORE - depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_CORE + depends on VIDEO_DEV=y || VIDEO_DEV=DVB_CORE select VIDEOBUF2_VMALLOC help This option enables DVB experimental memory-mapped API, which diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 2c1ed98d43c5..2ef2ff2a38ff 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -11,6 +11,23 @@ menu "Customise DVB Frontends" comment "Multistandard (satellite) frontends" depends on DVB_CORE +config DVB_M88DS3103 + tristate "Montage Technology M88DS3103" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_MXL5XX + tristate "MaxLinear MxL5xx based tuner-demodulators" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL5xx family of DVB-S/S2 tuners/demodulators. + + Say Y when you want to support these frontends. + config DVB_STB0899 tristate "STB0899 based" depends on DVB_CORE && I2C @@ -60,23 +77,6 @@ config DVB_STV6111 Say Y when you want to support these frontends. -config DVB_MXL5XX - tristate "MaxLinear MxL5xx based tuner-demodulators" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - MaxLinear MxL5xx family of DVB-S/S2 tuners/demodulators. - - Say Y when you want to support these frontends. - -config DVB_M88DS3103 - tristate "Montage Technology M88DS3103" - depends on DVB_CORE && I2C && I2C_MUX - select REGMAP_I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y when you want to support this frontend. - comment "Multistandard (cable + terrestrial) frontends" depends on DVB_CORE @@ -89,40 +89,40 @@ config DVB_DRXK Say Y when you want to support this frontend. -config DVB_TDA18271C2DD - tristate "NXP TDA18271C2 silicon tuner" +config DVB_MN88472 + tristate "Panasonic MN88472" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - NXP TDA18271 silicon tuner. - - Say Y when you want to support this tuner. + Say Y when you want to support this frontend. -config DVB_SI2165 - tristate "Silicon Labs si2165 based" +config DVB_MN88473 + tristate "Panasonic MN88473" depends on DVB_CORE && I2C select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-C/T demodulator. - Say Y when you want to support this frontend. -config DVB_MN88472 - tristate "Panasonic MN88472" +config DVB_SI2165 + tristate "Silicon Labs si2165 based" depends on DVB_CORE && I2C select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help + A DVB-C/T demodulator. + Say Y when you want to support this frontend. -config DVB_MN88473 - tristate "Panasonic MN88473" +config DVB_TDA18271C2DD + tristate "NXP TDA18271C2 silicon tuner" depends on DVB_CORE && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y when you want to support this frontend. + NXP TDA18271 silicon tuner. + + Say Y when you want to support this tuner. comment "DVB-S (satellite) frontends" depends on DVB_CORE @@ -134,6 +134,27 @@ config DVB_CX24110 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_CX24116 + tristate "Conexant CX24116 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_CX24117 + tristate "Conexant CX24117 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. + +config DVB_CX24120 + tristate "Conexant CX24120 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + config DVB_CX24123 tristate "Conexant CX24123 based" depends on DVB_CORE && I2C @@ -141,22 +162,23 @@ config DVB_CX24123 help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_MT312 - tristate "Zarlink VP310/MT312/ZL10313 based" +config DVB_DS3000 + tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S tuner module. Say Y when you want to support this frontend. + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. -config DVB_ZL10036 - tristate "Zarlink ZL10036 silicon tuner" +config DVB_MB86A16 + tristate "Fujitsu MB86A16 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S tuner module. Say Y when you want to support this frontend. + A DVB-S/DSS Direct Conversion reveiver. + Say Y when you want to support this frontend. -config DVB_ZL10039 - tristate "Zarlink ZL10039 silicon tuner" +config DVB_MT312 + tristate "Zarlink VP310/MT312/ZL10313 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -169,8 +191,8 @@ config DVB_S5H1420 help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_STV0288 - tristate "ST STV0288 based" +config DVB_SI21XX + tristate "Silicon Labs SI21XX based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -183,19 +205,19 @@ config DVB_STB6000 help A DVB-S silicon tuner module. Say Y when you want to support this tuner. -config DVB_STV0299 - tristate "ST STV0299 based" +config DVB_STV0288 + tristate "ST STV0288 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_STV6110 - tristate "ST STV6110 silicon tuner" +config DVB_STV0299 + tristate "ST STV0299 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S silicon tuner module. Say Y when you want to support this tuner. + A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STV0900 tristate "ST STV0900 based" @@ -204,49 +226,42 @@ config DVB_STV0900 help A DVB-S/S2 demodulator. Say Y when you want to support this frontend. -config DVB_TDA8083 - tristate "Philips TDA8083 based" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-S tuner module. Say Y when you want to support this frontend. - -config DVB_TDA10086 - tristate "Philips TDA10086 based" +config DVB_STV6110 + tristate "ST STV6110 silicon tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S tuner module. Say Y when you want to support this frontend. + A DVB-S silicon tuner module. Say Y when you want to support this tuner. -config DVB_TDA8261 - tristate "Philips TDA8261 based" +config DVB_TDA10071 + tristate "NXP TDA10071" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S tuner module. Say Y when you want to support this frontend. + Say Y when you want to support this frontend. -config DVB_VES1X93 - tristate "VLSI VES1893 or VES1993 based" +config DVB_TDA10086 + tristate "Philips TDA10086 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_TUNER_ITD1000 - tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" +config DVB_TDA8083 + tristate "Philips TDA8083 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_TUNER_CX24113 - tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" +config DVB_TDA8261 + tristate "Philips TDA8261 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. - config DVB_TDA826X tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C @@ -254,86 +269,71 @@ config DVB_TDA826X help A DVB-S silicon tuner module. Say Y when you want to support this tuner. -config DVB_TUA6100 - tristate "Infineon TUA6100 PLL" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-S PLL chip. - -config DVB_CX24116 - tristate "Conexant CX24116 based" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-S/S2 tuner module. Say Y when you want to support this frontend. - -config DVB_CX24117 - tristate "Conexant CX24117 based" +config DVB_TS2020 + tristate "Montage Tehnology TS2020 based tuners" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. + A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. -config DVB_CX24120 - tristate "Conexant CX24120 based" +config DVB_TUA6100 + tristate "Infineon TUA6100 PLL" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + A DVB-S PLL chip. -config DVB_SI21XX - tristate "Silicon Labs SI21XX based" +config DVB_TUNER_CX24113 + tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_TS2020 - tristate "Montage Tehnology TS2020 based tuners" +config DVB_TUNER_ITD1000 + tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" depends on DVB_CORE && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. + A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_DS3000 - tristate "Montage Tehnology DS3000 based" +config DVB_VES1X93 + tristate "VLSI VES1893 or VES1993 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_MB86A16 - tristate "Fujitsu MB86A16 based" +config DVB_ZL10036 + tristate "Zarlink ZL10036 silicon tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S/DSS Direct Conversion reveiver. - Say Y when you want to support this frontend. + A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_TDA10071 - tristate "NXP TDA10071" +config DVB_ZL10039 + tristate "Zarlink ZL10039 silicon tuner" depends on DVB_CORE && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y when you want to support this frontend. + A DVB-S tuner module. Say Y when you want to support this frontend. comment "DVB-T (terrestrial) frontends" depends on DVB_CORE -config DVB_SP887X - tristate "Spase sp887x based" - depends on DVB_CORE && I2C +config DVB_AF9013 + tristate "Afatech AF9013 demodulator" + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-T tuner module. Say Y when you want to support this frontend. + Say Y when you want to support this frontend. - This driver needs external firmware. Please use the command - "<kerneldir>/scripts/get_dvb_firmware sp887x" to - download/extract it, and then copy it to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). +config DVB_AS102_FE + tristate + depends on DVB_CORE + default DVB_AS102 config DVB_CX22700 tristate "Conexant CX22700 based" @@ -349,64 +349,20 @@ config DVB_CX22702 help A DVB-T tuner module. Say Y when you want to support this frontend. -config DVB_S5H1432 - tristate "Samsung s5h1432 demodulator (OFDM)" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - -config DVB_DRXD - tristate "Micronas DRXD driver" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - - Note: this driver was based on vendor driver reference code (released - under the GPL) as opposed to the existing drx397xd driver, which - was written via reverse engineering. - -config DVB_L64781 - tristate "LSI L64781" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - -config DVB_TDA1004X - tristate "Philips TDA10045H/TDA10046H based" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - - This driver needs external firmware. Please use the commands - "<kerneldir>/scripts/get_dvb_firmware tda10045", - "<kerneldir>/scripts/get_dvb_firmware tda10046" to - download/extract them, and then copy them to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). - -config DVB_NXT6000 - tristate "NxtWave Communications NXT6000 based" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - -config DVB_MT352 - tristate "Zarlink MT352 based" +config DVB_CXD2820R + tristate "Sony CXD2820R" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-T tuner module. Say Y when you want to support this frontend. + Say Y when you want to support this frontend. -config DVB_ZL10353 - tristate "Zarlink ZL10353 based" +config DVB_CXD2841ER + tristate "Sony CXD2841ER" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-T tuner module. Say Y when you want to support this frontend. + Say Y when you want to support this frontend. config DVB_DIB3000MB tristate "DiBcom 3000M-B" @@ -448,20 +404,16 @@ config DVB_DIB9000 A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. -config DVB_TDA10048 - tristate "Philips TDA10048HN based" +config DVB_DRXD + tristate "Micronas DRXD driver" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-T tuner module. Say Y when you want to support this frontend. -config DVB_AF9013 - tristate "Afatech AF9013 demodulator" - depends on DVB_CORE && I2C && I2C_MUX - select REGMAP - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y when you want to support this frontend. + Note: this driver was based on vendor driver reference code (released + under the GPL) as opposed to the existing drx397xd driver, which + was written via reverse engineering. config DVB_EC100 tristate "E3C EC100" @@ -470,27 +422,31 @@ config DVB_EC100 help Say Y when you want to support this frontend. -config DVB_STV0367 - tristate "ST STV0367 based" +config DVB_GP8PSK_FE + tristate + depends on DVB_CORE + default DVB_USB_GP8PSK + +config DVB_L64781 + tristate "LSI L64781" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-T/C tuner module. Say Y when you want to support this frontend. + A DVB-T tuner module. Say Y when you want to support this frontend. -config DVB_CXD2820R - tristate "Sony CXD2820R" +config DVB_MT352 + tristate "Zarlink MT352 based" depends on DVB_CORE && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y when you want to support this frontend. + A DVB-T tuner module. Say Y when you want to support this frontend. -config DVB_CXD2841ER - tristate "Sony CXD2841ER" +config DVB_NXT6000 + tristate "NxtWave Communications NXT6000 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y when you want to support this frontend. + A DVB-T tuner module. Say Y when you want to support this frontend. config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" @@ -510,13 +466,20 @@ config DVB_RTL2832 config DVB_RTL2832_SDR tristate "Realtek RTL2832 SDR" - depends on DVB_CORE && I2C && I2C_MUX && VIDEO_V4L2 && MEDIA_SDR_SUPPORT && USB + depends on DVB_CORE && I2C && I2C_MUX && VIDEO_DEV && MEDIA_SDR_SUPPORT && USB select DVB_RTL2832 select VIDEOBUF2_VMALLOC default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this SDR module. +config DVB_S5H1432 + tristate "Samsung s5h1432 demodulator (OFDM)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + config DVB_SI2168 tristate "Silicon Labs Si2168" depends on DVB_CORE && I2C && I2C_MUX @@ -524,10 +487,44 @@ config DVB_SI2168 help Say Y when you want to support this frontend. -config DVB_AS102_FE - tristate - depends on DVB_CORE - default DVB_AS102 +config DVB_SP887X + tristate "Spase sp887x based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the command + "<kerneldir>/scripts/get_dvb_firmware sp887x" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_STV0367 + tristate "ST STV0367 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T/C tuner module. Say Y when you want to support this frontend. + +config DVB_TDA10048 + tristate "Philips TDA10048HN based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + +config DVB_TDA1004X + tristate "Philips TDA10045H/TDA10046H based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware tda10045", + "<kerneldir>/scripts/get_dvb_firmware tda10046" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). config DVB_ZD1301_DEMOD tristate "ZyDAS ZD1301" @@ -536,18 +533,20 @@ config DVB_ZD1301_DEMOD help Say Y when you want to support this frontend. -config DVB_GP8PSK_FE - tristate - depends on DVB_CORE - default DVB_USB_GP8PSK +config DVB_ZL10353 + tristate "Zarlink ZL10353 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. source "drivers/media/dvb-frontends/cxd2880/Kconfig" comment "DVB-C (cable) frontends" depends on DVB_CORE -config DVB_VES1820 - tristate "VLSI VES1820 based" +config DVB_STV0297 + tristate "ST STV0297 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -567,8 +566,8 @@ config DVB_TDA10023 help A DVB-C tuner module. Say Y when you want to support this frontend. -config DVB_STV0297 - tristate "ST STV0297 based" +config DVB_VES1820 + tristate "VLSI VES1820 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -577,46 +576,27 @@ config DVB_STV0297 comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" depends on DVB_CORE -config DVB_NXT200X - tristate "NxtWave Communications NXT2002/NXT2004 based" +config DVB_AU8522 depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want - to support this frontend. - - This driver needs external firmware. Please use the commands - "<kerneldir>/scripts/get_dvb_firmware nxt2002" and - "<kerneldir>/scripts/get_dvb_firmware nxt2004" to - download/extract them, and then copy them to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). + tristate -config DVB_OR51211 - tristate "Oren OR51211 based" +config DVB_AU8522_DTV + tristate "Auvitek AU8522 based DTV demod" depends on DVB_CORE && I2C + select DVB_AU8522 default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC 8VSB tuner module. Say Y when you want to support this frontend. - - This driver needs external firmware. Please use the command - "<kerneldir>/scripts/get_dvb_firmware or51211" to - download it, and then copy it to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable DTV demodulation support for this frontend. -config DVB_OR51132 - tristate "Oren OR51132 based" - depends on DVB_CORE && I2C +config DVB_AU8522_V4L + tristate "Auvitek AU8522 based ATV demod" + depends on VIDEO_DEV && DVB_CORE && I2C + select DVB_AU8522 default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want - to support this frontend. - - This driver needs external firmware. Please use the commands - "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or - "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to - download firmwares for 8VSB and QAM64/256, respectively. Copy them to - /usr/lib/hotplug/firmware or /lib/firmware (depending on - configuration of firmware hotplug). + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable ATV demodulation support for this frontend. config DVB_BCM3510 tristate "Broadcom BCM3510" @@ -626,12 +606,12 @@ config DVB_BCM3510 An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -config DVB_LGDT330X - tristate "LG Electronics LGDT3302/LGDT3303 based" +config DVB_LG2160 + tristate "LG Electronics LG216x based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + An ATSC/MH demodulator module. Say Y when you want to support this frontend. config DVB_LGDT3305 @@ -650,72 +630,83 @@ config DVB_LGDT3306A An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want to support this frontend. -config DVB_LG2160 - tristate "LG Electronics LG216x based" +config DVB_LGDT330X + tristate "LG Electronics LGDT3302/LGDT3303 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC/MH demodulator module. Say Y when you want + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -config DVB_S5H1409 - tristate "Samsung S5H1409 based" +config DVB_MXL692 + tristate "MaxLinear MXL692 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL692 is a combo tuner-demodulator that + supports ATSC 8VSB and QAM modes. Say Y when you want to + support this frontend. + +config DVB_NXT200X + tristate "NxtWave Communications NXT2002/NXT2004 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -config DVB_AU8522 - depends on DVB_CORE && I2C - tristate + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware nxt2002" and + "<kerneldir>/scripts/get_dvb_firmware nxt2004" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). -config DVB_AU8522_DTV - tristate "Auvitek AU8522 based DTV demod" +config DVB_OR51132 + tristate "Oren OR51132 based" depends on DVB_CORE && I2C - select DVB_AU8522 default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when - you want to enable DTV demodulation support for this frontend. + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. -config DVB_AU8522_V4L - tristate "Auvitek AU8522 based ATV demod" - depends on VIDEO_V4L2 && DVB_CORE && I2C - select DVB_AU8522 + This driver needs external firmware. Please use the commands + "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or + "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to + download firmwares for 8VSB and QAM64/256, respectively. Copy them to + /usr/lib/hotplug/firmware or /lib/firmware (depending on + configuration of firmware hotplug). + +config DVB_OR51211 + tristate "Oren OR51211 based" + depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when - you want to enable ATV demodulation support for this frontend. + An ATSC 8VSB tuner module. Say Y when you want to support this frontend. -config DVB_S5H1411 - tristate "Samsung S5H1411 based" + This driver needs external firmware. Please use the command + "<kerneldir>/scripts/get_dvb_firmware or51211" to + download it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + +config DVB_S5H1409 + tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -config DVB_MXL692 - tristate "MaxLinear MXL692 based" +config DVB_S5H1411 + tristate "Samsung S5H1411 based" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - MaxLinear MxL692 is a combo tuner-demodulator that - supports ATSC 8VSB and QAM modes. Say Y when you want to - support this frontend. + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. comment "ISDB-T (terrestrial) frontends" depends on DVB_CORE -config DVB_S921 - tristate "Sharp S921 frontend" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. - Say Y when you want to support this frontend. - config DVB_DIB8000 tristate "DiBcom 8000MB/MC" depends on DVB_CORE && I2C @@ -732,17 +723,17 @@ config DVB_MB86A20S A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. -comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends" - depends on DVB_CORE - -config DVB_TC90522 - tristate "Toshiba TC90522" +config DVB_S921 + tristate "Sharp S921 frontend" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator. + AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. Say Y when you want to support this frontend. +comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends" + depends on DVB_CORE + config DVB_MN88443X tristate "Socionext MN88443x" depends on DVB_CORE && I2C @@ -753,6 +744,14 @@ config DVB_MN88443X ISDB-S + ISDB-T demodulator. Say Y when you want to support this frontend. +config DVB_TC90522 + tristate "Toshiba TC90522" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE @@ -785,42 +784,44 @@ config DVB_TUNER_DIB0090 comment "SEC control devices for DVB-S" depends on DVB_CORE -source "drivers/media/dvb-frontends/drx39xyj/Kconfig" +config DVB_A8293 + tristate "Allegro A8293" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT -config DVB_LNBH25 - tristate "LNBH25 SEC controller" +config DVB_AF9033 + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + +config DVB_ASCOT2E + tristate "Sony Ascot2E tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - An SEC control chip. - Say Y when you want to support this chip. + Say Y when you want to support this frontend. -config DVB_LNBH29 - tristate "LNBH29 SEC controller" +config DVB_ATBM8830 + tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - LNB power supply and control voltage - regulator chip with step-up converter - and I2C interface for STMicroelectronics LNBH29. - Say Y when you want to support this chip. + A DMB-TH tuner module. Say Y when you want to support this frontend. -config DVB_LNBP21 - tristate "LNBP21/LNBH24 SEC controllers" +config DVB_HELENE + tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - An SEC control chips. + Say Y when you want to support this frontend. -config DVB_LNBP22 - tristate "LNBP22 SEC controllers" +config DVB_HORUS3A + tristate "Sony Horus3A tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - LNB power supply and control voltage - regulator chip with step-up converter - and I2C interface. - Say Y when you want to support this chip. + Say Y when you want to support this frontend. config DVB_ISL6405 tristate "ISL6405 SEC controller" @@ -843,10 +844,12 @@ config DVB_ISL6423 help A SEC controller chip from Intersil -config DVB_A8293 - tristate "Allegro A8293" +config DVB_IX2505V + tristate "Sharp IX2505V silicon tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" @@ -863,30 +866,40 @@ config DVB_LGS8GXX help A DMB-TH tuner module. Say Y when you want to support this frontend. -config DVB_ATBM8830 - tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" +config DVB_LNBH25 + tristate "LNBH25 SEC controller" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DMB-TH tuner module. Say Y when you want to support this frontend. + An SEC control chip. + Say Y when you want to support this chip. -config DVB_TDA665x - tristate "TDA665x tuner" +config DVB_LNBH29 + tristate "LNBH29 SEC controller" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Support for tuner modules based on Philips TDA6650/TDA6651 chips. + LNB power supply and control voltage + regulator chip with step-up converter + and I2C interface for STMicroelectronics LNBH29. Say Y when you want to support this chip. - Currently supported tuners: - * Panasonic ENV57H12D5 (ET-50DT) +config DVB_LNBP21 + tristate "LNBP21/LNBH24 SEC controllers" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chips. -config DVB_IX2505V - tristate "Sharp IX2505V silicon tuner" +config DVB_LNBP22 + tristate "LNBP22 SEC controllers" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-S tuner module. Say Y when you want to support this frontend. + LNB power supply and control voltage + regulator chip with step-up converter + and I2C interface. + Say Y when you want to support this chip. config DVB_M88RS2000 tristate "M88RS2000 DVB-S demodulator and tuner" @@ -896,32 +909,18 @@ config DVB_M88RS2000 A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_AF9033 - tristate "Afatech AF9033 DVB-T demodulator" - depends on DVB_CORE && I2C - select REGMAP_I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - -config DVB_HORUS3A - tristate "Sony Horus3A tuner" +config DVB_TDA665x + tristate "TDA665x tuner" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y when you want to support this frontend. + Support for tuner modules based on Philips TDA6650/TDA6651 chips. + Say Y when you want to support this chip. -config DVB_ASCOT2E - tristate "Sony Ascot2E tuner" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y when you want to support this frontend. + Currently supported tuners: + * Panasonic ENV57H12D5 (ET-50DT) -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. +source "drivers/media/dvb-frontends/drx39xyj/Kconfig" comment "Common Interface (EN50221) controller drivers" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index d32e4c0be576..a93146cb428c 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -10,126 +10,129 @@ ifdef CONFIG_DVB_RTL2832_SDR ccflags-y += -I$(srctree)/drivers/media/usb/dvb-usb-v2 endif -stb0899-objs := stb0899_drv.o stb0899_algo.o -stv0900-objs := stv0900_core.o stv0900_sw.o -drxd-objs := drxd_firm.o drxd_hard.o cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o +drxd-objs := drxd_firm.o drxd_hard.o drxk-objs := drxk_hard.o +stb0899-objs := stb0899_drv.o stb0899_algo.o +stv0900-objs := stv0900_core.o stv0900_sw.o -obj-$(CONFIG_DVB_PLL) += dvb-pll.o -obj-$(CONFIG_DVB_STV0299) += stv0299.o -obj-$(CONFIG_DVB_STB0899) += stb0899.o -obj-$(CONFIG_DVB_STB6100) += stb6100.o +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) + +obj-$(CONFIG_DVB_A8293) += a8293.o +obj-$(CONFIG_DVB_AF9013) += af9013.o +obj-$(CONFIG_DVB_AF9033) += af9033.o +obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o +obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o +obj-$(CONFIG_DVB_AU8522) += au8522_common.o +obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o +obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o +obj-$(CONFIG_DVB_BCM3510) += bcm3510.o obj-$(CONFIG_DVB_CX22700) += cx22700.o -obj-$(CONFIG_DVB_S5H1432) += s5h1432.o +obj-$(CONFIG_DVB_CX22702) += cx22702.o obj-$(CONFIG_DVB_CX24110) += cx24110.o -obj-$(CONFIG_DVB_TDA8083) += tda8083.o -obj-$(CONFIG_DVB_L64781) += l64781.o +obj-$(CONFIG_DVB_CX24116) += cx24116.o +obj-$(CONFIG_DVB_CX24117) += cx24117.o +obj-$(CONFIG_DVB_CX24120) += cx24120.o +obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o +obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o +obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o +obj-$(CONFIG_DVB_CXD2880) += cxd2880/ obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o -obj-$(CONFIG_DVB_MT312) += mt312.o -obj-$(CONFIG_DVB_VES1820) += ves1820.o -obj-$(CONFIG_DVB_VES1X93) += ves1x93.o -obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o -obj-$(CONFIG_DVB_SP887X) += sp887x.o -obj-$(CONFIG_DVB_NXT6000) += nxt6000.o -obj-$(CONFIG_DVB_MT352) += mt352.o -obj-$(CONFIG_DVB_ZL10036) += zl10036.o -obj-$(CONFIG_DVB_ZL10039) += zl10039.o -obj-$(CONFIG_DVB_ZL10353) += zl10353.o -obj-$(CONFIG_DVB_CX22702) += cx22702.o +obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj/ obj-$(CONFIG_DVB_DRXD) += drxd.o -obj-$(CONFIG_DVB_TDA10021) += tda10021.o -obj-$(CONFIG_DVB_TDA10023) += tda10023.o -obj-$(CONFIG_DVB_STV0297) += stv0297.o -obj-$(CONFIG_DVB_NXT200X) += nxt200x.o -obj-$(CONFIG_DVB_OR51211) += or51211.o -obj-$(CONFIG_DVB_OR51132) += or51132.o -obj-$(CONFIG_DVB_BCM3510) += bcm3510.o -obj-$(CONFIG_DVB_S5H1420) += s5h1420.o -obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o +obj-$(CONFIG_DVB_DRXK) += drxk.o +obj-$(CONFIG_DVB_DS3000) += ds3000.o +obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o +obj-$(CONFIG_DVB_EC100) += ec100.o +obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o +obj-$(CONFIG_DVB_HELENE) += helene.o +obj-$(CONFIG_DVB_HORUS3A) += horus3a.o +obj-$(CONFIG_DVB_ISL6405) += isl6405.o +obj-$(CONFIG_DVB_ISL6421) += isl6421.o +obj-$(CONFIG_DVB_ISL6423) += isl6423.o +obj-$(CONFIG_DVB_IX2505V) += ix2505v.o +obj-$(CONFIG_DVB_L64781) += l64781.o +obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o -obj-$(CONFIG_DVB_MXL692) += mxl692.o -obj-$(CONFIG_DVB_LG2160) += lg2160.o -obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o +obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o obj-$(CONFIG_DVB_LNBH25) += lnbh25.o obj-$(CONFIG_DVB_LNBH29) += lnbh29.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_LNBP22) += lnbp22.o -obj-$(CONFIG_DVB_ISL6405) += isl6405.o -obj-$(CONFIG_DVB_ISL6421) += isl6421.o -obj-$(CONFIG_DVB_TDA10086) += tda10086.o -obj-$(CONFIG_DVB_TDA826X) += tda826x.o -obj-$(CONFIG_DVB_TDA8261) += tda8261.o -obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o -obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o -obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o +obj-$(CONFIG_DVB_MB86A16) += mb86a16.o +obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o +obj-$(CONFIG_DVB_MN88443X) += mn88443x.o +obj-$(CONFIG_DVB_MN88472) += mn88472.o +obj-$(CONFIG_DVB_MN88473) += mn88473.o +obj-$(CONFIG_DVB_MT312) += mt312.o +obj-$(CONFIG_DVB_MT352) += mt352.o +obj-$(CONFIG_DVB_MXL5XX) += mxl5xx.o +obj-$(CONFIG_DVB_MXL692) += mxl692.o +obj-$(CONFIG_DVB_NXT200X) += nxt200x.o +obj-$(CONFIG_DVB_NXT6000) += nxt6000.o +obj-$(CONFIG_DVB_OR51132) += or51132.o +obj-$(CONFIG_DVB_OR51211) += or51211.o +obj-$(CONFIG_DVB_PLL) += dvb-pll.o +obj-$(CONFIG_DVB_RTL2830) += rtl2830.o +obj-$(CONFIG_DVB_RTL2832) += rtl2832.o +obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o -obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o -obj-$(CONFIG_DVB_AU8522) += au8522_common.o -obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o -obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o -obj-$(CONFIG_DVB_TDA10048) += tda10048.o -obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o -obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o -obj-$(CONFIG_DVB_TDA665x) += tda665x.o -obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o -obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o -obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o -obj-$(CONFIG_DVB_AF9013) += af9013.o -obj-$(CONFIG_DVB_CX24116) += cx24116.o -obj-$(CONFIG_DVB_CX24117) += cx24117.o -obj-$(CONFIG_DVB_CX24120) += cx24120.o -obj-$(CONFIG_DVB_SI21XX) += si21xx.o +obj-$(CONFIG_DVB_S5H1420) += s5h1420.o +obj-$(CONFIG_DVB_S5H1432) += s5h1432.o +obj-$(CONFIG_DVB_S921) += s921.o +obj-$(CONFIG_DVB_SI2165) += si2165.o obj-$(CONFIG_DVB_SI2168) += si2168.o -obj-$(CONFIG_DVB_STV0288) += stv0288.o +obj-$(CONFIG_DVB_SI21XX) += si21xx.o +obj-$(CONFIG_DVB_SP2) += sp2.o +obj-$(CONFIG_DVB_SP887X) += sp887x.o +obj-$(CONFIG_DVB_STB0899) += stb0899.o obj-$(CONFIG_DVB_STB6000) += stb6000.o -obj-$(CONFIG_DVB_S921) += s921.o -obj-$(CONFIG_DVB_STV6110) += stv6110.o +obj-$(CONFIG_DVB_STB6100) += stb6100.o +obj-$(CONFIG_DVB_STV0288) += stv0288.o +obj-$(CONFIG_DVB_STV0297) += stv0297.o +obj-$(CONFIG_DVB_STV0299) += stv0299.o +obj-$(CONFIG_DVB_STV0367) += stv0367.o 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 -obj-$(CONFIG_DVB_DS3000) += ds3000.o -obj-$(CONFIG_DVB_TS2020) += ts2020.o -obj-$(CONFIG_DVB_MB86A16) += mb86a16.o -obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj/ -obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o -obj-$(CONFIG_DVB_IX2505V) += ix2505v.o -obj-$(CONFIG_DVB_STV0367) += stv0367.o -obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o -obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o -obj-$(CONFIG_DVB_DRXK) += drxk.o -obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_STV0910) += stv0910.o +obj-$(CONFIG_DVB_STV6110) += stv6110.o +obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_STV6111) += stv6111.o -obj-$(CONFIG_DVB_MXL5XX) += mxl5xx.o -obj-$(CONFIG_DVB_SI2165) += si2165.o -obj-$(CONFIG_DVB_A8293) += a8293.o -obj-$(CONFIG_DVB_SP2) += sp2.o -obj-$(CONFIG_DVB_TDA10071) += tda10071.o -obj-$(CONFIG_DVB_RTL2830) += rtl2830.o -obj-$(CONFIG_DVB_RTL2832) += rtl2832.o -obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o -obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o -obj-$(CONFIG_DVB_AF9033) += af9033.o -obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o -obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o -obj-$(CONFIG_DVB_MN88443X) += mn88443x.o -obj-$(CONFIG_DVB_HORUS3A) += horus3a.o -obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o -obj-$(CONFIG_DVB_HELENE) += helene.o +obj-$(CONFIG_DVB_TDA10021) += tda10021.o +obj-$(CONFIG_DVB_TDA10023) += tda10023.o +obj-$(CONFIG_DVB_TDA10048) += tda10048.o +obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o +obj-$(CONFIG_DVB_TDA10071) += tda10071.o +obj-$(CONFIG_DVB_TDA10086) += tda10086.o +obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o +obj-$(CONFIG_DVB_TDA665x) += tda665x.o +obj-$(CONFIG_DVB_TDA8083) += tda8083.o +obj-$(CONFIG_DVB_TDA8261) += tda8261.o +obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TS2020) += ts2020.o +obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o +obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o +obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o +obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o +obj-$(CONFIG_DVB_VES1820) += ves1820.o +obj-$(CONFIG_DVB_VES1X93) += ves1x93.o obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o -obj-$(CONFIG_DVB_CXD2099) += cxd2099.o -obj-$(CONFIG_DVB_CXD2880) += cxd2880/ +obj-$(CONFIG_DVB_ZL10036) += zl10036.o +obj-$(CONFIG_DVB_ZL10039) += zl10039.o +obj-$(CONFIG_DVB_ZL10353) += zl10353.o diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c index 692600ce5f23..2e11a246aae0 100644 --- a/drivers/media/dvb-frontends/dib3000mc.c +++ b/drivers/media/dvb-frontends/dib3000mc.c @@ -859,7 +859,7 @@ int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defa int k; u8 new_addr; - static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26}; + static const u8 DIB3000MC_I2C_ADDRESS[] = { 20, 22, 24, 26 }; dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); if (dmcst == NULL) diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 55bee50aa871..a90d2f51868f 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -1188,8 +1188,8 @@ static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) { - static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; - static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, + static const s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; + static const u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index 001b23588389..2d29d2c4d434 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -336,7 +336,7 @@ static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) dprintk("%s\n", __func__); while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { - if (jiffies - start > timeout) { + if (time_is_before_jiffies(start + timeout)) { dprintk("%s: timeout!!\n", __func__); return -ETIMEDOUT; } diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 421395ea3334..b5263a0ee5aa 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -161,8 +161,9 @@ static int stv0299_set_FEC(struct stv0299_state *state, enum fe_code_rate fec) static enum fe_code_rate stv0299_get_fec(struct stv0299_state *state) { - static enum fe_code_rate fec_tab[] = { FEC_2_3, FEC_3_4, FEC_5_6, - FEC_7_8, FEC_1_2 }; + static const enum fe_code_rate fec_tab[] = { + FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_1_2 + }; u8 index; dprintk ("%s\n", __func__); @@ -183,7 +184,7 @@ static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) dprintk ("%s\n", __func__); while (stv0299_readreg(state, 0x0a) & 1) { - if (jiffies - start > timeout) { + if (time_is_before_jiffies(start + timeout)) { dprintk ("%s: timeout!!\n", __func__); return -ETIMEDOUT; } @@ -200,7 +201,7 @@ static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) dprintk ("%s\n", __func__); while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { - if (jiffies - start > timeout) { + if (time_is_before_jiffies(start + timeout)) { dprintk ("%s: timeout!!\n", __func__); return -ETIMEDOUT; } diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c index 5be11fd65e3b..e3e1c3db2c85 100644 --- a/drivers/media/dvb-frontends/tda8083.c +++ b/drivers/media/dvb-frontends/tda8083.c @@ -162,7 +162,7 @@ static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) { unsigned long start = jiffies; - while (jiffies - start < timeout && + while (time_is_after_jiffies(start + timeout) && !(tda8083_readreg(state, 0x02) & 0x80)) { msleep(50); diff --git a/drivers/media/firewire/Makefile b/drivers/media/firewire/Makefile index 3670c85af6f5..d5551e6389bf 100644 --- a/drivers/media/firewire/Makefile +++ b/drivers/media/firewire/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o firedtv-y += firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o -firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o +firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 69c56e24a612..fae2baabb773 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -3,7 +3,7 @@ # Multimedia Video device configuration # -if VIDEO_V4L2 +if VIDEO_DEV comment "IR I2C driver auto-selected by 'Autoselect ancillary drivers'" depends on MEDIA_SUBDRV_AUTOSELECT && I2C && RC_CORE @@ -22,705 +22,6 @@ config VIDEO_IR_I2C In doubt, say Y. # -# V4L2 I2C drivers that aren't related with Camera support -# - -comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'" - depends on MEDIA_HIDE_ANCILLARY_SUBDRV -# -# Encoder / Decoder module configuration -# - -menu "Audio decoders, processors and mixers" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_TVAUDIO - tristate "Simple audio decoder chips" - depends on VIDEO_V4L2 && I2C - help - Support for several audio decoder chips found on some bt8xx boards: - Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, - tea6320, tea6420, tda8425, ta8874z. - Microchip: pic16c54 based design on ProVideo PV951 board. - - To compile this driver as a module, choose M here: the - module will be called tvaudio. - -config VIDEO_TDA7432 - tristate "Philips TDA7432 audio processor" - depends on VIDEO_V4L2 && I2C - help - Support for tda7432 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tda7432. - -config VIDEO_TDA9840 - tristate "Philips TDA9840 audio processor" - depends on I2C - help - Support for tda9840 audio decoder chip found on some Zoran boards. - - To compile this driver as a module, choose M here: the - module will be called tda9840. - -config VIDEO_TDA1997X - tristate "NXP TDA1997x HDMI receiver" - depends on VIDEO_V4L2 && I2C - depends on SND_SOC - select HDMI - select SND_PCM - select V4L2_FWNODE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. - - To compile this driver as a module, choose M here: the - module will be called tda1997x. - -config VIDEO_TEA6415C - tristate "Philips TEA6415C audio processor" - depends on I2C - help - Support for tea6415c audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6415c. - -config VIDEO_TEA6420 - tristate "Philips TEA6420 audio processor" - depends on I2C - help - Support for tea6420 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tea6420. - -config VIDEO_MSP3400 - tristate "Micronas MSP34xx audio decoders" - depends on VIDEO_V4L2 && I2C - help - Support for the Micronas MSP34xx series of audio decoders. - - To compile this driver as a module, choose M here: the - module will be called msp3400. - -config VIDEO_CS3308 - tristate "Cirrus Logic CS3308 audio ADC" - depends on VIDEO_V4L2 && I2C - help - Support for the Cirrus Logic CS3308 High Performance 8-Channel - Analog Volume Control - - To compile this driver as a module, choose M here: the - module will be called cs3308. - -config VIDEO_CS5345 - tristate "Cirrus Logic CS5345 audio ADC" - depends on VIDEO_V4L2 && I2C - help - Support for the Cirrus Logic CS5345 24-bit, 192 kHz - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs5345. - -config VIDEO_CS53L32A - tristate "Cirrus Logic CS53L32A audio ADC" - depends on VIDEO_V4L2 && I2C - help - Support for the Cirrus Logic CS53L32A low voltage - stereo A/D converter. - - To compile this driver as a module, choose M here: the - module will be called cs53l32a. - -config VIDEO_TLV320AIC23B - tristate "Texas Instruments TLV320AIC23B audio codec" - depends on VIDEO_V4L2 && I2C - help - Support for the Texas Instruments TLV320AIC23B audio codec. - - To compile this driver as a module, choose M here: the - module will be called tlv320aic23b. - -config VIDEO_UDA1342 - tristate "Philips UDA1342 audio codec" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips UDA1342 audio codec. - - To compile this driver as a module, choose M here: the - module will be called uda1342. - -config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" - depends on VIDEO_V4L2 && I2C - help - Support for the Wolfson Microelectronics WM8775 high - performance stereo A/D Converter with a 4 channel input mixer. - - To compile this driver as a module, choose M here: the - module will be called wm8775. - -config VIDEO_WM8739 - tristate "Wolfson Microelectronics WM8739 stereo audio ADC" - depends on VIDEO_V4L2 && I2C - help - Support for the Wolfson Microelectronics WM8739 - stereo A/D Converter. - - To compile this driver as a module, choose M here: the - module will be called wm8739. - -config VIDEO_VP27SMPX - tristate "Panasonic VP27's internal MPX" - depends on VIDEO_V4L2 && I2C - help - Support for the internal MPX of the Panasonic VP27s tuner. - - To compile this driver as a module, choose M here: the - module will be called vp27smpx. - -config VIDEO_SONY_BTF_MPX - tristate "Sony BTF's internal MPX" - depends on VIDEO_V4L2 && I2C - help - Support for the internal MPX of the Sony BTF-PG472Z tuner. - - To compile this driver as a module, choose M here: the - module will be called sony-btf-mpx. -endmenu - -menu "RDS decoders" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_SAA6588 - tristate "SAA6588 Radio Chip RDS decoder support" - depends on VIDEO_V4L2 && I2C - - help - Support for this Radio Data System (RDS) decoder. This allows - seeing radio station identification transmitted using this - standard. - - To compile this driver as a module, choose M here: the - module will be called saa6588. -endmenu - -menu "Video decoders" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_ADV7180 - tristate "Analog Devices ADV7180 decoder" - depends on GPIOLIB && VIDEO_V4L2 && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_ASYNC - help - Support for the Analog Devices ADV7180 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7180. - -config VIDEO_ADV7183 - tristate "Analog Devices ADV7183 decoder" - depends on VIDEO_V4L2 && I2C - help - V4l2 subdevice driver for the Analog Devices - ADV7183 video decoder. - - To compile this driver as a module, choose M here: the - module will be called adv7183. - -config VIDEO_ADV748X - tristate "Analog Devices ADV748x decoder" - depends on VIDEO_V4L2 && I2C - depends on OF - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select REGMAP_I2C - select V4L2_FWNODE - help - V4L2 subdevice driver for the Analog Devices - ADV7481 and ADV7482 HDMI/Analog video decoders. - - To compile this driver as a module, choose M here: the - module will be called adv748x. - -config VIDEO_ADV7604 - tristate "Analog Devices ADV7604 decoder" - depends on VIDEO_V4L2 && I2C - depends on GPIOLIB || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select REGMAP_I2C - select HDMI - select V4L2_FWNODE - help - Support for the Analog Devices ADV7604 video decoder. - - This is a Analog Devices Component/Graphics Digitizer - with 4:1 Multiplexed HDMI Receiver. - - To compile this driver as a module, choose M here: the - module will be called adv7604. - -config VIDEO_ADV7604_CEC - bool "Enable Analog Devices ADV7604 CEC support" - depends on VIDEO_ADV7604 - select CEC_CORE - help - When selected the adv7604 will support the optional - HDMI CEC feature. - -config VIDEO_ADV7842 - tristate "Analog Devices ADV7842 decoder" - depends on VIDEO_V4L2 && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select HDMI - help - Support for the Analog Devices ADV7842 video decoder. - - This is a Analog Devices Component/Graphics/SD Digitizer - with 2:1 Multiplexed HDMI Receiver. - - To compile this driver as a module, choose M here: the - module will be called adv7842. - -config VIDEO_ADV7842_CEC - bool "Enable Analog Devices ADV7842 CEC support" - depends on VIDEO_ADV7842 - select CEC_CORE - help - When selected the adv7842 will support the optional - HDMI CEC feature. - -config VIDEO_BT819 - tristate "BT819A VideoStream decoder" - depends on VIDEO_V4L2 && I2C - help - Support for BT819A video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt819. - -config VIDEO_BT856 - tristate "BT856 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - help - Support for BT856 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt856. - -config VIDEO_BT866 - tristate "BT866 VideoStream decoder" - depends on VIDEO_V4L2 && I2C - help - Support for BT866 video decoder. - - To compile this driver as a module, choose M here: the - module will be called bt866. - -config VIDEO_KS0127 - tristate "KS0127 video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for KS0127 video decoder. - - This chip is used on AverMedia AVS6EYES Zoran-based MJPEG - cards. - - To compile this driver as a module, choose M here: the - module will be called ks0127. - -config VIDEO_ML86V7667 - tristate "OKI ML86V7667 video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for the OKI Semiconductor ML86V7667 video decoder. - - To compile this driver as a module, choose M here: the - module will be called ml86v7667. - -config VIDEO_SAA7110 - tristate "Philips SAA7110 video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips SAA7110 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7110. - -config VIDEO_SAA711X - tristate "Philips SAA7111/3/4/5 video decoders" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips SAA7111/3/4/5 video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa7115. - -config VIDEO_TC358743 - tristate "Toshiba TC358743 decoder" - depends on VIDEO_V4L2 && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select HDMI - select V4L2_FWNODE - help - Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. - - To compile this driver as a module, choose M here: the - module will be called tc358743. - -config VIDEO_TC358743_CEC - bool "Enable Toshiba TC358743 CEC support" - depends on VIDEO_TC358743 - select CEC_CORE - help - When selected the tc358743 will support the optional - HDMI CEC feature. - -config VIDEO_TVP514X - tristate "Texas Instruments TVP514x video decoder" - depends on VIDEO_V4L2 && I2C - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the TI TVP5146/47 - decoder. It is currently working with the TI OMAP3 camera - controller. - - To compile this driver as a module, choose M here: the - module will be called tvp514x. - -config VIDEO_TVP5150 - tristate "Texas Instruments TVP5150 video decoder" - depends on VIDEO_V4L2 && I2C - select V4L2_FWNODE - select REGMAP_I2C - help - Support for the Texas Instruments TVP5150 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp5150. - -config VIDEO_TVP7002 - tristate "Texas Instruments TVP7002 video decoder" - depends on VIDEO_V4L2 && I2C - select V4L2_FWNODE - help - Support for the Texas Instruments TVP7002 video decoder. - - To compile this driver as a module, choose M here: the - module will be called tvp7002. - -config VIDEO_TW2804 - tristate "Techwell TW2804 multiple video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Techwell tw2804 multiple video decoder. - - To compile this driver as a module, choose M here: the - module will be called tw2804. - -config VIDEO_TW9903 - tristate "Techwell TW9903 video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Techwell tw9903 multi-standard video decoder - with high quality down scaler. - - To compile this driver as a module, choose M here: the - module will be called tw9903. - -config VIDEO_TW9906 - tristate "Techwell TW9906 video decoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Techwell tw9906 enhanced multi-standard comb filter - video decoder with YCbCr input support. - - To compile this driver as a module, choose M here: the - module will be called tw9906. - -config VIDEO_TW9910 - tristate "Techwell TW9910 video decoder" - depends on VIDEO_V4L2 && I2C - select V4L2_ASYNC - help - Support for Techwell TW9910 NTSC/PAL/SECAM video decoder. - - To compile this driver as a module, choose M here: the - module will be called tw9910. - -config VIDEO_VPX3220 - tristate "vpx3220a, vpx3216b & vpx3214c video decoders" - depends on VIDEO_V4L2 && I2C - help - Support for VPX322x video decoders. - - To compile this driver as a module, choose M here: the - module will be called vpx3220. - -config VIDEO_MAX9286 - tristate "Maxim MAX9286 GMSL deserializer support" - depends on I2C && I2C_MUX - depends on VIDEO_V4L2 - depends on OF_GPIO - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - help - This driver supports the Maxim MAX9286 GMSL deserializer. - - To compile this driver as a module, choose M here: the - module will be called max9286. - -comment "Video and audio decoders" - -config VIDEO_SAA717X - tristate "Philips SAA7171/3/4 audio/video decoders" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips SAA7171/3/4 audio/video decoders. - - To compile this driver as a module, choose M here: the - module will be called saa717x. - -source "drivers/media/i2c/cx25840/Kconfig" - -endmenu - -menu "Video encoders" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_SAA7127 - tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips SAA7127/9 digital video encoders. - - To compile this driver as a module, choose M here: the - module will be called saa7127. - -config VIDEO_SAA7185 - tristate "Philips SAA7185 video encoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Philips SAA7185 video encoder. - - To compile this driver as a module, choose M here: the - module will be called saa7185. - -config VIDEO_ADV7170 - tristate "Analog Devices ADV7170 video encoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Analog Devices ADV7170 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7170. - -config VIDEO_ADV7175 - tristate "Analog Devices ADV7175 video encoder" - depends on VIDEO_V4L2 && I2C - help - Support for the Analog Devices ADV7175 video encoder driver - - To compile this driver as a module, choose M here: the - module will be called adv7175. - -config VIDEO_ADV7343 - tristate "ADV7343 video encoder" - depends on I2C - select V4L2_ASYNC - help - Support for Analog Devices I2C bus based ADV7343 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7343. - -config VIDEO_ADV7393 - tristate "ADV7393 video encoder" - depends on I2C - help - Support for Analog Devices I2C bus based ADV7393 encoder. - - To compile this driver as a module, choose M here: the - module will be called adv7393. - -config VIDEO_ADV7511 - tristate "Analog Devices ADV7511 encoder" - depends on VIDEO_V4L2 && I2C - depends on DRM_I2C_ADV7511=n || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select HDMI - help - Support for the Analog Devices ADV7511 video encoder. - - This is a Analog Devices HDMI transmitter. - - To compile this driver as a module, choose M here: the - module will be called adv7511. - -config VIDEO_ADV7511_CEC - bool "Enable Analog Devices ADV7511 CEC support" - depends on VIDEO_ADV7511 - select CEC_CORE - help - When selected the adv7511 will support the optional - HDMI CEC feature. - -config VIDEO_AD9389B - tristate "Analog Devices AD9389B encoder" - depends on VIDEO_V4L2 && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - - help - Support for the Analog Devices AD9389B video encoder. - - This is a Analog Devices HDMI transmitter. - - To compile this driver as a module, choose M here: the - module will be called ad9389b. - -config VIDEO_AK881X - tristate "AK8813/AK8814 video encoders" - depends on I2C - help - Video output driver for AKM AK8813 and AK8814 TV encoders - -config VIDEO_THS8200 - tristate "Texas Instruments THS8200 video encoder" - depends on VIDEO_V4L2 && I2C - select V4L2_ASYNC - help - Support for the Texas Instruments THS8200 video encoder. - - To compile this driver as a module, choose M here: the - module will be called ths8200. -endmenu - -menu "Video improvement chips" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_UPD64031A - tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_V4L2 && I2C - select V4L2_ASYNC - help - Support for the NEC Electronics uPD64031A Ghost Reduction - video chip. It is most often found in NTSC TV cards made for - Japan and is used to reduce the 'ghosting' effect that can - be present in analog TV broadcasts. - - To compile this driver as a module, choose M here: the - module will be called upd64031a. - -config VIDEO_UPD64083 - tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_V4L2 && I2C - help - Support for the NEC Electronics uPD64083 3-Dimensional Y/C - separation video chip. It is used to improve the quality of - the colors of a composite signal. - - To compile this driver as a module, choose M here: the - module will be called upd64083. -endmenu - -menu "Audio/Video compression chips" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_SAA6752HS - tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" - depends on VIDEO_V4L2 && I2C - select CRC32 - help - Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 - audio encoder with multiplexer. - - To compile this driver as a module, choose M here: the - module will be called saa6752hs. - -endmenu - -menu "SDR tuner chips" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config SDR_MAX2175 - tristate "Maxim 2175 RF to Bits tuner" - depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C - select REGMAP_I2C - select V4L2_ASYNC - help - Support for Maxim 2175 tuner. It is an advanced analog/digital - radio receiver with RF-to-Bits front-end designed for SDR solutions. - - To compile this driver as a module, choose M here; the - module will be called max2175. - - -endmenu - -menu "Miscellaneous helper chips" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_THS7303 - tristate "THS7303/53 Video Amplifier" - depends on VIDEO_V4L2 && I2C - select V4L2_ASYNC - help - Support for TI THS7303/53 video amplifier - - To compile this driver as a module, choose M here: the - module will be called ths7303. - -config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - help - Support for the Mitsubishi M52790 A/V switch. - - To compile this driver as a module, choose M here: the - module will be called m52790. - -config VIDEO_I2C - tristate "I2C transport video support" - depends on VIDEO_V4L2 && I2C - select VIDEOBUF2_VMALLOC - imply HWMON - help - Enable the I2C transport video support which supports the - following: - * Panasonic AMG88xx Grid-Eye Sensors - * Melexis MLX90640 Thermal Cameras - - To compile this driver as a module, choose M here: the - module will be called video-i2c - -config VIDEO_ST_MIPID02 - tristate "STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE - help - Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. - It is used to allow usage of CSI-2 sensor with PARALLEL port - controller. - - To compile this driver as a module, choose M here: the - module will be called st-mipid02. -endmenu - -# # V4L2 I2C drivers that are related with Camera support # @@ -735,7 +36,7 @@ config VIDEO_CCS_PLL config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -748,7 +49,7 @@ config VIDEO_HI556 config VIDEO_HI846 tristate "Hynix Hi-846 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -759,9 +60,22 @@ config VIDEO_HI846 To compile this driver as a module, choose M here: the module will be called hi846. +config VIDEO_HI847 + tristate "Hynix Hi-847 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Hynix + Hi-847 camera. + + To compile this driver as a module, choose M here: the + module will be called hi847. + config VIDEO_IMX208 tristate "Sony IMX208 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_DEV && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony @@ -772,7 +86,7 @@ config VIDEO_IMX208 config VIDEO_IMX214 tristate "Sony IMX214 sensor support" - depends on GPIOLIB && I2C && VIDEO_V4L2 + depends on GPIOLIB && I2C && VIDEO_DEV select V4L2_FWNODE select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API @@ -786,7 +100,7 @@ config VIDEO_IMX214 config VIDEO_IMX219 tristate "Sony IMX219 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -799,7 +113,7 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API help @@ -811,7 +125,7 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C @@ -821,7 +135,7 @@ config VIDEO_IMX274 config VIDEO_IMX290 tristate "Sony IMX290 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C @@ -835,7 +149,7 @@ config VIDEO_IMX290 config VIDEO_IMX319 tristate "Sony IMX319 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API help @@ -848,7 +162,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select V4L2_FWNODE @@ -862,7 +176,7 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select V4L2_FWNODE @@ -875,7 +189,7 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API help @@ -888,7 +202,7 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select V4L2_FWNODE @@ -899,9 +213,119 @@ config VIDEO_IMX412 To compile this driver as a module, choose M here: the module will be called imx412. +config VIDEO_MAX9271_LIB + tristate + +config VIDEO_MT9M001 + tristate "mt9m001 support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This driver supports MT9M001 cameras from Micron, monochrome + and colour models. + +config VIDEO_MT9M032 + tristate "MT9M032 camera sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEO_APTINA_PLL + help + This driver supports MT9M032 camera sensors from Aptina, monochrome + models only. + +config VIDEO_MT9M111 + tristate "mt9m111, mt9m112 and mt9m131 support" + depends on I2C && VIDEO_DEV + select V4L2_FWNODE + help + This driver supports MT9M111, MT9M112 and MT9M131 cameras from + Micron/Aptina + +config VIDEO_MT9P031 + tristate "Aptina MT9P031 support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEO_APTINA_PLL + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Aptina + (Micron) mt9p031 5 Mpixel camera. + +config VIDEO_MT9T001 + tristate "Aptina MT9T001 support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor driver for the Aptina + (Micron) mt0t001 3 Mpixel camera. + +config VIDEO_MT9T112 + tristate "Aptina MT9T111/MT9T112 support" + depends on I2C && VIDEO_DEV + help + This is a Video4Linux2 sensor driver for the Aptina + (Micron) MT9T111 and MT9T112 3 Mpixel camera. + + To compile this driver as a module, choose M here: the + module will be called mt9t112. + +config VIDEO_MT9V011 + tristate "Micron mt9v011 sensor support" + depends on I2C && VIDEO_DEV + help + This is a Video4Linux2 sensor driver for the Micron + mt0v011 1.3 Mpixel camera. It currently only works with the + em28xx driver. + +config VIDEO_MT9V032 + tristate "Micron MT9V032 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select REGMAP_I2C + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Micron + MT9V032 752x480 CMOS sensor. + +config VIDEO_MT9V111 + tristate "Aptina MT9V111 sensor support" + depends on I2C && VIDEO_DEV + help + This is a Video4Linux2 sensor driver for the Aptina/Micron + MT9V111 sensor. + + To compile this driver as a module, choose M here: the + module will be called mt9v111. + +config VIDEO_NOON010PC30 + tristate "Siliconfile NOON010PC30 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This driver supports NOON010PC30 CIF camera from Siliconfile + +config VIDEO_OG01A1B + tristate "OmniVision OG01A1B sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OG01A1B camera. + + To compile this driver as a module, choose M here: the + module will be called og01a1b. + config VIDEO_OV02A10 tristate "OmniVision OV02A10 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -912,9 +336,42 @@ config VIDEO_OV02A10 To compile this driver as a module, choose M here: the module will be called ov02a10. +config VIDEO_OV08D10 + tristate "OmniVision OV08D10 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV08D10 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called ov08d10. + +config VIDEO_OV13858 + tristate "OmniVision OV13858 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV13858 camera. + +config VIDEO_OV13B10 + tristate "OmniVision OV13B10 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV13B10 camera. + config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C help This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. @@ -924,7 +381,7 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" - depends on VIDEO_V4L2 && I2C && GPIOLIB + depends on VIDEO_DEV && I2C && GPIOLIB select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -935,7 +392,7 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C select MEDIA_CONTROLLER select V4L2_FWNODE help @@ -947,7 +404,7 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C select MEDIA_CONTROLLER select V4L2_FWNODE help @@ -959,7 +416,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API @@ -975,7 +432,7 @@ config VIDEO_OV2740 config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF - depends on GPIOLIB && VIDEO_V4L2 && I2C + depends on GPIOLIB && VIDEO_DEV && I2C select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -986,7 +443,7 @@ config VIDEO_OV5640 config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -999,7 +456,7 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1012,7 +469,7 @@ config VIDEO_OV5647 config VIDEO_OV5648 tristate "OmniVision OV5648 sensor support" - depends on I2C && PM && VIDEO_V4L2 + depends on I2C && PM && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1023,19 +480,9 @@ config VIDEO_OV5648 To compile this driver as a module, choose M here: the module will be called ov5648. -config VIDEO_OV6650 - tristate "OmniVision OV6650 sensor support" - depends on I2C && VIDEO_V4L2 - help - This is a Video4Linux2 sensor driver for the OmniVision - OV6650 camera. - - To compile this driver as a module, choose M here: the - module will be called ov6650. - config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1048,7 +495,7 @@ config VIDEO_OV5670 config VIDEO_OV5675 tristate "OmniVision OV5675 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1061,7 +508,7 @@ config VIDEO_OV5675 config VIDEO_OV5693 tristate "OmniVision OV5693 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -1072,7 +519,7 @@ config VIDEO_OV5693 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -1081,9 +528,19 @@ config VIDEO_OV5695 To compile this driver as a module, choose M here: the module will be called ov5695. +config VIDEO_OV6650 + tristate "OmniVision OV6650 sensor support" + depends on I2C && VIDEO_DEV + help + This is a Video4Linux2 sensor driver for the OmniVision + OV6650 camera. + + To compile this driver as a module, choose M here: the + module will be called ov6650. + config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1094,21 +551,9 @@ config VIDEO_OV7251 To compile this driver as a module, choose M here: the module will be called ov7251. -config VIDEO_OV772X - tristate "OmniVision OV772x sensor support" - depends on I2C && VIDEO_V4L2 - select REGMAP_SCCB - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the OmniVision - OV772x camera. - - To compile this driver as a module, choose M here: the - module will be called ov772x. - config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. @@ -1118,16 +563,28 @@ config VIDEO_OV7640 config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_OV772X + tristate "OmniVision OV772x sensor support" + depends on I2C && VIDEO_DEV + select REGMAP_SCCB + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV772x camera. + + To compile this driver as a module, choose M here: the + module will be called ov772x. + config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision @@ -1135,7 +592,7 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1148,7 +605,7 @@ config VIDEO_OV8856 config VIDEO_OV8865 tristate "OmniVision OV8865 sensor support" - depends on I2C && PM && VIDEO_V4L2 + depends on I2C && PM && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1162,7 +619,7 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select V4L2_FWNODE @@ -1175,14 +632,14 @@ config VIDEO_OV9282 config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV9640 camera sensor. config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select REGMAP_SCCB @@ -1192,7 +649,7 @@ config VIDEO_OV9650 config VIDEO_OV9734 tristate "OmniVision OV9734 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API @@ -1204,141 +661,6 @@ config VIDEO_OV9734 To compile this driver as a module, choose M here: the module's name is ov9734. -config VIDEO_OV13858 - tristate "OmniVision OV13858 sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the OmniVision - OV13858 camera. - -config VIDEO_OV13B10 - tristate "OmniVision OV13B10 sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the OmniVision - OV13B10 camera. - -config VIDEO_VS6624 - tristate "ST VS6624 sensor support" - depends on VIDEO_V4L2 && I2C - help - This is a Video4Linux2 sensor driver for the ST VS6624 - camera. - - To compile this driver as a module, choose M here: the - module will be called vs6624. - -config VIDEO_MT9M001 - tristate "mt9m001 support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This driver supports MT9M001 cameras from Micron, monochrome - and colour models. - -config VIDEO_MT9M032 - tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEO_APTINA_PLL - help - This driver supports MT9M032 camera sensors from Aptina, monochrome - models only. - -config VIDEO_MT9M111 - tristate "mt9m111, mt9m112 and mt9m131 support" - depends on I2C && VIDEO_V4L2 - select V4L2_FWNODE - help - This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina - -config VIDEO_MT9P031 - tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEO_APTINA_PLL - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the Aptina - (Micron) mt9p031 5 Mpixel camera. - -config VIDEO_MT9T001 - tristate "Aptina MT9T001 support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This is a Video4Linux2 sensor driver for the Aptina - (Micron) mt0t001 3 Mpixel camera. - -config VIDEO_MT9T112 - tristate "Aptina MT9T111/MT9T112 support" - depends on I2C && VIDEO_V4L2 - help - This is a Video4Linux2 sensor driver for the Aptina - (Micron) MT9T111 and MT9T112 3 Mpixel camera. - - To compile this driver as a module, choose M here: the - module will be called mt9t112. - -config VIDEO_MT9V011 - tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_V4L2 - help - This is a Video4Linux2 sensor driver for the Micron - mt0v011 1.3 Mpixel camera. It currently only works with the - em28xx driver. - -config VIDEO_MT9V032 - tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select REGMAP_I2C - select V4L2_FWNODE - help - This is a Video4Linux2 sensor driver for the Micron - MT9V032 752x480 CMOS sensor. - -config VIDEO_MT9V111 - tristate "Aptina MT9V111 sensor support" - depends on I2C && VIDEO_V4L2 - help - This is a Video4Linux2 sensor driver for the Aptina/Micron - MT9V111 sensor. - - To compile this driver as a module, choose M here: the - module will be called mt9v111. - -config VIDEO_SR030PC30 - tristate "Siliconfile SR030PC30 sensor support" - depends on I2C && VIDEO_V4L2 - help - This driver supports SR030PC30 VGA camera from Siliconfile - -config VIDEO_NOON010PC30 - tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This driver supports NOON010PC30 CIF camera from Siliconfile - -source "drivers/media/i2c/m5mols/Kconfig" - -config VIDEO_MAX9271_LIB - tristate - config VIDEO_RDACM20 tristate "IMI RDACM20 camera support" depends on I2C @@ -1369,7 +691,7 @@ config VIDEO_RDACM21 config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. @@ -1377,27 +699,19 @@ config VIDEO_RJ54N1 To compile this driver as a module, choose M here: the module will be called rj54n1. -config VIDEO_S5K6AA - tristate "Samsung S5K6AAFX sensor support" - depends on I2C && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M - camera sensor with an embedded SoC image signal processor. - -config VIDEO_S5K6A3 - tristate "Samsung S5K6A3 sensor support" - depends on I2C && VIDEO_V4L2 +config VIDEO_S5C73M3 + tristate "Samsung S5C73M3 sensor support" + depends on I2C && SPI && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE help - This is a V4L2 sensor driver for Samsung S5K6A3 raw - camera sensor. + This is a V4L2 sensor driver for Samsung S5C73M3 + 8 Mpixel camera. config VIDEO_S5K4ECGX tristate "Samsung S5K4ECGX sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select CRC32 @@ -1407,7 +721,7 @@ config VIDEO_S5K4ECGX config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1415,18 +729,43 @@ config VIDEO_S5K5BAF This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. -source "drivers/media/i2c/ccs/Kconfig" -source "drivers/media/i2c/et8ek8/Kconfig" +config VIDEO_S5K6A3 + tristate "Samsung S5K6A3 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This is a V4L2 sensor driver for Samsung S5K6A3 raw + camera sensor. -config VIDEO_S5C73M3 - tristate "Samsung S5C73M3 sensor support" - depends on I2C && SPI && VIDEO_V4L2 +config VIDEO_S5K6AA + tristate "Samsung S5K6AAFX sensor support" + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help - This is a V4L2 sensor driver for Samsung S5C73M3 - 8 Mpixel camera. + This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M + camera sensor with an embedded SoC image signal processor. + +config VIDEO_SR030PC30 + tristate "Siliconfile SR030PC30 sensor support" + depends on I2C && VIDEO_DEV + help + This driver supports SR030PC30 VGA camera from Siliconfile + +config VIDEO_VS6624 + tristate "ST VS6624 sensor support" + depends on VIDEO_DEV && I2C + help + This is a Video4Linux2 sensor driver for the ST VS6624 + camera. + + To compile this driver as a module, choose M here: the + module will be called vs6624. + +source "drivers/media/i2c/ccs/Kconfig" +source "drivers/media/i2c/et8ek8/Kconfig" +source "drivers/media/i2c/m5mols/Kconfig" endmenu @@ -1435,7 +774,7 @@ menu "Lens drivers" config VIDEO_AD5820 tristate "AD5820 lens voice coil support" - depends on GPIOLIB && I2C && VIDEO_V4L2 + depends on GPIOLIB && I2C && VIDEO_DEV select MEDIA_CONTROLLER select V4L2_ASYNC help @@ -1444,7 +783,7 @@ config VIDEO_AD5820 config VIDEO_AK7375 tristate "AK7375 lens voice coil support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_ASYNC @@ -1456,7 +795,7 @@ config VIDEO_AK7375 config VIDEO_DW9714 tristate "DW9714 lens voice coil support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_ASYNC @@ -1468,7 +807,7 @@ config VIDEO_DW9714 config VIDEO_DW9768 tristate "DW9768 lens voice coil support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1480,7 +819,7 @@ config VIDEO_DW9768 config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_ASYNC @@ -1497,7 +836,7 @@ menu "Flash devices" config VIDEO_ADP1653 tristate "ADP1653 flash support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select V4L2_ASYNC help @@ -1506,7 +845,7 @@ config VIDEO_ADP1653 config VIDEO_LM3560 tristate "LM3560 dual flash driver support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select REGMAP_I2C select V4L2_ASYNC @@ -1516,13 +855,727 @@ config VIDEO_LM3560 config VIDEO_LM3646 tristate "LM3646 dual flash driver support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select REGMAP_I2C select V4L2_ASYNC help This is a driver for the lm3646 dual flash controllers. It controls flash, torch LEDs. + +endmenu + +# +# V4L2 I2C drivers that aren't related with Camera support +# + +comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'" + depends on MEDIA_HIDE_ANCILLARY_SUBDRV +# +# Encoder / Decoder module configuration +# + +menu "Audio decoders, processors and mixers" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_CS3308 + tristate "Cirrus Logic CS3308 audio ADC" + depends on VIDEO_DEV && I2C + help + Support for the Cirrus Logic CS3308 High Performance 8-Channel + Analog Volume Control + + To compile this driver as a module, choose M here: the + module will be called cs3308. + +config VIDEO_CS5345 + tristate "Cirrus Logic CS5345 audio ADC" + depends on VIDEO_DEV && I2C + help + Support for the Cirrus Logic CS5345 24-bit, 192 kHz + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs5345. + +config VIDEO_CS53L32A + tristate "Cirrus Logic CS53L32A audio ADC" + depends on VIDEO_DEV && I2C + help + Support for the Cirrus Logic CS53L32A low voltage + stereo A/D converter. + + To compile this driver as a module, choose M here: the + module will be called cs53l32a. + +config VIDEO_MSP3400 + tristate "Micronas MSP34xx audio decoders" + depends on VIDEO_DEV && I2C + help + Support for the Micronas MSP34xx series of audio decoders. + + To compile this driver as a module, choose M here: the + module will be called msp3400. + +config VIDEO_SONY_BTF_MPX + tristate "Sony BTF's internal MPX" + depends on VIDEO_DEV && I2C + help + Support for the internal MPX of the Sony BTF-PG472Z tuner. + + To compile this driver as a module, choose M here: the + module will be called sony-btf-mpx. + +config VIDEO_TDA1997X + tristate "NXP TDA1997x HDMI receiver" + depends on VIDEO_DEV && I2C + depends on SND_SOC + select HDMI + select SND_PCM + select V4L2_FWNODE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. + + To compile this driver as a module, choose M here: the + module will be called tda1997x. + +config VIDEO_TDA7432 + tristate "Philips TDA7432 audio processor" + depends on VIDEO_DEV && I2C + help + Support for tda7432 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tda7432. + +config VIDEO_TDA9840 + tristate "Philips TDA9840 audio processor" + depends on I2C + help + Support for tda9840 audio decoder chip found on some Zoran boards. + + To compile this driver as a module, choose M here: the + module will be called tda9840. + +config VIDEO_TEA6415C + tristate "Philips TEA6415C audio processor" + depends on I2C + help + Support for tea6415c audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6415c. + +config VIDEO_TEA6420 + tristate "Philips TEA6420 audio processor" + depends on I2C + help + Support for tea6420 audio decoder chip found on some bt8xx boards. + + To compile this driver as a module, choose M here: the + module will be called tea6420. + +config VIDEO_TLV320AIC23B + tristate "Texas Instruments TLV320AIC23B audio codec" + depends on VIDEO_DEV && I2C + help + Support for the Texas Instruments TLV320AIC23B audio codec. + + To compile this driver as a module, choose M here: the + module will be called tlv320aic23b. + +config VIDEO_TVAUDIO + tristate "Simple audio decoder chips" + depends on VIDEO_DEV && I2C + help + Support for several audio decoder chips found on some bt8xx boards: + Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, + tea6320, tea6420, tda8425, ta8874z. + Microchip: pic16c54 based design on ProVideo PV951 board. + + To compile this driver as a module, choose M here: the + module will be called tvaudio. + +config VIDEO_UDA1342 + tristate "Philips UDA1342 audio codec" + depends on VIDEO_DEV && I2C + help + Support for the Philips UDA1342 audio codec. + + To compile this driver as a module, choose M here: the + module will be called uda1342. + +config VIDEO_VP27SMPX + tristate "Panasonic VP27's internal MPX" + depends on VIDEO_DEV && I2C + help + Support for the internal MPX of the Panasonic VP27s tuner. + + To compile this driver as a module, choose M here: the + module will be called vp27smpx. + +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_DEV && I2C + help + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739. + +config VIDEO_WM8775 + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" + depends on VIDEO_DEV && I2C + help + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. + + To compile this driver as a module, choose M here: the + module will be called wm8775. + +endmenu + +menu "RDS decoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_SAA6588 + tristate "SAA6588 Radio Chip RDS decoder support" + depends on VIDEO_DEV && I2C + + help + Support for this Radio Data System (RDS) decoder. This allows + seeing radio station identification transmitted using this + standard. + + To compile this driver as a module, choose M here: the + module will be called saa6588. + +endmenu + +menu "Video decoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_ADV7180 + tristate "Analog Devices ADV7180 decoder" + depends on GPIOLIB && VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_ASYNC + help + Support for the Analog Devices ADV7180 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7180. + +config VIDEO_ADV7183 + tristate "Analog Devices ADV7183 decoder" + depends on VIDEO_DEV && I2C + help + V4l2 subdevice driver for the Analog Devices + ADV7183 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7183. + +config VIDEO_ADV748X + tristate "Analog Devices ADV748x decoder" + depends on VIDEO_DEV && I2C + depends on OF + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select REGMAP_I2C + select V4L2_FWNODE + help + V4L2 subdevice driver for the Analog Devices + ADV7481 and ADV7482 HDMI/Analog video decoders. + + To compile this driver as a module, choose M here: the + module will be called adv748x. + +config VIDEO_ADV7604 + tristate "Analog Devices ADV7604 decoder" + depends on VIDEO_DEV && I2C + depends on GPIOLIB || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select REGMAP_I2C + select HDMI + select V4L2_FWNODE + help + Support for the Analog Devices ADV7604 video decoder. + + This is a Analog Devices Component/Graphics Digitizer + with 4:1 Multiplexed HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called adv7604. + +config VIDEO_ADV7604_CEC + bool "Enable Analog Devices ADV7604 CEC support" + depends on VIDEO_ADV7604 + select CEC_CORE + help + When selected the adv7604 will support the optional + HDMI CEC feature. + +config VIDEO_ADV7842 + tristate "Analog Devices ADV7842 decoder" + depends on VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select HDMI + help + Support for the Analog Devices ADV7842 video decoder. + + This is a Analog Devices Component/Graphics/SD Digitizer + with 2:1 Multiplexed HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called adv7842. + +config VIDEO_ADV7842_CEC + bool "Enable Analog Devices ADV7842 CEC support" + depends on VIDEO_ADV7842 + select CEC_CORE + help + When selected the adv7842 will support the optional + HDMI CEC feature. + +config VIDEO_BT819 + tristate "BT819A VideoStream decoder" + depends on VIDEO_DEV && I2C + help + Support for BT819A video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt819. + +config VIDEO_BT856 + tristate "BT856 VideoStream decoder" + depends on VIDEO_DEV && I2C + help + Support for BT856 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt856. + +config VIDEO_BT866 + tristate "BT866 VideoStream decoder" + depends on VIDEO_DEV && I2C + help + Support for BT866 video decoder. + + To compile this driver as a module, choose M here: the + module will be called bt866. + +config VIDEO_ISL7998X + tristate "Intersil ISL7998x video decoder" + depends on VIDEO_DEV && I2C + depends on OF_GPIO + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + Support for Intersil ISL7998x analog to MIPI-CSI2 or + BT.656 decoder. + +config VIDEO_KS0127 + tristate "KS0127 video decoder" + depends on VIDEO_DEV && I2C + help + Support for KS0127 video decoder. + + This chip is used on AverMedia AVS6EYES Zoran-based MJPEG + cards. + + To compile this driver as a module, choose M here: the + module will be called ks0127. + +config VIDEO_MAX9286 + tristate "Maxim MAX9286 GMSL deserializer support" + depends on I2C && I2C_MUX + depends on VIDEO_DEV + depends on OF_GPIO + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the Maxim MAX9286 GMSL deserializer. + + To compile this driver as a module, choose M here: the + module will be called max9286. + +config VIDEO_ML86V7667 + tristate "OKI ML86V7667 video decoder" + depends on VIDEO_DEV && I2C + help + Support for the OKI Semiconductor ML86V7667 video decoder. + + To compile this driver as a module, choose M here: the + module will be called ml86v7667. + +config VIDEO_SAA7110 + tristate "Philips SAA7110 video decoder" + depends on VIDEO_DEV && I2C + help + Support for the Philips SAA7110 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7110. + +config VIDEO_SAA711X + tristate "Philips SAA7111/3/4/5 video decoders" + depends on VIDEO_DEV && I2C + help + Support for the Philips SAA7111/3/4/5 video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa7115. + +config VIDEO_TC358743 + tristate "Toshiba TC358743 decoder" + depends on VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select HDMI + select V4L2_FWNODE + help + Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. + + To compile this driver as a module, choose M here: the + module will be called tc358743. + +config VIDEO_TC358743_CEC + bool "Enable Toshiba TC358743 CEC support" + depends on VIDEO_TC358743 + select CEC_CORE + help + When selected the tc358743 will support the optional + HDMI CEC feature. + +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_DEV && I2C + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + +config VIDEO_TVP5150 + tristate "Texas Instruments TVP5150 video decoder" + depends on VIDEO_DEV && I2C + select V4L2_FWNODE + select REGMAP_I2C + help + Support for the Texas Instruments TVP5150 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp5150. + +config VIDEO_TVP7002 + tristate "Texas Instruments TVP7002 video decoder" + depends on VIDEO_DEV && I2C + select V4L2_FWNODE + help + Support for the Texas Instruments TVP7002 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp7002. + +config VIDEO_TW2804 + tristate "Techwell TW2804 multiple video decoder" + depends on VIDEO_DEV && I2C + help + Support for the Techwell tw2804 multiple video decoder. + + To compile this driver as a module, choose M here: the + module will be called tw2804. + +config VIDEO_TW9903 + tristate "Techwell TW9903 video decoder" + depends on VIDEO_DEV && I2C + help + Support for the Techwell tw9903 multi-standard video decoder + with high quality down scaler. + + To compile this driver as a module, choose M here: the + module will be called tw9903. + +config VIDEO_TW9906 + tristate "Techwell TW9906 video decoder" + depends on VIDEO_DEV && I2C + help + Support for the Techwell tw9906 enhanced multi-standard comb filter + video decoder with YCbCr input support. + + To compile this driver as a module, choose M here: the + module will be called tw9906. + +config VIDEO_TW9910 + tristate "Techwell TW9910 video decoder" + depends on VIDEO_DEV && I2C + select V4L2_ASYNC + help + Support for Techwell TW9910 NTSC/PAL/SECAM video decoder. + + To compile this driver as a module, choose M here: the + module will be called tw9910. + +config VIDEO_VPX3220 + tristate "vpx3220a, vpx3216b & vpx3214c video decoders" + depends on VIDEO_DEV && I2C + help + Support for VPX322x video decoders. + + To compile this driver as a module, choose M here: the + module will be called vpx3220. + +comment "Video and audio decoders" + +config VIDEO_SAA717X + tristate "Philips SAA7171/3/4 audio/video decoders" + depends on VIDEO_DEV && I2C + help + Support for the Philips SAA7171/3/4 audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa717x. + +source "drivers/media/i2c/cx25840/Kconfig" + +endmenu + +menu "Video encoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_AD9389B + tristate "Analog Devices AD9389B encoder" + depends on VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + + help + Support for the Analog Devices AD9389B video encoder. + + This is a Analog Devices HDMI transmitter. + + To compile this driver as a module, choose M here: the + module will be called ad9389b. + +config VIDEO_ADV7170 + tristate "Analog Devices ADV7170 video encoder" + depends on VIDEO_DEV && I2C + help + Support for the Analog Devices ADV7170 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7170. + +config VIDEO_ADV7175 + tristate "Analog Devices ADV7175 video encoder" + depends on VIDEO_DEV && I2C + help + Support for the Analog Devices ADV7175 video encoder driver + + To compile this driver as a module, choose M here: the + module will be called adv7175. + +config VIDEO_ADV7343 + tristate "ADV7343 video encoder" + depends on I2C + select V4L2_ASYNC + help + Support for Analog Devices I2C bus based ADV7343 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7343. + +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + +config VIDEO_ADV7511 + tristate "Analog Devices ADV7511 encoder" + depends on VIDEO_DEV && I2C + depends on DRM_I2C_ADV7511=n || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select HDMI + help + Support for the Analog Devices ADV7511 video encoder. + + This is a Analog Devices HDMI transmitter. + + To compile this driver as a module, choose M here: the + module will be called adv7511. + +config VIDEO_ADV7511_CEC + bool "Enable Analog Devices ADV7511 CEC support" + depends on VIDEO_ADV7511 + select CEC_CORE + help + When selected the adv7511 will support the optional + HDMI CEC feature. + +config VIDEO_AK881X + tristate "AK8813/AK8814 video encoders" + depends on I2C + help + Video output driver for AKM AK8813 and AK8814 TV encoders + +config VIDEO_SAA7127 + tristate "Philips SAA7127/9 digital video encoders" + depends on VIDEO_DEV && I2C + help + Support for the Philips SAA7127/9 digital video encoders. + + To compile this driver as a module, choose M here: the + module will be called saa7127. + +config VIDEO_SAA7185 + tristate "Philips SAA7185 video encoder" + depends on VIDEO_DEV && I2C + help + Support for the Philips SAA7185 video encoder. + + To compile this driver as a module, choose M here: the + module will be called saa7185. + +config VIDEO_THS8200 + tristate "Texas Instruments THS8200 video encoder" + depends on VIDEO_DEV && I2C + select V4L2_ASYNC + help + Support for the Texas Instruments THS8200 video encoder. + + To compile this driver as a module, choose M here: the + module will be called ths8200. + +endmenu + +menu "Video improvement chips" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_DEV && I2C + select V4L2_ASYNC + help + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_DEV && I2C + help + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. + +endmenu + +menu "Audio/Video compression chips" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_SAA6752HS + tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" + depends on VIDEO_DEV && I2C + select CRC32 + help + Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 + audio encoder with multiplexer. + + To compile this driver as a module, choose M here: the + module will be called saa6752hs. + +endmenu + +menu "SDR tuner chips" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config SDR_MAX2175 + tristate "Maxim 2175 RF to Bits tuner" + depends on VIDEO_DEV && MEDIA_SDR_SUPPORT && I2C + select REGMAP_I2C + select V4L2_ASYNC + help + Support for Maxim 2175 tuner. It is an advanced analog/digital + radio receiver with RF-to-Bits front-end designed for SDR solutions. + + To compile this driver as a module, choose M here; the + module will be called max2175. + +endmenu + +menu "Miscellaneous helper chips" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV + +config VIDEO_I2C + tristate "I2C transport video support" + depends on VIDEO_DEV && I2C + select VIDEOBUF2_VMALLOC + imply HWMON + help + Enable the I2C transport video support which supports the + following: + * Panasonic AMG88xx Grid-Eye Sensors + * Melexis MLX90640 Thermal Cameras + + To compile this driver as a module, choose M here: the + module will be called video-i2c + +config VIDEO_M52790 + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_DEV && I2C + help + Support for the Mitsubishi M52790 A/V switch. + + To compile this driver as a module, choose M here: the + module will be called m52790. + +config VIDEO_ST_MIPID02 + tristate "STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. + It is used to allow usage of CSI-2 sensor with PARALLEL port + controller. + + To compile this driver as a module, choose M here: the + module will be called st-mipid02. + +config VIDEO_THS7303 + tristate "THS7303/53 Video Amplifier" + depends on VIDEO_DEV && I2C + select V4L2_ASYNC + help + Support for TI THS7303/53 video amplifier + + To compile this driver as a module, choose M here: the + module will be called ths7303. + endmenu -endif # VIDEO_V4L2 +endif # VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b01f6cd05ee8..3e1696963e7f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -1,31 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 -msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o -obj-$(CONFIG_VIDEO_CCS) += ccs/ -obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ -obj-$(CONFIG_VIDEO_CX25840) += cx25840/ -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ +msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o -obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o -obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o -obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o -obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o -obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o -obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o -obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o -obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o -obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o -obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o -obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o -obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o -obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o -obj-$(CONFIG_VIDEO_AD5820) += ad5820.o -obj-$(CONFIG_VIDEO_AK7375) += ak7375.o -obj-$(CONFIG_VIDEO_DW9714) += dw9714.o -obj-$(CONFIG_VIDEO_DW9768) += dw9768.o -obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o +obj-$(CONFIG_SDR_MAX2175) += max2175.o +obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o +obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o @@ -33,39 +13,68 @@ obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV748X) += adv748x/ +obj-$(CONFIG_VIDEO_ADV7511) += adv7511-v4l2.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o -obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o -obj-$(CONFIG_VIDEO_ADV7511) += adv7511-v4l2.o -obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o -obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +obj-$(CONFIG_VIDEO_AK7375) += ak7375.o +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o +obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o -obj-$(CONFIG_VIDEO_KS0127) += ks0127.o -obj-$(CONFIG_VIDEO_THS7303) += ths7303.o -obj-$(CONFIG_VIDEO_THS8200) += ths8200.o -obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o -obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o -obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o -obj-$(CONFIG_VIDEO_TW2804) += tw2804.o -obj-$(CONFIG_VIDEO_TW9903) += tw9903.o -obj-$(CONFIG_VIDEO_TW9906) += tw9906.o -obj-$(CONFIG_VIDEO_TW9910) += tw9910.o +obj-$(CONFIG_VIDEO_CCS) += ccs/ +obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o obj-$(CONFIG_VIDEO_CS3308) += cs3308.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o +obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9768) += dw9768.o +obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o +obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ +obj-$(CONFIG_VIDEO_HI556) += hi556.o +obj-$(CONFIG_VIDEO_HI846) += hi846.o +obj-$(CONFIG_VIDEO_HI847) += hi847.o +obj-$(CONFIG_VIDEO_I2C) += video-i2c.o +obj-$(CONFIG_VIDEO_IMX208) += imx208.o +obj-$(CONFIG_VIDEO_IMX214) += imx214.o +obj-$(CONFIG_VIDEO_IMX219) += imx219.o +obj-$(CONFIG_VIDEO_IMX258) += imx258.o +obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX290) += imx290.o +obj-$(CONFIG_VIDEO_IMX319) += imx319.o +obj-$(CONFIG_VIDEO_IMX334) += imx334.o +obj-$(CONFIG_VIDEO_IMX335) += imx335.o +obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_IMX412) += imx412.o +obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o +obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +obj-$(CONFIG_VIDEO_LM3560) += lm3560.o +obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_M52790) += m52790.o -obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o -obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o -obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -obj-$(CONFIG_VIDEO_WM8739) += wm8739.o -obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o -obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o -obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o -obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ +obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o +obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o +obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o +obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o +obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o +obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o +obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o +obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o +obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o +obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o +obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o +obj-$(CONFIG_VIDEO_OV13858) += ov13858.o +obj-$(CONFIG_VIDEO_OV13B10) += ov13b10.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o @@ -89,51 +98,46 @@ obj-$(CONFIG_VIDEO_OV9282) += ov9282.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV9734) += ov9734.o -obj-$(CONFIG_VIDEO_OV13858) += ov13858.o -obj-$(CONFIG_VIDEO_OV13B10) += ov13b10.o -obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o -obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o -obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o -obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o -obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o -obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o -obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o -obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o -obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o -obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o -obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o -obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o -obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o -obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o -obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o -obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o -obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ -obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o -obj-$(CONFIG_VIDEO_LM3560) += lm3560.o -obj-$(CONFIG_VIDEO_LM3646) += lm3646.o -obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o -obj-$(CONFIG_VIDEO_AK881X) += ak881x.o -obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o -obj-$(CONFIG_VIDEO_I2C) += video-i2c.o -obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o -obj-$(CONFIG_VIDEO_OV2659) += ov2659.o -obj-$(CONFIG_VIDEO_TC358743) += tc358743.o -obj-$(CONFIG_VIDEO_HI556) += hi556.o -obj-$(CONFIG_VIDEO_HI846) += hi846.o -obj-$(CONFIG_VIDEO_IMX208) += imx208.o -obj-$(CONFIG_VIDEO_IMX214) += imx214.o -obj-$(CONFIG_VIDEO_IMX219) += imx219.o -obj-$(CONFIG_VIDEO_IMX258) += imx258.o -obj-$(CONFIG_VIDEO_IMX274) += imx274.o -obj-$(CONFIG_VIDEO_IMX290) += imx290.o -obj-$(CONFIG_VIDEO_IMX319) += imx319.o -obj-$(CONFIG_VIDEO_IMX334) += imx334.o -obj-$(CONFIG_VIDEO_IMX335) += imx335.o -obj-$(CONFIG_VIDEO_IMX355) += imx355.o -obj-$(CONFIG_VIDEO_IMX412) += imx412.o -obj-$(CONFIG_VIDEO_MAX9286) += max9286.o -obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o -obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o -obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o +obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o +obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o +obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ +obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o +obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o +obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o +obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o +obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o +obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o +obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o +obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o +obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o +obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o -obj-$(CONFIG_SDR_MAX2175) += max2175.o +obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o +obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o +obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o +obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +obj-$(CONFIG_VIDEO_THS7303) += ths7303.o +obj-$(CONFIG_VIDEO_THS8200) += ths8200.o +obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +obj-$(CONFIG_VIDEO_TW2804) += tw2804.o +obj-$(CONFIG_VIDEO_TW9903) += tw9903.o +obj-$(CONFIG_VIDEO_TW9906) += tw9906.o +obj-$(CONFIG_VIDEO_TW9910) += tw9910.o +obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +obj-$(CONFIG_VIDEO_WM8775) += wm8775.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index d9a99fcfacb1..4f5db195e66d 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -784,16 +784,16 @@ static int adv7180_get_mbus_config(struct v4l2_subdev *sd, if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_1_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + cfg->bus.mipi_csi2.num_data_lanes = 1; + cfg->bus.mipi_csi2.flags = 0; } else { /* * The ADV7180 sensor supports BT.601/656 output modes. * The BT.656 is default and not yet configurable by s/w. */ - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER | + V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_BT656; } diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 92cafdea3f1f..ba746a19fd39 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -7,7 +7,7 @@ #include <linux/delay.h> #include <linux/errno.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> @@ -28,8 +28,8 @@ struct adv7183 { v4l2_std_id std; /* Current set standard */ u32 input; u32 output; - unsigned reset_pin; - unsigned oe_pin; + struct gpio_desc *reset_pin; + struct gpio_desc *oe_pin; struct v4l2_mbus_framefmt fmt; }; @@ -465,9 +465,9 @@ static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) struct adv7183 *decoder = to_adv7183(sd); if (enable) - gpio_set_value(decoder->oe_pin, 0); + gpiod_set_value(decoder->oe_pin, 1); else - gpio_set_value(decoder->oe_pin, 1); + gpiod_set_value(decoder->oe_pin, 0); udelay(1); return 0; } @@ -531,7 +531,6 @@ static int adv7183_probe(struct i2c_client *client, struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - const unsigned *pin_array; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -540,29 +539,28 @@ static int adv7183_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - pin_array = client->dev.platform_data; - if (pin_array == NULL) - return -EINVAL; - decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL); if (decoder == NULL) return -ENOMEM; - decoder->reset_pin = pin_array[0]; - decoder->oe_pin = pin_array[1]; - - if (devm_gpio_request_one(&client->dev, decoder->reset_pin, - GPIOF_OUT_INIT_LOW, "ADV7183 Reset")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); - return -EBUSY; - } - - if (devm_gpio_request_one(&client->dev, decoder->oe_pin, - GPIOF_OUT_INIT_HIGH, - "ADV7183 Output Enable")) { - v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); - return -EBUSY; - } + /* + * Requesting high will assert reset, the line should be + * flagged as active low in descriptor table or machine description. + */ + decoder->reset_pin = devm_gpiod_get(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(decoder->reset_pin)) + return PTR_ERR(decoder->reset_pin); + gpiod_set_consumer_name(decoder->reset_pin, "ADV7183 Reset"); + /* + * Requesting low will start with output disabled, the line should be + * flagged as active low in descriptor table or machine description. + */ + decoder->oe_pin = devm_gpiod_get(&client->dev, "oe", + GPIOD_OUT_LOW); + if (IS_ERR(decoder->oe_pin)) + return PTR_ERR(decoder->oe_pin); + gpiod_set_consumer_name(decoder->reset_pin, "ADV7183 Output Enable"); sd = &decoder->sd; v4l2_i2c_subdev_init(sd, client, &adv7183_ops); @@ -594,7 +592,8 @@ static int adv7183_probe(struct i2c_client *client, /* reset chip */ /* reset pulse width at least 5ms */ mdelay(10); - gpio_set_value(decoder->reset_pin, 1); + /* De-assert reset line (descriptor tagged active low) */ + gpiod_set_value(decoder->reset_pin, 0); /* wait 5ms before any further i2c writes are performed */ mdelay(5); diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 589e9644fcdc..bd4f3fe0e309 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -222,23 +222,7 @@ static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad return -EINVAL; config->type = V4L2_MBUS_CSI2_DPHY; - switch (tx->active_lanes) { - case 1: - config->flags = V4L2_MBUS_CSI2_1_LANE; - break; - - case 2: - config->flags = V4L2_MBUS_CSI2_2_LANE; - break; - - case 3: - config->flags = V4L2_MBUS_CSI2_3_LANE; - break; - - case 4: - config->flags = V4L2_MBUS_CSI2_4_LANE; - break; - } + config->bus.mipi_csi2.num_data_lanes = tx->active_lanes; return 0; } diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 8e13cae40ec5..202e0cd83f90 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -17,7 +17,6 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> -#include <linux/gpio.h> #include <linux/workqueue.h> #include <linux/hdmi.h> #include <linux/v4l2-dv-timings.h> @@ -522,7 +521,7 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_ buffer[3] = 0; buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); - if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, len + 4) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index a2fa408d2d9f..bb0c8fc6d383 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2484,7 +2484,7 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, buffer[i + 3] = infoframe_read(sd, adv76xx_cri[index].payload_addr + i); - if (hdmi_infoframe_unpack(frame, buffer, sizeof(buffer)) < 0) { + if (hdmi_infoframe_unpack(frame, buffer, len + 3) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, adv76xx_cri[index].desc); return -ENOENT; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 9d6eed0f8281..22caa070273b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2583,7 +2583,7 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7842_cfg_read_ for (i = 0; i < len; i++) buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, len + 3) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig index 59f35b33ddc1..71671db3d993 100644 --- a/drivers/media/i2c/ccs/Kconfig +++ b/drivers/media/i2c/ccs/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CCS tristate "MIPI CCS/SMIA++/SMIA sensor support" - depends on I2C && VIDEO_V4L2 && HAVE_CLK + depends on I2C && VIDEO_DEV && HAVE_CLK select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEO_CCS_PLL diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 9158d3ce45c0..03e841b8443f 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -17,7 +17,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/pm_runtime.h> diff --git a/drivers/media/i2c/cx25840/Kconfig b/drivers/media/i2c/cx25840/Kconfig index e392f8e023f6..46f15702cf55 100644 --- a/drivers/media/i2c/cx25840/Kconfig +++ b/drivers/media/i2c/cx25840/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CX25840 tristate "Conexant CX2584x audio/video decoders" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C help Support for the Conexant CX2584x audio/video decoders. diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 3863dfeb8293..cd7008ad8f2f 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -5,6 +5,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -36,6 +37,7 @@ struct dw9714_device { struct v4l2_ctrl_handler ctrls_vcm; struct v4l2_subdev sd; u16 current_val; + struct regulator *vcc; }; static inline struct dw9714_device *to_dw9714_vcm(struct v4l2_ctrl *ctrl) @@ -145,6 +147,16 @@ static int dw9714_probe(struct i2c_client *client) if (dw9714_dev == NULL) return -ENOMEM; + dw9714_dev->vcc = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(dw9714_dev->vcc)) + return PTR_ERR(dw9714_dev->vcc); + + rval = regulator_enable(dw9714_dev->vcc); + if (rval < 0) { + dev_err(&client->dev, "failed to enable vcc: %d\n", rval); + return rval; + } + v4l2_i2c_subdev_init(&dw9714_dev->sd, client, &dw9714_ops); dw9714_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; @@ -181,8 +193,18 @@ static int dw9714_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd); + int ret; pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + ret = regulator_disable(dw9714_dev->vcc); + if (ret) { + dev_err(&client->dev, + "Failed to disable vcc: %d\n", ret); + return ret; + } + } + pm_runtime_set_suspended(&client->dev); dw9714_subdev_cleanup(dw9714_dev); return 0; @@ -200,6 +222,9 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev) struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd); int ret, val; + if (pm_runtime_suspended(&client->dev)) + return 0; + for (val = dw9714_dev->current_val & ~(DW9714_CTRL_STEPS - 1); val >= 0; val -= DW9714_CTRL_STEPS) { ret = dw9714_i2c_write(client, @@ -208,7 +233,12 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev) dev_err_once(dev, "%s I2C failure: %d", __func__, ret); usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10); } - return 0; + + ret = regulator_disable(dw9714_dev->vcc); + if (ret) + dev_err(dev, "Failed to disable vcc: %d\n", ret); + + return ret; } /* @@ -224,6 +254,16 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd); int ret, val; + if (pm_runtime_suspended(&client->dev)) + return 0; + + ret = regulator_enable(dw9714_dev->vcc); + if (ret) { + dev_err(dev, "Failed to enable vcc: %d\n", ret); + return ret; + } + usleep_range(1000, 2000); + for (val = dw9714_dev->current_val % DW9714_CTRL_STEPS; val < dw9714_dev->current_val + DW9714_CTRL_STEPS - 1; val += DW9714_CTRL_STEPS) { diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig index afcc4ea764f6..398dd4d21df1 100644 --- a/drivers/media/i2c/et8ek8/Kconfig +++ b/drivers/media/i2c/et8ek8/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_ET8EK8 tristate "ET8EK8 camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c new file mode 100644 index 000000000000..7e85349e1852 --- /dev/null +++ b/drivers/media/i2c/hi847.c @@ -0,0 +1,3012 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define HI847_REG_VALUE_08BIT 1 +#define HI847_REG_VALUE_16BIT 2 +#define HI847_REG_VALUE_24BIT 3 + +#define HI847_LINK_FREQ_400MHZ 400000000ULL +#define HI847_LINK_FREQ_200MHZ 200000000ULL +#define HI847_SCLK 72000000ULL +#define HI847_MCLK 19200000 +#define HI847_DATA_LANES 4 +#define HI847_RGB_DEPTH 10 + +#define HI847_REG_CHIP_ID 0x0716 +#define HI847_CHIP_ID 0x0847 + +#define HI847_REG_MODE_SELECT 0x0B00 +#define HI847_MODE_STANDBY 0x0000 +#define HI847_MODE_STREAMING 0x0100 + +#define HI847_REG_MODE_TG 0x027E +#define HI847_REG_MODE_TG_ENABLE 0x0100 +#define HI847_REG_MODE_TG_DISABLE 0x0000 + +/* vertical-timings from sensor */ +#define HI847_REG_FLL 0x020E +#define HI847_FLL_30FPS 0x0B51 +#define HI847_FLL_30FPS_MIN 0x0B51 +#define HI847_FLL_60FPS 0x05A9 +#define HI847_FLL_60FPS_MIN 0x05A9 +#define HI847_FLL_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define HI847_REG_LLP 0x0206 + +/* Exposure controls from sensor */ +#define HI847_REG_EXPOSURE 0x020A +#define HI847_EXPOSURE_MIN 4 +#define HI847_EXPOSURE_MAX_MARGIN 4 +#define HI847_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define HI847_REG_ANALOG_GAIN 0x0212 +#define HI847_ANAL_GAIN_MIN 0 +#define HI847_ANAL_GAIN_MAX 240 +#define HI847_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define HI847_REG_MWB_GR_GAIN 0x0214 +#define HI847_REG_MWB_GB_GAIN 0x0216 +#define HI847_REG_MWB_R_GAIN 0x0218 +#define HI847_REG_MWB_B_GAIN 0x021A +#define HI847_DGTL_GAIN_MIN 1 +#define HI847_DGTL_GAIN_MAX 8191 +#define HI847_DGTL_GAIN_STEP 1 +#define HI847_DGTL_GAIN_DEFAULT 512 + +/* Test Pattern Control */ +#define HI847_REG_ISP 0X0B04 +#define HI847_REG_ISP_TPG_EN 0x0001 +#define HI847_REG_TEST_PATTERN 0x0C0A + +/* Flip Mirror Controls from sensor */ +#define HI847_REG_MIRROR_FLIP 0x0202 + +#define HI847_REG_FORMAT_X 0x0F04 +#define HI847_REG_FORMAT_Y 0x0F06 + +enum { + HI847_LINK_FREQ_400MHZ_INDEX, + HI847_LINK_FREQ_200MHZ_INDEX, +}; + +struct hi847_reg { + u16 address; + u16 val; +}; + +struct hi847_reg_list { + u32 num_of_regs; + const struct hi847_reg *regs; +}; + +struct hi847_link_freq_config { + const struct hi847_reg_list reg_list; +}; + +struct hi847_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 llp; + + /* Default vertical timining size */ + u32 fll_def; + + /* Min vertical timining size */ + u32 fll_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct hi847_reg_list reg_list; +}; + +#define to_hi847(_sd) container_of(_sd, struct hi847, sd) + +//SENSOR_INITIALIZATION +static const struct hi847_reg mipi_data_rate_lane_4[] = { + {0x0790, 0x0100}, + {0x2000, 0x0000}, + {0x2002, 0x0058}, + {0x2006, 0x40B2}, + {0x2008, 0xB05C}, + {0x200A, 0x8446}, + {0x200C, 0x40B2}, + {0x200E, 0xB082}, + {0x2010, 0x8450}, + {0x2012, 0x40B2}, + {0x2014, 0xB0AE}, + {0x2016, 0x84C6}, + {0x2018, 0x40B2}, + {0x201A, 0xB11A}, + {0x201C, 0x84BC}, + {0x201E, 0x40B2}, + {0x2020, 0xB34A}, + {0x2022, 0x84B4}, + {0x2024, 0x40B2}, + {0x2026, 0xB386}, + {0x2028, 0x84B0}, + {0x202A, 0x40B2}, + {0x202C, 0xB3B4}, + {0x202E, 0x84B8}, + {0x2030, 0x40B2}, + {0x2032, 0xB0F4}, + {0x2034, 0x8470}, + {0x2036, 0x40B2}, + {0x2038, 0xB3EA}, + {0x203A, 0x847C}, + {0x203C, 0x40B2}, + {0x203E, 0xB658}, + {0x2040, 0x8478}, + {0x2042, 0x40B2}, + {0x2044, 0xB67E}, + {0x2046, 0x847E}, + {0x2048, 0x40B2}, + {0x204A, 0xB78E}, + {0x204C, 0x843A}, + {0x204E, 0x40B2}, + {0x2050, 0xB980}, + {0x2052, 0x845C}, + {0x2054, 0x40B2}, + {0x2056, 0xB9B0}, + {0x2058, 0x845E}, + {0x205A, 0x4130}, + {0x205C, 0x1292}, + {0x205E, 0xD016}, + {0x2060, 0xB3D2}, + {0x2062, 0x0B00}, + {0x2064, 0x2002}, + {0x2066, 0xD2E2}, + {0x2068, 0x0381}, + {0x206A, 0x93C2}, + {0x206C, 0x0263}, + {0x206E, 0x2001}, + {0x2070, 0x4130}, + {0x2072, 0x422D}, + {0x2074, 0x403E}, + {0x2076, 0x888E}, + {0x2078, 0x403F}, + {0x207A, 0x192A}, + {0x207C, 0x1292}, + {0x207E, 0x843E}, + {0x2080, 0x3FF7}, + {0x2082, 0x422D}, + {0x2084, 0x403E}, + {0x2086, 0x192A}, + {0x2088, 0x403F}, + {0x208A, 0x888E}, + {0x208C, 0x1292}, + {0x208E, 0x843E}, + {0x2090, 0xB3D2}, + {0x2092, 0x0267}, + {0x2094, 0x2403}, + {0x2096, 0xD0F2}, + {0x2098, 0x0040}, + {0x209A, 0x0381}, + {0x209C, 0x90F2}, + {0x209E, 0x0010}, + {0x20A0, 0x0260}, + {0x20A2, 0x2002}, + {0x20A4, 0x1292}, + {0x20A6, 0x84BC}, + {0x20A8, 0x1292}, + {0x20AA, 0xD020}, + {0x20AC, 0x4130}, + {0x20AE, 0x1292}, + {0x20B0, 0x8470}, + {0x20B2, 0x1292}, + {0x20B4, 0x8452}, + {0x20B6, 0x0900}, + {0x20B8, 0x7118}, + {0x20BA, 0x1292}, + {0x20BC, 0x848E}, + {0x20BE, 0x0900}, + {0x20C0, 0x7112}, + {0x20C2, 0x0800}, + {0x20C4, 0x7A20}, + {0x20C6, 0x4292}, + {0x20C8, 0x87DE}, + {0x20CA, 0x7334}, + {0x20CC, 0x0F00}, + {0x20CE, 0x7304}, + {0x20D0, 0x421F}, + {0x20D2, 0x8718}, + {0x20D4, 0x1292}, + {0x20D6, 0x846E}, + {0x20D8, 0x1292}, + {0x20DA, 0x8488}, + {0x20DC, 0x0B00}, + {0x20DE, 0x7114}, + {0x20E0, 0x0002}, + {0x20E2, 0x1292}, + {0x20E4, 0x848C}, + {0x20E6, 0x1292}, + {0x20E8, 0x8454}, + {0x20EA, 0x43C2}, + {0x20EC, 0x86EE}, + {0x20EE, 0x1292}, + {0x20F0, 0x8444}, + {0x20F2, 0x4130}, + {0x20F4, 0x4392}, + {0x20F6, 0x7360}, + {0x20F8, 0xB3D2}, + {0x20FA, 0x0B00}, + {0x20FC, 0x2402}, + {0x20FE, 0xC2E2}, + {0x2100, 0x0381}, + {0x2102, 0x0900}, + {0x2104, 0x732C}, + {0x2106, 0x4382}, + {0x2108, 0x7360}, + {0x210A, 0x422D}, + {0x210C, 0x403E}, + {0x210E, 0x87F0}, + {0x2110, 0x403F}, + {0x2112, 0x87E8}, + {0x2114, 0x1292}, + {0x2116, 0x843E}, + {0x2118, 0x4130}, + {0x211A, 0x120B}, + {0x211C, 0x120A}, + {0x211E, 0x4392}, + {0x2120, 0x87FA}, + {0x2122, 0x4392}, + {0x2124, 0x760E}, + {0x2126, 0x0900}, + {0x2128, 0x760C}, + {0x212A, 0x421B}, + {0x212C, 0x760A}, + {0x212E, 0x903B}, + {0x2130, 0x0201}, + {0x2132, 0x2408}, + {0x2134, 0x903B}, + {0x2136, 0x0102}, + {0x2138, 0x2405}, + {0x213A, 0x4292}, + {0x213C, 0x030A}, + {0x213E, 0x87F8}, + {0x2140, 0x1292}, + {0x2142, 0x849A}, + {0x2144, 0x903B}, + {0x2146, 0x0020}, + {0x2148, 0x2010}, + {0x214A, 0x403B}, + {0x214C, 0x8498}, + {0x214E, 0x422F}, + {0x2150, 0x12AB}, + {0x2152, 0x403F}, + {0x2154, 0x0028}, + {0x2156, 0x12AB}, + {0x2158, 0x403B}, + {0x215A, 0x84C4}, + {0x215C, 0x407F}, + {0x215E, 0xFFAA}, + {0x2160, 0x12AB}, + {0x2162, 0x407F}, + {0x2164, 0x0055}, + {0x2166, 0x12AB}, + {0x2168, 0x3FDC}, + {0x216A, 0x903B}, + {0x216C, 0x0021}, + {0x216E, 0x2890}, + {0x2170, 0x903B}, + {0x2172, 0x0100}, + {0x2174, 0x200D}, + {0x2176, 0x403F}, + {0x2178, 0x0028}, + {0x217A, 0x1292}, + {0x217C, 0x8498}, + {0x217E, 0x425F}, + {0x2180, 0x0306}, + {0x2182, 0x1292}, + {0x2184, 0x84C4}, + {0x2186, 0x4FC2}, + {0x2188, 0x0318}, + {0x218A, 0x0261}, + {0x218C, 0x0000}, + {0x218E, 0x3FC9}, + {0x2190, 0x903B}, + {0x2192, 0x0101}, + {0x2194, 0x2858}, + {0x2196, 0x903B}, + {0x2198, 0x0200}, + {0x219A, 0x2450}, + {0x219C, 0x903B}, + {0x219E, 0x0201}, + {0x21A0, 0x2C47}, + {0x21A2, 0x903B}, + {0x21A4, 0x0102}, + {0x21A6, 0x2041}, + {0x21A8, 0x93E2}, + {0x21AA, 0x0262}, + {0x21AC, 0x240A}, + {0x21AE, 0x425F}, + {0x21B0, 0x0306}, + {0x21B2, 0x1292}, + {0x21B4, 0x84C4}, + {0x21B6, 0x4F4E}, + {0x21B8, 0x4EC2}, + {0x21BA, 0x0318}, + {0x21BC, 0x0260}, + {0x21BE, 0x0000}, + {0x21C0, 0x3FB0}, + {0x21C2, 0x403A}, + {0x21C4, 0x8030}, + {0x21C6, 0x4382}, + {0x21C8, 0x0326}, + {0x21CA, 0x4382}, + {0x21CC, 0x0328}, + {0x21CE, 0x421B}, + {0x21D0, 0x030C}, + {0x21D2, 0x930B}, + {0x21D4, 0x2420}, + {0x21D6, 0x4A5F}, + {0x21D8, 0x0001}, + {0x21DA, 0x1292}, + {0x21DC, 0x84C4}, + {0x21DE, 0x4F4E}, + {0x21E0, 0x4A5F}, + {0x21E2, 0x0001}, + {0x21E4, 0x9F0E}, + {0x21E6, 0x2402}, + {0x21E8, 0x5392}, + {0x21EA, 0x0326}, + {0x21EC, 0x4ECA}, + {0x21EE, 0x0001}, + {0x21F0, 0x533B}, + {0x21F2, 0x2411}, + {0x21F4, 0x4A6F}, + {0x21F6, 0x1292}, + {0x21F8, 0x84C4}, + {0x21FA, 0x4F4E}, + {0x21FC, 0x4A6F}, + {0x21FE, 0x9F0E}, + {0x2200, 0x2402}, + {0x2202, 0x5392}, + {0x2204, 0x0326}, + {0x2206, 0x4ECA}, + {0x2208, 0x0000}, + {0x220A, 0x533B}, + {0x220C, 0x532A}, + {0x220E, 0x0260}, + {0x2210, 0x0000}, + {0x2212, 0x930B}, + {0x2214, 0x23E0}, + {0x2216, 0x40B2}, + {0x2218, 0xAA55}, + {0x221A, 0x0328}, + {0x221C, 0xB0F2}, + {0x221E, 0x0040}, + {0x2220, 0x0381}, + {0x2222, 0x277F}, + {0x2224, 0xD3D2}, + {0x2226, 0x0267}, + {0x2228, 0x3F7C}, + {0x222A, 0x0261}, + {0x222C, 0x0000}, + {0x222E, 0x3F79}, + {0x2230, 0x903B}, + {0x2232, 0x0201}, + {0x2234, 0x23FA}, + {0x2236, 0x1292}, + {0x2238, 0x84C0}, + {0x223A, 0x3F73}, + {0x223C, 0x1292}, + {0x223E, 0x84C0}, + {0x2240, 0x0261}, + {0x2242, 0x0000}, + {0x2244, 0x3F6E}, + {0x2246, 0x903B}, + {0x2248, 0x0040}, + {0x224A, 0x2018}, + {0x224C, 0x422F}, + {0x224E, 0x1292}, + {0x2250, 0x8498}, + {0x2252, 0x12B0}, + {0x2254, 0xF0EA}, + {0x2256, 0x907F}, + {0x2258, 0xFFAA}, + {0x225A, 0x240D}, + {0x225C, 0x5392}, + {0x225E, 0x0312}, + {0x2260, 0x12B0}, + {0x2262, 0xF0EA}, + {0x2264, 0x907F}, + {0x2266, 0x0055}, + {0x2268, 0x2403}, + {0x226A, 0x5392}, + {0x226C, 0x0312}, + {0x226E, 0x3F59}, + {0x2270, 0x5392}, + {0x2272, 0x0310}, + {0x2274, 0x3F56}, + {0x2276, 0x5392}, + {0x2278, 0x0310}, + {0x227A, 0x3FF2}, + {0x227C, 0x903B}, + {0x227E, 0x0080}, + {0x2280, 0x23D4}, + {0x2282, 0x4382}, + {0x2284, 0x0312}, + {0x2286, 0x4382}, + {0x2288, 0x0310}, + {0x228A, 0x0261}, + {0x228C, 0x0000}, + {0x228E, 0x3F49}, + {0x2290, 0x932B}, + {0x2292, 0x2005}, + {0x2294, 0x403F}, + {0x2296, 0x0028}, + {0x2298, 0x1292}, + {0x229A, 0x8498}, + {0x229C, 0x3F42}, + {0x229E, 0x903B}, + {0x22A0, 0x0003}, + {0x22A2, 0x284B}, + {0x22A4, 0x923B}, + {0x22A6, 0x2015}, + {0x22A8, 0x403F}, + {0x22AA, 0x0023}, + {0x22AC, 0x1292}, + {0x22AE, 0x8498}, + {0x22B0, 0x421B}, + {0x22B2, 0x87F8}, + {0x22B4, 0x421F}, + {0x22B6, 0x030C}, + {0x22B8, 0x9F0B}, + {0x22BA, 0x2F33}, + {0x22BC, 0x1292}, + {0x22BE, 0x84BA}, + {0x22C0, 0x930F}, + {0x22C2, 0x2004}, + {0x22C4, 0x5392}, + {0x22C6, 0x0312}, + {0x22C8, 0x531B}, + {0x22CA, 0x3FF4}, + {0x22CC, 0x5392}, + {0x22CE, 0x0310}, + {0x22D0, 0x3FFB}, + {0x22D2, 0x903B}, + {0x22D4, 0x0009}, + {0x22D6, 0x2818}, + {0x22D8, 0x903B}, + {0x22DA, 0x0010}, + {0x22DC, 0x23A6}, + {0x22DE, 0x403F}, + {0x22E0, 0x0027}, + {0x22E2, 0x1292}, + {0x22E4, 0x8498}, + {0x22E6, 0x421B}, + {0x22E8, 0x87F8}, + {0x22EA, 0x421F}, + {0x22EC, 0x030C}, + {0x22EE, 0x9F0B}, + {0x22F0, 0x2F18}, + {0x22F2, 0x1292}, + {0x22F4, 0x84BA}, + {0x22F6, 0x930F}, + {0x22F8, 0x2004}, + {0x22FA, 0x5392}, + {0x22FC, 0x0312}, + {0x22FE, 0x531B}, + {0x2300, 0x3FF4}, + {0x2302, 0x5392}, + {0x2304, 0x0310}, + {0x2306, 0x3FFB}, + {0x2308, 0x922B}, + {0x230A, 0x238F}, + {0x230C, 0x421B}, + {0x230E, 0x87F8}, + {0x2310, 0x421F}, + {0x2312, 0x030C}, + {0x2314, 0x9F0B}, + {0x2316, 0x2C0B}, + {0x2318, 0x1292}, + {0x231A, 0x84C2}, + {0x231C, 0x934F}, + {0x231E, 0x240A}, + {0x2320, 0x5392}, + {0x2322, 0x0312}, + {0x2324, 0x531B}, + {0x2326, 0x421F}, + {0x2328, 0x030C}, + {0x232A, 0x9F0B}, + {0x232C, 0x2BF5}, + {0x232E, 0x0261}, + {0x2330, 0x0000}, + {0x2332, 0x3EF7}, + {0x2334, 0x5392}, + {0x2336, 0x0310}, + {0x2338, 0x3FF5}, + {0x233A, 0x930B}, + {0x233C, 0x277F}, + {0x233E, 0x931B}, + {0x2340, 0x277A}, + {0x2342, 0x3F73}, + {0x2344, 0x413A}, + {0x2346, 0x413B}, + {0x2348, 0x4130}, + {0x234A, 0x4F0C}, + {0x234C, 0x403F}, + {0x234E, 0x0267}, + {0x2350, 0xF0FF}, + {0x2352, 0xFFDF}, + {0x2354, 0x0000}, + {0x2356, 0xF0FF}, + {0x2358, 0xFFEF}, + {0x235A, 0x0000}, + {0x235C, 0x421D}, + {0x235E, 0x84B0}, + {0x2360, 0x403E}, + {0x2362, 0x06F9}, + {0x2364, 0x4C0F}, + {0x2366, 0x1292}, + {0x2368, 0x84AC}, + {0x236A, 0x4F4E}, + {0x236C, 0xB31E}, + {0x236E, 0x2403}, + {0x2370, 0xD0F2}, + {0x2372, 0x0020}, + {0x2374, 0x0267}, + {0x2376, 0xB32E}, + {0x2378, 0x2403}, + {0x237A, 0xD0F2}, + {0x237C, 0x0010}, + {0x237E, 0x0267}, + {0x2380, 0xC3E2}, + {0x2382, 0x0267}, + {0x2384, 0x4130}, + {0x2386, 0x120B}, + {0x2388, 0x120A}, + {0x238A, 0x403A}, + {0x238C, 0x1140}, + {0x238E, 0x1292}, + {0x2390, 0xD080}, + {0x2392, 0x430B}, + {0x2394, 0x4A0F}, + {0x2396, 0x532A}, + {0x2398, 0x1292}, + {0x239A, 0x84A4}, + {0x239C, 0x4F0E}, + {0x239E, 0x430F}, + {0x23A0, 0x5E82}, + {0x23A2, 0x87FC}, + {0x23A4, 0x6F82}, + {0x23A6, 0x87FE}, + {0x23A8, 0x531B}, + {0x23AA, 0x923B}, + {0x23AC, 0x2BF3}, + {0x23AE, 0x413A}, + {0x23B0, 0x413B}, + {0x23B2, 0x4130}, + {0x23B4, 0xF0F2}, + {0x23B6, 0x007F}, + {0x23B8, 0x0267}, + {0x23BA, 0x421D}, + {0x23BC, 0x84B6}, + {0x23BE, 0x403E}, + {0x23C0, 0x01F9}, + {0x23C2, 0x1292}, + {0x23C4, 0x84AC}, + {0x23C6, 0x4F4E}, + {0x23C8, 0xF35F}, + {0x23CA, 0x2403}, + {0x23CC, 0xD0F2}, + {0x23CE, 0xFF80}, + {0x23D0, 0x0267}, + {0x23D2, 0xB36E}, + {0x23D4, 0x2404}, + {0x23D6, 0xD0F2}, + {0x23D8, 0x0040}, + {0x23DA, 0x0267}, + {0x23DC, 0x3C03}, + {0x23DE, 0xF0F2}, + {0x23E0, 0xFFBF}, + {0x23E2, 0x0267}, + {0x23E4, 0xC2E2}, + {0x23E6, 0x0267}, + {0x23E8, 0x4130}, + {0x23EA, 0x120B}, + {0x23EC, 0x120A}, + {0x23EE, 0x8231}, + {0x23F0, 0x430B}, + {0x23F2, 0x93C2}, + {0x23F4, 0x0C0A}, + {0x23F6, 0x2404}, + {0x23F8, 0xB3D2}, + {0x23FA, 0x0B05}, + {0x23FC, 0x2401}, + {0x23FE, 0x431B}, + {0x2400, 0x422D}, + {0x2402, 0x403E}, + {0x2404, 0x192A}, + {0x2406, 0x403F}, + {0x2408, 0x888E}, + {0x240A, 0x1292}, + {0x240C, 0x843E}, + {0x240E, 0x930B}, + {0x2410, 0x20F4}, + {0x2412, 0x93E2}, + {0x2414, 0x0241}, + {0x2416, 0x24EB}, + {0x2418, 0x403A}, + {0x241A, 0x0292}, + {0x241C, 0x4AA2}, + {0x241E, 0x0A00}, + {0x2420, 0xB2E2}, + {0x2422, 0x0361}, + {0x2424, 0x2405}, + {0x2426, 0x4A2F}, + {0x2428, 0x1292}, + {0x242A, 0x8474}, + {0x242C, 0x4F82}, + {0x242E, 0x0A1C}, + {0x2430, 0x93C2}, + {0x2432, 0x0360}, + {0x2434, 0x34CD}, + {0x2436, 0x430C}, + {0x2438, 0x4C0F}, + {0x243A, 0x5F0F}, + {0x243C, 0x4F0D}, + {0x243E, 0x510D}, + {0x2440, 0x4F0E}, + {0x2442, 0x5A0E}, + {0x2444, 0x4E1E}, + {0x2446, 0x0002}, + {0x2448, 0x4F1F}, + {0x244A, 0x192A}, + {0x244C, 0x1202}, + {0x244E, 0xC232}, + {0x2450, 0x4303}, + {0x2452, 0x4E82}, + {0x2454, 0x0130}, + {0x2456, 0x4F82}, + {0x2458, 0x0138}, + {0x245A, 0x421E}, + {0x245C, 0x013A}, + {0x245E, 0x421F}, + {0x2460, 0x013C}, + {0x2462, 0x4132}, + {0x2464, 0x108E}, + {0x2466, 0x108F}, + {0x2468, 0xEF4E}, + {0x246A, 0xEF0E}, + {0x246C, 0xF37F}, + {0x246E, 0xC312}, + {0x2470, 0x100F}, + {0x2472, 0x100E}, + {0x2474, 0x4E8D}, + {0x2476, 0x0000}, + {0x2478, 0x531C}, + {0x247A, 0x922C}, + {0x247C, 0x2BDD}, + {0x247E, 0xB3D2}, + {0x2480, 0x1921}, + {0x2482, 0x2403}, + {0x2484, 0x410F}, + {0x2486, 0x1292}, + {0x2488, 0x847E}, + {0x248A, 0x403B}, + {0x248C, 0x843E}, + {0x248E, 0x422D}, + {0x2490, 0x410E}, + {0x2492, 0x403F}, + {0x2494, 0x1908}, + {0x2496, 0x12AB}, + {0x2498, 0x403D}, + {0x249A, 0x0005}, + {0x249C, 0x403E}, + {0x249E, 0x0292}, + {0x24A0, 0x403F}, + {0x24A2, 0x86E4}, + {0x24A4, 0x12AB}, + {0x24A6, 0x421F}, + {0x24A8, 0x060E}, + {0x24AA, 0x9F82}, + {0x24AC, 0x8720}, + {0x24AE, 0x288D}, + {0x24B0, 0x9382}, + {0x24B2, 0x060E}, + {0x24B4, 0x248A}, + {0x24B6, 0x90BA}, + {0x24B8, 0x0010}, + {0x24BA, 0x0000}, + {0x24BC, 0x2C0B}, + {0x24BE, 0x93C2}, + {0x24C0, 0x86EE}, + {0x24C2, 0x2008}, + {0x24C4, 0x403F}, + {0x24C6, 0x06A7}, + {0x24C8, 0xD0FF}, + {0x24CA, 0x0007}, + {0x24CC, 0x0000}, + {0x24CE, 0xF0FF}, + {0x24D0, 0xFFF8}, + {0x24D2, 0x0000}, + {0x24D4, 0x4392}, + {0x24D6, 0x8720}, + {0x24D8, 0x403F}, + {0x24DA, 0x06A7}, + {0x24DC, 0xD2EF}, + {0x24DE, 0x0000}, + {0x24E0, 0xC2EF}, + {0x24E2, 0x0000}, + {0x24E4, 0x93C2}, + {0x24E6, 0x87D3}, + {0x24E8, 0x2068}, + {0x24EA, 0xB0F2}, + {0x24EC, 0x0040}, + {0x24EE, 0x0B05}, + {0x24F0, 0x2461}, + {0x24F2, 0xD3D2}, + {0x24F4, 0x0410}, + {0x24F6, 0xB3E2}, + {0x24F8, 0x0381}, + {0x24FA, 0x2089}, + {0x24FC, 0x90B2}, + {0x24FE, 0x0030}, + {0x2500, 0x0A00}, + {0x2502, 0x2C52}, + {0x2504, 0x93C2}, + {0x2506, 0x86EE}, + {0x2508, 0x204F}, + {0x250A, 0x430E}, + {0x250C, 0x430C}, + {0x250E, 0x4C0F}, + {0x2510, 0x5F0F}, + {0x2512, 0x5F0F}, + {0x2514, 0x5F0F}, + {0x2516, 0x4F1F}, + {0x2518, 0x8668}, + {0x251A, 0xF03F}, + {0x251C, 0x07FF}, + {0x251E, 0x903F}, + {0x2520, 0x0400}, + {0x2522, 0x343E}, + {0x2524, 0x5F0E}, + {0x2526, 0x531C}, + {0x2528, 0x923C}, + {0x252A, 0x2BF1}, + {0x252C, 0x4E0F}, + {0x252E, 0x930E}, + {0x2530, 0x3834}, + {0x2532, 0x110F}, + {0x2534, 0x110F}, + {0x2536, 0x110F}, + {0x2538, 0x9382}, + {0x253A, 0x86EE}, + {0x253C, 0x2023}, + {0x253E, 0x5F82}, + {0x2540, 0x87D6}, + {0x2542, 0x403B}, + {0x2544, 0x87D6}, + {0x2546, 0x4B2F}, + {0x2548, 0x12B0}, + {0x254A, 0xB624}, + {0x254C, 0x4F8B}, + {0x254E, 0x0000}, + {0x2550, 0x430C}, + {0x2552, 0x4C0D}, + {0x2554, 0x5D0D}, + {0x2556, 0x5D0D}, + {0x2558, 0x5D0D}, + {0x255A, 0x403A}, + {0x255C, 0x87D8}, + {0x255E, 0x421B}, + {0x2560, 0x87D6}, + {0x2562, 0x4B0F}, + {0x2564, 0x8A2F}, + {0x2566, 0x4F0E}, + {0x2568, 0x4E0F}, + {0x256A, 0x5F0F}, + {0x256C, 0x7F0F}, + {0x256E, 0xE33F}, + {0x2570, 0x8E8D}, + {0x2572, 0x8668}, + {0x2574, 0x7F8D}, + {0x2576, 0x866A}, + {0x2578, 0x531C}, + {0x257A, 0x923C}, + {0x257C, 0x2BEA}, + {0x257E, 0x4B8A}, + {0x2580, 0x0000}, + {0x2582, 0x3C45}, + {0x2584, 0x9382}, + {0x2586, 0x86F0}, + {0x2588, 0x2005}, + {0x258A, 0x4382}, + {0x258C, 0x87D6}, + {0x258E, 0x4382}, + {0x2590, 0x87D8}, + {0x2592, 0x3FD7}, + {0x2594, 0x4F82}, + {0x2596, 0x87D6}, + {0x2598, 0x3FD4}, + {0x259A, 0x503F}, + {0x259C, 0x0007}, + {0x259E, 0x3FC9}, + {0x25A0, 0x5F0E}, + {0x25A2, 0x503E}, + {0x25A4, 0xF800}, + {0x25A6, 0x3FBF}, + {0x25A8, 0x430F}, + {0x25AA, 0x12B0}, + {0x25AC, 0xB624}, + {0x25AE, 0x4382}, + {0x25B0, 0x87D6}, + {0x25B2, 0x3C2D}, + {0x25B4, 0xC3D2}, + {0x25B6, 0x0410}, + {0x25B8, 0x3F9E}, + {0x25BA, 0x430D}, + {0x25BC, 0x403E}, + {0x25BE, 0x0050}, + {0x25C0, 0x403F}, + {0x25C2, 0x85C8}, + {0x25C4, 0x1292}, + {0x25C6, 0x844E}, + {0x25C8, 0x3F90}, + {0x25CA, 0x5392}, + {0x25CC, 0x8720}, + {0x25CE, 0x3F84}, + {0x25D0, 0x403B}, + {0x25D2, 0x843E}, + {0x25D4, 0x4A0F}, + {0x25D6, 0x532F}, + {0x25D8, 0x422D}, + {0x25DA, 0x4F0E}, + {0x25DC, 0x403F}, + {0x25DE, 0x0E08}, + {0x25E0, 0x12AB}, + {0x25E2, 0x422D}, + {0x25E4, 0x403E}, + {0x25E6, 0x192A}, + {0x25E8, 0x410F}, + {0x25EA, 0x12AB}, + {0x25EC, 0x3F48}, + {0x25EE, 0x93C2}, + {0x25F0, 0x86EE}, + {0x25F2, 0x2312}, + {0x25F4, 0x403A}, + {0x25F6, 0x86E4}, + {0x25F8, 0x3F11}, + {0x25FA, 0x403D}, + {0x25FC, 0x0200}, + {0x25FE, 0x422E}, + {0x2600, 0x403F}, + {0x2602, 0x192A}, + {0x2604, 0x1292}, + {0x2606, 0x844E}, + {0x2608, 0xC3D2}, + {0x260A, 0x1921}, + {0x260C, 0x3F02}, + {0x260E, 0x422D}, + {0x2610, 0x403E}, + {0x2612, 0x888E}, + {0x2614, 0x403F}, + {0x2616, 0x192A}, + {0x2618, 0x1292}, + {0x261A, 0x843E}, + {0x261C, 0x5231}, + {0x261E, 0x413A}, + {0x2620, 0x413B}, + {0x2622, 0x4130}, + {0x2624, 0x4382}, + {0x2626, 0x052C}, + {0x2628, 0x4F0D}, + {0x262A, 0x930D}, + {0x262C, 0x3402}, + {0x262E, 0xE33D}, + {0x2630, 0x531D}, + {0x2632, 0xF03D}, + {0x2634, 0x07F0}, + {0x2636, 0x4D0E}, + {0x2638, 0xC312}, + {0x263A, 0x100E}, + {0x263C, 0x110E}, + {0x263E, 0x110E}, + {0x2640, 0x110E}, + {0x2642, 0x930F}, + {0x2644, 0x3803}, + {0x2646, 0x4EC2}, + {0x2648, 0x052C}, + {0x264A, 0x3C04}, + {0x264C, 0x4EC2}, + {0x264E, 0x052D}, + {0x2650, 0xE33D}, + {0x2652, 0x531D}, + {0x2654, 0x4D0F}, + {0x2656, 0x4130}, + {0x2658, 0x1292}, + {0x265A, 0xD048}, + {0x265C, 0x93C2}, + {0x265E, 0x86EE}, + {0x2660, 0x200D}, + {0x2662, 0xB0F2}, + {0x2664, 0x0020}, + {0x2666, 0x0381}, + {0x2668, 0x2407}, + {0x266A, 0x9292}, + {0x266C, 0x8722}, + {0x266E, 0x0384}, + {0x2670, 0x2C03}, + {0x2672, 0xD3D2}, + {0x2674, 0x0649}, + {0x2676, 0x4130}, + {0x2678, 0xC3D2}, + {0x267A, 0x0649}, + {0x267C, 0x4130}, + {0x267E, 0x120B}, + {0x2680, 0x120A}, + {0x2682, 0x1209}, + {0x2684, 0x1208}, + {0x2686, 0x1207}, + {0x2688, 0x1206}, + {0x268A, 0x1205}, + {0x268C, 0x1204}, + {0x268E, 0x8231}, + {0x2690, 0x4F81}, + {0x2692, 0x0000}, + {0x2694, 0x4381}, + {0x2696, 0x0002}, + {0x2698, 0x4304}, + {0x269A, 0x411C}, + {0x269C, 0x0002}, + {0x269E, 0x5C0C}, + {0x26A0, 0x4C0F}, + {0x26A2, 0x5F0F}, + {0x26A4, 0x5F0F}, + {0x26A6, 0x5F0F}, + {0x26A8, 0x5F0F}, + {0x26AA, 0x5F0F}, + {0x26AC, 0x503F}, + {0x26AE, 0x1980}, + {0x26B0, 0x440D}, + {0x26B2, 0x5D0D}, + {0x26B4, 0x4D0E}, + {0x26B6, 0x5F0E}, + {0x26B8, 0x4E2E}, + {0x26BA, 0x4D05}, + {0x26BC, 0x5505}, + {0x26BE, 0x5F05}, + {0x26C0, 0x4516}, + {0x26C2, 0x0008}, + {0x26C4, 0x4517}, + {0x26C6, 0x000A}, + {0x26C8, 0x460A}, + {0x26CA, 0x470B}, + {0x26CC, 0xF30A}, + {0x26CE, 0xF32B}, + {0x26D0, 0x4A81}, + {0x26D2, 0x0004}, + {0x26D4, 0x4B81}, + {0x26D6, 0x0006}, + {0x26D8, 0xB03E}, + {0x26DA, 0x2000}, + {0x26DC, 0x2404}, + {0x26DE, 0xF03E}, + {0x26E0, 0x1FFF}, + {0x26E2, 0xE33E}, + {0x26E4, 0x531E}, + {0x26E6, 0xF317}, + {0x26E8, 0x503E}, + {0x26EA, 0x2000}, + {0x26EC, 0x4E0F}, + {0x26EE, 0x5F0F}, + {0x26F0, 0x7F0F}, + {0x26F2, 0xE33F}, + {0x26F4, 0x512C}, + {0x26F6, 0x4C28}, + {0x26F8, 0x4309}, + {0x26FA, 0x4E0A}, + {0x26FC, 0x4F0B}, + {0x26FE, 0x480C}, + {0x2700, 0x490D}, + {0x2702, 0x1202}, + {0x2704, 0xC232}, + {0x2706, 0x12B0}, + {0x2708, 0xFFC0}, + {0x270A, 0x4132}, + {0x270C, 0x108E}, + {0x270E, 0x108F}, + {0x2710, 0xEF4E}, + {0x2712, 0xEF0E}, + {0x2714, 0xF37F}, + {0x2716, 0xC312}, + {0x2718, 0x100F}, + {0x271A, 0x100E}, + {0x271C, 0x4E85}, + {0x271E, 0x0018}, + {0x2720, 0x4F85}, + {0x2722, 0x001A}, + {0x2724, 0x480A}, + {0x2726, 0x490B}, + {0x2728, 0x460C}, + {0x272A, 0x470D}, + {0x272C, 0x1202}, + {0x272E, 0xC232}, + {0x2730, 0x12B0}, + {0x2732, 0xFFC0}, + {0x2734, 0x4132}, + {0x2736, 0x4E0C}, + {0x2738, 0x4F0D}, + {0x273A, 0x108C}, + {0x273C, 0x108D}, + {0x273E, 0xED4C}, + {0x2740, 0xED0C}, + {0x2742, 0xF37D}, + {0x2744, 0xC312}, + {0x2746, 0x100D}, + {0x2748, 0x100C}, + {0x274A, 0x411E}, + {0x274C, 0x0004}, + {0x274E, 0x411F}, + {0x2750, 0x0006}, + {0x2752, 0x5E0E}, + {0x2754, 0x6F0F}, + {0x2756, 0x5E0E}, + {0x2758, 0x6F0F}, + {0x275A, 0x5E0E}, + {0x275C, 0x6F0F}, + {0x275E, 0xDE0C}, + {0x2760, 0xDF0D}, + {0x2762, 0x4C85}, + {0x2764, 0x002C}, + {0x2766, 0x4D85}, + {0x2768, 0x002E}, + {0x276A, 0x5314}, + {0x276C, 0x9224}, + {0x276E, 0x2B95}, + {0x2770, 0x5391}, + {0x2772, 0x0002}, + {0x2774, 0x92A1}, + {0x2776, 0x0002}, + {0x2778, 0x2B8F}, + {0x277A, 0x5231}, + {0x277C, 0x4134}, + {0x277E, 0x4135}, + {0x2780, 0x4136}, + {0x2782, 0x4137}, + {0x2784, 0x4138}, + {0x2786, 0x4139}, + {0x2788, 0x413A}, + {0x278A, 0x413B}, + {0x278C, 0x4130}, + {0x278E, 0x120B}, + {0x2790, 0x120A}, + {0x2792, 0x1209}, + {0x2794, 0x8031}, + {0x2796, 0x000C}, + {0x2798, 0x425F}, + {0x279A, 0x0205}, + {0x279C, 0xC312}, + {0x279E, 0x104F}, + {0x27A0, 0x114F}, + {0x27A2, 0x114F}, + {0x27A4, 0x114F}, + {0x27A6, 0x114F}, + {0x27A8, 0x114F}, + {0x27AA, 0xF37F}, + {0x27AC, 0x4F0B}, + {0x27AE, 0xF31B}, + {0x27B0, 0x5B0B}, + {0x27B2, 0x5B0B}, + {0x27B4, 0x5B0B}, + {0x27B6, 0x503B}, + {0x27B8, 0xD194}, + {0x27BA, 0x4219}, + {0x27BC, 0x0508}, + {0x27BE, 0xF039}, + {0x27C0, 0x2000}, + {0x27C2, 0x4F0A}, + {0x27C4, 0xC312}, + {0x27C6, 0x100A}, + {0x27C8, 0xE31A}, + {0x27CA, 0x421F}, + {0x27CC, 0x87DE}, + {0x27CE, 0x503F}, + {0x27D0, 0xFF60}, + {0x27D2, 0x903F}, + {0x27D4, 0x00C8}, + {0x27D6, 0x2C02}, + {0x27D8, 0x403F}, + {0x27DA, 0x00C8}, + {0x27DC, 0x4F82}, + {0x27DE, 0x7322}, + {0x27E0, 0xB3D2}, + {0x27E2, 0x0381}, + {0x27E4, 0x2009}, + {0x27E6, 0x421F}, + {0x27E8, 0x86F0}, + {0x27EA, 0xD21F}, + {0x27EC, 0x86EE}, + {0x27EE, 0x930F}, + {0x27F0, 0x24B9}, + {0x27F2, 0x40F2}, + {0x27F4, 0xFF80}, + {0x27F6, 0x0619}, + {0x27F8, 0x1292}, + {0x27FA, 0xD00A}, + {0x27FC, 0xB3D2}, + {0x27FE, 0x0385}, + {0x2800, 0x2405}, + {0x2802, 0x421F}, + {0x2804, 0x880A}, + {0x2806, 0x4F92}, + {0x2808, 0x0002}, + {0x280A, 0x8714}, + {0x280C, 0x430D}, + {0x280E, 0x93C2}, + {0x2810, 0x87D0}, + {0x2812, 0x2003}, + {0x2814, 0xB2F2}, + {0x2816, 0x0360}, + {0x2818, 0x2001}, + {0x281A, 0x431D}, + {0x281C, 0x425F}, + {0x281E, 0x87D3}, + {0x2820, 0xD25F}, + {0x2822, 0x87D2}, + {0x2824, 0xF37F}, + {0x2826, 0x5F0F}, + {0x2828, 0x425E}, + {0x282A, 0x87CD}, + {0x282C, 0xDE0F}, + {0x282E, 0x5F0F}, + {0x2830, 0x5B0F}, + {0x2832, 0x4FA2}, + {0x2834, 0x0402}, + {0x2836, 0x930D}, + {0x2838, 0x2007}, + {0x283A, 0x930A}, + {0x283C, 0x248E}, + {0x283E, 0x4F5F}, + {0x2840, 0x0001}, + {0x2842, 0xF37F}, + {0x2844, 0x4FC2}, + {0x2846, 0x0403}, + {0x2848, 0x93C2}, + {0x284A, 0x87CD}, + {0x284C, 0x2483}, + {0x284E, 0xC2F2}, + {0x2850, 0x0400}, + {0x2852, 0xB2E2}, + {0x2854, 0x0265}, + {0x2856, 0x2407}, + {0x2858, 0x421F}, + {0x285A, 0x0508}, + {0x285C, 0xF03F}, + {0x285E, 0xFFDF}, + {0x2860, 0xD90F}, + {0x2862, 0x4F82}, + {0x2864, 0x0508}, + {0x2866, 0xB3D2}, + {0x2868, 0x0383}, + {0x286A, 0x2484}, + {0x286C, 0x403F}, + {0x286E, 0x0508}, + {0x2870, 0x4FB1}, + {0x2872, 0x0000}, + {0x2874, 0x4FB1}, + {0x2876, 0x0002}, + {0x2878, 0x4FB1}, + {0x287A, 0x0004}, + {0x287C, 0x403F}, + {0x287E, 0x0500}, + {0x2880, 0x4FB1}, + {0x2882, 0x0006}, + {0x2884, 0x4FB1}, + {0x2886, 0x0008}, + {0x2888, 0x4FB1}, + {0x288A, 0x000A}, + {0x288C, 0xB3E2}, + {0x288E, 0x0383}, + {0x2890, 0x2412}, + {0x2892, 0xC2E1}, + {0x2894, 0x0002}, + {0x2896, 0xB2E2}, + {0x2898, 0x0383}, + {0x289A, 0x434F}, + {0x289C, 0x634F}, + {0x289E, 0xF37F}, + {0x28A0, 0x4F4E}, + {0x28A2, 0x114E}, + {0x28A4, 0x434E}, + {0x28A6, 0x104E}, + {0x28A8, 0x415F}, + {0x28AA, 0x0007}, + {0x28AC, 0xF07F}, + {0x28AE, 0x007F}, + {0x28B0, 0xDE4F}, + {0x28B2, 0x4FC1}, + {0x28B4, 0x0007}, + {0x28B6, 0xB2F2}, + {0x28B8, 0x0383}, + {0x28BA, 0x2415}, + {0x28BC, 0xF0F1}, + {0x28BE, 0xFFBF}, + {0x28C0, 0x0000}, + {0x28C2, 0xB0F2}, + {0x28C4, 0x0010}, + {0x28C6, 0x0383}, + {0x28C8, 0x434E}, + {0x28CA, 0x634E}, + {0x28CC, 0x5E4E}, + {0x28CE, 0x5E4E}, + {0x28D0, 0x5E4E}, + {0x28D2, 0x5E4E}, + {0x28D4, 0x5E4E}, + {0x28D6, 0x5E4E}, + {0x28D8, 0x415F}, + {0x28DA, 0x0006}, + {0x28DC, 0xF07F}, + {0x28DE, 0xFFBF}, + {0x28E0, 0xDE4F}, + {0x28E2, 0x4FC1}, + {0x28E4, 0x0006}, + {0x28E6, 0xB0F2}, + {0x28E8, 0x0020}, + {0x28EA, 0x0383}, + {0x28EC, 0x2410}, + {0x28EE, 0xF0F1}, + {0x28F0, 0xFFDF}, + {0x28F2, 0x0002}, + {0x28F4, 0xB0F2}, + {0x28F6, 0x0040}, + {0x28F8, 0x0383}, + {0x28FA, 0x434E}, + {0x28FC, 0x634E}, + {0x28FE, 0x5E4E}, + {0x2900, 0x5E4E}, + {0x2902, 0x415F}, + {0x2904, 0x0008}, + {0x2906, 0xC26F}, + {0x2908, 0xDE4F}, + {0x290A, 0x4FC1}, + {0x290C, 0x0008}, + {0x290E, 0x93C2}, + {0x2910, 0x0383}, + {0x2912, 0x3412}, + {0x2914, 0xF0F1}, + {0x2916, 0xFFDF}, + {0x2918, 0x0000}, + {0x291A, 0x425E}, + {0x291C, 0x0382}, + {0x291E, 0xF35E}, + {0x2920, 0x5E4E}, + {0x2922, 0x5E4E}, + {0x2924, 0x5E4E}, + {0x2926, 0x5E4E}, + {0x2928, 0x5E4E}, + {0x292A, 0x415F}, + {0x292C, 0x0006}, + {0x292E, 0xF07F}, + {0x2930, 0xFFDF}, + {0x2932, 0xDE4F}, + {0x2934, 0x4FC1}, + {0x2936, 0x0006}, + {0x2938, 0x410F}, + {0x293A, 0x4FB2}, + {0x293C, 0x0508}, + {0x293E, 0x4FB2}, + {0x2940, 0x050A}, + {0x2942, 0x4FB2}, + {0x2944, 0x050C}, + {0x2946, 0x4FB2}, + {0x2948, 0x0500}, + {0x294A, 0x4FB2}, + {0x294C, 0x0502}, + {0x294E, 0x4FB2}, + {0x2950, 0x0504}, + {0x2952, 0x3C10}, + {0x2954, 0xD2F2}, + {0x2956, 0x0400}, + {0x2958, 0x3F7C}, + {0x295A, 0x4F6F}, + {0x295C, 0xF37F}, + {0x295E, 0x4FC2}, + {0x2960, 0x0402}, + {0x2962, 0x3F72}, + {0x2964, 0x90F2}, + {0x2966, 0x0011}, + {0x2968, 0x0619}, + {0x296A, 0x2B46}, + {0x296C, 0x50F2}, + {0x296E, 0xFFF0}, + {0x2970, 0x0619}, + {0x2972, 0x3F42}, + {0x2974, 0x5031}, + {0x2976, 0x000C}, + {0x2978, 0x4139}, + {0x297A, 0x413A}, + {0x297C, 0x413B}, + {0x297E, 0x4130}, + {0x2980, 0x0900}, + {0x2982, 0x7312}, + {0x2984, 0x421F}, + {0x2986, 0x0A08}, + {0x2988, 0xF03F}, + {0x298A, 0xF7FF}, + {0x298C, 0x4F82}, + {0x298E, 0x0A88}, + {0x2990, 0x0900}, + {0x2992, 0x7312}, + {0x2994, 0x421F}, + {0x2996, 0x0A0E}, + {0x2998, 0xF03F}, + {0x299A, 0x7FFF}, + {0x299C, 0x4F82}, + {0x299E, 0x0A8E}, + {0x29A0, 0x0900}, + {0x29A2, 0x7312}, + {0x29A4, 0x421F}, + {0x29A6, 0x0A1E}, + {0x29A8, 0xC31F}, + {0x29AA, 0x4F82}, + {0x29AC, 0x0A9E}, + {0x29AE, 0x4130}, + {0x29B0, 0x4292}, + {0x29B2, 0x0A08}, + {0x29B4, 0x0A88}, + {0x29B6, 0x0900}, + {0x29B8, 0x7312}, + {0x29BA, 0x4292}, + {0x29BC, 0x0A0E}, + {0x29BE, 0x0A8E}, + {0x29C0, 0x0900}, + {0x29C2, 0x7312}, + {0x29C4, 0x4292}, + {0x29C6, 0x0A1E}, + {0x29C8, 0x0A9E}, + {0x29CA, 0x4130}, + {0x29CC, 0x7400}, + {0x29CE, 0x8058}, + {0x29D0, 0x1807}, + {0x29D2, 0x00E0}, + {0x29D4, 0x7002}, + {0x29D6, 0x17C7}, + {0x29D8, 0x0045}, + {0x29DA, 0x0006}, + {0x29DC, 0x17CC}, + {0x29DE, 0x0015}, + {0x29E0, 0x1512}, + {0x29E2, 0x216F}, + {0x29E4, 0x005B}, + {0x29E6, 0x005D}, + {0x29E8, 0x00DE}, + {0x29EA, 0x00DD}, + {0x29EC, 0x5023}, + {0x29EE, 0x00DE}, + {0x29F0, 0x005B}, + {0x29F2, 0x0410}, + {0x29F4, 0x0091}, + {0x29F6, 0x0015}, + {0x29F8, 0x0040}, + {0x29FA, 0x7023}, + {0x29FC, 0x1653}, + {0x29FE, 0x0156}, + {0x2A00, 0x0001}, + {0x2A02, 0x2081}, + {0x2A04, 0x7020}, + {0x2A06, 0x2F99}, + {0x2A08, 0x005C}, + {0x2A0A, 0x0000}, + {0x2A0C, 0x5040}, + {0x2A0E, 0x0045}, + {0x2A10, 0x213A}, + {0x2A12, 0x0303}, + {0x2A14, 0x0148}, + {0x2A16, 0x0049}, + {0x2A18, 0x0045}, + {0x2A1A, 0x0046}, + {0x2A1C, 0x05DD}, + {0x2A1E, 0x00DE}, + {0x2A20, 0x00DD}, + {0x2A22, 0x00DC}, + {0x2A24, 0x00DE}, + {0x2A26, 0x04D6}, + {0x2A28, 0x2014}, + {0x2A2A, 0x2081}, + {0x2A2C, 0x7087}, + {0x2A2E, 0x2F99}, + {0x2A30, 0x005C}, + {0x2A32, 0x0002}, + {0x2A34, 0x5060}, + {0x2A36, 0x31C0}, + {0x2A38, 0x2122}, + {0x2A3A, 0x7800}, + {0x2A3C, 0xC08C}, + {0x2A3E, 0x0001}, + {0x2A40, 0x9038}, + {0x2A42, 0x59F7}, + {0x2A44, 0x907A}, + {0x2A46, 0x03D8}, + {0x2A48, 0x8D90}, + {0x2A4A, 0x01C0}, + {0x2A4C, 0x7400}, + {0x2A4E, 0x8058}, + {0x2A50, 0x1807}, + {0x2A52, 0x00E0}, + {0x2A54, 0x7002}, + {0x2A56, 0x17C7}, + {0x2A58, 0x0045}, + {0x2A5A, 0x0006}, + {0x2A5C, 0x17CC}, + {0x2A5E, 0x0015}, + {0x2A60, 0x1512}, + {0x2A62, 0x216F}, + {0x2A64, 0x005B}, + {0x2A66, 0x005D}, + {0x2A68, 0x00DE}, + {0x2A6A, 0x00DD}, + {0x2A6C, 0x5023}, + {0x2A6E, 0x00DE}, + {0x2A70, 0x005B}, + {0x2A72, 0x0410}, + {0x2A74, 0x0091}, + {0x2A76, 0x0015}, + {0x2A78, 0x0040}, + {0x2A7A, 0x7023}, + {0x2A7C, 0x1653}, + {0x2A7E, 0x0156}, + {0x2A80, 0x0001}, + {0x2A82, 0x2081}, + {0x2A84, 0x7020}, + {0x2A86, 0x2F99}, + {0x2A88, 0x005C}, + {0x2A8A, 0x0000}, + {0x2A8C, 0x5040}, + {0x2A8E, 0x0045}, + {0x2A90, 0x213A}, + {0x2A92, 0x0303}, + {0x2A94, 0x0148}, + {0x2A96, 0x0049}, + {0x2A98, 0x0045}, + {0x2A9A, 0x0046}, + {0x2A9C, 0x05DD}, + {0x2A9E, 0x00DE}, + {0x2AA0, 0x00DD}, + {0x2AA2, 0x00DC}, + {0x2AA4, 0x00DE}, + {0x2AA6, 0x0296}, + {0x2AA8, 0x2014}, + {0x2AAA, 0x2081}, + {0x2AAC, 0x7087}, + {0x2AAE, 0x2F99}, + {0x2AB0, 0x005C}, + {0x2AB2, 0x0002}, + {0x2AB4, 0x5060}, + {0x2AB6, 0x31C0}, + {0x2AB8, 0x2122}, + {0x2ABA, 0x7800}, + {0x2ABC, 0xC08C}, + {0x2ABE, 0x0001}, + {0x2AC0, 0x9038}, + {0x2AC2, 0x59F7}, + {0x2AC4, 0x907A}, + {0x2AC6, 0x03D8}, + {0x2AC8, 0x8D90}, + {0x2ACA, 0x01C0}, + {0x2ACC, 0x7400}, + {0x2ACE, 0x2002}, + {0x2AD0, 0x70DF}, + {0x2AD2, 0x2F21}, + {0x2AD4, 0x04C1}, + {0x2AD6, 0x0D80}, + {0x2AD8, 0x7800}, + {0x2ADA, 0x0041}, + {0x2ADC, 0x7400}, + {0x2ADE, 0x2004}, + {0x2AE0, 0x70DF}, + {0x2AE2, 0x2F21}, + {0x2AE4, 0x04C2}, + {0x2AE6, 0x0D80}, + {0x2AE8, 0x7800}, + {0x2AEA, 0x7400}, + {0x2AEC, 0x2008}, + {0x2AEE, 0x70DF}, + {0x2AF0, 0x2F21}, + {0x2AF2, 0x04C3}, + {0x2AF4, 0x0D80}, + {0x2AF6, 0x7800}, + {0x2AF8, 0x7400}, + {0x2AFA, 0x0004}, + {0x2AFC, 0x70DF}, + {0x2AFE, 0x2F22}, + {0x2B00, 0x7008}, + {0x2B02, 0x2F1F}, + {0x2B04, 0x7021}, + {0x2B06, 0x2F01}, + {0x2B08, 0x7800}, + {0x2B0A, 0x7400}, + {0x2B0C, 0x0002}, + {0x2B0E, 0x70DF}, + {0x2B10, 0x3F5F}, + {0x2B12, 0x703A}, + {0x2B14, 0x2F01}, + {0x2B16, 0x7800}, + {0x2B18, 0x7400}, + {0x2B1A, 0x2010}, + {0x2B1C, 0x70DF}, + {0x2B1E, 0x3F40}, + {0x2B20, 0x700A}, + {0x2B22, 0x0FC0}, + {0x2B24, 0x7800}, + {0x2B26, 0x7400}, + {0x2B28, 0x2004}, + {0x2B2A, 0x70DF}, + {0x2B2C, 0x2F21}, + {0x2B2E, 0x04C2}, + {0x2B30, 0x0D80}, + {0x2B32, 0x7800}, + {0x2B34, 0x0041}, + {0x2B36, 0x7400}, + {0x2B38, 0x2002}, + {0x2B3A, 0x70DF}, + {0x2B3C, 0x2F22}, + {0x2B3E, 0x04C1}, + {0x2B40, 0x0D80}, + {0x2B42, 0x7800}, + {0x2B44, 0x7400}, + {0x2B46, 0x0001}, + {0x2B48, 0x70DF}, + {0x2B4A, 0x3F5F}, + {0x2B4C, 0x703A}, + {0x2B4E, 0x2F01}, + {0x2B50, 0x7800}, + {0x2B52, 0x7400}, + {0x2B54, 0x200A}, + {0x2B56, 0x70DF}, + {0x2B58, 0x3F40}, + {0x2B5A, 0x700A}, + {0x2B5C, 0x0FC0}, + {0x2B5E, 0x7800}, + {0x2B60, 0x7400}, + {0x2B62, 0x2015}, + {0x2B64, 0x70DF}, + {0x2B66, 0x3F5F}, + {0x2B68, 0x703A}, + {0x2B6A, 0x2F01}, + {0x2B6C, 0x7800}, + {0x2B6E, 0x7400}, + {0x2B70, 0x7800}, + {0x2B72, 0x007F}, + {0x2B74, 0x0000}, + {0x2B76, 0xB9CC}, + {0x2B78, 0x0000}, + {0x2B7A, 0xB9CC}, + {0x2B7C, 0xBA3C}, + {0x2B7E, 0x0002}, + {0x2B80, 0x0000}, + {0x2B82, 0xBA4C}, + {0x2B84, 0x0000}, + {0x2B86, 0xBA4C}, + {0x2B88, 0xBABC}, + {0x2B8A, 0x0002}, + {0x2B8C, 0x0063}, + {0x2B8E, 0xBB26}, + {0x2B90, 0x0063}, + {0x2B92, 0xBB36}, + {0x2B94, 0x0063}, + {0x2B96, 0xBAEA}, + {0x2B98, 0x0063}, + {0x2B9A, 0xBAF8}, + {0x2B9C, 0xBADA}, + {0x2B9E, 0x0004}, + {0x2BA0, 0x0063}, + {0x2BA2, 0xBAEA}, + {0x2BA4, 0x0063}, + {0x2BA6, 0xBB18}, + {0x2BA8, 0x0063}, + {0x2BAA, 0xBB26}, + {0x2BAC, 0x0063}, + {0x2BAE, 0xBB44}, + {0x2BB0, 0xBADA}, + {0x2BB2, 0x0004}, + {0x2BB4, 0x0063}, + {0x2BB6, 0xBACC}, + {0x2BB8, 0x0063}, + {0x2BBA, 0xBADC}, + {0x2BBC, 0x0063}, + {0x2BBE, 0xBAEA}, + {0x2BC0, 0x0063}, + {0x2BC2, 0xBAF8}, + {0x2BC4, 0xBADA}, + {0x2BC6, 0x0004}, + {0x2BC8, 0x0063}, + {0x2BCA, 0xBAEA}, + {0x2BCC, 0x0063}, + {0x2BCE, 0xBB18}, + {0x2BD0, 0x0063}, + {0x2BD2, 0xBACC}, + {0x2BD4, 0x0063}, + {0x2BD6, 0xBB0A}, + {0x2BD8, 0xBADA}, + {0x2BDA, 0x0004}, + {0x2BDC, 0x0063}, + {0x2BDE, 0xBACC}, + {0x2BE0, 0x0063}, + {0x2BE2, 0xBADC}, + {0x2BE4, 0x0063}, + {0x2BE6, 0xBAEA}, + {0x2BE8, 0x0063}, + {0x2BEA, 0xBB18}, + {0x2BEC, 0xBADA}, + {0x2BEE, 0x0004}, + {0x2BF0, 0xFFFF}, + {0x2BF2, 0xBB6E}, + {0x2BF4, 0x0000}, + {0x2BF6, 0x0000}, + {0x2BF8, 0x0000}, + {0x2BFA, 0x0000}, + {0x2BFC, 0x0000}, + {0x2BFE, 0x0000}, + {0x2C00, 0xBB72}, + {0x2C02, 0x0001}, + {0x2C04, 0x0063}, + {0x2C06, 0xBB52}, + {0x2C08, 0x0063}, + {0x2C0A, 0xBB60}, + {0x2C0C, 0x0000}, + {0x2C0E, 0x0000}, + {0x2C10, 0x0000}, + {0x2C12, 0x0000}, + {0x2C14, 0xBADA}, + {0x2C16, 0x0002}, + {0x2C18, 0x0066}, + {0x2C1A, 0x0067}, + {0x2C1C, 0x00AF}, + {0x2C1E, 0x01CF}, + {0x2C20, 0x0087}, + {0x2C22, 0x0083}, + {0x2C24, 0x011B}, + {0x2C26, 0x035A}, + {0x2C28, 0x00FA}, + {0x2C2A, 0x00F2}, + {0x2C2C, 0x00A6}, + {0x2C2E, 0x00A4}, + {0x2C30, 0xFFFF}, + {0x2C32, 0x002C}, + {0x2C34, 0x0058}, + {0x2C36, 0x0000}, + {0x2C38, 0x0000}, + {0x2C3A, 0xBC18}, + {0x2C3C, 0xBB74}, + {0x2C3E, 0xBB80}, + {0x2C40, 0xBC32}, + {0x2C42, 0xBB8C}, + {0x2C44, 0xBBA0}, + {0x2C46, 0xBB8C}, + {0x2C48, 0xBBA0}, + {0x2C4A, 0xBC04}, + {0x2C4C, 0xBC04}, + {0x2C4E, 0xBBF0}, + {0x2C50, 0xBBF0}, + {0x2C52, 0xBBB4}, + {0x2C54, 0xBBC8}, + {0x2C56, 0xBBB4}, + {0x2C58, 0xBBC8}, + {0x2C5A, 0xBC04}, + {0x2C5C, 0xBC04}, + {0x2C5E, 0xBBF0}, + {0x2C60, 0xBBF0}, + {0x2C62, 0xBB8C}, + {0x2C64, 0xBBA0}, + {0x2C66, 0xBB8C}, + {0x2C68, 0xBBA0}, + {0x2C6A, 0xBC04}, + {0x2C6C, 0xBC04}, + {0x2C6E, 0xBBF0}, + {0x2C70, 0xBBF0}, + {0x2C72, 0xBBB4}, + {0x2C74, 0xBBC8}, + {0x2C76, 0xBBB4}, + {0x2C78, 0xBBC8}, + {0x2C7A, 0xBC04}, + {0x2C7C, 0xBC04}, + {0x2C7E, 0xBBF0}, + {0x2C80, 0xBBF0}, + {0x3800, 0x880E}, + {0x3802, 0xBC62}, + {0x3804, 0xBC40}, + {0x3806, 0xD13E}, + {0x3808, 0xBC42}, + {0x380A, 0xBC3C}, + {0x380C, 0x0000}, + {0x380E, 0x0040}, + {0x3810, 0x0040}, + {0x3812, 0x0040}, + {0x3814, 0x0043}, + {0x3816, 0x0046}, + {0x3818, 0x004B}, + {0x381A, 0x004D}, + {0x381C, 0x0051}, + {0x381E, 0x0055}, + {0x3820, 0x005A}, + {0x3822, 0x005E}, + {0x3824, 0x0062}, + {0x3826, 0x0067}, + {0x3828, 0x006C}, + {0x382A, 0x0070}, + {0x382C, 0x0078}, + {0x382E, 0x0086}, + {0x3830, 0x0090}, + {0x3832, 0x0096}, + {0x3834, 0x009D}, + {0x3836, 0x00A5}, + {0x3838, 0x00AD}, + {0x383A, 0x00B4}, + {0x383C, 0x00B9}, + {0x383E, 0x00BE}, + {0x3840, 0x00C3}, + {0x3842, 0x00C8}, + {0x3844, 0x00CD}, + {0x3846, 0x00D2}, + {0x3848, 0x00D7}, + {0x384A, 0x00DC}, + {0x384C, 0x00DC}, + {0x384E, 0x0000}, + {0x3850, 0x0000}, + {0x3852, 0x0000}, + {0x3854, 0x0000}, + {0x3856, 0x0000}, + {0x3858, 0x0000}, + {0x385A, 0x0000}, + {0x385C, 0x0000}, + {0x385E, 0x0000}, + {0x3860, 0x0000}, + {0x3862, 0x0000}, + {0x3864, 0x0000}, + {0x3866, 0x0000}, + {0x3868, 0x0000}, + {0x386A, 0x0000}, + {0x386C, 0x0000}, + {0x386E, 0x0000}, + {0x3870, 0x0000}, + {0x3872, 0x0000}, + {0x3874, 0x0000}, + {0x3876, 0x0000}, + {0x3878, 0x0000}, + {0x387A, 0x0000}, + {0x387C, 0x0000}, + {0x387E, 0x0000}, + {0x3880, 0x0000}, + {0x3882, 0x0000}, + {0x3884, 0x0000}, + {0x3886, 0x0000}, + {0x3888, 0x0000}, + {0x388A, 0x0000}, + {0x388C, 0x0000}, + {0x026A, 0xFFFF}, + {0x026C, 0x00FF}, + {0x026E, 0x0000}, + {0x0360, 0x1E8E}, + {0x040E, 0x01EB}, + {0x0600, 0x1130}, + {0x0602, 0x3112}, + {0x0604, 0x8048}, + {0x0606, 0x00E9}, + {0x067A, 0x0404}, + {0x067C, 0x0404}, + {0x06A8, 0x0240}, + {0x06AA, 0x00CA}, + {0x06AC, 0x0041}, + {0x06B4, 0x3FFF}, + {0x06DE, 0x0404}, + {0x06E0, 0x0404}, + {0x06E2, 0xFF00}, + {0x06E4, 0x8333}, + {0x06E6, 0x8333}, + {0x06E8, 0x8333}, + {0x06EA, 0x8333}, + {0x052A, 0x0000}, + {0x052C, 0x0000}, + {0x0F06, 0x0002}, + {0x0A04, 0xB4C5}, + {0x0A06, 0xC400}, + {0x0A08, 0x988A}, + {0x0A0A, 0xA387}, + {0x0A0E, 0xEEC0}, + {0x0A12, 0x0000}, + {0x0A18, 0x0010}, + {0x0A1C, 0x0040}, + {0x0A20, 0x0015}, + {0x0C00, 0x0021}, + {0x0C16, 0x0002}, + {0x0708, 0x6FC0}, + {0x070C, 0x0000}, + {0x120C, 0x1428}, + {0x121A, 0x0000}, + {0x121C, 0x1896}, + {0x121E, 0x0032}, + {0x1220, 0x0000}, + {0x1222, 0x96FF}, + {0x1244, 0x0000}, + {0x105C, 0x0F0B}, + {0x1958, 0x0000}, + {0x195A, 0x004C}, + {0x195C, 0x0097}, + {0x195E, 0x0221}, + {0x1960, 0x03FE}, + {0x1980, 0x00E0}, + {0x1982, 0x0010}, + {0x1984, 0x2018}, + {0x1986, 0x0008}, + {0x1988, 0x0000}, + {0x198A, 0x0000}, + {0x198C, 0x0880}, + {0x198E, 0x0000}, + {0x1990, 0x1A00}, + {0x1992, 0x0000}, + {0x1994, 0x2800}, + {0x1996, 0x0002}, + {0x1962, 0x0000}, + {0x1964, 0x004C}, + {0x1966, 0x0097}, + {0x1968, 0x0221}, + {0x196A, 0x03FE}, + {0x19C0, 0x00E0}, + {0x19C2, 0x0010}, + {0x19C4, 0x2018}, + {0x19C6, 0x0008}, + {0x19C8, 0x0000}, + {0x19CA, 0x0000}, + {0x19CC, 0x0880}, + {0x19CE, 0x0000}, + {0x19D0, 0x1A00}, + {0x19D2, 0x0000}, + {0x19D4, 0x2800}, + {0x19D6, 0x0002}, + {0x196C, 0x0000}, + {0x196E, 0x004C}, + {0x1970, 0x0097}, + {0x1972, 0x0221}, + {0x1974, 0x03FE}, + {0x1A00, 0x00E0}, + {0x1A02, 0x0010}, + {0x1A04, 0x2018}, + {0x1A06, 0x0008}, + {0x1A08, 0x0000}, + {0x1A0A, 0x0000}, + {0x1A0C, 0x0880}, + {0x1A0E, 0x0000}, + {0x1A10, 0x1A00}, + {0x1A12, 0x0000}, + {0x1A14, 0x2800}, + {0x1A16, 0x0002}, + {0x1976, 0x0000}, + {0x1978, 0x004C}, + {0x197A, 0x0097}, + {0x197C, 0x0221}, + {0x197E, 0x03FE}, + {0x1A40, 0x00E0}, + {0x1A42, 0x0010}, + {0x1A44, 0x2018}, + {0x1A46, 0x0008}, + {0x1A48, 0x0000}, + {0x1A4A, 0x0000}, + {0x1A4C, 0x0880}, + {0x1A4E, 0x0000}, + {0x1A50, 0x1A00}, + {0x1A52, 0x0000}, + {0x1A54, 0x2800}, + {0x1A56, 0x0002}, + {0x192A, 0x0201}, + {0x0384, 0x0001}, + {0x027E, 0x0100}, +}; + +static const struct hi847_reg mode_3264x2448_regs[] = { + {0x0B00, 0x0000}, + {0x0204, 0x0000}, + {0x0206, 0x033C}, + {0x020A, 0x0B4D}, + {0x020E, 0x0B51}, + {0x0214, 0x0200}, + {0x0216, 0x0200}, + {0x0218, 0x0200}, + {0x021A, 0x0200}, + {0x0224, 0x002E}, + {0x022A, 0x0017}, + {0x022C, 0x0E1F}, + {0x022E, 0x09C1}, + {0x0234, 0x1111}, + {0x0236, 0x1111}, + {0x0238, 0x1111}, + {0x023A, 0x1111}, + {0x0250, 0x0000}, + {0x0252, 0x0006}, + {0x0254, 0x0000}, + {0x0256, 0x0000}, + {0x0258, 0x0000}, + {0x025A, 0x0000}, + {0x025C, 0x0000}, + {0x025E, 0x0202}, + {0x0268, 0x00CD}, + {0x0440, 0x0002}, + {0x0F00, 0x0000}, + {0x0F04, 0x0008}, + {0x0F06, 0x0002}, + {0x0B02, 0x0100}, + {0x0B04, 0x00DC}, + {0x0B12, 0x0CC0}, + {0x0B14, 0x0990}, + {0x0B20, 0x0100}, + {0x1100, 0x1100}, + {0x1102, 0x0008}, + {0x1108, 0x0202}, + {0x1118, 0x0000}, + {0x0A10, 0xB040}, + {0x0C14, 0x0008}, + {0x0C18, 0x0CC0}, + {0x0C1A, 0x0990}, + {0x0730, 0x0001}, + {0x0732, 0x0000}, + {0x0734, 0x0300}, + {0x0736, 0x004B}, + {0x0738, 0x0001}, + {0x073C, 0x0900}, + {0x0740, 0x0000}, + {0x0742, 0x0000}, + {0x0744, 0x0300}, + {0x0746, 0x007D}, + {0x0748, 0x0002}, + {0x074A, 0x0900}, + {0x074C, 0x0000}, + {0x074E, 0x0100}, + {0x0750, 0x0000}, + {0x1200, 0x0946}, + {0x1202, 0x1A00}, + {0x120E, 0x6027}, + {0x1210, 0x8027}, + {0x1246, 0x0105}, + {0x1000, 0x0300}, + {0x1002, 0xC311}, + {0x1004, 0x2BB0}, + {0x1010, 0x087B}, + {0x1012, 0x0040}, + {0x1014, 0x0020}, + {0x1016, 0x0020}, + {0x101A, 0x0020}, + {0x1020, 0xC107}, + {0x1022, 0x081E}, + {0x1024, 0x0509}, + {0x1026, 0x0B0A}, + {0x1028, 0x1409}, + {0x102A, 0x0B05}, + {0x102C, 0x1400}, + {0x1038, 0x0000}, + {0x103E, 0x0001}, + {0x1040, 0x0000}, + {0x1042, 0x0008}, + {0x1044, 0x0120}, + {0x1046, 0x01B0}, + {0x1048, 0x0090}, + {0x1066, 0x089C}, + {0x1600, 0x0000}, + {0x1608, 0x0028}, + {0x160A, 0x0C80}, + {0x160C, 0x001A}, + {0x160E, 0x0960}, + {0x0252, 0x0009}, + {0x0202, 0x0000}, +}; + +static const struct hi847_reg mode_1632x1224_regs[] = { + {0x0B00, 0x0000}, + {0x0204, 0x0200}, + {0x0206, 0x033C}, + {0x020A, 0x05A5}, + {0x020E, 0x05A9}, + {0x0214, 0x0200}, + {0x0216, 0x0200}, + {0x0218, 0x0200}, + {0x021A, 0x0200}, + {0x0224, 0x002C}, + {0x022A, 0x0015}, + {0x022C, 0x0E2D}, + {0x022E, 0x09C1}, + {0x0234, 0x3311}, + {0x0236, 0x3311}, + {0x0238, 0x3311}, + {0x023A, 0x2222}, + {0x0250, 0x0000}, + {0x0252, 0x0006}, + {0x0254, 0x0000}, + {0x0256, 0x0000}, + {0x0258, 0x0000}, + {0x025A, 0x0000}, + {0x025C, 0x0000}, + {0x025E, 0x0202}, + {0x0268, 0x00CD}, + {0x0440, 0x0002}, + {0x0F00, 0x0400}, + {0x0F04, 0x0004}, + {0x0F06, 0x0002}, + {0x0B02, 0x0100}, + {0x0B04, 0x00FC}, + {0x0B12, 0x0660}, + {0x0B14, 0x04C8}, + {0x0B20, 0x0200}, + {0x1100, 0x1100}, + {0x1102, 0x0008}, + {0x1108, 0x0402}, + {0x1118, 0x0000}, + {0x0A10, 0xB060}, + {0x0C14, 0x0008}, + {0x0C18, 0x0CC0}, + {0x0C1A, 0x04C8}, + {0x0730, 0x0001}, + {0x0732, 0x0000}, + {0x0734, 0x0300}, + {0x0736, 0x004B}, + {0x0738, 0x0001}, + {0x073C, 0x0900}, + {0x0740, 0x0000}, + {0x0742, 0x0000}, + {0x0744, 0x0300}, + {0x0746, 0x007D}, + {0x0748, 0x0002}, + {0x074A, 0x0900}, + {0x074C, 0x0100}, + {0x074E, 0x0100}, + {0x0750, 0x0000}, + {0x1200, 0x0946}, + {0x1202, 0x1A00}, + {0x120E, 0x6027}, + {0x1210, 0x8027}, + {0x1246, 0x0105}, + {0x1000, 0x0300}, + {0x1002, 0xC311}, + {0x1004, 0x2BB0}, + {0x1010, 0x042B}, + {0x1012, 0x0012}, + {0x1014, 0x0020}, + {0x1016, 0x0020}, + {0x101A, 0x0020}, + {0x1020, 0xC103}, + {0x1022, 0x040F}, + {0x1024, 0x0304}, + {0x1026, 0x0607}, + {0x1028, 0x0D06}, + {0x102A, 0x0605}, + {0x102C, 0x0C00}, + {0x1038, 0x0000}, + {0x103E, 0x0101}, + {0x1040, 0x0000}, + {0x1042, 0x0008}, + {0x1044, 0x0120}, + {0x1046, 0x01B0}, + {0x1048, 0x0090}, + {0x1066, 0x043B}, + {0x1600, 0x0400}, + {0x1608, 0x0028}, + {0x160A, 0x0C80}, + {0x160C, 0x001A}, + {0x160E, 0x0960}, + {0x0252, 0x0009}, + {0x0202, 0x0000}, +}; + +static const char * const hi847_test_pattern_menu[] = { + "No Pattern", + "Solid Colour", + "100% Colour Bars", + "Fade To Grey Colour Bars", + "PN9", + "Horizontal Gradient Pattern", + "Vertical Gradient Pattern", + "Check Board", + "Slant Pattern", +}; + +static const s64 link_freq_menu_items[] = { + HI847_LINK_FREQ_400MHZ, + HI847_LINK_FREQ_200MHZ, +}; + +static const struct hi847_link_freq_config link_freq_configs[] = { + [HI847_LINK_FREQ_400MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_lane_4), + .regs = mipi_data_rate_lane_4, + } + }, + [HI847_LINK_FREQ_200MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_lane_4), + .regs = mipi_data_rate_lane_4, + } + } +}; + +static const struct hi847_mode supported_modes[] = { + { + .width = 3264, + .height = 2448, + .fll_def = HI847_FLL_30FPS, + .fll_min = HI847_FLL_30FPS_MIN, + .llp = 0x033C, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + .link_freq_index = HI847_LINK_FREQ_400MHZ_INDEX, + }, + { + .width = 1632, + .height = 1224, + .fll_def = HI847_FLL_60FPS, + .fll_min = HI847_FLL_60FPS_MIN, + .llp = 0x033C, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1632x1224_regs), + .regs = mode_1632x1224_regs, + }, + .link_freq_index = HI847_LINK_FREQ_200MHZ_INDEX, + } +}; + +struct hi847 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + /* Current mode */ + const struct hi847_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * HI847_DATA_LANES; + + do_div(pixel_rate, HI847_RGB_DEPTH); + + return pixel_rate; +} + +static int hi847_read_reg(struct hi847 *hi847, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int hi847_write_reg(struct hi847 *hi847, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int hi847_write_reg_list(struct hi847 *hi847, + const struct hi847_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = hi847_write_reg(hi847, r_list->regs[i].address, + HI847_REG_VALUE_16BIT, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int hi847_update_digital_gain(struct hi847 *hi847, u32 d_gain) +{ + int ret; + + ret = hi847_write_reg(hi847, HI847_REG_MWB_GR_GAIN, + HI847_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi847_write_reg(hi847, HI847_REG_MWB_GB_GAIN, + HI847_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi847_write_reg(hi847, HI847_REG_MWB_R_GAIN, + HI847_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_MWB_B_GAIN, + HI847_REG_VALUE_16BIT, d_gain); +} + +static int hi847_test_pattern(struct hi847 *hi847, u32 pattern) +{ + int ret; + u32 val; + + if (pattern) { + ret = hi847_read_reg(hi847, HI847_REG_ISP, + HI847_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + ret = hi847_write_reg(hi847, HI847_REG_ISP, + HI847_REG_VALUE_16BIT, + val | HI847_REG_ISP_TPG_EN); + if (ret) + return ret; + } + + ret = hi847_read_reg(hi847, HI847_REG_TEST_PATTERN, + HI847_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_TEST_PATTERN, + HI847_REG_VALUE_16BIT, val | pattern << 8); +} + +static int hi847_grbg_shift(struct hi847 *hi847) +{ + int ret; + int hflip, vflip; + + /* regs shift for full size */ + static const u32 FORMAT_X_SHIFT_1[2][2] = { + { 0x0008, 0x0007, }, + { 0x0008, 0x0007, }, + }; + + static const u32 FORMAT_Y_SHIFT_1[2][2] = { + { 0x0002, 0x0002, }, + { 0x0001, 0x0001, }, + }; + + /* regs shift for binning size */ + static const u32 FORMAT_X_SHIFT_2[2][2] = { + { 0x0004, 0x0003, }, + { 0x0004, 0x0003, }, + }; + + static const u32 FORMAT_Y_SHIFT_2[2][2] = { + { 0x0002, 0x0002, }, + { 0x0001, 0x0001, }, + }; + + hflip = hi847->hflip->val; + vflip = hi847->vflip->val; + + if (hi847->cur_mode->width == 3264) { + ret = hi847_write_reg(hi847, HI847_REG_FORMAT_X, + HI847_REG_VALUE_16BIT, + FORMAT_X_SHIFT_1[vflip][hflip]); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_FORMAT_Y, + HI847_REG_VALUE_16BIT, + FORMAT_Y_SHIFT_1[vflip][hflip]); + } else { + ret = hi847_write_reg(hi847, HI847_REG_FORMAT_X, + HI847_REG_VALUE_16BIT, + FORMAT_X_SHIFT_2[vflip][hflip]); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_FORMAT_Y, + HI847_REG_VALUE_16BIT, + FORMAT_Y_SHIFT_2[vflip][hflip]); + } +} + +static int hi847_set_ctrl_hflip(struct hi847 *hi847, u32 ctrl_val) +{ + int ret; + u32 val; + + ret = hi847_read_reg(hi847, HI847_REG_MIRROR_FLIP, + HI847_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + ret = hi847_grbg_shift(hi847); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_MIRROR_FLIP, + HI847_REG_VALUE_16BIT, + ctrl_val ? val | BIT(8) : val & ~BIT(8)); +} + +static int hi847_set_ctrl_vflip(struct hi847 *hi847, u8 ctrl_val) +{ + int ret; + u32 val; + + ret = hi847_read_reg(hi847, HI847_REG_MIRROR_FLIP, + HI847_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + ret = hi847_grbg_shift(hi847); + if (ret) + return ret; + + return hi847_write_reg(hi847, HI847_REG_MIRROR_FLIP, + HI847_REG_VALUE_16BIT, + ctrl_val ? val | BIT(9) : val & ~BIT(9)); +} + +static int hi847_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hi847 *hi847 = container_of(ctrl->handler, + struct hi847, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = hi847->cur_mode->height + ctrl->val - + HI847_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(hi847->exposure, + hi847->exposure->minimum, + exposure_max, hi847->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = hi847_write_reg(hi847, HI847_REG_ANALOG_GAIN, + HI847_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = hi847_update_digital_gain(hi847, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = hi847_write_reg(hi847, HI847_REG_EXPOSURE, + HI847_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = hi847_write_reg(hi847, HI847_REG_FLL, + HI847_REG_VALUE_16BIT, + hi847->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = hi847_test_pattern(hi847, ctrl->val); + break; + + case V4L2_CID_HFLIP: + hi847_set_ctrl_hflip(hi847, ctrl->val); + break; + + case V4L2_CID_VFLIP: + hi847_set_ctrl_vflip(hi847, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops hi847_ctrl_ops = { + .s_ctrl = hi847_set_ctrl, +}; + +static int hi847_init_controls(struct hi847 *hi847) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &hi847->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &hi847->mutex; + hi847->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (hi847->link_freq) + hi847->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hi847->pixel_rate = v4l2_ctrl_new_std + (ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(HI847_LINK_FREQ_400MHZ_INDEX), + 1, + to_pixel_rate(HI847_LINK_FREQ_400MHZ_INDEX)); + hi847->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_VBLANK, + hi847->cur_mode->fll_min - + hi847->cur_mode->height, + HI847_FLL_MAX - + hi847->cur_mode->height, 1, + hi847->cur_mode->fll_def - + hi847->cur_mode->height); + + h_blank = hi847->cur_mode->llp - hi847->cur_mode->width; + + hi847->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (hi847->hblank) + hi847->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + HI847_ANAL_GAIN_MIN, HI847_ANAL_GAIN_MAX, + HI847_ANAL_GAIN_STEP, HI847_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + HI847_DGTL_GAIN_MIN, HI847_DGTL_GAIN_MAX, + HI847_DGTL_GAIN_STEP, HI847_DGTL_GAIN_DEFAULT); + exposure_max = hi847->cur_mode->fll_def - HI847_EXPOSURE_MAX_MARGIN; + hi847->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_EXPOSURE, + HI847_EXPOSURE_MIN, exposure_max, + HI847_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(hi847_test_pattern_menu) - 1, + 0, 0, hi847_test_pattern_menu); + hi847->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + hi847->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &hi847_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + hi847->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void hi847_assign_pad_format(const struct hi847_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int hi847_start_streaming(struct hi847 *hi847) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + const struct hi847_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = hi847->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = hi847_write_reg_list(hi847, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &hi847->cur_mode->reg_list; + ret = hi847_write_reg_list(hi847, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(hi847->sd.ctrl_handler); + if (ret) + return ret; + + ret = hi847_write_reg(hi847, HI847_REG_MODE_TG, + HI847_REG_VALUE_16BIT, HI847_REG_MODE_TG_ENABLE); + + ret = hi847_write_reg(hi847, HI847_REG_MODE_SELECT, + HI847_REG_VALUE_16BIT, HI847_MODE_STREAMING); + + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void hi847_stop_streaming(struct hi847 *hi847) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + + if (hi847_write_reg(hi847, HI847_REG_MODE_TG, + HI847_REG_VALUE_16BIT, HI847_REG_MODE_TG_DISABLE)) + dev_err(&client->dev, "failed to set stream 0x%x", + HI847_REG_MODE_TG); + + if (hi847_write_reg(hi847, HI847_REG_MODE_SELECT, + HI847_REG_VALUE_16BIT, HI847_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream 0x%x", + HI847_REG_MODE_SELECT); +} + +static int hi847_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct hi847 *hi847 = to_hi847(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (hi847->streaming == enable) + return 0; + + mutex_lock(&hi847->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&hi847->mutex); + return ret; + } + + ret = hi847_start_streaming(hi847); + if (ret) { + enable = 0; + hi847_stop_streaming(hi847); + pm_runtime_put(&client->dev); + } + } else { + hi847_stop_streaming(hi847); + pm_runtime_put(&client->dev); + } + + hi847->streaming = enable; + mutex_unlock(&hi847->mutex); + + return ret; +} + +static int __maybe_unused hi847_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi847 *hi847 = to_hi847(sd); + + mutex_lock(&hi847->mutex); + if (hi847->streaming) + hi847_stop_streaming(hi847); + + mutex_unlock(&hi847->mutex); + + return 0; +} + +static int __maybe_unused hi847_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi847 *hi847 = to_hi847(sd); + int ret; + + mutex_lock(&hi847->mutex); + if (hi847->streaming) { + ret = hi847_start_streaming(hi847); + if (ret) + goto error; + } + + mutex_unlock(&hi847->mutex); + + return 0; + +error: + hi847_stop_streaming(hi847); + hi847->streaming = 0; + mutex_unlock(&hi847->mutex); + return ret; +} + +static int hi847_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct hi847 *hi847 = to_hi847(sd); + const struct hi847_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&hi847->mutex); + hi847_assign_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + fmt->format; + } else { + hi847->cur_mode = mode; + __v4l2_ctrl_s_ctrl(hi847->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(hi847->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->fll_def - mode->height; + __v4l2_ctrl_modify_range(hi847->vblank, + mode->fll_min - mode->height, + HI847_FLL_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(hi847->vblank, vblank_def); + + h_blank = hi847->cur_mode->llp - hi847->cur_mode->width; + + __v4l2_ctrl_modify_range(hi847->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&hi847->mutex); + + return 0; +} + +static int hi847_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct hi847 *hi847 = to_hi847(sd); + + mutex_lock(&hi847->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&hi847->sd, + sd_state, + fmt->pad); + else + hi847_assign_pad_format(hi847->cur_mode, &fmt->format); + + mutex_unlock(&hi847->mutex); + + return 0; +} + +static int hi847_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int hi847_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int hi847_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct hi847 *hi847 = to_hi847(sd); + + mutex_lock(&hi847->mutex); + hi847_assign_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&hi847->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops hi847_video_ops = { + .s_stream = hi847_set_stream, +}; + +static const struct v4l2_subdev_pad_ops hi847_pad_ops = { + .set_fmt = hi847_set_format, + .get_fmt = hi847_get_format, + .enum_mbus_code = hi847_enum_mbus_code, + .enum_frame_size = hi847_enum_frame_size, +}; + +static const struct v4l2_subdev_ops hi847_subdev_ops = { + .video = &hi847_video_ops, + .pad = &hi847_pad_ops, +}; + +static const struct media_entity_operations hi847_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops hi847_internal_ops = { + .open = hi847_open, +}; + +static int hi847_identify_module(struct hi847 *hi847) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi847->sd); + int ret; + u32 val; + + ret = hi847_read_reg(hi847, HI847_REG_CHIP_ID, + HI847_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + if (val != HI847_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + HI847_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int hi847_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != HI847_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != HI847_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int hi847_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi847 *hi847 = to_hi847(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&hi847->mutex); + + return 0; +} + +static int hi847_probe(struct i2c_client *client) +{ + struct hi847 *hi847; + int ret; + + hi847 = devm_kzalloc(&client->dev, sizeof(*hi847), GFP_KERNEL); + if (!hi847) + return -ENOMEM; + + ret = hi847_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to get HW configuration: %d", + ret); + return ret; + } + + v4l2_i2c_subdev_init(&hi847->sd, client, &hi847_subdev_ops); + ret = hi847_identify_module(hi847); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&hi847->mutex); + hi847->cur_mode = &supported_modes[0]; + ret = hi847_init_controls(hi847); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + hi847->sd.internal_ops = &hi847_internal_ops; + hi847->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + hi847->sd.entity.ops = &hi847_subdev_entity_ops; + hi847->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + hi847->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&hi847->sd.entity, 1, &hi847->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&hi847->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&hi847->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(hi847->sd.ctrl_handler); + mutex_destroy(&hi847->mutex); + + return ret; +} + +static const struct dev_pm_ops hi847_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hi847_suspend, hi847_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hi847_acpi_ids[] = { + {"HYV0847"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, hi847_acpi_ids); +#endif + +static struct i2c_driver hi847_i2c_driver = { + .driver = { + .name = "hi847", + .pm = &hi847_pm_ops, + .acpi_match_table = ACPI_PTR(hi847_acpi_ids), + }, + .probe_new = hi847_probe, + .remove = hi847_remove, +}; + +module_i2c_driver(hi847_i2c_driver); + +MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_DESCRIPTION("Hynix HI847 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 2aa15b9c23cc..7de1f2948e53 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -11,13 +11,11 @@ #include <linux/clk.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c new file mode 100644 index 000000000000..dc3068549dfa --- /dev/null +++ b/drivers/media/i2c/isl7998x.c @@ -0,0 +1,1628 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intersil ISL7998x analog to MIPI CSI-2 or BT.656 decoder driver. + * + * Copyright (C) 2018-2019 Marek Vasut <marex@denx.de> + * Copyright (C) 2021 Michael Tretter <kernel@pengutronix.de> + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> + +/* + * This control allows to activate and deactivate the test pattern on + * selected output channels. + * This value is ISL7998x specific. + */ +#define V4L2_CID_TEST_PATTERN_CHANNELS (V4L2_CID_USER_ISL7998X_BASE + 0) + +/* + * This control allows to specify the color of the test pattern. + * This value is ISL7998x specific. + */ +#define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_ISL7998X_BASE + 1) + +/* + * This control allows to specify the bar pattern in the test pattern. + * This value is ISL7998x specific. + */ +#define V4L2_CID_TEST_PATTERN_BARS (V4L2_CID_USER_ISL7998X_BASE + 2) + +#define ISL7998X_INPUTS 4 + +#define ISL7998X_REG(page, reg) (((page) << 8) | (reg)) + +#define ISL7998X_REG_PN_SIZE 256 +#define ISL7998X_REG_PN_BASE(n) ((n) * ISL7998X_REG_PN_SIZE) + +#define ISL7998X_REG_PX_DEC_PAGE(page) ISL7998X_REG((page), 0xff) +#define ISL7998X_REG_PX_DEC_PAGE_MASK 0xf +#define ISL7998X_REG_P0_PRODUCT_ID_CODE ISL7998X_REG(0, 0x00) +#define ISL7998X_REG_P0_PRODUCT_REV_CODE ISL7998X_REG(0, 0x01) +#define ISL7998X_REG_P0_SW_RESET_CTL ISL7998X_REG(0, 0x02) +#define ISL7998X_REG_P0_IO_BUFFER_CTL ISL7998X_REG(0, 0x03) +#define ISL7998X_REG_P0_IO_BUFFER_CTL_1_1 ISL7998X_REG(0, 0x04) +#define ISL7998X_REG_P0_IO_PAD_PULL_EN_CTL ISL7998X_REG(0, 0x05) +#define ISL7998X_REG_P0_IO_BUFFER_CTL_1_2 ISL7998X_REG(0, 0x06) +#define ISL7998X_REG_P0_VIDEO_IN_CHAN_CTL ISL7998X_REG(0, 0x07) +#define ISL7998X_REG_P0_CLK_CTL_1 ISL7998X_REG(0, 0x08) +#define ISL7998X_REG_P0_CLK_CTL_2 ISL7998X_REG(0, 0x09) +#define ISL7998X_REG_P0_CLK_CTL_3 ISL7998X_REG(0, 0x0a) +#define ISL7998X_REG_P0_CLK_CTL_4 ISL7998X_REG(0, 0x0b) +#define ISL7998X_REG_P0_MPP1_SYNC_CTL ISL7998X_REG(0, 0x0c) +#define ISL7998X_REG_P0_MPP2_SYNC_CTL ISL7998X_REG(0, 0x0d) +#define ISL7998X_REG_P0_IRQ_SYNC_CTL ISL7998X_REG(0, 0x0e) +#define ISL7998X_REG_P0_INTERRUPT_STATUS ISL7998X_REG(0, 0x10) +#define ISL7998X_REG_P0_CHAN_1_IRQ ISL7998X_REG(0, 0x11) +#define ISL7998X_REG_P0_CHAN_2_IRQ ISL7998X_REG(0, 0x12) +#define ISL7998X_REG_P0_CHAN_3_IRQ ISL7998X_REG(0, 0x13) +#define ISL7998X_REG_P0_CHAN_4_IRQ ISL7998X_REG(0, 0x14) +#define ISL7998X_REG_P0_SHORT_DIAG_IRQ ISL7998X_REG(0, 0x15) +#define ISL7998X_REG_P0_CHAN_1_IRQ_EN ISL7998X_REG(0, 0x16) +#define ISL7998X_REG_P0_CHAN_2_IRQ_EN ISL7998X_REG(0, 0x17) +#define ISL7998X_REG_P0_CHAN_3_IRQ_EN ISL7998X_REG(0, 0x18) +#define ISL7998X_REG_P0_CHAN_4_IRQ_EN ISL7998X_REG(0, 0x19) +#define ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN ISL7998X_REG(0, 0x1a) +#define ISL7998X_REG_P0_CHAN_1_STATUS ISL7998X_REG(0, 0x1b) +#define ISL7998X_REG_P0_CHAN_2_STATUS ISL7998X_REG(0, 0x1c) +#define ISL7998X_REG_P0_CHAN_3_STATUS ISL7998X_REG(0, 0x1d) +#define ISL7998X_REG_P0_CHAN_4_STATUS ISL7998X_REG(0, 0x1e) +#define ISL7998X_REG_P0_SHORT_DIAG_STATUS ISL7998X_REG(0, 0x1f) +#define ISL7998X_REG_P0_CLOCK_DELAY ISL7998X_REG(0, 0x20) + +#define ISL7998X_REG_PX_DEC_INPUT_FMT(pg) ISL7998X_REG((pg), 0x02) +#define ISL7998X_REG_PX_DEC_STATUS_1(pg) ISL7998X_REG((pg), 0x03) +#define ISL7998X_REG_PX_DEC_STATUS_1_VDLOSS BIT(7) +#define ISL7998X_REG_PX_DEC_STATUS_1_HLOCK BIT(6) +#define ISL7998X_REG_PX_DEC_STATUS_1_VLOCK BIT(3) +#define ISL7998X_REG_PX_DEC_HS_DELAY_CTL(pg) ISL7998X_REG((pg), 0x04) +#define ISL7998X_REG_PX_DEC_ANCTL(pg) ISL7998X_REG((pg), 0x06) +#define ISL7998X_REG_PX_DEC_CROP_HI(pg) ISL7998X_REG((pg), 0x07) +#define ISL7998X_REG_PX_DEC_VDELAY_LO(pg) ISL7998X_REG((pg), 0x08) +#define ISL7998X_REG_PX_DEC_VACTIVE_LO(pg) ISL7998X_REG((pg), 0x09) +#define ISL7998X_REG_PX_DEC_HDELAY_LO(pg) ISL7998X_REG((pg), 0x0a) +#define ISL7998X_REG_PX_DEC_HACTIVE_LO(pg) ISL7998X_REG((pg), 0x0b) +#define ISL7998X_REG_PX_DEC_CNTRL1(pg) ISL7998X_REG((pg), 0x0c) +#define ISL7998X_REG_PX_DEC_CSC_CTL(pg) ISL7998X_REG((pg), 0x0d) +#define ISL7998X_REG_PX_DEC_BRIGHT(pg) ISL7998X_REG((pg), 0x10) +#define ISL7998X_REG_PX_DEC_CONTRAST(pg) ISL7998X_REG((pg), 0x11) +#define ISL7998X_REG_PX_DEC_SHARPNESS(pg) ISL7998X_REG((pg), 0x12) +#define ISL7998X_REG_PX_DEC_SAT_U(pg) ISL7998X_REG((pg), 0x13) +#define ISL7998X_REG_PX_DEC_SAT_V(pg) ISL7998X_REG((pg), 0x14) +#define ISL7998X_REG_PX_DEC_HUE(pg) ISL7998X_REG((pg), 0x15) +#define ISL7998X_REG_PX_DEC_VERT_PEAK(pg) ISL7998X_REG((pg), 0x17) +#define ISL7998X_REG_PX_DEC_CORING(pg) ISL7998X_REG((pg), 0x18) +#define ISL7998X_REG_PX_DEC_SDT(pg) ISL7998X_REG((pg), 0x1c) +#define ISL7998X_REG_PX_DEC_SDT_DET BIT(7) +#define ISL7998X_REG_PX_DEC_SDT_NOW GENMASK(6, 4) +#define ISL7998X_REG_PX_DEC_SDT_STANDARD GENMASK(2, 0) +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_M 0 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL 1 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_SECAM 2 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_443 3 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_M 4 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_CN 5 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_60 6 +#define ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN 7 +#define ISL7998X_REG_PX_DEC_SDTR(pg) ISL7998X_REG((pg), 0x1d) +#define ISL7998X_REG_PX_DEC_SDTR_ATSTART BIT(7) +#define ISL7998X_REG_PX_DEC_CLMPG(pg) ISL7998X_REG((pg), 0x20) +#define ISL7998X_REG_PX_DEC_IAGC(pg) ISL7998X_REG((pg), 0x21) +#define ISL7998X_REG_PX_DEC_AGCGAIN(pg) ISL7998X_REG((pg), 0x22) +#define ISL7998X_REG_PX_DEC_PEAKWT(pg) ISL7998X_REG((pg), 0x23) +#define ISL7998X_REG_PX_DEC_CLMPL(pg) ISL7998X_REG((pg), 0x24) +#define ISL7998X_REG_PX_DEC_SYNCT(pg) ISL7998X_REG((pg), 0x25) +#define ISL7998X_REG_PX_DEC_MISSCNT(pg) ISL7998X_REG((pg), 0x26) +#define ISL7998X_REG_PX_DEC_PCLAMP(pg) ISL7998X_REG((pg), 0x27) +#define ISL7998X_REG_PX_DEC_VERT_CTL_1(pg) ISL7998X_REG((pg), 0x28) +#define ISL7998X_REG_PX_DEC_VERT_CTL_2(pg) ISL7998X_REG((pg), 0x29) +#define ISL7998X_REG_PX_DEC_CLR_KILL_LVL(pg) ISL7998X_REG((pg), 0x2a) +#define ISL7998X_REG_PX_DEC_COMB_FILTER_CTL(pg) ISL7998X_REG((pg), 0x2b) +#define ISL7998X_REG_PX_DEC_LUMA_DELAY(pg) ISL7998X_REG((pg), 0x2c) +#define ISL7998X_REG_PX_DEC_MISC1(pg) ISL7998X_REG((pg), 0x2d) +#define ISL7998X_REG_PX_DEC_MISC2(pg) ISL7998X_REG((pg), 0x2e) +#define ISL7998X_REG_PX_DEC_MISC3(pg) ISL7998X_REG((pg), 0x2f) +#define ISL7998X_REG_PX_DEC_MVSN(pg) ISL7998X_REG((pg), 0x30) +#define ISL7998X_REG_PX_DEC_CSTATUS2(pg) ISL7998X_REG((pg), 0x31) +#define ISL7998X_REG_PX_DEC_HFREF(pg) ISL7998X_REG((pg), 0x32) +#define ISL7998X_REG_PX_DEC_CLMD(pg) ISL7998X_REG((pg), 0x33) +#define ISL7998X_REG_PX_DEC_ID_DET_CTL(pg) ISL7998X_REG((pg), 0x34) +#define ISL7998X_REG_PX_DEC_CLCNTL(pg) ISL7998X_REG((pg), 0x35) +#define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_1(pg) ISL7998X_REG((pg), 0x36) +#define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_2(pg) ISL7998X_REG((pg), 0x37) +#define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_3(pg) ISL7998X_REG((pg), 0x38) +#define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_4(pg) ISL7998X_REG((pg), 0x39) +#define ISL7998X_REG_PX_DEC_SHORT_DET_CTL(pg) ISL7998X_REG((pg), 0x3a) +#define ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(pg) ISL7998X_REG((pg), 0x3b) +#define ISL7998X_REG_PX_DEC_AFE_TST_MUX_CTL(pg) ISL7998X_REG((pg), 0x3c) +#define ISL7998X_REG_PX_DEC_DATA_CONV(pg) ISL7998X_REG((pg), 0x3d) +#define ISL7998X_REG_PX_DEC_INTERNAL_TEST(pg) ISL7998X_REG((pg), 0x3f) +#define ISL7998X_REG_PX_DEC_H_DELAY_CTL(pg) ISL7998X_REG((pg), 0x43) +#define ISL7998X_REG_PX_DEC_H_DELAY_II_HI(pg) ISL7998X_REG((pg), 0x44) +#define ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(pg) ISL7998X_REG((pg), 0x45) + +#define ISL7998X_REG_PX_ACA_CTL_1(pg) ISL7998X_REG((pg), 0x80) +#define ISL7998X_REG_PX_ACA_GAIN_CTL(pg) ISL7998X_REG((pg), 0x81) +#define ISL7998X_REG_PX_ACA_Y_AVG_HI_LIMIT(pg) ISL7998X_REG((pg), 0x82) +#define ISL7998X_REG_PX_ACA_Y_AVG_LO_LIMIT(pg) ISL7998X_REG((pg), 0x83) +#define ISL7998X_REG_PX_ACA_Y_DET_THRESHOLD(pg) ISL7998X_REG((pg), 0x84) +#define ISL7998X_REG_PX_ACA_BLACK_LVL(pg) ISL7998X_REG((pg), 0x85) +#define ISL7998X_REG_PX_ACA_CENTER_LVL(pg) ISL7998X_REG((pg), 0x86) +#define ISL7998X_REG_PX_ACA_WHITE_LVL(pg) ISL7998X_REG((pg), 0x87) +#define ISL7998X_REG_PX_ACA_MEAN_OFF_LIMIT(pg) ISL7998X_REG((pg), 0x88) +#define ISL7998X_REG_PX_ACA_MEAN_OFF_UPGAIN(pg) ISL7998X_REG((pg), 0x89) +#define ISL7998X_REG_PX_ACA_MEAN_OFF_SLOPE(pg) ISL7998X_REG((pg), 0x8a) +#define ISL7998X_REG_PX_ACA_MEAN_OFF_DNGAIN(pg) ISL7998X_REG((pg), 0x8b) +#define ISL7998X_REG_PX_ACA_DELTA_CO_THRES(pg) ISL7998X_REG((pg), 0x8c) +#define ISL7998X_REG_PX_ACA_DELTA_SLOPE(pg) ISL7998X_REG((pg), 0x8d) +#define ISL7998X_REG_PX_ACA_LO_HI_AVG_THRES(pg) ISL7998X_REG((pg), 0x8e) +#define ISL7998X_REG_PX_ACA_LO_MAX_LVL_CTL(pg) ISL7998X_REG((pg), 0x8f) +#define ISL7998X_REG_PX_ACA_HI_MAX_LVL_CTL(pg) ISL7998X_REG((pg), 0x90) +#define ISL7998X_REG_PX_ACA_LO_UPGAIN_CTL(pg) ISL7998X_REG((pg), 0x91) +#define ISL7998X_REG_PX_ACA_LO_DNGAIN_CTL(pg) ISL7998X_REG((pg), 0x92) +#define ISL7998X_REG_PX_ACA_HI_UPGAIN_CTL(pg) ISL7998X_REG((pg), 0x93) +#define ISL7998X_REG_PX_ACA_HI_DNGAIN_CTL(pg) ISL7998X_REG((pg), 0x94) +#define ISL7998X_REG_PX_ACA_LOPASS_FLT_COEF(pg) ISL7998X_REG((pg), 0x95) +#define ISL7998X_REG_PX_ACA_PDF_INDEX(pg) ISL7998X_REG((pg), 0x96) +#define ISL7998X_REG_PX_ACA_HIST_WIN_H_STT(pg) ISL7998X_REG((pg), 0x97) +#define ISL7998X_REG_PX_ACA_HIST_WIN_H_SZ1(pg) ISL7998X_REG((pg), 0x98) +#define ISL7998X_REG_PX_ACA_HIST_WIN_H_SZ2(pg) ISL7998X_REG((pg), 0x99) +#define ISL7998X_REG_PX_ACA_HIST_WIN_V_STT(pg) ISL7998X_REG((pg), 0x9a) +#define ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ1(pg) ISL7998X_REG((pg), 0x9b) +#define ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(pg) ISL7998X_REG((pg), 0x9c) +#define ISL7998X_REG_PX_ACA_Y_AVG(pg) ISL7998X_REG((pg), 0xa0) +#define ISL7998X_REG_PX_ACA_Y_AVG_LIM(pg) ISL7998X_REG((pg), 0xa1) +#define ISL7998X_REG_PX_ACA_LO_AVG(pg) ISL7998X_REG((pg), 0xa2) +#define ISL7998X_REG_PX_ACA_HI_AVG(pg) ISL7998X_REG((pg), 0xa3) +#define ISL7998X_REG_PX_ACA_Y_MAX(pg) ISL7998X_REG((pg), 0xa4) +#define ISL7998X_REG_PX_ACA_Y_MIN(pg) ISL7998X_REG((pg), 0xa5) +#define ISL7998X_REG_PX_ACA_MOFFSET(pg) ISL7998X_REG((pg), 0xa6) +#define ISL7998X_REG_PX_ACA_LO_GAIN(pg) ISL7998X_REG((pg), 0xa7) +#define ISL7998X_REG_PX_ACA_HI_GAIN(pg) ISL7998X_REG((pg), 0xa8) +#define ISL7998X_REG_PX_ACA_LL_SLOPE(pg) ISL7998X_REG((pg), 0xa9) +#define ISL7998X_REG_PX_ACA_LH_SLOPE(pg) ISL7998X_REG((pg), 0xaa) +#define ISL7998X_REG_PX_ACA_HL_SLOPE(pg) ISL7998X_REG((pg), 0xab) +#define ISL7998X_REG_PX_ACA_HH_SLOPE(pg) ISL7998X_REG((pg), 0xac) +#define ISL7998X_REG_PX_ACA_X_LOW(pg) ISL7998X_REG((pg), 0xad) +#define ISL7998X_REG_PX_ACA_X_MEAN(pg) ISL7998X_REG((pg), 0xae) +#define ISL7998X_REG_PX_ACA_X_HIGH(pg) ISL7998X_REG((pg), 0xaf) +#define ISL7998X_REG_PX_ACA_Y_LOW(pg) ISL7998X_REG((pg), 0xb0) +#define ISL7998X_REG_PX_ACA_Y_MEAN(pg) ISL7998X_REG((pg), 0xb1) +#define ISL7998X_REG_PX_ACA_Y_HIGH(pg) ISL7998X_REG((pg), 0xb2) +#define ISL7998X_REG_PX_ACA_CTL_2(pg) ISL7998X_REG((pg), 0xb3) +#define ISL7998X_REG_PX_ACA_CTL_3(pg) ISL7998X_REG((pg), 0xb4) +#define ISL7998X_REG_PX_ACA_CTL_4(pg) ISL7998X_REG((pg), 0xb5) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(pg) ISL7998X_REG((pg), 0xc0) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TL_H(pg) ISL7998X_REG((pg), 0xc1) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TL_L(pg) ISL7998X_REG((pg), 0xc2) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TL_H(pg) ISL7998X_REG((pg), 0xc3) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TL_L(pg) ISL7998X_REG((pg), 0xc4) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TR_H(pg) ISL7998X_REG((pg), 0xc5) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TR_L(pg) ISL7998X_REG((pg), 0xc6) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TR_H(pg) ISL7998X_REG((pg), 0xc7) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TR_L(pg) ISL7998X_REG((pg), 0xc8) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BL_H(pg) ISL7998X_REG((pg), 0xc9) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BL_L(pg) ISL7998X_REG((pg), 0xca) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BL_H(pg) ISL7998X_REG((pg), 0xcb) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BL_L(pg) ISL7998X_REG((pg), 0xcc) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BR_H(pg) ISL7998X_REG((pg), 0xcd) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BR_L(pg) ISL7998X_REG((pg), 0xce) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BR_H(pg) ISL7998X_REG((pg), 0xcf) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BR_L(pg) ISL7998X_REG((pg), 0xd0) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_LM_H(pg) ISL7998X_REG((pg), 0xd1) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_LM_L(pg) ISL7998X_REG((pg), 0xd2) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_LM_H(pg) ISL7998X_REG((pg), 0xd3) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_LM_L(pg) ISL7998X_REG((pg), 0xd4) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TM_H(pg) ISL7998X_REG((pg), 0xd5) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TM_L(pg) ISL7998X_REG((pg), 0xd6) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TM_H(pg) ISL7998X_REG((pg), 0xd7) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TM_L(pg) ISL7998X_REG((pg), 0xd8) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BM_H(pg) ISL7998X_REG((pg), 0xd9) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BM_L(pg) ISL7998X_REG((pg), 0xda) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BM_H(pg) ISL7998X_REG((pg), 0xdb) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BM_L(pg) ISL7998X_REG((pg), 0xdc) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_RM_H(pg) ISL7998X_REG((pg), 0xdd) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_X_RM_L(pg) ISL7998X_REG((pg), 0xde) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_RM_H(pg) ISL7998X_REG((pg), 0xdf) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_RM_L(pg) ISL7998X_REG((pg), 0xe0) +#define ISL7998X_REG_PX_ACA_HIST_DATA_LO(pg) ISL7998X_REG((pg), 0xe1) +#define ISL7998X_REG_PX_ACA_HIST_DATA_MID(pg) ISL7998X_REG((pg), 0xe2) +#define ISL7998X_REG_PX_ACA_HIST_DATA_HI(pg) ISL7998X_REG((pg), 0xe3) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_CLR(pg) ISL7998X_REG((pg), 0xe4) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_CB_CLR(pg) ISL7998X_REG((pg), 0xe5) +#define ISL7998X_REG_PX_ACA_FLEX_WIN_CR_CLR(pg) ISL7998X_REG((pg), 0xe6) +#define ISL7998X_REG_PX_ACA_XFER_HIST_HOST(pg) ISL7998X_REG((pg), 0xe7) + +#define ISL7998X_REG_P5_LI_ENGINE_CTL ISL7998X_REG(5, 0x00) +#define ISL7998X_REG_P5_LI_ENGINE_LINE_CTL ISL7998X_REG(5, 0x01) +#define ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH ISL7998X_REG(5, 0x02) +#define ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL ISL7998X_REG(5, 0x03) +#define ISL7998X_REG_P5_LI_ENGINE_VC_ASSIGNMENT ISL7998X_REG(5, 0x04) +#define ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL ISL7998X_REG(5, 0x05) +#define ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL ISL7998X_REG(5, 0x06) +#define ISL7998X_REG_P5_MIPI_READ_START_CTL ISL7998X_REG(5, 0x07) +#define ISL7998X_REG_P5_PSEUDO_FRM_FIELD_CTL ISL7998X_REG(5, 0x08) +#define ISL7998X_REG_P5_ONE_FIELD_MODE_CTL ISL7998X_REG(5, 0x09) +#define ISL7998X_REG_P5_MIPI_INT_HW_TST_CTR ISL7998X_REG(5, 0x0a) +#define ISL7998X_REG_P5_TP_GEN_BAR_PATTERN ISL7998X_REG(5, 0x0b) +#define ISL7998X_REG_P5_MIPI_PCNT_PSFRM ISL7998X_REG(5, 0x0c) +#define ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL ISL7998X_REG(5, 0x0d) +#define ISL7998X_REG_P5_MIPI_VBLANK_PSFRM ISL7998X_REG(5, 0x0e) +#define ISL7998X_REG_P5_LI_ENGINE_CTL_2 ISL7998X_REG(5, 0x0f) +#define ISL7998X_REG_P5_MIPI_WCNT_1 ISL7998X_REG(5, 0x10) +#define ISL7998X_REG_P5_MIPI_WCNT_2 ISL7998X_REG(5, 0x11) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_1 ISL7998X_REG(5, 0x12) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_2 ISL7998X_REG(5, 0x13) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_3 ISL7998X_REG(5, 0x14) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_4 ISL7998X_REG(5, 0x15) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_5 ISL7998X_REG(5, 0x16) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_6 ISL7998X_REG(5, 0x17) +#define ISL7998X_REG_P5_MIPI_DPHY_PARAMS_1 ISL7998X_REG(5, 0x18) +#define ISL7998X_REG_P5_MIPI_DPHY_SOT_PERIOD ISL7998X_REG(5, 0x19) +#define ISL7998X_REG_P5_MIPI_DPHY_EOT_PERIOD ISL7998X_REG(5, 0x1a) +#define ISL7998X_REG_P5_MIPI_DPHY_PARAMS_2 ISL7998X_REG(5, 0x1b) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_7 ISL7998X_REG(5, 0x1c) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_8 ISL7998X_REG(5, 0x1d) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_9 ISL7998X_REG(5, 0x1e) +#define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_10 ISL7998X_REG(5, 0x1f) +#define ISL7998X_REG_P5_TP_GEN_MIPI ISL7998X_REG(5, 0x20) +#define ISL7998X_REG_P5_ESC_MODE_TIME_CTL ISL7998X_REG(5, 0x21) +#define ISL7998X_REG_P5_AUTO_TEST_ERR_DET ISL7998X_REG(5, 0x22) +#define ISL7998X_REG_P5_MIPI_TIMING ISL7998X_REG(5, 0x23) +#define ISL7998X_REG_P5_PIC_HEIGHT_HIGH ISL7998X_REG(5, 0x24) +#define ISL7998X_REG_P5_PIC_HEIGHT_LOW ISL7998X_REG(5, 0x25) +#define ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL ISL7998X_REG(5, 0x26) +#define ISL7998X_REG_P5_FIFO_THRSH_CNT_1 ISL7998X_REG(5, 0x28) +#define ISL7998X_REG_P5_FIFO_THRSH_CNT_2 ISL7998X_REG(5, 0x29) +#define ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1 ISL7998X_REG(5, 0x2a) +#define ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2 ISL7998X_REG(5, 0x2b) +#define ISL7998X_REG_P5_PSF_FIELD_END_CTL_1 ISL7998X_REG(5, 0x2c) +#define ISL7998X_REG_P5_PSF_FIELD_END_CTL_2 ISL7998X_REG(5, 0x2d) +#define ISL7998X_REG_P5_PSF_FIELD_END_CTL_3 ISL7998X_REG(5, 0x2e) +#define ISL7998X_REG_P5_PSF_FIELD_END_CTL_4 ISL7998X_REG(5, 0x2f) +#define ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_1 ISL7998X_REG(5, 0x30) +#define ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_2 ISL7998X_REG(5, 0x31) +#define ISL7998X_REG_P5_MIPI_ANA_CLK_CTL ISL7998X_REG(5, 0x32) +#define ISL7998X_REG_P5_PLL_ANA_STATUS ISL7998X_REG(5, 0x33) +#define ISL7998X_REG_P5_PLL_ANA_MISC_CTL ISL7998X_REG(5, 0x34) +#define ISL7998X_REG_P5_MIPI_ANA ISL7998X_REG(5, 0x35) +#define ISL7998X_REG_P5_PLL_ANA ISL7998X_REG(5, 0x36) +#define ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1 ISL7998X_REG(5, 0x38) +#define ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_2 ISL7998X_REG(5, 0x39) +#define ISL7998X_REG_P5_H_LINE_CNT_1 ISL7998X_REG(5, 0x3a) +#define ISL7998X_REG_P5_H_LINE_CNT_2 ISL7998X_REG(5, 0x3b) +#define ISL7998X_REG_P5_HIST_LINE_CNT_1 ISL7998X_REG(5, 0x3c) +#define ISL7998X_REG_P5_HIST_LINE_CNT_2 ISL7998X_REG(5, 0x3d) + +static const struct reg_sequence isl7998x_init_seq_1[] = { + { ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN, 0xff }, + { ISL7998X_REG_PX_DEC_SDT(0x1), 0x00 }, + { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x1), 0x03 }, + { ISL7998X_REG_PX_DEC_SDT(0x2), 0x00 }, + { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x2), 0x03 }, + { ISL7998X_REG_PX_DEC_SDT(0x3), 0x00 }, + { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x3), 0x03 }, + { ISL7998X_REG_PX_DEC_SDT(0x4), 0x00 }, + { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x4), 0x03 }, + { ISL7998X_REG_P5_LI_ENGINE_CTL, 0x00 }, + { ISL7998X_REG_P0_SW_RESET_CTL, 0x1f, 10 }, + { ISL7998X_REG_P0_IO_BUFFER_CTL, 0x00 }, + { ISL7998X_REG_P0_MPP2_SYNC_CTL, 0xc9 }, + { ISL7998X_REG_P0_IRQ_SYNC_CTL, 0xc9 }, + { ISL7998X_REG_P0_CHAN_1_IRQ, 0x03 }, + { ISL7998X_REG_P0_CHAN_2_IRQ, 0x00 }, + { ISL7998X_REG_P0_CHAN_3_IRQ, 0x00 }, + { ISL7998X_REG_P0_CHAN_4_IRQ, 0x00 }, + { ISL7998X_REG_P5_LI_ENGINE_CTL, 0x02 }, + { ISL7998X_REG_P5_LI_ENGINE_LINE_CTL, 0x85 }, + { ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH, 0xa0 }, + { ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL, 0x18 }, + { ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL, 0x40 }, + { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x40 }, + { ISL7998X_REG_P5_MIPI_WCNT_1, 0x05 }, + { ISL7998X_REG_P5_MIPI_WCNT_2, 0xa0 }, + { ISL7998X_REG_P5_TP_GEN_MIPI, 0x00 }, + { ISL7998X_REG_P5_ESC_MODE_TIME_CTL, 0x0c }, + { ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, 0x00 }, + { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1, 0x00 }, + { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2, 0x19 }, + { ISL7998X_REG_P5_PSF_FIELD_END_CTL_1, 0x18 }, + { ISL7998X_REG_P5_PSF_FIELD_END_CTL_2, 0xf1 }, + { ISL7998X_REG_P5_PSF_FIELD_END_CTL_3, 0x00 }, + { ISL7998X_REG_P5_PSF_FIELD_END_CTL_4, 0xf1 }, + { ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_1, 0x00 }, + { ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_2, 0x00 }, + { ISL7998X_REG_P5_MIPI_ANA_CLK_CTL, 0x00 }, + { ISL7998X_REG_P5_PLL_ANA_STATUS, 0xc0 }, + { ISL7998X_REG_P5_PLL_ANA_MISC_CTL, 0x18 }, + { ISL7998X_REG_P5_PLL_ANA, 0x00 }, + { ISL7998X_REG_P0_SW_RESET_CTL, 0x10, 10 }, + /* Page 0xf means write to all of pages 1,2,3,4 */ + { ISL7998X_REG_PX_DEC_VDELAY_LO(0xf), 0x14 }, + { ISL7998X_REG_PX_DEC_MISC3(0xf), 0xe6 }, + { ISL7998X_REG_PX_DEC_CLMD(0xf), 0x85 }, + { ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(0xf), 0x11 }, + { ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), 0x00 }, + { ISL7998X_REG_P0_CLK_CTL_1, 0x1f }, + { ISL7998X_REG_P0_CLK_CTL_2, 0x43 }, + { ISL7998X_REG_P0_CLK_CTL_3, 0x4f }, +}; + +static const struct reg_sequence isl7998x_init_seq_2[] = { + { ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL, 0x10 }, + { ISL7998X_REG_P5_LI_ENGINE_VC_ASSIGNMENT, 0xe4 }, + { ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL, 0x00 }, + { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x60 }, + { ISL7998X_REG_P5_MIPI_READ_START_CTL, 0x2b }, + { ISL7998X_REG_P5_PSEUDO_FRM_FIELD_CTL, 0x02 }, + { ISL7998X_REG_P5_ONE_FIELD_MODE_CTL, 0x00 }, + { ISL7998X_REG_P5_MIPI_INT_HW_TST_CTR, 0x62 }, + { ISL7998X_REG_P5_TP_GEN_BAR_PATTERN, 0x02 }, + { ISL7998X_REG_P5_MIPI_PCNT_PSFRM, 0x36 }, + { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, 0x00 }, + { ISL7998X_REG_P5_MIPI_VBLANK_PSFRM, 0x6c }, + { ISL7998X_REG_P5_LI_ENGINE_CTL_2, 0x00 }, + { ISL7998X_REG_P5_MIPI_WCNT_1, 0x05 }, + { ISL7998X_REG_P5_MIPI_WCNT_2, 0xa0 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_1, 0x77 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_2, 0x17 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_3, 0x08 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_4, 0x38 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_5, 0x14 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_6, 0xf6 }, + { ISL7998X_REG_P5_MIPI_DPHY_PARAMS_1, 0x00 }, + { ISL7998X_REG_P5_MIPI_DPHY_SOT_PERIOD, 0x17 }, + { ISL7998X_REG_P5_MIPI_DPHY_EOT_PERIOD, 0x0a }, + { ISL7998X_REG_P5_MIPI_DPHY_PARAMS_2, 0x71 }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_7, 0x7a }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_8, 0x0f }, + { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_9, 0x8c }, + { ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, 0x08 }, + { ISL7998X_REG_P5_FIFO_THRSH_CNT_1, 0x01 }, + { ISL7998X_REG_P5_FIFO_THRSH_CNT_2, 0x0e }, + { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1, 0x00 }, + { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2, 0x00 }, + { ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, 0x03 }, + { ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_2, 0xc0 }, + { ISL7998X_REG_P5_H_LINE_CNT_1, 0x06 }, + { ISL7998X_REG_P5_H_LINE_CNT_2, 0xb3 }, + { ISL7998X_REG_P5_HIST_LINE_CNT_1, 0x00 }, + { ISL7998X_REG_P5_HIST_LINE_CNT_2, 0xf1 }, + { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x00 }, + { ISL7998X_REG_P5_MIPI_ANA, 0x00 }, + /* + * Wait a bit after reset so that the chip can capture a frame + * and update internal line counters. + */ + { ISL7998X_REG_P0_SW_RESET_CTL, 0x00, 50 }, +}; + +enum isl7998x_pads { + ISL7998X_PAD_OUT, + ISL7998X_PAD_VIN1, + ISL7998X_PAD_VIN2, + ISL7998X_PAD_VIN3, + ISL7998X_PAD_VIN4, + ISL7998X_NUM_PADS +}; + +struct isl7998x_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +static const struct isl7998x_datafmt isl7998x_colour_fmts[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB }, +}; + +/* Menu items for LINK_FREQ V4L2 control */ +static const s64 link_freq_menu_items[] = { + /* 1 channel, 1 lane or 2 channels, 2 lanes */ + 108000000, + /* 2 channels, 1 lane or 4 channels, 2 lanes */ + 216000000, + /* 4 channels, 1 lane */ + 432000000, +}; + +/* Menu items for TEST_PATTERN V4L2 control */ +static const char * const isl7998x_test_pattern_menu[] = { + "Disabled", + "Enabled", +}; + +static const char * const isl7998x_test_pattern_bars[] = { + "bbbbwb", "bbbwwb", "bbwbwb", "bbwwwb", +}; + +static const char * const isl7998x_test_pattern_colors[] = { + "Yellow", "Blue", "Green", "Pink", +}; + +struct isl7998x_mode { + unsigned int width; + unsigned int height; + enum v4l2_field field; +}; + +static const struct isl7998x_mode supported_modes[] = { + { + .width = 720, + .height = 576, + .field = V4L2_FIELD_SEQ_TB, + }, + { + .width = 720, + .height = 480, + .field = V4L2_FIELD_SEQ_BT, + }, +}; + +static const struct isl7998x_video_std { + const v4l2_std_id norm; + unsigned int id; + const struct isl7998x_mode *mode; +} isl7998x_std_res[] = { + { V4L2_STD_NTSC_443, + ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_443, + &supported_modes[1] }, + { V4L2_STD_PAL_M, + ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_M, + &supported_modes[1] }, + { V4L2_STD_PAL_Nc, + ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_CN, + &supported_modes[0] }, + { V4L2_STD_PAL_N, + ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL, + &supported_modes[0] }, + { V4L2_STD_PAL_60, + ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_60, + &supported_modes[1] }, + { V4L2_STD_NTSC, + ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_M, + &supported_modes[1] }, + { V4L2_STD_PAL, + ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL, + &supported_modes[0] }, + { V4L2_STD_SECAM, + ISL7998X_REG_PX_DEC_SDT_STANDARD_SECAM, + &supported_modes[0] }, + { V4L2_STD_UNKNOWN, + ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN, + &supported_modes[1] }, +}; + +struct isl7998x { + struct v4l2_subdev subdev; + struct regmap *regmap; + struct gpio_desc *pd_gpio; + struct gpio_desc *rstb_gpio; + unsigned int nr_mipi_lanes; + u32 nr_inputs; + + const struct isl7998x_datafmt *fmt; + v4l2_std_id norm; + struct media_pad pads[ISL7998X_NUM_PADS]; + + int enabled; + + /* protect fmt, norm, enabled */ + struct mutex lock; + + struct v4l2_ctrl_handler ctrl_handler; + /* protect ctrl_handler */ + struct mutex ctrl_mutex; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + u8 test_pattern; + u8 test_pattern_bars; + u8 test_pattern_chans; + u8 test_pattern_color; +}; + +static struct isl7998x *sd_to_isl7998x(struct v4l2_subdev *sd) +{ + return container_of(sd, struct isl7998x, subdev); +} + +static struct isl7998x *i2c_to_isl7998x(const struct i2c_client *client) +{ + return sd_to_isl7998x(i2c_get_clientdata(client)); +} + +static unsigned int isl7998x_norm_to_val(v4l2_std_id norm) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isl7998x_std_res); i++) + if (isl7998x_std_res[i].norm & norm) + break; + if (i == ARRAY_SIZE(isl7998x_std_res)) + return ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN; + + return isl7998x_std_res[i].id; +} + +static const struct isl7998x_mode *isl7998x_norm_to_mode(v4l2_std_id norm) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isl7998x_std_res); i++) + if (isl7998x_std_res[i].norm & norm) + break; + /* Use NTSC default resolution during standard detection */ + if (i == ARRAY_SIZE(isl7998x_std_res)) + return &supported_modes[1]; + + return isl7998x_std_res[i].mode; +} + +static int isl7998x_get_nr_inputs(struct device_node *of_node) +{ + struct device_node *port; + unsigned int inputs = 0; + unsigned int i; + + if (of_graph_get_endpoint_count(of_node) > ISL7998X_NUM_PADS) + return -EINVAL; + + /* + * The driver does not provide means to remap the input ports. It + * always configures input ports to start from VID1. Ensure that the + * device tree is correct. + */ + for (i = ISL7998X_PAD_VIN1; i <= ISL7998X_PAD_VIN4; i++) { + port = of_graph_get_port_by_id(of_node, i); + if (!port) + continue; + + inputs |= BIT(i); + of_node_put(port); + } + + switch (inputs) { + case BIT(ISL7998X_PAD_VIN1): + return 1; + case BIT(ISL7998X_PAD_VIN1) | BIT(ISL7998X_PAD_VIN2): + return 2; + case BIT(ISL7998X_PAD_VIN1) | BIT(ISL7998X_PAD_VIN2) | + BIT(ISL7998X_PAD_VIN3) | BIT(ISL7998X_PAD_VIN4): + return 4; + default: + return -EINVAL; + } +} + +static int isl7998x_wait_power_on(struct isl7998x *isl7998x) +{ + struct device *dev = isl7998x->subdev.dev; + u32 chip_id; + int ret; + int err; + + ret = read_poll_timeout(regmap_read, err, !err, 2000, 20000, false, + isl7998x->regmap, + ISL7998X_REG_P0_PRODUCT_ID_CODE, &chip_id); + if (ret) { + dev_err(dev, "timeout while waiting for ISL7998X\n"); + return ret; + } + + dev_dbg(dev, "Found ISL799%x\n", chip_id); + + return ret; +} + +static int isl7998x_set_standard(struct isl7998x *isl7998x, v4l2_std_id norm) +{ + const struct isl7998x_mode *mode = isl7998x_norm_to_mode(norm); + unsigned int val = isl7998x_norm_to_val(norm); + unsigned int width = mode->width; + unsigned int i; + int ret; + + for (i = 0; i < ISL7998X_INPUTS; i++) { + ret = regmap_write_bits(isl7998x->regmap, + ISL7998X_REG_PX_DEC_SDT(i + 1), + ISL7998X_REG_PX_DEC_SDT_STANDARD, + val); + if (ret) + return ret; + } + + ret = regmap_write(isl7998x->regmap, + ISL7998X_REG_P5_LI_ENGINE_LINE_CTL, + 0x20 | ((width >> 7) & 0x1f)); + if (ret) + return ret; + + ret = regmap_write(isl7998x->regmap, + ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH, + (width << 1) & 0xff); + if (ret) + return ret; + + return 0; +} + +static int isl7998x_init(struct isl7998x *isl7998x) +{ + const unsigned int lanes = isl7998x->nr_mipi_lanes; + const u32 isl7998x_video_in_chan_map[] = { 0x00, 0x11, 0x02, 0x02 }; + const struct reg_sequence isl7998x_init_seq_custom[] = { + { ISL7998X_REG_P0_VIDEO_IN_CHAN_CTL, + isl7998x_video_in_chan_map[isl7998x->nr_inputs - 1] }, + { ISL7998X_REG_P0_CLK_CTL_4, + (lanes == 1) ? 0x40 : 0x41 }, + { ISL7998X_REG_P5_LI_ENGINE_CTL, + (lanes == 1) ? 0x01 : 0x02 }, + }; + struct device *dev = isl7998x->subdev.dev; + struct regmap *regmap = isl7998x->regmap; + int ret; + + dev_dbg(dev, "configuring %d lanes for %d inputs (norm %s)\n", + isl7998x->nr_mipi_lanes, isl7998x->nr_inputs, + v4l2_norm_to_name(isl7998x->norm)); + + ret = regmap_register_patch(regmap, isl7998x_init_seq_1, + ARRAY_SIZE(isl7998x_init_seq_1)); + if (ret) + return ret; + + mutex_lock(&isl7998x->lock); + ret = isl7998x_set_standard(isl7998x, isl7998x->norm); + mutex_unlock(&isl7998x->lock); + if (ret) + return ret; + + ret = regmap_register_patch(regmap, isl7998x_init_seq_custom, + ARRAY_SIZE(isl7998x_init_seq_custom)); + if (ret) + return ret; + + return regmap_register_patch(regmap, isl7998x_init_seq_2, + ARRAY_SIZE(isl7998x_init_seq_2)); +} + +static int isl7998x_set_test_pattern(struct isl7998x *isl7998x) +{ + const struct reg_sequence isl7998x_init_seq_tpg_off[] = { + { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, 0 }, + { ISL7998X_REG_P5_LI_ENGINE_CTL_2, 0 } + }; + const struct reg_sequence isl7998x_init_seq_tpg_on[] = { + { ISL7998X_REG_P5_TP_GEN_BAR_PATTERN, + isl7998x->test_pattern_bars << 6 }, + { ISL7998X_REG_P5_LI_ENGINE_CTL_2, + isl7998x->norm & V4L2_STD_PAL ? BIT(2) : 0 }, + { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, + (isl7998x->test_pattern_chans << 4) | + (isl7998x->test_pattern_color << 2) } + }; + struct device *dev = isl7998x->subdev.dev; + struct regmap *regmap = isl7998x->regmap; + int ret; + + if (pm_runtime_get_if_in_use(dev) <= 0) + return 0; + + if (isl7998x->test_pattern != 0) { + dev_dbg(dev, "enabling test pattern: channels 0x%x, %s, %s\n", + isl7998x->test_pattern_chans, + isl7998x_test_pattern_bars[isl7998x->test_pattern_bars], + isl7998x_test_pattern_colors[isl7998x->test_pattern_color]); + ret = regmap_register_patch(regmap, isl7998x_init_seq_tpg_on, + ARRAY_SIZE(isl7998x_init_seq_tpg_on)); + } else { + ret = regmap_register_patch(regmap, isl7998x_init_seq_tpg_off, + ARRAY_SIZE(isl7998x_init_seq_tpg_off)); + } + + pm_runtime_put(dev); + + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int isl7998x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + int ret; + u32 val; + + ret = regmap_read(isl7998x->regmap, reg->reg, &val); + if (ret) + return ret; + + reg->size = 1; + reg->val = val; + + return 0; +} + +static int isl7998x_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + + return regmap_write(isl7998x->regmap, reg->reg, reg->val); +} +#endif + +static int isl7998x_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + + mutex_lock(&isl7998x->lock); + *norm = isl7998x->norm; + mutex_unlock(&isl7998x->lock); + + return 0; +} + +static int isl7998x_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + int ret = 0; + + mutex_lock(&isl7998x->lock); + if (isl7998x->enabled) { + ret = -EBUSY; + mutex_unlock(&isl7998x->lock); + return ret; + } + isl7998x->norm = norm; + mutex_unlock(&isl7998x->lock); + + if (pm_runtime_get_if_in_use(dev) <= 0) + return ret; + + ret = isl7998x_set_standard(isl7998x, norm); + + pm_runtime_put(dev); + + return ret; +} + +static int isl7998x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + unsigned int std_id[ISL7998X_INPUTS]; + unsigned int i; + int ret; + u32 reg; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + dev_dbg(dev, "starting video standard detection\n"); + + mutex_lock(&isl7998x->lock); + if (isl7998x->enabled) { + ret = -EBUSY; + goto out_unlock; + } + + ret = isl7998x_set_standard(isl7998x, V4L2_STD_UNKNOWN); + if (ret) + goto out_unlock; + + for (i = 0; i < ISL7998X_INPUTS; i++) { + ret = regmap_write(isl7998x->regmap, + ISL7998X_REG_PX_DEC_SDTR(i + 1), + ISL7998X_REG_PX_DEC_SDTR_ATSTART); + if (ret) + goto out_reset_std; + } + + for (i = 0; i < ISL7998X_INPUTS; i++) { + ret = regmap_read_poll_timeout(isl7998x->regmap, + ISL7998X_REG_PX_DEC_SDT(i + 1), + reg, + !(reg & ISL7998X_REG_PX_DEC_SDT_DET), + 2000, 500 * USEC_PER_MSEC); + if (ret) + goto out_reset_std; + std_id[i] = FIELD_GET(ISL7998X_REG_PX_DEC_SDT_NOW, reg); + } + + /* + * According to Renesas FAE, all input cameras must have the + * same standard on this chip. + */ + for (i = 0; i < isl7998x->nr_inputs; i++) { + dev_dbg(dev, "input %d: detected %s\n", + i, v4l2_norm_to_name(isl7998x_std_res[std_id[i]].norm)); + if (std_id[0] != std_id[i]) + dev_warn(dev, + "incompatible standards: %s on input %d (expected %s)\n", + v4l2_norm_to_name(isl7998x_std_res[std_id[i]].norm), i, + v4l2_norm_to_name(isl7998x_std_res[std_id[0]].norm)); + } + + *std = isl7998x_std_res[std_id[0]].norm; + +out_reset_std: + isl7998x_set_standard(isl7998x, isl7998x->norm); +out_unlock: + mutex_unlock(&isl7998x->lock); + pm_runtime_put(dev); + + return ret; +} + +static int isl7998x_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + *std = V4L2_STD_ALL; + + return 0; +} + +static int isl7998x_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + unsigned int i; + int ret = 0; + u32 reg; + + if (!pm_runtime_active(dev)) { + *status |= V4L2_IN_ST_NO_POWER; + return 0; + } + + for (i = 0; i < isl7998x->nr_inputs; i++) { + ret = regmap_read(isl7998x->regmap, + ISL7998X_REG_PX_DEC_STATUS_1(i + 1), ®); + if (!ret) { + if (reg & ISL7998X_REG_PX_DEC_STATUS_1_VDLOSS) + *status |= V4L2_IN_ST_NO_SIGNAL; + if (!(reg & ISL7998X_REG_PX_DEC_STATUS_1_HLOCK)) + *status |= V4L2_IN_ST_NO_H_LOCK; + if (!(reg & ISL7998X_REG_PX_DEC_STATUS_1_VLOCK)) + *status |= V4L2_IN_ST_NO_V_LOCK; + } + } + + return ret; +} + +static int isl7998x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + int ret = 0; + u32 reg; + + dev_dbg(dev, "stream %s\n", enable ? "ON" : "OFF"); + + mutex_lock(&isl7998x->lock); + if (isl7998x->enabled == enable) + goto out; + isl7998x->enabled = enable; + + if (enable) { + ret = isl7998x_set_test_pattern(isl7998x); + if (ret) + goto out; + } + + regmap_read(isl7998x->regmap, + ISL7998X_REG_P5_LI_ENGINE_CTL, ®); + if (enable) + reg &= ~BIT(7); + else + reg |= BIT(7); + ret = regmap_write(isl7998x->regmap, + ISL7998X_REG_P5_LI_ENGINE_CTL, reg); + +out: + mutex_unlock(&isl7998x->lock); + + return ret; +} + +static int isl7998x_pre_streamon(struct v4l2_subdev *sd, u32 flags) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int isl7998x_post_streamoff(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + + pm_runtime_put(dev); + + return 0; +} + +static int isl7998x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(isl7998x_colour_fmts)) + return -EINVAL; + + code->code = isl7998x_colour_fmts[code->index].code; + + return 0; +} + +static int isl7998x_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != isl7998x_colour_fmts[0].code) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int isl7998x_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + const struct isl7998x_mode *mode; + + mutex_lock(&isl7998x->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + format->format = *v4l2_subdev_get_try_format(sd, sd_state, + format->pad); + goto out; + } + + mode = isl7998x_norm_to_mode(isl7998x->norm); + + mf->width = mode->width; + mf->height = mode->height; + mf->code = isl7998x->fmt->code; + mf->field = mode->field; + mf->colorspace = 0; + +out: + mutex_unlock(&isl7998x->lock); + + return 0; +} + +static int isl7998x_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + const struct isl7998x_mode *mode; + + mutex_lock(&isl7998x->lock); + + mode = isl7998x_norm_to_mode(isl7998x->norm); + + mf->width = mode->width; + mf->height = mode->height; + mf->code = isl7998x->fmt->code; + mf->field = mode->field; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(sd, sd_state, format->pad) = format->format; + + mutex_unlock(&isl7998x->lock); + + return 0; +} + +static int isl7998x_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isl7998x *isl7998x = container_of(ctrl->handler, + struct isl7998x, ctrl_handler); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN_BARS: + mutex_lock(&isl7998x->lock); + isl7998x->test_pattern_bars = ctrl->val & 0x3; + ret = isl7998x_set_test_pattern(isl7998x); + mutex_unlock(&isl7998x->lock); + break; + case V4L2_CID_TEST_PATTERN_CHANNELS: + mutex_lock(&isl7998x->lock); + isl7998x->test_pattern_chans = ctrl->val & 0xf; + ret = isl7998x_set_test_pattern(isl7998x); + mutex_unlock(&isl7998x->lock); + break; + case V4L2_CID_TEST_PATTERN_COLOR: + mutex_lock(&isl7998x->lock); + isl7998x->test_pattern_color = ctrl->val & 0x3; + ret = isl7998x_set_test_pattern(isl7998x); + mutex_unlock(&isl7998x->lock); + break; + case V4L2_CID_TEST_PATTERN: + mutex_lock(&isl7998x->lock); + isl7998x->test_pattern = ctrl->val; + ret = isl7998x_set_test_pattern(isl7998x); + mutex_unlock(&isl7998x->lock); + break; + } + + return ret; +} + +static const struct v4l2_subdev_core_ops isl7998x_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = isl7998x_g_register, + .s_register = isl7998x_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops isl7998x_subdev_video_ops = { + .g_std = isl7998x_g_std, + .s_std = isl7998x_s_std, + .querystd = isl7998x_querystd, + .g_tvnorms = isl7998x_g_tvnorms, + .g_input_status = isl7998x_g_input_status, + .s_stream = isl7998x_s_stream, + .pre_streamon = isl7998x_pre_streamon, + .post_streamoff = isl7998x_post_streamoff, +}; + +static const struct v4l2_subdev_pad_ops isl7998x_subdev_pad_ops = { + .enum_mbus_code = isl7998x_enum_mbus_code, + .enum_frame_size = isl7998x_enum_frame_size, + .get_fmt = isl7998x_get_fmt, + .set_fmt = isl7998x_set_fmt, +}; + +static const struct v4l2_subdev_ops isl7998x_subdev_ops = { + .core = &isl7998x_subdev_core_ops, + .video = &isl7998x_subdev_video_ops, + .pad = &isl7998x_subdev_pad_ops, +}; + +static const struct media_entity_operations isl7998x_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_ctrl_ops isl7998x_ctrl_ops = { + .s_ctrl = isl7998x_set_ctrl, +}; + +static const struct v4l2_ctrl_config isl7998x_ctrls[] = { + { + .ops = &isl7998x_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN_BARS, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Test Pattern Bars", + .max = ARRAY_SIZE(isl7998x_test_pattern_bars) - 1, + .def = 0, + .qmenu = isl7998x_test_pattern_bars, + }, { + .ops = &isl7998x_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN_CHANNELS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test Pattern Channels", + .min = 0, + .max = 0xf, + .step = 1, + .def = 0xf, + .flags = 0, + }, { + .ops = &isl7998x_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN_COLOR, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Test Pattern Color", + .max = ARRAY_SIZE(isl7998x_test_pattern_colors) - 1, + .def = 0, + .qmenu = isl7998x_test_pattern_colors, + }, +}; + +#define ISL7998X_REG_DECODER_ACA_READABLE_RANGE(page) \ + /* Decoder range */ \ + regmap_reg_range(ISL7998X_REG_PX_DEC_INPUT_FMT(page), \ + ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_ANCTL(page), \ + ISL7998X_REG_PX_DEC_CSC_CTL(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_BRIGHT(page), \ + ISL7998X_REG_PX_DEC_HUE(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_VERT_PEAK(page), \ + ISL7998X_REG_PX_DEC_CORING(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ + ISL7998X_REG_PX_DEC_SDTR(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_CLMPG(page), \ + ISL7998X_REG_PX_DEC_DATA_CONV(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_INTERNAL_TEST(page), \ + ISL7998X_REG_PX_DEC_INTERNAL_TEST(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_H_DELAY_CTL(page), \ + ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(page)), \ + /* ACA range */ \ + regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_1(page), \ + ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_Y_AVG(page), \ + ISL7998X_REG_PX_ACA_CTL_4(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(page), \ + ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(page), \ + ISL7998X_REG_PX_DEC_PAGE(page)) + +#define ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(page) \ + /* Decoder range */ \ + regmap_reg_range(ISL7998X_REG_PX_DEC_INPUT_FMT(page), \ + ISL7998X_REG_PX_DEC_INPUT_FMT(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page), \ + ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_ANCTL(page), \ + ISL7998X_REG_PX_DEC_CSC_CTL(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_BRIGHT(page), \ + ISL7998X_REG_PX_DEC_HUE(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_VERT_PEAK(page), \ + ISL7998X_REG_PX_DEC_CORING(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ + ISL7998X_REG_PX_DEC_SDTR(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_CLMPG(page), \ + ISL7998X_REG_PX_DEC_MISC3(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_CLMD(page), \ + ISL7998X_REG_PX_DEC_DATA_CONV(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_INTERNAL_TEST(page), \ + ISL7998X_REG_PX_DEC_INTERNAL_TEST(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_H_DELAY_CTL(page), \ + ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(page)), \ + /* ACA range */ \ + regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_1(page), \ + ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_2(page), \ + ISL7998X_REG_PX_ACA_CTL_4(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(page), \ + ISL7998X_REG_PX_ACA_HIST_DATA_LO(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page), \ + ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(page), \ + ISL7998X_REG_PX_DEC_PAGE(page)) + +#define ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(page) \ + /* Decoder range */ \ + regmap_reg_range(ISL7998X_REG_PX_DEC_STATUS_1(page), \ + ISL7998X_REG_PX_DEC_STATUS_1(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ + ISL7998X_REG_PX_DEC_SDT(page)), \ + regmap_reg_range(ISL7998X_REG_PX_DEC_MVSN(page), \ + ISL7998X_REG_PX_DEC_HFREF(page)), \ + /* ACA range */ \ + regmap_reg_range(ISL7998X_REG_PX_ACA_Y_AVG(page), \ + ISL7998X_REG_PX_ACA_Y_HIGH(page)), \ + regmap_reg_range(ISL7998X_REG_PX_ACA_HIST_DATA_LO(page), \ + ISL7998X_REG_PX_ACA_FLEX_WIN_CR_CLR(page)) + +static const struct regmap_range isl7998x_readable_ranges[] = { + regmap_reg_range(ISL7998X_REG_P0_PRODUCT_ID_CODE, + ISL7998X_REG_P0_IRQ_SYNC_CTL), + regmap_reg_range(ISL7998X_REG_P0_INTERRUPT_STATUS, + ISL7998X_REG_P0_CLOCK_DELAY), + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(0), + ISL7998X_REG_PX_DEC_PAGE(0)), + + ISL7998X_REG_DECODER_ACA_READABLE_RANGE(1), + ISL7998X_REG_DECODER_ACA_READABLE_RANGE(2), + ISL7998X_REG_DECODER_ACA_READABLE_RANGE(3), + ISL7998X_REG_DECODER_ACA_READABLE_RANGE(4), + + regmap_reg_range(ISL7998X_REG_P5_LI_ENGINE_CTL, + ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL), + regmap_reg_range(ISL7998X_REG_P5_FIFO_THRSH_CNT_1, + ISL7998X_REG_P5_PLL_ANA), + regmap_reg_range(ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, + ISL7998X_REG_P5_HIST_LINE_CNT_2), + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(5), + ISL7998X_REG_PX_DEC_PAGE(5)), +}; + +static const struct regmap_range isl7998x_writeable_ranges[] = { + regmap_reg_range(ISL7998X_REG_P0_SW_RESET_CTL, + ISL7998X_REG_P0_IRQ_SYNC_CTL), + regmap_reg_range(ISL7998X_REG_P0_CHAN_1_IRQ, + ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN), + regmap_reg_range(ISL7998X_REG_P0_CLOCK_DELAY, + ISL7998X_REG_P0_CLOCK_DELAY), + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(0), + ISL7998X_REG_PX_DEC_PAGE(0)), + + ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(1), + ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(2), + ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(3), + ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(4), + + regmap_reg_range(ISL7998X_REG_P5_LI_ENGINE_CTL, + ISL7998X_REG_P5_ESC_MODE_TIME_CTL), + regmap_reg_range(ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, + ISL7998X_REG_P5_PLL_ANA), + regmap_reg_range(ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, + ISL7998X_REG_P5_HIST_LINE_CNT_2), + regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(5), + ISL7998X_REG_PX_DEC_PAGE(5)), + + ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(0xf), +}; + +static const struct regmap_range isl7998x_volatile_ranges[] = { + /* Product id code register is used to check availability */ + regmap_reg_range(ISL7998X_REG_P0_PRODUCT_ID_CODE, + ISL7998X_REG_P0_PRODUCT_ID_CODE), + regmap_reg_range(ISL7998X_REG_P0_MPP1_SYNC_CTL, + ISL7998X_REG_P0_IRQ_SYNC_CTL), + regmap_reg_range(ISL7998X_REG_P0_INTERRUPT_STATUS, + ISL7998X_REG_P0_INTERRUPT_STATUS), + regmap_reg_range(ISL7998X_REG_P0_CHAN_1_STATUS, + ISL7998X_REG_P0_SHORT_DIAG_STATUS), + + ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(1), + ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(2), + ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(3), + ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(4), + + regmap_reg_range(ISL7998X_REG_P5_AUTO_TEST_ERR_DET, + ISL7998X_REG_P5_PIC_HEIGHT_LOW), +}; + +static const struct regmap_access_table isl7998x_readable_table = { + .yes_ranges = isl7998x_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(isl7998x_readable_ranges), +}; + +static const struct regmap_access_table isl7998x_writeable_table = { + .yes_ranges = isl7998x_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(isl7998x_writeable_ranges), +}; + +static const struct regmap_access_table isl7998x_volatile_table = { + .yes_ranges = isl7998x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(isl7998x_volatile_ranges), +}; + +static const struct regmap_range_cfg isl7998x_ranges[] = { + { + .range_min = ISL7998X_REG_PN_BASE(0), + .range_max = ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), + .selector_reg = ISL7998X_REG_PX_DEC_PAGE(0), + .selector_mask = ISL7998X_REG_PX_DEC_PAGE_MASK, + .window_start = 0, + .window_len = 256, + } +}; + +static const struct regmap_config isl7998x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), + .ranges = isl7998x_ranges, + .num_ranges = ARRAY_SIZE(isl7998x_ranges), + .rd_table = &isl7998x_readable_table, + .wr_table = &isl7998x_writeable_table, + .volatile_table = &isl7998x_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +static int isl7998x_mc_init(struct isl7998x *isl7998x) +{ + unsigned int i; + + isl7998x->subdev.entity.ops = &isl7998x_entity_ops; + isl7998x->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + isl7998x->pads[ISL7998X_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; + for (i = ISL7998X_PAD_VIN1; i < ISL7998X_NUM_PADS; i++) + isl7998x->pads[i].flags = MEDIA_PAD_FL_SINK; + + return media_entity_pads_init(&isl7998x->subdev.entity, + ISL7998X_NUM_PADS, + isl7998x->pads); +} + +static int get_link_freq_menu_index(unsigned int lanes, + unsigned int inputs) +{ + int ret = -EINVAL; + + switch (lanes) { + case 1: + if (inputs == 1) + ret = 0; + if (inputs == 2) + ret = 1; + if (inputs == 4) + ret = 2; + break; + case 2: + if (inputs == 2) + ret = 0; + if (inputs == 4) + ret = 1; + break; + default: + break; + } + + return ret; +} + +static void isl7998x_remove_controls(struct isl7998x *isl7998x) +{ + v4l2_ctrl_handler_free(&isl7998x->ctrl_handler); + mutex_destroy(&isl7998x->ctrl_mutex); +} + +static int isl7998x_init_controls(struct isl7998x *isl7998x) +{ + struct v4l2_subdev *sd = &isl7998x->subdev; + int link_freq_index; + unsigned int i; + int ret; + + ret = v4l2_ctrl_handler_init(&isl7998x->ctrl_handler, + 2 + ARRAY_SIZE(isl7998x_ctrls)); + if (ret) + return ret; + + mutex_init(&isl7998x->ctrl_mutex); + isl7998x->ctrl_handler.lock = &isl7998x->ctrl_mutex; + link_freq_index = get_link_freq_menu_index(isl7998x->nr_mipi_lanes, + isl7998x->nr_inputs); + if (link_freq_index < 0 || + link_freq_index >= ARRAY_SIZE(link_freq_menu_items)) { + dev_err(sd->dev, + "failed to find MIPI link freq: %d lanes, %d inputs\n", + isl7998x->nr_mipi_lanes, isl7998x->nr_inputs); + ret = -EINVAL; + goto err; + } + + isl7998x->link_freq = v4l2_ctrl_new_int_menu(&isl7998x->ctrl_handler, + &isl7998x_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + link_freq_index, + link_freq_menu_items); + if (isl7998x->link_freq) + isl7998x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + for (i = 0; i < ARRAY_SIZE(isl7998x_ctrls); i++) + v4l2_ctrl_new_custom(&isl7998x->ctrl_handler, + &isl7998x_ctrls[i], NULL); + + v4l2_ctrl_new_std_menu_items(&isl7998x->ctrl_handler, + &isl7998x_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(isl7998x_test_pattern_menu) - 1, + 0, 0, isl7998x_test_pattern_menu); + + ret = isl7998x->ctrl_handler.error; + if (ret) + goto err; + + isl7998x->subdev.ctrl_handler = &isl7998x->ctrl_handler; + v4l2_ctrl_handler_setup(&isl7998x->ctrl_handler); + + return 0; + +err: + isl7998x_remove_controls(isl7998x); + + return ret; +} + +static int isl7998x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct v4l2_fwnode_endpoint endpoint = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep; + struct isl7998x *isl7998x; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int nr_inputs; + int ret; + + ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA); + if (!ret) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + isl7998x = devm_kzalloc(dev, sizeof(*isl7998x), GFP_KERNEL); + if (!isl7998x) + return -ENOMEM; + + isl7998x->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(isl7998x->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(isl7998x->pd_gpio), + "Failed to retrieve/request PD GPIO\n"); + + isl7998x->rstb_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(isl7998x->rstb_gpio)) + return dev_err_probe(dev, PTR_ERR(isl7998x->rstb_gpio), + "Failed to retrieve/request RSTB GPIO\n"); + + isl7998x->regmap = devm_regmap_init_i2c(client, &isl7998x_regmap); + if (IS_ERR(isl7998x->regmap)) + return dev_err_probe(dev, PTR_ERR(isl7998x->regmap), + "Failed to allocate register map\n"); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + ISL7998X_PAD_OUT, 0, 0); + if (!ep) + return dev_err_probe(dev, -EINVAL, "Missing endpoint node\n"); + + ret = v4l2_fwnode_endpoint_parse(ep, &endpoint); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse endpoint\n"); + + if (endpoint.bus.mipi_csi2.num_data_lanes == 0 || + endpoint.bus.mipi_csi2.num_data_lanes > 2) + return dev_err_probe(dev, -EINVAL, + "Invalid number of MIPI lanes\n"); + + isl7998x->nr_mipi_lanes = endpoint.bus.mipi_csi2.num_data_lanes; + + nr_inputs = isl7998x_get_nr_inputs(dev->of_node); + if (nr_inputs < 0) + return dev_err_probe(dev, nr_inputs, + "Invalid number of input ports\n"); + isl7998x->nr_inputs = nr_inputs; + + v4l2_i2c_subdev_init(&isl7998x->subdev, client, &isl7998x_subdev_ops); + isl7998x->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = isl7998x_mc_init(isl7998x); + if (ret < 0) + return ret; + + isl7998x->fmt = &isl7998x_colour_fmts[0]; + isl7998x->norm = V4L2_STD_NTSC; + isl7998x->enabled = 0; + + mutex_init(&isl7998x->lock); + + ret = isl7998x_init_controls(isl7998x); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_async_register_subdev(&isl7998x->subdev); + if (ret < 0) + goto err_controls_cleanup; + + pm_runtime_enable(dev); + + return 0; + +err_controls_cleanup: + isl7998x_remove_controls(isl7998x); +err_entity_cleanup: + media_entity_cleanup(&isl7998x->subdev.entity); + + return ret; +} + +static int isl7998x_remove(struct i2c_client *client) +{ + struct isl7998x *isl7998x = i2c_to_isl7998x(client); + + pm_runtime_disable(&client->dev); + v4l2_async_unregister_subdev(&isl7998x->subdev); + isl7998x_remove_controls(isl7998x); + media_entity_cleanup(&isl7998x->subdev.entity); + + return 0; +} + +static const struct of_device_id isl7998x_of_match[] = { + { .compatible = "isil,isl79987", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, isl7998x_of_match); + +static const struct i2c_device_id isl7998x_id[] = { + { "isl79987", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(i2c, isl7998x_id); + +static int __maybe_unused isl7998x_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + int ret; + + gpiod_set_value(isl7998x->rstb_gpio, 1); + gpiod_set_value(isl7998x->pd_gpio, 0); + gpiod_set_value(isl7998x->rstb_gpio, 0); + + ret = isl7998x_wait_power_on(isl7998x); + if (ret) + goto err; + + ret = isl7998x_init(isl7998x); + if (ret) + goto err; + + return 0; + +err: + gpiod_set_value(isl7998x->pd_gpio, 1); + + return ret; +} + +static int __maybe_unused isl7998x_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct isl7998x *isl7998x = sd_to_isl7998x(sd); + + gpiod_set_value(isl7998x->pd_gpio, 1); + + return 0; +} + +static const struct dev_pm_ops isl7998x_pm_ops = { + SET_RUNTIME_PM_OPS(isl7998x_runtime_suspend, + isl7998x_runtime_resume, + NULL) +}; + +static struct i2c_driver isl7998x_i2c_driver = { + .driver = { + .name = "isl7998x", + .of_match_table = of_match_ptr(isl7998x_of_match), + .pm = &isl7998x_pm_ops, + }, + .probe_new = isl7998x_probe, + .remove = isl7998x_remove, + .id_table = isl7998x_id, +}; + +module_i2c_driver(isl7998x_i2c_driver); + +MODULE_DESCRIPTION("Intersil ISL7998x Analog to MIPI CSI-2/BT656 decoder"); +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig index 6f0ef33b7ee1..7f0af32f4376 100644 --- a/drivers/media/i2c/m5mols/Kconfig +++ b/drivers/media/i2c/m5mols/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_M5MOLS tristate "Fujitsu M-5MOLS 8MP sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API help diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h index 60c102fa7df5..d8545d2280af 100644 --- a/drivers/media/i2c/m5mols/m5mols.h +++ b/drivers/media/i2c/m5mols/m5mols.h @@ -13,6 +13,7 @@ #define M5MOLS_H #include <linux/sizes.h> +#include <linux/gpio/consumer.h> #include <media/v4l2-subdev.h> #include "m5mols_reg.h" @@ -181,6 +182,7 @@ struct m5mols_version { * @stabilization: image stabilization control * @jpeg_quality: JPEG compression quality control * @set_power: optional power callback to the board code + * @reset: GPIO driving the reset pin of M-5MOLS * @lock: mutex protecting the structure fields below * @ffmt: current fmt according to resolution type * @res_type: current resolution type @@ -224,6 +226,7 @@ struct m5mols_info { struct v4l2_ctrl *jpeg_quality; int (*set_power)(struct device *dev, int on); + struct gpio_desc *reset; struct mutex lock; diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c index e1b1d689c044..275c5b2539fd 100644 --- a/drivers/media/i2c/m5mols/m5mols_capture.c +++ b/drivers/media/i2c/m5mols/m5mols_capture.c @@ -15,7 +15,6 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/videodev2.h> #include <media/v4l2-ctrls.h> diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index e29be0242f07..c19590389bfe 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -14,7 +14,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/videodev2.h> #include <linux/module.h> @@ -752,7 +752,6 @@ static int m5mols_sensor_power(struct m5mols_info *info, bool enable) { struct v4l2_subdev *sd = &info->sd; struct i2c_client *client = v4l2_get_subdevdata(sd); - const struct m5mols_platform_data *pdata = info->pdata; int ret; if (info->power == enable) @@ -772,7 +771,7 @@ static int m5mols_sensor_power(struct m5mols_info *info, bool enable) return ret; } - gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); + gpiod_set_value(info->reset, 0); info->power = 1; return ret; @@ -785,7 +784,7 @@ static int m5mols_sensor_power(struct m5mols_info *info, bool enable) if (info->set_power) info->set_power(&client->dev, 0); - gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); + gpiod_set_value(info->reset, 1); info->isp_ready = 0; info->power = 0; @@ -944,7 +943,6 @@ static int m5mols_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct m5mols_platform_data *pdata = client->dev.platform_data; - unsigned long gpio_flags; struct m5mols_info *info; struct v4l2_subdev *sd; int ret; @@ -954,11 +952,6 @@ static int m5mols_probe(struct i2c_client *client, return -EINVAL; } - if (!gpio_is_valid(pdata->gpio_reset)) { - dev_err(&client->dev, "No valid RESET GPIO specified\n"); - return -EINVAL; - } - if (!client->irq) { dev_err(&client->dev, "Interrupt not assigned\n"); return -EINVAL; @@ -968,18 +961,16 @@ static int m5mols_probe(struct i2c_client *client, if (!info) return -ENOMEM; + /* This asserts reset, descriptor shall have polarity specified */ + info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(info->reset)) + return PTR_ERR(info->reset); + /* Notice: the "N" in M5MOLS_NRST implies active low */ + gpiod_set_consumer_name(info->reset, "M5MOLS_NRST"); + info->pdata = pdata; info->set_power = pdata->set_power; - gpio_flags = pdata->reset_polarity - ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - ret = devm_gpio_request_one(&client->dev, pdata->gpio_reset, gpio_flags, - "M5MOLS_NRST"); - if (ret) { - dev_err(&client->dev, "Failed to request gpio: %d\n", ret); - return ret; - } - ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); if (ret) { diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index bc46a0957b40..0eea200124d2 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -257,7 +257,7 @@ static const struct regmap_config max2175_regmap_config = { .reg_defaults = max2175_reg_defaults, .num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults), .volatile_table = &max2175_volatile_regs, - .cache_type = REGCACHE_FLAT, + .cache_type = REGCACHE_RBTREE, }; struct max2175 { diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index eb2b8e42335b..d2a4915ed9f7 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -15,6 +15,7 @@ #include <linux/fwnode.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/module.h> @@ -168,6 +169,8 @@ struct max9286_priv { u32 init_rev_chan_mv; u32 rev_chan_mv; + u32 gpio_poc[2]; + struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *pixelrate; @@ -846,6 +849,10 @@ static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { .open = max9286_open, }; +static const struct media_entity_operations max9286_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) { switch (ctrl->id) { @@ -895,6 +902,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv) goto err_async; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max9286_media_ops; priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; for (i = 0; i < MAX9286_SRC_PAD; i++) @@ -1025,20 +1033,27 @@ static int max9286_setup(struct max9286_priv *priv) return 0; } -static void max9286_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset, + int value) { - struct max9286_priv *priv = gpiochip_get_data(chip); - if (value) priv->gpio_state |= BIT(offset); else priv->gpio_state &= ~BIT(offset); - max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); + return max9286_write(priv, 0x0f, + MAX9286_0X0F_RESERVED | priv->gpio_state); +} + +static void max9286_gpiochip_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + max9286_gpio_set(priv, offset, value); } -static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) +static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset) { struct max9286_priv *priv = gpiochip_get_data(chip); @@ -1057,13 +1072,10 @@ static int max9286_register_gpio(struct max9286_priv *priv) gpio->owner = THIS_MODULE; gpio->ngpio = 2; gpio->base = -1; - gpio->set = max9286_gpio_set; - gpio->get = max9286_gpio_get; + gpio->set = max9286_gpiochip_set; + gpio->get = max9286_gpiochip_get; gpio->can_sleep = true; - /* GPIO values default to high */ - priv->gpio_state = BIT(0) | BIT(1); - ret = devm_gpiochip_add_data(dev, gpio, priv); if (ret) dev_err(dev, "Unable to create gpio_chip\n"); @@ -1071,6 +1083,70 @@ static int max9286_register_gpio(struct max9286_priv *priv) return ret; } +static int max9286_parse_gpios(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + /* GPIO values default to high */ + priv->gpio_state = BIT(0) | BIT(1); + + /* + * Parse the "gpio-poc" vendor property. If the property is not + * specified the camera power is controlled by a regulator. + */ + ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-poc", + priv->gpio_poc, 2); + if (ret == -EINVAL) { + /* + * If gpio lines are not used for the camera power, register + * a gpio controller for consumers. + */ + ret = max9286_register_gpio(priv); + if (ret) + return ret; + + priv->regulator = devm_regulator_get(dev, "poc"); + if (IS_ERR(priv->regulator)) { + return dev_err_probe(dev, PTR_ERR(priv->regulator), + "Unable to get PoC regulator (%ld)\n", + PTR_ERR(priv->regulator)); + } + + return 0; + } + + /* If the property is specified make sure it is well formed. */ + if (ret || priv->gpio_poc[0] > 1 || + (priv->gpio_poc[1] != GPIO_ACTIVE_HIGH && + priv->gpio_poc[1] != GPIO_ACTIVE_LOW)) { + dev_err(dev, "Invalid 'gpio-poc' property\n"); + return -EINVAL; + } + + return 0; +} + +static int max9286_poc_enable(struct max9286_priv *priv, bool enable) +{ + int ret; + + /* If the regulator is not available, use gpio to control power. */ + if (!priv->regulator) + ret = max9286_gpio_set(priv, priv->gpio_poc[0], + enable ^ priv->gpio_poc[1]); + else if (enable) + ret = regulator_enable(priv->regulator); + else + ret = regulator_disable(priv->regulator); + + if (ret < 0) + dev_err(&priv->client->dev, "Unable to turn power %s\n", + enable ? "on" : "off"); + + return ret; +} + static int max9286_init(struct device *dev) { struct max9286_priv *priv; @@ -1080,17 +1156,14 @@ static int max9286_init(struct device *dev) client = to_i2c_client(dev); priv = i2c_get_clientdata(client); - /* Enable the bus power. */ - ret = regulator_enable(priv->regulator); - if (ret < 0) { - dev_err(&client->dev, "Unable to turn PoC on\n"); + ret = max9286_poc_enable(priv, true); + if (ret) return ret; - } ret = max9286_setup(priv); if (ret) { dev_err(dev, "Unable to setup max9286\n"); - goto err_regulator; + goto err_poc_disable; } /* @@ -1100,7 +1173,7 @@ static int max9286_init(struct device *dev) ret = max9286_v4l2_register(priv); if (ret) { dev_err(dev, "Failed to register with V4L2\n"); - goto err_regulator; + goto err_poc_disable; } ret = max9286_i2c_mux_init(priv); @@ -1116,8 +1189,8 @@ static int max9286_init(struct device *dev) err_v4l2_register: max9286_v4l2_unregister(priv); -err_regulator: - regulator_disable(priv->regulator); +err_poc_disable: + max9286_poc_enable(priv, false); return ret; } @@ -1288,18 +1361,10 @@ static int max9286_probe(struct i2c_client *client) */ max9286_configure_i2c(priv, false); - ret = max9286_register_gpio(priv); + ret = max9286_parse_gpios(priv); if (ret) goto err_powerdown; - priv->regulator = devm_regulator_get(&client->dev, "poc"); - if (IS_ERR(priv->regulator)) { - ret = PTR_ERR(priv->regulator); - dev_err_probe(&client->dev, ret, - "Unable to get PoC regulator\n"); - goto err_powerdown; - } - ret = max9286_parse_dt(priv); if (ret) goto err_powerdown; @@ -1326,7 +1391,7 @@ static int max9286_remove(struct i2c_client *client) max9286_v4l2_unregister(priv); - regulator_disable(priv->regulator); + max9286_poc_enable(priv, false); gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 4a1410ebb4c8..48cc0b0922f4 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -223,9 +223,10 @@ static int ml86v7667_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *cfg) { - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_BT656; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER | + V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_DATA_ACTIVE_HIGH; return 0; } diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index c9f0bd997ea7..ad13b0c890c0 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -695,10 +695,12 @@ static int mt9m001_get_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { /* MT9M001 has all capture_format parameters fixed */ - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; + cfg->bus.parallel.flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_MASTER; return 0; } diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 91a44359bcd3..afc86efa9e3e 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -9,7 +9,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> -#include <linux/gpio.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> #include <linux/v4l2-mediabus.h> @@ -1143,14 +1142,16 @@ static int mt9m111_get_mbus_config(struct v4l2_subdev *sd, { struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); - cfg->flags = V4L2_MBUS_MASTER | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags |= mt9m111->pclk_sample ? V4L2_MBUS_PCLK_SAMPLE_RISING : - V4L2_MBUS_PCLK_SAMPLE_FALLING; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; + cfg->bus.parallel.flags |= mt9m111->pclk_sample ? + V4L2_MBUS_PCLK_SAMPLE_RISING : + V4L2_MBUS_PCLK_SAMPLE_FALLING; return 0; } diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index f3ac379ef34a..bc5187f46365 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -10,7 +10,7 @@ */ #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -130,8 +130,8 @@ struct noon010_info { struct media_pad pad; struct v4l2_ctrl_handler hdl; struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; - u32 gpio_nreset; - u32 gpio_nstby; + struct gpio_desc *reset; + struct gpio_desc *stby; /* Protects the struct members below */ struct mutex lock; @@ -393,29 +393,33 @@ static int power_enable(struct noon010_info *info) return 0; } - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); + /* Assert standby: line should be flagged active low in descriptor */ + if (info->stby) + gpiod_set_value(info->stby, 1); - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); + /* Assert reset: line should be flagged active low in descriptor */ + if (info->reset) + gpiod_set_value(info->reset, 1); ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); if (ret) return ret; - if (gpio_is_valid(info->gpio_nreset)) { + /* De-assert reset and standby */ + if (info->reset) { msleep(50); - gpio_set_value(info->gpio_nreset, 1); + gpiod_set_value(info->reset, 0); } - if (gpio_is_valid(info->gpio_nstby)) { + if (info->stby) { udelay(1000); - gpio_set_value(info->gpio_nstby, 1); + gpiod_set_value(info->stby, 0); } - if (gpio_is_valid(info->gpio_nreset)) { + /* Cycle reset: assert and deassert */ + if (info->reset) { udelay(1000); - gpio_set_value(info->gpio_nreset, 0); + gpiod_set_value(info->reset, 1); msleep(100); - gpio_set_value(info->gpio_nreset, 1); + gpiod_set_value(info->reset, 0); msleep(20); } info->power = 1; @@ -438,11 +442,12 @@ static int power_disable(struct noon010_info *info) if (ret) return ret; - if (gpio_is_valid(info->gpio_nstby)) - gpio_set_value(info->gpio_nstby, 0); + /* Assert standby and reset */ + if (info->stby) + gpiod_set_value(info->stby, 1); - if (gpio_is_valid(info->gpio_nreset)) - gpio_set_value(info->gpio_nreset, 0); + if (info->reset) + gpiod_set_value(info->reset, 1); info->power = 0; @@ -741,34 +746,24 @@ static int noon010_probe(struct i2c_client *client, goto np_err; info->i2c_reg_page = -1; - info->gpio_nreset = -EINVAL; - info->gpio_nstby = -EINVAL; info->curr_fmt = &noon010_formats[0]; info->curr_win = &noon010_sizes[0]; - if (gpio_is_valid(pdata->gpio_nreset)) { - ret = devm_gpio_request_one(&client->dev, pdata->gpio_nreset, - GPIOF_OUT_INIT_LOW, - "NOON010PC30 NRST"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_err; - } - info->gpio_nreset = pdata->gpio_nreset; - gpio_export(info->gpio_nreset, 0); + /* Request reset asserted so we get put into reset */ + info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(info->reset)) { + ret = PTR_ERR(info->reset); + goto np_err; } + gpiod_set_consumer_name(info->reset, "NOON010PC30 NRST"); - if (gpio_is_valid(pdata->gpio_nstby)) { - ret = devm_gpio_request_one(&client->dev, pdata->gpio_nstby, - GPIOF_OUT_INIT_LOW, - "NOON010PC30 NSTBY"); - if (ret) { - dev_err(&client->dev, "GPIO request error: %d\n", ret); - goto np_err; - } - info->gpio_nstby = pdata->gpio_nstby; - gpio_export(info->gpio_nstby, 0); + /* Request standby asserted so we get put into standby */ + info->stby = devm_gpiod_get(&client->dev, "standby", GPIOD_OUT_HIGH); + if (IS_ERR(info->stby)) { + ret = PTR_ERR(info->stby); + goto np_err; } + gpiod_set_consumer_name(info->reset, "NOON010PC30 STBY"); for (i = 0; i < NOON010_NUM_SUPPLIES; i++) info->supply[i].supply = noon010_supply_name[i]; diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c new file mode 100644 index 000000000000..87179fc04e00 --- /dev/null +++ b/drivers/media/i2c/og01a1b.c @@ -0,0 +1,1128 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OG01A1B_REG_VALUE_08BIT 1 +#define OG01A1B_REG_VALUE_16BIT 2 +#define OG01A1B_REG_VALUE_24BIT 3 + +#define OG01A1B_LINK_FREQ_500MHZ 500000000ULL +#define OG01A1B_SCLK 120000000LL +#define OG01A1B_MCLK 19200000 +#define OG01A1B_DATA_LANES 2 +#define OG01A1B_RGB_DEPTH 10 + +#define OG01A1B_REG_CHIP_ID 0x300a +#define OG01A1B_CHIP_ID 0x470141 + +#define OG01A1B_REG_MODE_SELECT 0x0100 +#define OG01A1B_MODE_STANDBY 0x00 +#define OG01A1B_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OG01A1B_REG_VTS 0x380e +#define OG01A1B_VTS_120FPS 0x0498 +#define OG01A1B_VTS_120FPS_MIN 0x0498 +#define OG01A1B_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OG01A1B_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OG01A1B_REG_EXPOSURE 0x3501 +#define OG01A1B_EXPOSURE_MIN 1 +#define OG01A1B_EXPOSURE_MAX_MARGIN 14 +#define OG01A1B_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OG01A1B_REG_ANALOG_GAIN 0x3508 +#define OG01A1B_ANAL_GAIN_MIN 16 +#define OG01A1B_ANAL_GAIN_MAX 248 /* Max = 15.5x */ +#define OG01A1B_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OG01A1B_REG_DIG_GAIN 0x350a +#define OG01A1B_DGTL_GAIN_MIN 1024 +#define OG01A1B_DGTL_GAIN_MAX 16384 /* Max = 16x */ +#define OG01A1B_DGTL_GAIN_STEP 1 +#define OG01A1B_DGTL_GAIN_DEFAULT 1024 + +/* Group Access */ +#define OG01A1B_REG_GROUP_ACCESS 0x3208 +#define OG01A1B_GROUP_HOLD_START 0x0 +#define OG01A1B_GROUP_HOLD_END 0x10 +#define OG01A1B_GROUP_HOLD_LAUNCH 0xa0 + +/* Test Pattern Control */ +#define OG01A1B_REG_TEST_PATTERN 0x5100 +#define OG01A1B_TEST_PATTERN_ENABLE BIT(7) +#define OG01A1B_TEST_PATTERN_BAR_SHIFT 2 + +#define to_og01a1b(_sd) container_of(_sd, struct og01a1b, sd) + +enum { + OG01A1B_LINK_FREQ_1000MBPS, +}; + +struct og01a1b_reg { + u16 address; + u8 val; +}; + +struct og01a1b_reg_list { + u32 num_of_regs; + const struct og01a1b_reg *regs; +}; + +struct og01a1b_link_freq_config { + const struct og01a1b_reg_list reg_list; +}; + +struct og01a1b_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct og01a1b_reg_list reg_list; +}; + +static const struct og01a1b_reg mipi_data_rate_1000mbps[] = { + {0x0103, 0x01}, + {0x0303, 0x02}, + {0x0304, 0x00}, + {0x0305, 0xd2}, + {0x0323, 0x02}, + {0x0324, 0x01}, + {0x0325, 0x77}, +}; + +static const struct og01a1b_reg mode_1280x1024_regs[] = { + {0x0300, 0x0a}, + {0x0301, 0x29}, + {0x0302, 0x31}, + {0x0303, 0x02}, + {0x0304, 0x00}, + {0x0305, 0xd2}, + {0x0306, 0x00}, + {0x0307, 0x01}, + {0x0308, 0x02}, + {0x0309, 0x00}, + {0x0310, 0x00}, + {0x0311, 0x00}, + {0x0312, 0x07}, + {0x0313, 0x00}, + {0x0314, 0x00}, + {0x0315, 0x00}, + {0x0320, 0x02}, + {0x0321, 0x01}, + {0x0322, 0x01}, + {0x0323, 0x02}, + {0x0324, 0x01}, + {0x0325, 0x77}, + {0x0326, 0xce}, + {0x0327, 0x04}, + {0x0329, 0x02}, + {0x032a, 0x04}, + {0x032b, 0x04}, + {0x032c, 0x02}, + {0x032d, 0x01}, + {0x032e, 0x00}, + {0x300d, 0x02}, + {0x300e, 0x04}, + {0x3021, 0x08}, + {0x301e, 0x03}, + {0x3103, 0x00}, + {0x3106, 0x08}, + {0x3107, 0x40}, + {0x3216, 0x01}, + {0x3217, 0x00}, + {0x3218, 0xc0}, + {0x3219, 0x55}, + {0x3500, 0x00}, + {0x3501, 0x04}, + {0x3502, 0x8a}, + {0x3506, 0x01}, + {0x3507, 0x72}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x40}, + {0x3605, 0xe0}, + {0x3606, 0x41}, + {0x3614, 0x20}, + {0x3620, 0x0b}, + {0x3630, 0x07}, + {0x3636, 0xa0}, + {0x3637, 0xf9}, + {0x3638, 0x09}, + {0x3639, 0x38}, + {0x363f, 0x09}, + {0x3640, 0x17}, + {0x3662, 0x04}, + {0x3665, 0x80}, + {0x3670, 0x68}, + {0x3674, 0x00}, + {0x3677, 0x3f}, + {0x3679, 0x00}, + {0x369f, 0x19}, + {0x36a0, 0x03}, + {0x36a2, 0x19}, + {0x36a3, 0x03}, + {0x370d, 0x66}, + {0x370f, 0x00}, + {0x3710, 0x03}, + {0x3715, 0x03}, + {0x3716, 0x03}, + {0x3717, 0x06}, + {0x3733, 0x00}, + {0x3778, 0x00}, + {0x37a8, 0x0f}, + {0x37a9, 0x01}, + {0x37aa, 0x07}, + {0x37bd, 0x1c}, + {0x37c1, 0x2f}, + {0x37c3, 0x09}, + {0x37c8, 0x1d}, + {0x37ca, 0x30}, + {0x37df, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x04}, + {0x3807, 0x0f}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x04}, + {0x380b, 0x00}, + {0x380c, 0x03}, + {0x380d, 0x50}, + {0x380e, 0x04}, + {0x380f, 0x98}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x40}, + {0x3821, 0x04}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382a, 0x08}, + {0x382b, 0x52}, + {0x382d, 0xba}, + {0x383d, 0x14}, + {0x384a, 0xa2}, + {0x3866, 0x0e}, + {0x3867, 0x07}, + {0x3884, 0x00}, + {0x3885, 0x08}, + {0x3893, 0x68}, + {0x3894, 0x2a}, + {0x3898, 0x00}, + {0x3899, 0x31}, + {0x389a, 0x04}, + {0x389b, 0x00}, + {0x389c, 0x0b}, + {0x389d, 0xad}, + {0x389f, 0x08}, + {0x38a0, 0x00}, + {0x38a1, 0x00}, + {0x38a8, 0x70}, + {0x38ac, 0xea}, + {0x38b2, 0x00}, + {0x38b3, 0x08}, + {0x38bc, 0x20}, + {0x38c4, 0x0c}, + {0x38c5, 0x3a}, + {0x38c7, 0x3a}, + {0x38e1, 0xc0}, + {0x38ec, 0x3c}, + {0x38f0, 0x09}, + {0x38f1, 0x6f}, + {0x38fe, 0x3c}, + {0x391e, 0x00}, + {0x391f, 0x00}, + {0x3920, 0xa5}, + {0x3921, 0x00}, + {0x3922, 0x00}, + {0x3923, 0x00}, + {0x3924, 0x05}, + {0x3925, 0x00}, + {0x3926, 0x00}, + {0x3927, 0x00}, + {0x3928, 0x1a}, + {0x3929, 0x01}, + {0x392a, 0xb4}, + {0x392b, 0x00}, + {0x392c, 0x10}, + {0x392f, 0x40}, + {0x4000, 0xcf}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x07}, + {0x400a, 0x02}, + {0x400b, 0x54}, + {0x400c, 0x00}, + {0x400d, 0x07}, + {0x4010, 0xc0}, + {0x4012, 0x02}, + {0x4014, 0x04}, + {0x4015, 0x04}, + {0x4017, 0x02}, + {0x4042, 0x01}, + {0x4306, 0x04}, + {0x4307, 0x12}, + {0x4509, 0x00}, + {0x450b, 0x83}, + {0x4604, 0x68}, + {0x4608, 0x0a}, + {0x4700, 0x06}, + {0x4800, 0x64}, + {0x481b, 0x3c}, + {0x4825, 0x32}, + {0x4833, 0x18}, + {0x4837, 0x0f}, + {0x4850, 0x40}, + {0x4860, 0x00}, + {0x4861, 0xec}, + {0x4864, 0x00}, + {0x4883, 0x00}, + {0x4888, 0x90}, + {0x4889, 0x05}, + {0x488b, 0x04}, + {0x4f00, 0x04}, + {0x4f10, 0x04}, + {0x4f21, 0x01}, + {0x4f22, 0x40}, + {0x4f23, 0x44}, + {0x4f24, 0x51}, + {0x4f25, 0x41}, + {0x5000, 0x1f}, + {0x500a, 0x00}, + {0x5100, 0x00}, + {0x5111, 0x20}, + {0x3020, 0x20}, + {0x3613, 0x03}, + {0x38c9, 0x02}, + {0x5304, 0x01}, + {0x3620, 0x08}, + {0x3639, 0x58}, + {0x363a, 0x10}, + {0x3674, 0x04}, + {0x3780, 0xff}, + {0x3781, 0xff}, + {0x3782, 0x00}, + {0x3783, 0x01}, + {0x3798, 0xa3}, + {0x37aa, 0x10}, + {0x38a8, 0xf0}, + {0x38c4, 0x09}, + {0x38c5, 0xb0}, + {0x38df, 0x80}, + {0x38ff, 0x05}, + {0x4010, 0xf1}, + {0x4011, 0x70}, + {0x3667, 0x80}, + {0x4d00, 0x4a}, + {0x4d01, 0x18}, + {0x4d02, 0xbb}, + {0x4d03, 0xde}, + {0x4d04, 0x93}, + {0x4d05, 0xff}, + {0x4d09, 0x0a}, + {0x37aa, 0x16}, + {0x3606, 0x42}, + {0x3605, 0x00}, + {0x36a2, 0x17}, + {0x300d, 0x0a}, + {0x4d00, 0x4d}, + {0x4d01, 0x95}, + {0x3d8C, 0x70}, + {0x3d8d, 0xE9}, + {0x5300, 0x00}, + {0x5301, 0x10}, + {0x5302, 0x00}, + {0x5303, 0xE3}, + {0x3d88, 0x00}, + {0x3d89, 0x10}, + {0x3d8a, 0x00}, + {0x3d8b, 0xE3}, + {0x4f22, 0x00}, +}; + +static const char * const og01a1b_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar" +}; + +static const s64 link_freq_menu_items[] = { + OG01A1B_LINK_FREQ_500MHZ, +}; + +static const struct og01a1b_link_freq_config link_freq_configs[] = { + [OG01A1B_LINK_FREQ_1000MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1000mbps), + .regs = mipi_data_rate_1000mbps, + } + } +}; + +static const struct og01a1b_mode supported_modes[] = { + { + .width = 1280, + .height = 1024, + .hts = 848, + .vts_def = OG01A1B_VTS_120FPS, + .vts_min = OG01A1B_VTS_120FPS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x1024_regs), + .regs = mode_1280x1024_regs, + }, + .link_freq_index = OG01A1B_LINK_FREQ_1000MBPS, + }, +}; + +struct og01a1b { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct og01a1b_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OG01A1B_DATA_LANES; + + do_div(pixel_rate, OG01A1B_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OG01A1B_SCLK); + + return ppl; +} + +static int og01a1b_read_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int og01a1b_write_reg(struct og01a1b *og01a1b, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int og01a1b_write_reg_list(struct og01a1b *og01a1b, + const struct og01a1b_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = og01a1b_write_reg(og01a1b, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int og01a1b_test_pattern(struct og01a1b *og01a1b, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OG01A1B_TEST_PATTERN_BAR_SHIFT | + OG01A1B_TEST_PATTERN_ENABLE; + + return og01a1b_write_reg(og01a1b, OG01A1B_REG_TEST_PATTERN, + OG01A1B_REG_VALUE_08BIT, pattern); +} + +static int og01a1b_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct og01a1b *og01a1b = container_of(ctrl->handler, + struct og01a1b, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = og01a1b->cur_mode->height + ctrl->val - + OG01A1B_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(og01a1b->exposure, + og01a1b->exposure->minimum, + exposure_max, og01a1b->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_ANALOG_GAIN, + OG01A1B_REG_VALUE_16BIT, + ctrl->val << 4); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_DIG_GAIN, + OG01A1B_REG_VALUE_24BIT, + ctrl->val << 6); + break; + + case V4L2_CID_EXPOSURE: + ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_EXPOSURE, + OG01A1B_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_VBLANK: + ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_VTS, + OG01A1B_REG_VALUE_16BIT, + og01a1b->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = og01a1b_test_pattern(og01a1b, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops og01a1b_ctrl_ops = { + .s_ctrl = og01a1b_set_ctrl, +}; + +static int og01a1b_init_controls(struct og01a1b *og01a1b) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &og01a1b->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &og01a1b->mutex; + og01a1b->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &og01a1b_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE + (link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (og01a1b->link_freq) + og01a1b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + og01a1b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate + (OG01A1B_LINK_FREQ_1000MBPS), + 1, + to_pixel_rate + (OG01A1B_LINK_FREQ_1000MBPS)); + og01a1b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, + V4L2_CID_VBLANK, + og01a1b->cur_mode->vts_min - + og01a1b->cur_mode->height, + OG01A1B_VTS_MAX - + og01a1b->cur_mode->height, 1, + og01a1b->cur_mode->vts_def - + og01a1b->cur_mode->height); + h_blank = to_pixels_per_line(og01a1b->cur_mode->hts, + og01a1b->cur_mode->link_freq_index) - + og01a1b->cur_mode->width; + og01a1b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (og01a1b->hblank) + og01a1b->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OG01A1B_ANAL_GAIN_MIN, OG01A1B_ANAL_GAIN_MAX, + OG01A1B_ANAL_GAIN_STEP, OG01A1B_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OG01A1B_DGTL_GAIN_MIN, OG01A1B_DGTL_GAIN_MAX, + OG01A1B_DGTL_GAIN_STEP, OG01A1B_DGTL_GAIN_DEFAULT); + exposure_max = (og01a1b->cur_mode->vts_def - + OG01A1B_EXPOSURE_MAX_MARGIN); + og01a1b->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &og01a1b_ctrl_ops, + V4L2_CID_EXPOSURE, + OG01A1B_EXPOSURE_MIN, + exposure_max, + OG01A1B_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og01a1b_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(og01a1b_test_pattern_menu) - 1, + 0, 0, og01a1b_test_pattern_menu); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + og01a1b->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void og01a1b_update_pad_format(const struct og01a1b_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int og01a1b_start_streaming(struct og01a1b *og01a1b) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + const struct og01a1b_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = og01a1b->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + + ret = og01a1b_write_reg_list(og01a1b, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &og01a1b->cur_mode->reg_list; + ret = og01a1b_write_reg_list(og01a1b, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(og01a1b->sd.ctrl_handler); + if (ret) + return ret; + + ret = og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT, + OG01A1B_REG_VALUE_08BIT, + OG01A1B_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void og01a1b_stop_streaming(struct og01a1b *og01a1b) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + + if (og01a1b_write_reg(og01a1b, OG01A1B_REG_MODE_SELECT, + OG01A1B_REG_VALUE_08BIT, OG01A1B_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int og01a1b_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct og01a1b *og01a1b = to_og01a1b(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (og01a1b->streaming == enable) + return 0; + + mutex_lock(&og01a1b->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&og01a1b->mutex); + return ret; + } + + ret = og01a1b_start_streaming(og01a1b); + if (ret) { + enable = 0; + og01a1b_stop_streaming(og01a1b); + pm_runtime_put(&client->dev); + } + } else { + og01a1b_stop_streaming(og01a1b); + pm_runtime_put(&client->dev); + } + + og01a1b->streaming = enable; + mutex_unlock(&og01a1b->mutex); + + return ret; +} + +static int __maybe_unused og01a1b_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct og01a1b *og01a1b = to_og01a1b(sd); + + mutex_lock(&og01a1b->mutex); + if (og01a1b->streaming) + og01a1b_stop_streaming(og01a1b); + + mutex_unlock(&og01a1b->mutex); + + return 0; +} + +static int __maybe_unused og01a1b_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct og01a1b *og01a1b = to_og01a1b(sd); + int ret; + + mutex_lock(&og01a1b->mutex); + if (og01a1b->streaming) { + ret = og01a1b_start_streaming(og01a1b); + if (ret) { + og01a1b->streaming = false; + og01a1b_stop_streaming(og01a1b); + mutex_unlock(&og01a1b->mutex); + return ret; + } + } + + mutex_unlock(&og01a1b->mutex); + + return 0; +} + +static int og01a1b_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct og01a1b *og01a1b = to_og01a1b(sd); + const struct og01a1b_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&og01a1b->mutex); + og01a1b_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad) = fmt->format; + } else { + og01a1b->cur_mode = mode; + __v4l2_ctrl_s_ctrl(og01a1b->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(og01a1b->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(og01a1b->vblank, + mode->vts_min - mode->height, + OG01A1B_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(og01a1b->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(og01a1b->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&og01a1b->mutex); + + return 0; +} + +static int og01a1b_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct og01a1b *og01a1b = to_og01a1b(sd); + + mutex_lock(&og01a1b->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&og01a1b->sd, + sd_state, + fmt->pad); + else + og01a1b_update_pad_format(og01a1b->cur_mode, &fmt->format); + + mutex_unlock(&og01a1b->mutex); + + return 0; +} + +static int og01a1b_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int og01a1b_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int og01a1b_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct og01a1b *og01a1b = to_og01a1b(sd); + + mutex_lock(&og01a1b->mutex); + og01a1b_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&og01a1b->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops og01a1b_video_ops = { + .s_stream = og01a1b_set_stream, +}; + +static const struct v4l2_subdev_pad_ops og01a1b_pad_ops = { + .set_fmt = og01a1b_set_format, + .get_fmt = og01a1b_get_format, + .enum_mbus_code = og01a1b_enum_mbus_code, + .enum_frame_size = og01a1b_enum_frame_size, +}; + +static const struct v4l2_subdev_ops og01a1b_subdev_ops = { + .video = &og01a1b_video_ops, + .pad = &og01a1b_pad_ops, +}; + +static const struct media_entity_operations og01a1b_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops og01a1b_internal_ops = { + .open = og01a1b_open, +}; + +static int og01a1b_identify_module(struct og01a1b *og01a1b) +{ + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + int ret; + u32 val; + + ret = og01a1b_read_reg(og01a1b, OG01A1B_REG_CHIP_ID, + OG01A1B_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OG01A1B_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OG01A1B_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int og01a1b_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != OG01A1B_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OG01A1B_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int og01a1b_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct og01a1b *og01a1b = to_og01a1b(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&og01a1b->mutex); + + return 0; +} + +static int og01a1b_probe(struct i2c_client *client) +{ + struct og01a1b *og01a1b; + int ret; + + ret = og01a1b_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL); + if (!og01a1b) + return -ENOMEM; + + v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); + ret = og01a1b_identify_module(og01a1b); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&og01a1b->mutex); + og01a1b->cur_mode = &supported_modes[0]; + ret = og01a1b_init_controls(og01a1b); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + og01a1b->sd.internal_ops = &og01a1b_internal_ops; + og01a1b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + og01a1b->sd.entity.ops = &og01a1b_subdev_entity_ops; + og01a1b->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + og01a1b->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&og01a1b->sd.entity, 1, &og01a1b->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&og01a1b->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&og01a1b->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(og01a1b->sd.ctrl_handler); + mutex_destroy(&og01a1b->mutex); + + return ret; +} + +static const struct dev_pm_ops og01a1b_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(og01a1b_suspend, og01a1b_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id og01a1b_acpi_ids[] = { + {"OVTI01AC"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, og01a1b_acpi_ids); +#endif + +static struct i2c_driver og01a1b_i2c_driver = { + .driver = { + .name = "og01a1b", + .pm = &og01a1b_pm_ops, + .acpi_match_table = ACPI_PTR(og01a1b_acpi_ids), + }, + .probe_new = og01a1b_probe, + .remove = og01a1b_remove, +}; + +module_i2c_driver(og01a1b_i2c_driver); + +MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_DESCRIPTION("OmniVision OG01A1B sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c new file mode 100644 index 000000000000..e5ef6466a3ec --- /dev/null +++ b/drivers/media/i2c/ov08d10.c @@ -0,0 +1,1528 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV08D10_SCLK 144000000ULL +#define OV08D10_XVCLK_19_2 19200000 +#define OV08D10_ROWCLK 36000 +#define OV08D10_DATA_LANES 2 +#define OV08D10_RGB_DEPTH 10 + +#define OV08D10_REG_PAGE 0xfd +#define OV08D10_REG_GLOBAL_EFFECTIVE 0x01 +#define OV08D10_REG_CHIP_ID_0 0x00 +#define OV08D10_REG_CHIP_ID_1 0x01 +#define OV08D10_ID_MASK GENMASK(15, 0) +#define OV08D10_CHIP_ID 0x5608 + +#define OV08D10_REG_MODE_SELECT 0xa0 +#define OV08D10_MODE_STANDBY 0x00 +#define OV08D10_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV08D10_REG_VTS_H 0x05 +#define OV08D10_REG_VTS_L 0x06 +#define OV08D10_VTS_MAX 0x7fff + +/* Exposure controls from sensor */ +#define OV08D10_REG_EXPOSURE_H 0x02 +#define OV08D10_REG_EXPOSURE_M 0x03 +#define OV08D10_REG_EXPOSURE_L 0x04 +#define OV08D10_EXPOSURE_MIN 6 +#define OV08D10_EXPOSURE_MAX_MARGIN 6 +#define OV08D10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV08D10_REG_ANALOG_GAIN 0x24 +#define OV08D10_ANAL_GAIN_MIN 128 +#define OV08D10_ANAL_GAIN_MAX 2047 +#define OV08D10_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV08D10_REG_MWB_DGAIN_C 0x21 +#define OV08D10_REG_MWB_DGAIN_F 0x22 +#define OV08D10_DGTL_GAIN_MIN 0 +#define OV08D10_DGTL_GAIN_MAX 4095 +#define OV08D10_DGTL_GAIN_STEP 1 +#define OV08D10_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV08D10_REG_TEST_PATTERN 0x12 +#define OV08D10_TEST_PATTERN_ENABLE 0x01 +#define OV08D10_TEST_PATTERN_DISABLE 0x00 + +/* Flip Mirror Controls from sensor */ +#define OV08D10_REG_FLIP_OPT 0x32 +#define OV08D10_REG_FLIP_MASK 0x3 + +#define to_ov08d10(_sd) container_of(_sd, struct ov08d10, sd) + +struct ov08d10_reg { + u8 address; + u8 val; +}; + +struct ov08d10_reg_list { + u32 num_of_regs; + const struct ov08d10_reg *regs; +}; + +struct ov08d10_link_freq_config { + const struct ov08d10_reg_list reg_list; +}; + +struct ov08d10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov08d10_reg_list reg_list; + + /* Number of data lanes */ + u8 data_lanes; +}; + +/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes */ +static const struct ov08d10_reg mipi_data_rate_720mbps[] = { + {0xfd, 0x00}, + {0x11, 0x2a}, + {0x14, 0x43}, + {0x1a, 0x04}, + {0x1b, 0xe1}, + {0x1e, 0x13}, + {0xb7, 0x02} +}; + +/* 1632x1224 needs 360Mbps/lane, 2 lanes */ +static const struct ov08d10_reg mipi_data_rate_360mbps[] = { + {0xfd, 0x00}, + {0x1a, 0x04}, + {0x1b, 0xe1}, + {0x1d, 0x00}, + {0x1c, 0x19}, + {0x11, 0x2a}, + {0x14, 0x54}, + {0x1e, 0x13}, + {0xb7, 0x02} +}; + +static const struct ov08d10_reg lane_2_mode_3280x2460[] = { + /* 3280x2460 resolution */ + {0xfd, 0x01}, + {0x12, 0x00}, + {0x03, 0x12}, + {0x04, 0x58}, + {0x07, 0x05}, + {0x21, 0x02}, + {0x24, 0x30}, + {0x33, 0x03}, + {0x01, 0x03}, + {0x19, 0x10}, + {0x42, 0x55}, + {0x43, 0x00}, + {0x47, 0x07}, + {0x48, 0x08}, + {0xb2, 0x7f}, + {0xb3, 0x7b}, + {0xbd, 0x08}, + {0xd2, 0x57}, + {0xd3, 0x10}, + {0xd4, 0x08}, + {0xd5, 0x08}, + {0xd6, 0x06}, + {0xb1, 0x00}, + {0xb4, 0x00}, + {0xb7, 0x0a}, + {0xbc, 0x44}, + {0xbf, 0x48}, + {0xc1, 0x10}, + {0xc3, 0x24}, + {0xc8, 0x03}, + {0xc9, 0xf8}, + {0xe1, 0x33}, + {0xe2, 0xbb}, + {0x51, 0x0c}, + {0x52, 0x0a}, + {0x57, 0x8c}, + {0x59, 0x09}, + {0x5a, 0x08}, + {0x5e, 0x10}, + {0x60, 0x02}, + {0x6d, 0x5c}, + {0x76, 0x16}, + {0x7c, 0x11}, + {0x90, 0x28}, + {0x91, 0x16}, + {0x92, 0x1c}, + {0x93, 0x24}, + {0x95, 0x48}, + {0x9c, 0x06}, + {0xca, 0x0c}, + {0xce, 0x0d}, + {0xfd, 0x01}, + {0xc0, 0x00}, + {0xdd, 0x18}, + {0xde, 0x19}, + {0xdf, 0x32}, + {0xe0, 0x70}, + {0xfd, 0x01}, + {0xc2, 0x05}, + {0xd7, 0x88}, + {0xd8, 0x77}, + {0xd9, 0x00}, + {0xfd, 0x07}, + {0x00, 0xf8}, + {0x01, 0x2b}, + {0x05, 0x40}, + {0x08, 0x06}, + {0x09, 0x11}, + {0x28, 0x6f}, + {0x2a, 0x20}, + {0x2b, 0x05}, + {0x5e, 0x10}, + {0x52, 0x00}, + {0x53, 0x7c}, + {0x54, 0x00}, + {0x55, 0x7c}, + {0x56, 0x00}, + {0x57, 0x7c}, + {0x58, 0x00}, + {0x59, 0x7c}, + {0xfd, 0x02}, + {0x9a, 0x30}, + {0xa8, 0x02}, + {0xfd, 0x02}, + {0xa1, 0x01}, + {0xa2, 0x09}, + {0xa3, 0x9c}, + {0xa5, 0x00}, + {0xa6, 0x0c}, + {0xa7, 0xd0}, + {0xfd, 0x00}, + {0x24, 0x01}, + {0xc0, 0x16}, + {0xc1, 0x08}, + {0xc2, 0x30}, + {0x8e, 0x0c}, + {0x8f, 0xd0}, + {0x90, 0x09}, + {0x91, 0x9c}, + {0xfd, 0x05}, + {0x04, 0x40}, + {0x07, 0x00}, + {0x0d, 0x01}, + {0x0f, 0x01}, + {0x10, 0x00}, + {0x11, 0x00}, + {0x12, 0x0c}, + {0x13, 0xcf}, + {0x14, 0x00}, + {0x15, 0x00}, + {0xfd, 0x00}, + {0x20, 0x0f}, + {0xe7, 0x03}, + {0xe7, 0x00} +}; + +static const struct ov08d10_reg lane_2_mode_3264x2448[] = { + /* 3264x2448 resolution */ + {0xfd, 0x01}, + {0x12, 0x00}, + {0x03, 0x12}, + {0x04, 0x58}, + {0x07, 0x05}, + {0x21, 0x02}, + {0x24, 0x30}, + {0x33, 0x03}, + {0x01, 0x03}, + {0x19, 0x10}, + {0x42, 0x55}, + {0x43, 0x00}, + {0x47, 0x07}, + {0x48, 0x08}, + {0xb2, 0x7f}, + {0xb3, 0x7b}, + {0xbd, 0x08}, + {0xd2, 0x57}, + {0xd3, 0x10}, + {0xd4, 0x08}, + {0xd5, 0x08}, + {0xd6, 0x06}, + {0xb1, 0x00}, + {0xb4, 0x00}, + {0xb7, 0x0a}, + {0xbc, 0x44}, + {0xbf, 0x48}, + {0xc1, 0x10}, + {0xc3, 0x24}, + {0xc8, 0x03}, + {0xc9, 0xf8}, + {0xe1, 0x33}, + {0xe2, 0xbb}, + {0x51, 0x0c}, + {0x52, 0x0a}, + {0x57, 0x8c}, + {0x59, 0x09}, + {0x5a, 0x08}, + {0x5e, 0x10}, + {0x60, 0x02}, + {0x6d, 0x5c}, + {0x76, 0x16}, + {0x7c, 0x11}, + {0x90, 0x28}, + {0x91, 0x16}, + {0x92, 0x1c}, + {0x93, 0x24}, + {0x95, 0x48}, + {0x9c, 0x06}, + {0xca, 0x0c}, + {0xce, 0x0d}, + {0xfd, 0x01}, + {0xc0, 0x00}, + {0xdd, 0x18}, + {0xde, 0x19}, + {0xdf, 0x32}, + {0xe0, 0x70}, + {0xfd, 0x01}, + {0xc2, 0x05}, + {0xd7, 0x88}, + {0xd8, 0x77}, + {0xd9, 0x00}, + {0xfd, 0x07}, + {0x00, 0xf8}, + {0x01, 0x2b}, + {0x05, 0x40}, + {0x08, 0x06}, + {0x09, 0x11}, + {0x28, 0x6f}, + {0x2a, 0x20}, + {0x2b, 0x05}, + {0x5e, 0x10}, + {0x52, 0x00}, + {0x53, 0x7c}, + {0x54, 0x00}, + {0x55, 0x7c}, + {0x56, 0x00}, + {0x57, 0x7c}, + {0x58, 0x00}, + {0x59, 0x7c}, + {0xfd, 0x02}, + {0x9a, 0x30}, + {0xa8, 0x02}, + {0xfd, 0x02}, + {0xa1, 0x09}, + {0xa2, 0x09}, + {0xa3, 0x90}, + {0xa5, 0x08}, + {0xa6, 0x0c}, + {0xa7, 0xc0}, + {0xfd, 0x00}, + {0x24, 0x01}, + {0xc0, 0x16}, + {0xc1, 0x08}, + {0xc2, 0x30}, + {0x8e, 0x0c}, + {0x8f, 0xc0}, + {0x90, 0x09}, + {0x91, 0x90}, + {0xfd, 0x05}, + {0x04, 0x40}, + {0x07, 0x00}, + {0x0d, 0x01}, + {0x0f, 0x01}, + {0x10, 0x00}, + {0x11, 0x00}, + {0x12, 0x0c}, + {0x13, 0xcf}, + {0x14, 0x00}, + {0x15, 0x00}, + {0xfd, 0x00}, + {0x20, 0x0f}, + {0xe7, 0x03}, + {0xe7, 0x00} +}; + +static const struct ov08d10_reg lane_2_mode_1632x1224[] = { + /* 1640x1232 resolution */ + {0xfd, 0x01}, + {0x1a, 0x0a}, + {0x1b, 0x08}, + {0x2a, 0x01}, + {0x2b, 0x9a}, + {0xfd, 0x01}, + {0x12, 0x00}, + {0x03, 0x05}, + {0x04, 0xe2}, + {0x07, 0x05}, + {0x21, 0x02}, + {0x24, 0x30}, + {0x33, 0x03}, + {0x31, 0x06}, + {0x33, 0x03}, + {0x01, 0x03}, + {0x19, 0x10}, + {0x42, 0x55}, + {0x43, 0x00}, + {0x47, 0x07}, + {0x48, 0x08}, + {0xb2, 0x7f}, + {0xb3, 0x7b}, + {0xbd, 0x08}, + {0xd2, 0x57}, + {0xd3, 0x10}, + {0xd4, 0x08}, + {0xd5, 0x08}, + {0xd6, 0x06}, + {0xb1, 0x00}, + {0xb4, 0x00}, + {0xb7, 0x0a}, + {0xbc, 0x44}, + {0xbf, 0x48}, + {0xc1, 0x10}, + {0xc3, 0x24}, + {0xc8, 0x03}, + {0xc9, 0xf8}, + {0xe1, 0x33}, + {0xe2, 0xbb}, + {0x51, 0x0c}, + {0x52, 0x0a}, + {0x57, 0x8c}, + {0x59, 0x09}, + {0x5a, 0x08}, + {0x5e, 0x10}, + {0x60, 0x02}, + {0x6d, 0x5c}, + {0x76, 0x16}, + {0x7c, 0x1a}, + {0x90, 0x28}, + {0x91, 0x16}, + {0x92, 0x1c}, + {0x93, 0x24}, + {0x95, 0x48}, + {0x9c, 0x06}, + {0xca, 0x0c}, + {0xce, 0x0d}, + {0xfd, 0x01}, + {0xc0, 0x00}, + {0xdd, 0x18}, + {0xde, 0x19}, + {0xdf, 0x32}, + {0xe0, 0x70}, + {0xfd, 0x01}, + {0xc2, 0x05}, + {0xd7, 0x88}, + {0xd8, 0x77}, + {0xd9, 0x00}, + {0xfd, 0x07}, + {0x00, 0xf8}, + {0x01, 0x2b}, + {0x05, 0x40}, + {0x08, 0x03}, + {0x09, 0x08}, + {0x28, 0x6f}, + {0x2a, 0x20}, + {0x2b, 0x05}, + {0x2c, 0x01}, + {0x50, 0x02}, + {0x51, 0x03}, + {0x5e, 0x00}, + {0x52, 0x00}, + {0x53, 0x7c}, + {0x54, 0x00}, + {0x55, 0x7c}, + {0x56, 0x00}, + {0x57, 0x7c}, + {0x58, 0x00}, + {0x59, 0x7c}, + {0xfd, 0x02}, + {0x9a, 0x30}, + {0xa8, 0x02}, + {0xfd, 0x02}, + {0xa9, 0x04}, + {0xaa, 0xd0}, + {0xab, 0x06}, + {0xac, 0x68}, + {0xa1, 0x09}, + {0xa2, 0x04}, + {0xa3, 0xc8}, + {0xa5, 0x04}, + {0xa6, 0x06}, + {0xa7, 0x60}, + {0xfd, 0x05}, + {0x06, 0x80}, + {0x18, 0x06}, + {0x19, 0x68}, + {0xfd, 0x00}, + {0x24, 0x01}, + {0xc0, 0x16}, + {0xc1, 0x08}, + {0xc2, 0x30}, + {0x8e, 0x06}, + {0x8f, 0x60}, + {0x90, 0x04}, + {0x91, 0xc8}, + {0x93, 0x0e}, + {0x94, 0x77}, + {0x95, 0x77}, + {0x96, 0x10}, + {0x98, 0x88}, + {0x9c, 0x1a}, + {0xfd, 0x05}, + {0x04, 0x40}, + {0x07, 0x99}, + {0x0d, 0x03}, + {0x0f, 0x03}, + {0x10, 0x00}, + {0x11, 0x00}, + {0x12, 0x0c}, + {0x13, 0xcf}, + {0x14, 0x00}, + {0x15, 0x00}, + {0xfd, 0x00}, + {0x20, 0x0f}, + {0xe7, 0x03}, + {0xe7, 0x00}, +}; + +static const char * const ov08d10_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", +}; + +struct ov08d10 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + struct clk *xvclk; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov08d10_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* lanes index */ + u8 nlanes; + + const struct ov08d10_lane_cfg *priv_lane; + u8 modes_size; +}; + +struct ov08d10_lane_cfg { + const s64 link_freq_menu[2]; + const struct ov08d10_link_freq_config link_freq_configs[2]; + const struct ov08d10_mode sp_modes[3]; +}; + +static const struct ov08d10_lane_cfg lane_cfg_2 = { + { + 720000000, + 360000000, + }, + {{ + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mipi_data_rate_720mbps), + .regs = mipi_data_rate_720mbps, + } + }, + { + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, + } + }}, + {{ + .width = 3280, + .height = 2460, + .hts = 1840, + .vts_def = 2504, + .vts_min = 2504, + .reg_list = { + .num_of_regs = ARRAY_SIZE(lane_2_mode_3280x2460), + .regs = lane_2_mode_3280x2460, + }, + .link_freq_index = 0, + .data_lanes = 2, + }, + { + .width = 3264, + .height = 2448, + .hts = 1840, + .vts_def = 2504, + .vts_min = 2504, + .reg_list = { + .num_of_regs = ARRAY_SIZE(lane_2_mode_3264x2448), + .regs = lane_2_mode_3264x2448, + }, + .link_freq_index = 0, + .data_lanes = 2, + }, + { + .width = 1632, + .height = 1224, + .hts = 1912, + .vts_def = 3736, + .vts_min = 3736, + .reg_list = { + .num_of_regs = ARRAY_SIZE(lane_2_mode_1632x1224), + .regs = lane_2_mode_1632x1224, + }, + .link_freq_index = 1, + .data_lanes = 2, + }} +}; + +static u32 ov08d10_get_format_code(struct ov08d10 *ov08d10) +{ + static const u32 codes[2][2] = { + { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10}, + { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10}, + }; + + return codes[ov08d10->vflip->val][ov08d10->hflip->val]; +} + +static unsigned int ov08d10_modes_num(const struct ov08d10 *ov08d10) +{ + unsigned int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(ov08d10->priv_lane->sp_modes); i++) { + if (ov08d10->priv_lane->sp_modes[i].width == 0) + break; + count++; + } + + return count; +} + +static u64 to_rate(const s64 *link_freq_menu, + u32 f_index, u8 nlanes) +{ + u64 pixel_rate = link_freq_menu[f_index] * 2 * nlanes; + + do_div(pixel_rate, OV08D10_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(const s64 *link_freq_menu, u32 hts, + u32 f_index, u8 nlanes) +{ + u64 ppl = hts * to_rate(link_freq_menu, f_index, nlanes); + + do_div(ppl, OV08D10_SCLK); + + return ppl; +} + +static int ov08d10_write_reg_list(struct ov08d10 *ov08d10, + const struct ov08d10_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = i2c_smbus_write_byte_data(client, r_list->regs[i].address, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%2.2x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov08d10_update_analog_gain(struct ov08d10 *ov08d10, u32 a_gain) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + int ret; + + val = ((a_gain >> 3) & 0xFF); + /* CIS control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + /* update AGAIN */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_ANALOG_GAIN, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_update_digital_gain(struct ov08d10 *ov08d10, u32 d_gain) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + int ret; + + d_gain = (d_gain >> 1); + /* CIS control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + val = ((d_gain >> 8) & 0x3F); + /* update DGAIN */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MWB_DGAIN_C, val); + if (ret < 0) + return ret; + + val = d_gain & 0xFF; + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MWB_DGAIN_F, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_set_exposure(struct ov08d10 *ov08d10, u32 exposure) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + u8 hts_h, hts_l; + u32 hts, cur_vts, exp_cal; + int ret; + + cur_vts = ov08d10->cur_mode->vts_def; + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + hts_h = i2c_smbus_read_byte_data(client, 0x37); + hts_l = i2c_smbus_read_byte_data(client, 0x38); + hts = ((hts_h << 8) | (hts_l)); + exp_cal = 66 * OV08D10_ROWCLK / hts; + exposure = exposure * exp_cal / (cur_vts - OV08D10_EXPOSURE_MAX_MARGIN); + /* CIS control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + /* update exposure */ + val = ((exposure >> 16) & 0xFF); + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_H, val); + if (ret < 0) + return ret; + + val = ((exposure >> 8) & 0xFF); + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_M, val); + if (ret < 0) + return ret; + + val = exposure & 0xFF; + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_L, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_set_vblank(struct ov08d10 *ov08d10, u32 vblank) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + int ret; + + /* CIS control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + val = ((vblank >> 8) & 0xFF); + /* update vblank */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_VTS_H, val); + if (ret < 0) + return ret; + + val = vblank & 0xFF; + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_VTS_L, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_test_pattern(struct ov08d10 *ov08d10, u32 pattern) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + int ret; + + if (pattern) + val = OV08D10_TEST_PATTERN_ENABLE; + else + val = OV08D10_TEST_PATTERN_DISABLE; + + /* CIS control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, + OV08D10_REG_TEST_PATTERN, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_set_ctrl_flip(struct ov08d10 *ov08d10, u32 ctrl_val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u8 val; + int ret; + + /* System control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, OV08D10_REG_FLIP_OPT); + if (ret < 0) + return ret; + + val = ret | (ctrl_val & OV08D10_REG_FLIP_MASK); + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_FLIP_OPT, val); + + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, + OV08D10_REG_GLOBAL_EFFECTIVE, 0x01); +} + +static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov08d10 *ov08d10 = container_of(ctrl->handler, + struct ov08d10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + s64 exposure_max; + int ret; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov08d10->cur_mode->height + ctrl->val - + OV08D10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov08d10->exposure, + ov08d10->exposure->minimum, + exposure_max, ov08d10->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov08d10_update_analog_gain(ov08d10, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov08d10_update_digital_gain(ov08d10, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = ov08d10_set_exposure(ov08d10, ctrl->val); + break; + + case V4L2_CID_VBLANK: + ret = ov08d10_set_vblank(ov08d10, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov08d10_test_pattern(ov08d10, ctrl->val); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = ov08d10_set_ctrl_flip(ov08d10, + ov08d10->hflip->val | + ov08d10->vflip->val << 1); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov08d10_ctrl_ops = { + .s_ctrl = ov08d10_set_ctrl, +}; + +static int ov08d10_init_controls(struct ov08d10 *ov08d10) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + u8 link_freq_size; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + s64 h_blank; + s64 pixel_rate_max; + const struct ov08d10_mode *mode; + int ret; + + ctrl_hdlr = &ov08d10->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov08d10->mutex; + link_freq_size = ARRAY_SIZE(ov08d10->priv_lane->link_freq_menu); + ov08d10->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_LINK_FREQ, + link_freq_size - 1, + 0, + ov08d10->priv_lane->link_freq_menu); + if (ov08d10->link_freq) + ov08d10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate_max = to_rate(ov08d10->priv_lane->link_freq_menu, 0, + ov08d10->cur_mode->data_lanes); + ov08d10->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, pixel_rate_max, 1, + pixel_rate_max); + + mode = ov08d10->cur_mode; + vblank_def = mode->vts_def - mode->height; + vblank_min = mode->vts_min - mode->height; + ov08d10->vblank = + v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + OV08D10_VTS_MAX - mode->height, 1, + vblank_def); + + h_blank = to_pixels_per_line(ov08d10->priv_lane->link_freq_menu, + mode->hts, mode->link_freq_index, + mode->data_lanes) - + mode->width; + ov08d10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov08d10->hblank) + ov08d10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV08D10_ANAL_GAIN_MIN, OV08D10_ANAL_GAIN_MAX, + OV08D10_ANAL_GAIN_STEP, OV08D10_ANAL_GAIN_MIN); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV08D10_DGTL_GAIN_MIN, OV08D10_DGTL_GAIN_MAX, + OV08D10_DGTL_GAIN_STEP, OV08D10_DGTL_GAIN_DEFAULT); + + exposure_max = mode->vts_def - OV08D10_EXPOSURE_MAX_MARGIN; + ov08d10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV08D10_EXPOSURE_MIN, + exposure_max, + OV08D10_EXPOSURE_STEP, + exposure_max); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov08d10_test_pattern_menu) - 1, + 0, 0, ov08d10_test_pattern_menu); + + ov08d10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + ov08d10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov08d10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov08d10_update_pad_format(struct ov08d10 *ov08d10, + const struct ov08d10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = ov08d10_get_format_code(ov08d10); + fmt->field = V4L2_FIELD_NONE; +} + +static int ov08d10_start_streaming(struct ov08d10 *ov08d10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + const struct ov08d10_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov08d10->cur_mode->link_freq_index; + reg_list = + &ov08d10->priv_lane->link_freq_configs[link_freq_index].reg_list; + + /* soft reset */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); + if (ret < 0) { + dev_err(&client->dev, "failed to reset sensor"); + return ret; + } + ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e); + if (ret < 0) { + dev_err(&client->dev, "failed to reset sensor"); + return ret; + } + usleep_range(3000, 4000); + ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b); + if (ret < 0) { + dev_err(&client->dev, "failed to reset sensor"); + return ret; + } + + /* update sensor setting */ + ret = ov08d10_write_reg_list(ov08d10, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov08d10->cur_mode->reg_list; + ret = ov08d10_write_reg_list(ov08d10, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov08d10->sd.ctrl_handler); + if (ret) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT, + OV08D10_MODE_STREAMING); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); +} + +static void ov08d10_stop_streaming(struct ov08d10 *ov08d10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); + if (ret < 0) { + dev_err(&client->dev, "failed to stop streaming"); + return; + } + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT, + OV08D10_MODE_STANDBY); + if (ret < 0) { + dev_err(&client->dev, "failed to stop streaming"); + return; + } + + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01); + if (ret < 0) { + dev_err(&client->dev, "failed to stop streaming"); + return; + } +} + +static int ov08d10_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov08d10->streaming == enable) + return 0; + + mutex_lock(&ov08d10->mutex); + if (enable) { + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) { + mutex_unlock(&ov08d10->mutex); + return ret; + } + + ret = ov08d10_start_streaming(ov08d10); + if (ret) { + enable = 0; + ov08d10_stop_streaming(ov08d10); + pm_runtime_put(&client->dev); + } + } else { + ov08d10_stop_streaming(ov08d10); + pm_runtime_put(&client->dev); + } + + ov08d10->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(ov08d10->vflip, enable); + __v4l2_ctrl_grab(ov08d10->hflip, enable); + + mutex_unlock(&ov08d10->mutex); + + return ret; +} + +static int __maybe_unused ov08d10_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + mutex_lock(&ov08d10->mutex); + if (ov08d10->streaming) + ov08d10_stop_streaming(ov08d10); + + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static int __maybe_unused ov08d10_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08d10 *ov08d10 = to_ov08d10(sd); + int ret; + + mutex_lock(&ov08d10->mutex); + + if (ov08d10->streaming) { + ret = ov08d10_start_streaming(ov08d10); + if (ret) { + ov08d10->streaming = false; + ov08d10_stop_streaming(ov08d10); + mutex_unlock(&ov08d10->mutex); + return ret; + } + } + + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static int ov08d10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + const struct ov08d10_mode *mode; + s32 vblank_def, h_blank; + s64 pixel_rate; + + mode = v4l2_find_nearest_size(ov08d10->priv_lane->sp_modes, + ov08d10->modes_size, + width, height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov08d10->mutex); + ov08d10_update_pad_format(ov08d10, mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = + fmt->format; + } else { + ov08d10->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov08d10->link_freq, mode->link_freq_index); + pixel_rate = to_rate(ov08d10->priv_lane->link_freq_menu, + mode->link_freq_index, + ov08d10->cur_mode->data_lanes); + __v4l2_ctrl_s_ctrl_int64(ov08d10->pixel_rate, pixel_rate); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov08d10->vblank, + mode->vts_min - mode->height, + OV08D10_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov08d10->vblank, vblank_def); + h_blank = to_pixels_per_line(ov08d10->priv_lane->link_freq_menu, + mode->hts, + mode->link_freq_index, + ov08d10->cur_mode->data_lanes) + - mode->width; + __v4l2_ctrl_modify_range(ov08d10->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static int ov08d10_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + mutex_lock(&ov08d10->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov08d10->sd, + sd_state, + fmt->pad); + else + ov08d10_update_pad_format(ov08d10, ov08d10->cur_mode, + &fmt->format); + + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static int ov08d10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + if (code->index > 0) + return -EINVAL; + + mutex_lock(&ov08d10->mutex); + code->code = ov08d10_get_format_code(ov08d10); + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static int ov08d10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + if (fse->index >= ov08d10->modes_size) + return -EINVAL; + + mutex_lock(&ov08d10->mutex); + if (fse->code != ov08d10_get_format_code(ov08d10)) { + mutex_unlock(&ov08d10->mutex); + return -EINVAL; + } + mutex_unlock(&ov08d10->mutex); + + fse->min_width = ov08d10->priv_lane->sp_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = ov08d10->priv_lane->sp_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov08d10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + mutex_lock(&ov08d10->mutex); + ov08d10_update_pad_format(ov08d10, &ov08d10->priv_lane->sp_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); + mutex_unlock(&ov08d10->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov08d10_video_ops = { + .s_stream = ov08d10_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov08d10_pad_ops = { + .set_fmt = ov08d10_set_format, + .get_fmt = ov08d10_get_format, + .enum_mbus_code = ov08d10_enum_mbus_code, + .enum_frame_size = ov08d10_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov08d10_subdev_ops = { + .video = &ov08d10_video_ops, + .pad = &ov08d10_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ov08d10_internal_ops = { + .open = ov08d10_open, +}; + +static int ov08d10_identify_module(struct ov08d10 *ov08d10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd); + u32 val; + u16 chip_id; + int ret; + + /* System control registers */ + ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00); + if (ret < 0) + return ret; + + /* Validate the chip ID */ + ret = i2c_smbus_read_byte_data(client, OV08D10_REG_CHIP_ID_0); + if (ret < 0) + return ret; + + val = ret << 8; + + ret = i2c_smbus_read_byte_data(client, OV08D10_REG_CHIP_ID_1); + if (ret < 0) + return ret; + + chip_id = val | ret; + + if ((chip_id & OV08D10_ID_MASK) != OV08D10_CHIP_ID) { + dev_err(&client->dev, "unexpected sensor id(0x%04x)\n", + chip_id); + return -EINVAL; + } + + return 0; +} + +static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10, struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 xvclk_rate; + unsigned int i, j; + int ret; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); + if (ret) + return ret; + + if (xvclk_rate != OV08D10_XVCLK_19_2) + dev_warn(dev, "external clock rate %u is unsupported", + xvclk_rate); + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + /* Get number of data lanes */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + dev_dbg(dev, "Using %u data lanes\n", ov08d10->cur_mode->data_lanes); + + ov08d10->priv_lane = &lane_cfg_2; + ov08d10->modes_size = ov08d10_modes_num(ov08d10); + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(ov08d10->priv_lane->link_freq_menu); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (ov08d10->priv_lane->link_freq_menu[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + ov08d10->priv_lane->link_freq_menu[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov08d10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov08d10 *ov08d10 = to_ov08d10(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov08d10->mutex); + + return 0; +} + +static int ov08d10_probe(struct i2c_client *client) +{ + struct ov08d10 *ov08d10; + int ret; + + ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL); + if (!ov08d10) + return -ENOMEM; + + ret = ov08d10_get_hwcfg(ov08d10, &client->dev); + if (ret) { + dev_err(&client->dev, "failed to get HW configuration: %d", + ret); + return ret; + } + + v4l2_i2c_subdev_init(&ov08d10->sd, client, &ov08d10_subdev_ops); + + ret = ov08d10_identify_module(ov08d10); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&ov08d10->mutex); + ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0]; + ret = ov08d10_init_controls(ov08d10); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov08d10->sd.internal_ops = &ov08d10_internal_ops; + ov08d10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov08d10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor(&ov08d10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov08d10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov08d10->sd.ctrl_handler); + mutex_destroy(&ov08d10->mutex); + + return ret; +} + +static const struct dev_pm_ops ov08d10_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov08d10_suspend, ov08d10_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov08d10_acpi_ids[] = { + { "OVTI08D1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov08d10_acpi_ids); +#endif + +static struct i2c_driver ov08d10_i2c_driver = { + .driver = { + .name = "ov08d10", + .pm = &ov08d10_pm_ops, + .acpi_match_table = ACPI_PTR(ov08d10_acpi_ids), + }, + .probe_new = ov08d10_probe, + .remove = ov08d10_remove, +}; + +module_i2c_driver(ov08d10_i2c_driver); + +MODULE_AUTHOR("Su, Jimmy <jimmy.su@intel.com>"); +MODULE_DESCRIPTION("OmniVision ov08d10 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index bab720c7c1de..d5f0eabf20c6 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1162,6 +1162,7 @@ static int ov2740_probe(struct i2c_client *client) if (!ov2740) return -ENOMEM; + v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); full_power = acpi_dev_state_d0(&client->dev); if (full_power) { ret = ov2740_identify_module(ov2740); @@ -1171,13 +1172,6 @@ static int ov2740_probe(struct i2c_client *client) } } - v4l2_i2c_subdev_init(&ov2740->sd, client, &ov2740_subdev_ops); - ret = ov2740_identify_module(ov2740); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); - return ret; - } - mutex_init(&ov2740->mutex); ov2740->cur_mode = &supported_modes[0]; ret = ov2740_init_controls(ov2740); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index ddbd71394db3..db5a19babe67 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2293,7 +2293,6 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, struct ov5640_dev *sensor = to_ov5640_dev(sd); const struct ov5640_mode_info *new_mode; struct v4l2_mbus_framefmt *mbus_fmt = &format->format; - struct v4l2_mbus_framefmt *fmt; int ret; if (format->pad != 0) @@ -2311,12 +2310,10 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (ret) goto out; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - else - fmt = &sensor->fmt; - - *fmt = *mbus_fmt; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, 0) = *mbus_fmt; + goto out; + } if (new_mode != sensor->current_mode) { sensor->current_mode = new_mode; @@ -2325,6 +2322,9 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (mbus_fmt->code != sensor->fmt.code) sensor->pending_fmt_change = true; + /* update format even if code is unchanged, resolution might change */ + sensor->fmt = *mbus_fmt; + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, ov5640_calc_pixel_rate(sensor)); out: diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index 947d437ed0ef..930ff6897044 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -639,7 +639,7 @@ struct ov5648_ctrls { struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl_handler handler; -} __packed; +}; struct ov5648_sensor { struct device *dev; @@ -1112,7 +1112,7 @@ static int ov5648_pad_configure(struct ov5648_sensor *sensor) static int ov5648_mipi_configure(struct ov5648_sensor *sensor) { - struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = &sensor->endpoint.bus.mipi_csi2; unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; int ret; @@ -1692,7 +1692,7 @@ static int ov5648_state_mipi_configure(struct ov5648_sensor *sensor, u32 mbus_code) { struct ov5648_ctrls *ctrls = &sensor->ctrls; - struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = &sensor->endpoint.bus.mipi_csi2; unsigned long mipi_clk_rate; unsigned int bits_per_sample; @@ -1778,8 +1778,14 @@ static int ov5648_state_configure(struct ov5648_sensor *sensor, static int ov5648_state_init(struct ov5648_sensor *sensor) { - return ov5648_state_configure(sensor, &ov5648_modes[0], - ov5648_mbus_codes[0]); + int ret; + + mutex_lock(&sensor->mutex); + ret = ov5648_state_configure(sensor, &ov5648_modes[0], + ov5648_mbus_codes[0]); + mutex_unlock(&sensor->mutex); + + return ret; } /* Sensor Base */ diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 00925850fa7c..82ba9f56baec 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -50,14 +50,21 @@ #define OV5675_ANAL_GAIN_STEP 1 /* Digital gain controls from sensor */ +#define OV5675_REG_DIGITAL_GAIN 0x350a #define OV5675_REG_MWB_R_GAIN 0x5019 #define OV5675_REG_MWB_G_GAIN 0x501b #define OV5675_REG_MWB_B_GAIN 0x501d -#define OV5675_DGTL_GAIN_MIN 0 +#define OV5675_DGTL_GAIN_MIN 1024 #define OV5675_DGTL_GAIN_MAX 4095 #define OV5675_DGTL_GAIN_STEP 1 #define OV5675_DGTL_GAIN_DEFAULT 1024 +/* Group Access */ +#define OV5675_REG_GROUP_ACCESS 0x3208 +#define OV5675_GROUP_HOLD_START 0x0 +#define OV5675_GROUP_HOLD_END 0x10 +#define OV5675_GROUP_HOLD_LAUNCH 0xa0 + /* Test Pattern Control */ #define OV5675_REG_TEST_PATTERN 0x4503 #define OV5675_TEST_PATTERN_ENABLE BIT(7) @@ -587,6 +594,12 @@ static int ov5675_update_digital_gain(struct ov5675 *ov5675, u32 d_gain) { int ret; + ret = ov5675_write_reg(ov5675, OV5675_REG_GROUP_ACCESS, + OV5675_REG_VALUE_08BIT, + OV5675_GROUP_HOLD_START); + if (ret) + return ret; + ret = ov5675_write_reg(ov5675, OV5675_REG_MWB_R_GAIN, OV5675_REG_VALUE_16BIT, d_gain); if (ret) @@ -597,8 +610,21 @@ static int ov5675_update_digital_gain(struct ov5675 *ov5675, u32 d_gain) if (ret) return ret; - return ov5675_write_reg(ov5675, OV5675_REG_MWB_B_GAIN, - OV5675_REG_VALUE_16BIT, d_gain); + ret = ov5675_write_reg(ov5675, OV5675_REG_MWB_B_GAIN, + OV5675_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_GROUP_ACCESS, + OV5675_REG_VALUE_08BIT, + OV5675_GROUP_HOLD_END); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_GROUP_ACCESS, + OV5675_REG_VALUE_08BIT, + OV5675_GROUP_HOLD_LAUNCH); + return ret; } static int ov5675_test_pattern(struct ov5675 *ov5675, u32 pattern) diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 2784fcf67f3b..117ff5403312 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -950,7 +950,6 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd, unsigned int width, height; unsigned int hblank; int exposure_max; - int ret = 0; crop = __ov5693_get_pad_crop(ov5693, state, format->pad, format->which); @@ -982,13 +981,13 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd, format->format = *fmt; if (format->which == V4L2_SUBDEV_FORMAT_TRY) - return ret; + return 0; mutex_lock(&ov5693->lock); - ov5693->mode.binning_x = hratio > 1 ? true : false; + ov5693->mode.binning_x = hratio > 1; ov5693->mode.inc_x_odd = hratio > 1 ? 3 : 1; - ov5693->mode.binning_y = vratio > 1 ? true : false; + ov5693->mode.binning_y = vratio > 1; ov5693->mode.inc_y_odd = vratio > 1 ? 3 : 1; ov5693->mode.vts = __ov5693_calc_vts(fmt->height); @@ -1012,7 +1011,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd, exposure_max)); mutex_unlock(&ov5693->lock); - return ret; + return 0; } static int ov5693_get_selection(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index f67412150b16..6458e96d9091 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -472,9 +472,16 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); + struct v4l2_rect *rect; - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + /* pre-select try crop rectangle */ + rect = &sd_state->pads->try_crop; + + } else { + /* pre-select active crop rectangle */ + rect = &priv->rect; + } switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -483,14 +490,33 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, sel->r.width = W_CIF; sel->r.height = H_CIF; return 0; + case V4L2_SEL_TGT_CROP: - sel->r = priv->rect; + /* use selected crop rectangle */ + sel->r = *rect; return 0; + default: return -EINVAL; } } +static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) +{ + return width > rect->width >> 1 || height > rect->height >> 1; +} + +static void ov6650_bind_align_crop_rectangle(struct v4l2_rect *rect) +{ + v4l_bound_align_image(&rect->width, 2, W_CIF, 1, + &rect->height, 2, H_CIF, 1, 0); + v4l_bound_align_image(&rect->left, DEF_HSTRT << 1, + (DEF_HSTRT << 1) + W_CIF - (__s32)rect->width, 1, + &rect->top, DEF_VSTRT << 1, + (DEF_VSTRT << 1) + H_CIF - (__s32)rect->height, + 1, 0); +} + static int ov6650_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -499,18 +525,30 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, struct ov6650 *priv = to_ov6650(client); int ret; - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) + if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - v4l_bound_align_image(&sel->r.width, 2, W_CIF, 1, - &sel->r.height, 2, H_CIF, 1, 0); - v4l_bound_align_image(&sel->r.left, DEF_HSTRT << 1, - (DEF_HSTRT << 1) + W_CIF - (__s32)sel->r.width, 1, - &sel->r.top, DEF_VSTRT << 1, - (DEF_VSTRT << 1) + H_CIF - (__s32)sel->r.height, - 1, 0); + ov6650_bind_align_crop_rectangle(&sel->r); + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *crop = &sd_state->pads->try_crop; + struct v4l2_mbus_framefmt *mf = &sd_state->pads->try_fmt; + /* detect current pad config scaling factor */ + bool half_scale = !is_unscaled_ok(mf->width, mf->height, crop); + /* store new crop rectangle */ + *crop = sel->r; + + /* adjust frame size */ + mf->width = crop->width >> half_scale; + mf->height = crop->height >> half_scale; + + return 0; + } + + /* V4L2_SUBDEV_FORMAT_ACTIVE */ + + /* apply new crop rectangle */ ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1); if (!ret) { priv->rect.width += priv->rect.left - sel->r.left; @@ -562,30 +600,13 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, return 0; } -static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) -{ - return width > rect->width >> 1 || height > rect->height >> 1; -} - #define to_clkrc(div) ((div) - 1) /* set the format we will capture in */ -static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) +static int ov6650_s_fmt(struct v4l2_subdev *sd, u32 code, bool half_scale) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = priv->rect.left + (priv->rect.width >> 1) - - (mf->width >> (1 - half_scale)), - .r.top = priv->rect.top + (priv->rect.height >> 1) - - (mf->height >> (1 - half_scale)), - .r.width = mf->width << half_scale, - .r.height = mf->height << half_scale, - }; - u32 code = mf->code; u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask; int ret; @@ -653,9 +674,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) coma_mask |= COMA_QCIF; } - ret = ov6650_set_selection(sd, NULL, &sel); - if (!ret) - ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); + ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); if (!ret) { priv->half_scale = half_scale; @@ -674,14 +693,12 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); + struct v4l2_rect *crop; + bool half_scale; if (format->pad) return -EINVAL; - if (is_unscaled_ok(mf->width, mf->height, &priv->rect)) - v4l_bound_align_image(&mf->width, 2, W_CIF, 1, - &mf->height, 2, H_CIF, 1, 0); - switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; @@ -699,10 +716,17 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, break; } + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + crop = &sd_state->pads->try_crop; + else + crop = &priv->rect; + + half_scale = !is_unscaled_ok(mf->width, mf->height, crop); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - /* store media bus format code and frame size in pad config */ - sd_state->pads->try_fmt.width = mf->width; - sd_state->pads->try_fmt.height = mf->height; + /* store new mbus frame format code and size in pad config */ + sd_state->pads->try_fmt.width = crop->width >> half_scale; + sd_state->pads->try_fmt.height = crop->height >> half_scale; sd_state->pads->try_fmt.code = mf->code; /* return default mbus frame format updated with pad config */ @@ -712,9 +736,11 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, mf->code = sd_state->pads->try_fmt.code; } else { - /* apply new media bus format code and frame size */ - int ret = ov6650_s_fmt(sd, mf); + int ret = 0; + /* apply new media bus frame format and scaling if changed */ + if (mf->code != priv->code || half_scale != priv->half_scale) + ret = ov6650_s_fmt(sd, mf->code, half_scale); if (ret) return ret; @@ -738,6 +764,33 @@ static int ov6650_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int ov6650_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + int i; + + /* enumerate supported frame intervals not exceeding 1 second */ + if (fie->index > CLKRC_DIV_MASK || + GET_CLKRC_DIV(fie->index) > FRAME_RATE_MAX) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ov6650_codes); i++) + if (fie->code == ov6650_codes[i]) + break; + if (i == ARRAY_SIZE(ov6650_codes)) + return -EINVAL; + + if (!fie->width || fie->width > W_CIF || + !fie->height || fie->height > H_CIF) + return -EINVAL; + + fie->interval.numerator = GET_CLKRC_DIV(fie->index); + fie->interval.denominator = FRAME_RATE_MAX; + + return 0; +} + static int ov6650_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *ival) { @@ -890,9 +943,8 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) if (!ret) ret = ov6650_prog_dflt(client, xclk->clkrc); if (!ret) { - struct v4l2_mbus_framefmt mf = ov6650_def_fmt; - - ret = ov6650_s_fmt(sd, &mf); + /* driver default frame format, no scaling */ + ret = ov6650_s_fmt(sd, ov6650_def_fmt.code, false); } if (!ret) ret = v4l2_ctrl_handler_setup(&priv->hdl); @@ -932,54 +984,18 @@ static int ov6650_get_mbus_config(struct v4l2_subdev *sd, if (ret) return ret; - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH - | ((comj & COMJ_VSYNC_HIGH) ? V4L2_MBUS_VSYNC_ACTIVE_HIGH - : V4L2_MBUS_VSYNC_ACTIVE_LOW) - | ((comf & COMF_HREF_LOW) ? V4L2_MBUS_HSYNC_ACTIVE_LOW - : V4L2_MBUS_HSYNC_ACTIVE_HIGH) - | ((comj & COMJ_PCLK_RISING) ? V4L2_MBUS_PCLK_SAMPLE_RISING - : V4L2_MBUS_PCLK_SAMPLE_FALLING); cfg->type = V4L2_MBUS_PARALLEL; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH + | ((comj & COMJ_VSYNC_HIGH) ? V4L2_MBUS_VSYNC_ACTIVE_HIGH + : V4L2_MBUS_VSYNC_ACTIVE_LOW) + | ((comf & COMF_HREF_LOW) ? V4L2_MBUS_HSYNC_ACTIVE_LOW + : V4L2_MBUS_HSYNC_ACTIVE_HIGH) + | ((comj & COMJ_PCLK_RISING) ? V4L2_MBUS_PCLK_SAMPLE_RISING + : V4L2_MBUS_PCLK_SAMPLE_FALLING); return 0; } -/* Alter bus settings on camera side */ -static int ov6650_set_mbus_config(struct v4l2_subdev *sd, - unsigned int pad, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - - if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); - else if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); - if (ret) - return ret; - - if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); - else if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); - if (ret) - return ret; - - if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); - else if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); - if (ret) - return ret; - - /* - * Update the configuration to report what is actually applied to - * the hardware. - */ - return ov6650_get_mbus_config(sd, pad, cfg); -} - static const struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, .g_frame_interval = ov6650_g_frame_interval, @@ -987,13 +1003,13 @@ static const struct v4l2_subdev_video_ops ov6650_video_ops = { }; static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { - .enum_mbus_code = ov6650_enum_mbus_code, - .get_selection = ov6650_get_selection, - .set_selection = ov6650_set_selection, - .get_fmt = ov6650_get_fmt, - .set_fmt = ov6650_set_fmt, - .get_mbus_config = ov6650_get_mbus_config, - .set_mbus_config = ov6650_set_mbus_config, + .enum_mbus_code = ov6650_enum_mbus_code, + .enum_frame_interval = ov6650_enum_frame_interval, + .get_selection = ov6650_get_selection, + .set_selection = ov6650_set_selection, + .get_fmt = ov6650_get_fmt, + .set_fmt = ov6650_set_fmt, + .get_mbus_config = ov6650_get_mbus_config, }; static const struct v4l2_subdev_ops ov6650_subdev_ops = { diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index d9d016cfa9ac..b8f4f0d3e33d 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -457,8 +457,8 @@ #define OV8865_NATIVE_WIDTH 3296 #define OV8865_NATIVE_HEIGHT 2528 -#define OV8865_ACTIVE_START_TOP 32 -#define OV8865_ACTIVE_START_LEFT 80 +#define OV8865_ACTIVE_START_LEFT 16 +#define OV8865_ACTIVE_START_TOP 40 #define OV8865_ACTIVE_WIDTH 3264 #define OV8865_ACTIVE_HEIGHT 2448 @@ -1471,7 +1471,7 @@ static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor) static int ov8865_mipi_configure(struct ov8865_sensor *sensor) { - struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = &sensor->endpoint.bus.mipi_csi2; unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; int ret; @@ -2241,7 +2241,7 @@ static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor, u32 mbus_code) { struct ov8865_ctrls *ctrls = &sensor->ctrls; - struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = &sensor->endpoint.bus.mipi_csi2; unsigned long mipi_clk_rate; unsigned int bits_per_sample; @@ -2838,8 +2838,8 @@ static int ov8865_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: mutex_lock(&sensor->mutex); - __ov8865_get_pad_crop(sensor, state, sel->pad, - sel->which, &sel->r); + __ov8865_get_pad_crop(sensor, state, sel->pad, + sel->which, &sel->r); mutex_unlock(&sensor->mutex); break; case V4L2_SEL_TGT_NATIVE_SIZE: diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 0bab8c2cf160..9f44ed52d164 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -652,10 +652,12 @@ static int ov9640_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *cfg) { - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; + cfg->bus.parallel.flags = V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; return 0; } diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index a958bbc2c33d..15ff80e6301e 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1129,7 +1129,7 @@ static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_forma static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) { - static u16 lcr2vbi[] = { + static const u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_CAPTION_525, /* 4 */ V4L2_SLICED_WSS_625, 0, /* 5 */ diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 3205cd8298dd..e18b8947ad7e 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -69,7 +69,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = { struct tc358743_state { struct tc358743_platform_data pdata; - struct v4l2_fwnode_bus_mipi_csi2 bus; + struct v4l2_mbus_config_mipi_csi2 bus; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -717,7 +717,7 @@ static void tc358743_set_csi(struct v4l2_subdev *sd) ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) ? 0 : MASK_CONTCLKMODE); i2c_wr32(sd, STARTCNTRL, MASK_START); i2c_wr32(sd, CSI_START, MASK_STRT); @@ -1613,24 +1613,8 @@ static int tc358743_get_mbus_config(struct v4l2_subdev *sd, cfg->type = V4L2_MBUS_CSI2_DPHY; /* Support for non-continuous CSI-2 clock is missing in the driver */ - cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - switch (state->csi_lanes_in_use) { - case 1: - cfg->flags |= V4L2_MBUS_CSI2_1_LANE; - break; - case 2: - cfg->flags |= V4L2_MBUS_CSI2_2_LANE; - break; - case 3: - cfg->flags |= V4L2_MBUS_CSI2_3_LANE; - break; - case 4: - cfg->flags |= V4L2_MBUS_CSI2_4_LANE; - break; - default: - return -EINVAL; - } + cfg->bus.mipi_csi2.flags = 0; + cfg->bus.mipi_csi2.num_data_lanes = state->csi_lanes_in_use; return 0; } @@ -2055,7 +2039,7 @@ static int tc358743_probe(struct i2c_client *client) /* platform data */ if (pdata) { state->pdata = *pdata; - state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + state->bus.flags = 0; } else { err = tc358743_probe_of(state); if (err == -ENODEV) diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 4b16ffcaef98..65472438444b 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1198,8 +1198,10 @@ static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, struct tvp5150 *decoder = to_tvp5150(sd); cfg->type = decoder->mbus_type; - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING - | V4L2_MBUS_FIELD_EVEN_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER + | V4L2_MBUS_PCLK_SAMPLE_RISING + | V4L2_MBUS_FIELD_EVEN_LOW + | V4L2_MBUS_DATA_ACTIVE_HIGH; return 0; } diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index b411f9796191..8ab0913d8d82 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -396,20 +396,21 @@ __must_check int __media_pipeline_start(struct media_entity *entity, struct media_link *link; int ret; - if (!pipe->streaming_count++) { - ret = media_graph_walk_init(&pipe->graph, mdev); - if (ret) - goto error_graph_walk_start; + if (pipe->streaming_count) { + pipe->streaming_count++; + return 0; } + ret = media_graph_walk_init(&pipe->graph, mdev); + if (ret) + return ret; + media_graph_walk_start(&pipe->graph, entity); while ((entity = media_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - entity->stream_count++; - if (entity->pipe && entity->pipe != pipe) { pr_err("Pipe active for %s. Can't start for %s\n", entity->name, @@ -418,12 +419,12 @@ __must_check int __media_pipeline_start(struct media_entity *entity, goto error; } - entity->pipe = pipe; - /* Already streaming --- no need to check. */ - if (entity->stream_count > 1) + if (entity->pipe) continue; + entity->pipe = pipe; + if (!entity->ops || !entity->ops->link_validate) continue; @@ -479,6 +480,8 @@ __must_check int __media_pipeline_start(struct media_entity *entity, } } + pipe->streaming_count++; + return 0; error: @@ -489,24 +492,17 @@ error: media_graph_walk_start(graph, entity_err); while ((entity_err = media_graph_walk_next(graph))) { - /* Sanity check for negative stream_count */ - if (!WARN_ON_ONCE(entity_err->stream_count <= 0)) { - entity_err->stream_count--; - if (entity_err->stream_count == 0) - entity_err->pipe = NULL; - } + entity_err->pipe = NULL; /* - * We haven't increased stream_count further than this - * so we quit here. + * We haven't started entities further than this so we quit + * here. */ if (entity_err == entity) break; } -error_graph_walk_start: - if (!--pipe->streaming_count) - media_graph_walk_cleanup(graph); + media_graph_walk_cleanup(graph); return ret; } @@ -537,19 +533,15 @@ void __media_pipeline_stop(struct media_entity *entity) if (WARN_ON(!pipe)) return; + if (--pipe->streaming_count) + return; + media_graph_walk_start(graph, entity); - while ((entity = media_graph_walk_next(graph))) { - /* Sanity check for negative stream_count */ - if (!WARN_ON_ONCE(entity->stream_count <= 0)) { - entity->stream_count--; - if (entity->stream_count == 0) - entity->pipe = NULL; - } - } + while ((entity = media_graph_walk_next(graph))) + entity->pipe = NULL; - if (!--pipe->streaming_count) - media_graph_walk_cleanup(graph); + media_graph_walk_cleanup(graph); } EXPORT_SYMBOL_GPL(__media_pipeline_stop); @@ -834,7 +826,8 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) sink = link->sink->entity; if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && - (source->stream_count || sink->stream_count)) + (media_entity_is_streaming(source) || + media_entity_is_streaming(sink))) return -EBUSY; mdev = source->graph_obj.mdev; diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig index 75aa6de08d53..2f9877bc61e4 100644 --- a/drivers/media/mmc/Kconfig +++ b/drivers/media/mmc/Kconfig @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only + source "drivers/media/mmc/siano/Kconfig" diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 2cd8e328dda9..1224d908713a 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -12,61 +12,68 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" + source "drivers/media/pci/meye/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" + endif if MEDIA_ANALOG_TV_SUPPORT comment "Media capture/analog TV support" + +source "drivers/media/pci/dt3155/Kconfig" source "drivers/media/pci/ivtv/Kconfig" source "drivers/media/pci/saa7146/Kconfig" -source "drivers/media/pci/dt3155/Kconfig" + endif if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT comment "Media capture/analog/hybrid TV support" + +source "drivers/media/pci/bt8xx/Kconfig" +source "drivers/media/pci/cobalt/Kconfig" source "drivers/media/pci/cx18/Kconfig" source "drivers/media/pci/cx23885/Kconfig" source "drivers/media/pci/cx25821/Kconfig" source "drivers/media/pci/cx88/Kconfig" -source "drivers/media/pci/bt8xx/Kconfig" source "drivers/media/pci/saa7134/Kconfig" source "drivers/media/pci/saa7164/Kconfig" -source "drivers/media/pci/cobalt/Kconfig" endif if MEDIA_DIGITAL_TV_SUPPORT comment "Media digital TV PCI Adapters" -source "drivers/media/pci/ttpci/Kconfig" + source "drivers/media/pci/b2c2/Kconfig" -source "drivers/media/pci/pluto2/Kconfig" +source "drivers/media/pci/ddbridge/Kconfig" source "drivers/media/pci/dm1105/Kconfig" -source "drivers/media/pci/pt1/Kconfig" -source "drivers/media/pci/pt3/Kconfig" source "drivers/media/pci/mantis/Kconfig" +source "drivers/media/pci/netup_unidvb/Kconfig" source "drivers/media/pci/ngene/Kconfig" -source "drivers/media/pci/ddbridge/Kconfig" +source "drivers/media/pci/pluto2/Kconfig" +source "drivers/media/pci/pt1/Kconfig" +source "drivers/media/pci/pt3/Kconfig" source "drivers/media/pci/smipcie/Kconfig" -source "drivers/media/pci/netup_unidvb/Kconfig" -endif +source "drivers/media/pci/ttpci/Kconfig" -source "drivers/media/pci/intel/ipu3/Kconfig" +endif config VIDEO_PCI_SKELETON tristate "Skeleton PCI V4L2 driver" depends on SAMPLES depends on MEDIA_TEST_SUPPORT - depends on PCI && VIDEO_V4L2 + depends on PCI && VIDEO_DEV select VIDEOBUF2_MEMOPS select VIDEOBUF2_DMA_CONTIG help Enable build of the skeleton PCI driver, used as a reference when developing new drivers. +source "drivers/media/pci/intel/ipu3/Kconfig" + endif #MEDIA_PCI_SUPPORT endif #PCI diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 984fa247096d..551169a3e434 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -3,6 +3,8 @@ # Makefile for the kernel multimedia device drivers. # +# Please keep it alphabetically sorted by directory +# (e. g. LC_ALL=C sort Makefile) obj-y += ttpci/ \ b2c2/ \ pluto2/ \ @@ -17,19 +19,23 @@ obj-y += ttpci/ \ netup_unidvb/ \ intel/ -obj-$(CONFIG_VIDEO_IVTV) += ivtv/ +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) + +obj-$(CONFIG_STA2X11_VIP) += sta2x11/ + +obj-$(CONFIG_VIDEO_BT848) += bt8xx/ +obj-$(CONFIG_VIDEO_COBALT) += cobalt/ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_CX88) += cx88/ -obj-$(CONFIG_VIDEO_BT848) += bt8xx/ -obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ -obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ -obj-$(CONFIG_VIDEO_TW68) += tw68/ -obj-$(CONFIG_VIDEO_TW686X) += tw686x/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ +obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_MEYE) += meye/ -obj-$(CONFIG_STA2X11_VIP) += sta2x11/ +obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ +obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ -obj-$(CONFIG_VIDEO_COBALT) += cobalt/ obj-$(CONFIG_VIDEO_TW5864) += tw5864/ +obj-$(CONFIG_VIDEO_TW686X) += tw686x/ +obj-$(CONFIG_VIDEO_TW68) += tw68/ diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index 3f56decbb681..927190281bd5 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_BT848 tristate "BT848 Video For Linux" - depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 + depends on PCI && I2C && VIDEO_DEV select I2C_ALGOBIT select VIDEOBUF_DMA_SG depends on RC_CORE diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 8cc9bec43688..5ca3d0cc653a 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3890,7 +3890,7 @@ static int bttv_register_video(struct bttv *btv) /* video */ vdev_init(btv, &btv->video_dev, &bttv_video_template, "video"); - btv->video_dev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + btv->video_dev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; if (btv->tuner_type != TUNER_ABSENT) btv->video_dev.device_caps |= V4L2_CAP_TUNER; @@ -3911,7 +3911,7 @@ static int bttv_register_video(struct bttv *btv) /* vbi */ vdev_init(btv, &btv->vbi_dev, &bttv_video_template, "vbi"); btv->vbi_dev.device_caps = V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | V4L2_CAP_TUNER; + V4L2_CAP_STREAMING; if (btv->tuner_type != TUNER_ABSENT) btv->vbi_dev.device_caps |= V4L2_CAP_TUNER; diff --git a/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig index d8d9ea6b09bc..e13e36141199 100644 --- a/drivers/media/pci/cobalt/Kconfig +++ b/drivers/media/pci/cobalt/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_COBALT tristate "Cisco Cobalt support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C depends on PCI_MSI && MTD_COMPLEX_MAPPINGS depends on (GPIOLIB && DRM_I2C_ADV7511=n) || COMPILE_TEST depends on SND diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig index 7074a1071302..a4e32fdcfd3d 100644 --- a/drivers/media/pci/cx18/Kconfig +++ b/drivers/media/pci/cx18/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CX18 tristate "Conexant cx23418 MPEG encoder support" - depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C + depends on VIDEO_DEV && DVB_CORE && PCI && I2C select I2C_ALGOBIT select VIDEOBUF_VMALLOC depends on RC_CORE diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 59497ba6bf1f..84260972c343 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -23,7 +23,7 @@ #include "cx18-mailbox.h" #include "cx18-ioctl.h" #include "cx18-controls.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include <linux/dma-mapping.h> #include <media/tveeprom.h> @@ -899,7 +899,7 @@ static int cx18_probe(struct pci_dev *pci_dev, return -ENOMEM; } - cx = kzalloc(sizeof(*cx), GFP_ATOMIC); + cx = kzalloc(sizeof(*cx), GFP_KERNEL); if (!cx) return -ENOMEM; diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c index 4c57a294b9fa..33e5a5b5fab4 100644 --- a/drivers/media/pci/cx18/cx18-dvb.c +++ b/drivers/media/pci/cx18/cx18-dvb.c @@ -22,7 +22,7 @@ #include <linux/firmware.h> #include "mt352.h" #include "mt352_priv.h" -#include "tuner-xc2028.h" +#include "xc2028.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c index cf7cfda94107..160c8377e352 100644 --- a/drivers/media/pci/cx18/cx18-gpio.c +++ b/drivers/media/pci/cx18/cx18-gpio.c @@ -12,7 +12,7 @@ #include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" -#include "tuner-xc2028.h" +#include "xc2028.h" /********************* GPIO stuffs *********************/ diff --git a/drivers/media/pci/cx18/cx18-queue.h b/drivers/media/pci/cx18/cx18-queue.h index e0a34bd6539e..26f2097c0496 100644 --- a/drivers/media/pci/cx18/cx18-queue.h +++ b/drivers/media/pci/cx18/cx18-queue.h @@ -15,15 +15,15 @@ static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, struct cx18_buffer *buf) { - pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle, + dma_sync_single_for_cpu(&s->cx->pci_dev->dev, buf->dma_handle, s->buf_size, s->dma); } static inline void cx18_buf_sync_for_device(struct cx18_stream *s, struct cx18_buffer *buf) { - pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle, - s->buf_size, s->dma); + dma_sync_single_for_device(&s->cx->pci_dev->dev, buf->dma_handle, + s->buf_size, s->dma); } void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 0160f909f38c..9244b4320558 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -15,7 +15,7 @@ #include <linux/firmware.h> #include <misc/altera.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include "netup-eeprom.h" #include "netup-init.h" #include "altera-ci.h" diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 45c2f4afceb8..8fd5b6ef2428 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -28,7 +28,7 @@ #include "xc5000.h" #include "max2165.h" #include "tda10048.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "tuner-simple.h" #include "dib7000p.h" #include "dib0070.h" diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 19c34e5510ee..d2e84c6457e0 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -55,7 +55,7 @@ static void cx23885_input_process_measurements(struct cx23885_dev *dev, } while (num != 0); if (overrun) - ir_raw_event_reset(kernel_ir->rc); + ir_raw_event_overflow(kernel_ir->rc); else if (handle) ir_raw_event_handle(kernel_ir->rc); } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index a380e0920a21..3d03f5e95786 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -24,7 +24,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> #include "cx23885-ioctl.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include <media/drv-intf/cx25840.h> diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index 680e1e3fe89b..2c1d5137ac47 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -162,6 +162,9 @@ int cx8802_start_dma(struct cx8802_dev *dev, cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); q->count = 0; + /* clear interrupt status register */ + cx_write(MO_TS_INTSTAT, 0x1f1111); + /* enable irqs */ dprintk(1, "setting the interrupt mask\n"); cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT); diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index ce4acf6de6aa..2ff3226a52ec 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -28,7 +28,7 @@ #include <media/i2c/wm8775.h> #include "cx88-reg.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include <linux/mutex.h> diff --git a/drivers/media/pci/dt3155/Kconfig b/drivers/media/pci/dt3155/Kconfig index a3d24b8a719b..2b76de195aa5 100644 --- a/drivers/media/pci/dt3155/Kconfig +++ b/drivers/media/pci/dt3155/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_DT3155 tristate "DT3155 frame grabber" - depends on PCI && VIDEO_DEV && VIDEO_V4L2 + depends on PCI && VIDEO_DEV select VIDEOBUF2_DMA_CONTIG help Enables dt3155 device driver for the DataTranslation DT3155 frame grabber. diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index dce8274c81e6..39bd3be0b43d 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_IPU3_CIO2 tristate "Intel ipu3-cio2 driver" - depends on VIDEO_V4L2 && PCI + depends on VIDEO_DEV && PCI depends on ACPI || COMPILE_TEST depends on X86 select MEDIA_CONTROLLER diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig index e70502902b73..9be52101bc4f 100644 --- a/drivers/media/pci/ivtv/Kconfig +++ b/drivers/media/pci/ivtv/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_IVTV tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" - depends on VIDEO_V4L2 && PCI && I2C + depends on VIDEO_DEV && PCI && I2C select I2C_ALGOBIT depends on RC_CORE select VIDEO_TUNER diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 57d4d5485d7a..f5846c22c799 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -57,7 +57,7 @@ #include <linux/dma-mapping.h> #include <media/tveeprom.h> #include <media/i2c/saa7115.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include <uapi/linux/sched/types.h> /* If you have already X v4l cards, then set this to X. This way diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h index 4cf92dee6527..ce3a7ca51736 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.h +++ b/drivers/media/pci/ivtv/ivtv-driver.h @@ -330,7 +330,6 @@ struct ivtv_stream { struct ivtv *itv; /* for ease of use */ const char *name; /* name of the stream */ int type; /* stream type */ - u32 caps; /* V4L2 capabilities */ struct v4l2_fh *fh; /* pointer to the streaming filehandle */ spinlock_t qlock; /* locks access to the queues */ diff --git a/drivers/media/pci/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c index 856e7ab7f33e..6434c0d03a6d 100644 --- a/drivers/media/pci/ivtv/ivtv-gpio.c +++ b/drivers/media/pci/ivtv/ivtv-gpio.c @@ -10,7 +10,7 @@ #include "ivtv-driver.h" #include "ivtv-cards.h" #include "ivtv-gpio.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include <media/tuner.h> #include <media/v4l2-ctrls.h> diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 0cdf6b3210c2..fee460e2ca86 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -438,7 +438,7 @@ static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_f struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; struct v4l2_window *winfmt = &fmt->fmt.win; - if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + if (!(s->vdev.device_caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) return -EINVAL; if (!itv->osd_video_pbase) return -EINVAL; @@ -549,7 +549,7 @@ static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2 u32 chromakey = fmt->fmt.win.chromakey; u8 global_alpha = fmt->fmt.win.global_alpha; - if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + if (!(s->vdev.device_caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) return -EINVAL; if (!itv->osd_video_pbase) return -EINVAL; @@ -1383,7 +1383,7 @@ static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) 0, }; - if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + if (!(s->vdev.device_caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) return -ENOTTY; if (!itv->osd_video_pbase) return -ENOTTY; @@ -1450,7 +1450,7 @@ static int ivtv_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffe struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; struct yuv_playback_info *yi = &itv->yuv_info; - if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + if (!(s->vdev.device_caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) return -ENOTTY; if (!itv->osd_video_pbase) return -ENOTTY; @@ -1470,7 +1470,7 @@ static int ivtv_overlay(struct file *file, void *fh, unsigned int on) struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; - if (!(s->caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + if (!(s->vdev.device_caps & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) return -ENOTTY; if (!itv->osd_video_pbase) return -ENOTTY; diff --git a/drivers/media/pci/ivtv/ivtv-queue.h b/drivers/media/pci/ivtv/ivtv-queue.h index 586b0bf63c26..983e99642364 100644 --- a/drivers/media/pci/ivtv/ivtv-queue.h +++ b/drivers/media/pci/ivtv/ivtv-queue.h @@ -17,20 +17,20 @@ static inline int ivtv_might_use_pio(struct ivtv_stream *s) { - return s->dma == PCI_DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI); + return s->dma == DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI); } static inline int ivtv_use_pio(struct ivtv_stream *s) { struct ivtv *itv = s->itv; - return s->dma == PCI_DMA_NONE || + return s->dma == DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); } static inline int ivtv_might_use_dma(struct ivtv_stream *s) { - return s->dma != PCI_DMA_NONE; + return s->dma != DMA_NONE; } static inline int ivtv_use_dma(struct ivtv_stream *s) @@ -41,15 +41,16 @@ static inline int ivtv_use_dma(struct ivtv_stream *s) static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle, - s->buf_size + 256, s->dma); + dma_sync_single_for_cpu(&s->itv->pdev->dev, buf->dma_handle, + s->buf_size + 256, s->dma); } static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle, - s->buf_size + 256, s->dma); + dma_sync_single_for_device(&s->itv->pdev->dev, + buf->dma_handle, s->buf_size + 256, + s->dma); } int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes); @@ -70,15 +71,17 @@ void ivtv_stream_free(struct ivtv_stream *s); static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle, - sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); + dma_sync_single_for_cpu(&s->itv->pdev->dev, s->sg_handle, + sizeof(struct ivtv_sg_element), + DMA_TO_DEVICE); } static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle, - sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); + dma_sync_single_for_device(&s->itv->pdev->dev, s->sg_handle, + sizeof(struct ivtv_sg_element), + DMA_TO_DEVICE); } #endif diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index 6e455948cc77..13d7d55e6594 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -176,7 +176,7 @@ static void ivtv_stream_init(struct ivtv *itv, int type) s->itv = itv; s->type = type; s->name = ivtv_stream_info[type].name; - s->caps = ivtv_stream_info[type].v4l2_caps; + s->vdev.device_caps = ivtv_stream_info[type].v4l2_caps; if (ivtv_stream_info[type].pio) s->dma = DMA_NONE; @@ -299,12 +299,9 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) if (s_mpg->vdev.v4l2_dev) num = s_mpg->vdev.num + ivtv_stream_info[type].num_offset; } - s->vdev.device_caps = s->caps; - if (itv->osd_video_pbase) { - itv->streams[IVTV_DEC_STREAM_TYPE_YUV].vdev.device_caps |= - V4L2_CAP_VIDEO_OUTPUT_OVERLAY; - itv->streams[IVTV_DEC_STREAM_TYPE_MPG].vdev.device_caps |= - V4L2_CAP_VIDEO_OUTPUT_OVERLAY; + if (itv->osd_video_pbase && (type == IVTV_DEC_STREAM_TYPE_YUV || + type == IVTV_DEC_STREAM_TYPE_MPG)) { + s->vdev.device_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; itv->v4l2_cap |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; } video_set_drvdata(&s->vdev, s); diff --git a/drivers/media/pci/ivtv/ivtv-udma.h b/drivers/media/pci/ivtv/ivtv-udma.h index 0eef104e03b9..12b9426b2db2 100644 --- a/drivers/media/pci/ivtv/ivtv-udma.h +++ b/drivers/media/pci/ivtv/ivtv-udma.h @@ -23,14 +23,14 @@ void ivtv_udma_start(struct ivtv *itv); static inline void ivtv_udma_sync_for_device(struct ivtv *itv) { - pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle, - sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + dma_sync_single_for_device(&itv->pdev->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), DMA_TO_DEVICE); } static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) { - pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle, - sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + dma_sync_single_for_cpu(&itv->pdev->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), DMA_TO_DEVICE); } #endif diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig index fed1f4a01817..3e69b66f1a5b 100644 --- a/drivers/media/pci/meye/Kconfig +++ b/drivers/media/pci/meye/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && VIDEO_V4L2 + depends on PCI && VIDEO_DEV depends on SONY_LAPTOP depends on X86 || COMPILE_TEST help diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index fb24d2ed3621..d3cde05a6eba 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -1214,7 +1214,7 @@ static int alsa_device_exit(struct saa7134_dev *dev) static int saa7134_alsa_init(void) { - struct saa7134_dev *dev = NULL; + struct saa7134_dev *dev; saa7134_dmasound_init = alsa_device_init; saa7134_dmasound_exit = alsa_device_exit; @@ -1229,7 +1229,7 @@ static int saa7134_alsa_init(void) alsa_device_init(dev); } - if (dev == NULL) + if (list_empty(&saa7134_devlist)) pr_info("saa7134 ALSA: no saa7134 cards found\n"); return 0; diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index 0d82a4b27d5b..99be59af3560 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -15,7 +15,7 @@ #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include <media/v4l2-common.h> #include <media/tveeprom.h> #include "tea5767.h" diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index d17a1b15faee..9c6cfef03331 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -26,7 +26,7 @@ #include "mt352_priv.h" /* FIXME */ #include "tda1004x.h" #include "nxt200x.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "xc5000.h" #include "tda10086.h" diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 374c8e1087de..48543ad3d595 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -823,7 +823,7 @@ static int buffer_activate(struct saa7134_dev *dev, { struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_buf.vb2_queue->drv_priv; unsigned long base,control,bpl; - unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ + unsigned long bpl_uv, lines_uv, base2, base3; /* planar */ video_dbg("buffer_activate buf=%p\n", buf); buf->top_seen = 0; @@ -868,11 +868,8 @@ static int buffer_activate(struct saa7134_dev *dev, lines_uv = dev->height >> dev->fmt->vshift; base2 = base + bpl * dev->height; base3 = base2 + bpl_uv * lines_uv; - if (dev->fmt->uvswap) { - tmp = base2; - base2 = base3; - base3 = tmp; - } + if (dev->fmt->uvswap) + swap(base2, base3); video_dbg("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", bpl_uv,lines_uv,base2,base3); if (V4L2_FIELD_HAS_BOTH(dev->field)) { @@ -1538,8 +1535,6 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) return -EINVAL; } - id = tvnorms[i].id; - if (!is_empress(file) && fh == dev->overlay_owner) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig index 8e83cd044075..3bbb68a0ed7b 100644 --- a/drivers/media/pci/saa7146/Kconfig +++ b/drivers/media/pci/saa7146/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_HEXIUM_GEMINI tristate "Hexium Gemini frame grabber" - depends on PCI && VIDEO_V4L2 && I2C + depends on PCI && VIDEO_DEV && I2C select VIDEO_SAA7146_VV help This is a video4linux driver for the Hexium Gemini frame @@ -13,7 +13,7 @@ config VIDEO_HEXIUM_GEMINI config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on PCI && VIDEO_V4L2 && I2C + depends on PCI && VIDEO_DEV && I2C select VIDEO_SAA7146_VV help This is a video4linux driver for the Hexium HV-PCI6 and @@ -24,7 +24,7 @@ config VIDEO_HEXIUM_ORION config VIDEO_MXB tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" - depends on PCI && VIDEO_V4L2 && I2C + depends on PCI && VIDEO_DEV && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c index a65d810ce212..42bd8e76005b 100644 --- a/drivers/media/pci/saa7164/saa7164-cmd.c +++ b/drivers/media/pci/saa7164/saa7164-cmd.c @@ -187,7 +187,6 @@ static int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg, mutex_lock(&dev->cmds[msg->id].lock); size = msg->size; - idx = 0; cmds = size / bus->m_wMaxReqSize; if (size % bus->m_wMaxReqSize == 0) cmds -= 1; diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 27bb78513631..a96e170ab04e 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" - depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS && I2C + depends on PCI && VIDEO_DEV && VIRT_TO_BUS && I2C depends on STA2X11 || COMPILE_TEST select GPIOLIB if MEDIA_SUBDRV_AUTOSELECT select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/tw5864/Kconfig b/drivers/media/pci/tw5864/Kconfig index d376d4ed65b9..111da223efb0 100644 --- a/drivers/media/pci/tw5864/Kconfig +++ b/drivers/media/pci/tw5864/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_TW5864 tristate "Techwell TW5864 video/audio grabber and encoder" - depends on VIDEO_DEV && PCI && VIDEO_V4L2 + depends on VIDEO_DEV && PCI select VIDEOBUF2_DMA_CONTIG help Support for boards based on Techwell TW5864 chip which provides diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig index af0cb60337bb..ef9c0e886a09 100644 --- a/drivers/media/pci/tw68/Kconfig +++ b/drivers/media/pci/tw68/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_TW68 tristate "Techwell tw68x Video For Linux" - depends on VIDEO_DEV && PCI && VIDEO_V4L2 + depends on VIDEO_DEV && PCI select VIDEOBUF2_DMA_SG help Support for Techwell tw68xx based frame grabber boards. diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig index 631c90868b8b..a4edad6aaf89 100644 --- a/drivers/media/pci/tw686x/Kconfig +++ b/drivers/media/pci/tw686x/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_TW686X tristate "Intersil/Techwell TW686x video capture cards" - depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND + depends on PCI && VIDEO_DEV && SND select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 9fbdba0fd1e7..f1056ceaf5a8 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -3,677 +3,85 @@ # Platform drivers # Most drivers here are currently for webcam support -menuconfig V4L_PLATFORM_DRIVERS - bool "V4L platform devices" - help - Say Y here to enable support for platform-specific V4L drivers. - -if V4L_PLATFORM_DRIVERS - -source "drivers/media/platform/marvell-ccic/Kconfig" - -config VIDEO_VIA_CAMERA - tristate "VIAFB camera controller support" - depends on FB_VIA && VIDEO_V4L2 - select VIDEOBUF2_DMA_SG - select VIDEO_OV7670 - help - Driver support for the integrated camera controller in VIA - Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems - with ov7670 sensors. - -# -# Platform multimedia device configuration -# -source "drivers/media/platform/cadence/Kconfig" - -source "drivers/media/platform/davinci/Kconfig" - -source "drivers/media/platform/omap/Kconfig" - -config VIDEO_ASPEED - tristate "Aspeed AST2400 and AST2500 Video Engine driver" - depends on VIDEO_V4L2 - select VIDEOBUF2_DMA_CONTIG - help - Support for the Aspeed Video Engine (VE) embedded in the Aspeed - AST2400 and AST2500 SOCs. The VE can capture and compress video data - from digital or analog sources. - -config VIDEO_SH_VOU - tristate "SuperH VOU video output driver" - depends on VIDEO_DEV && I2C - depends on ARCH_SHMOBILE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - help - Support for the Video Output Unit (VOU) on SuperH SoCs. - -config VIDEO_VIU - tristate "Freescale VIU Video Driver" - depends on VIDEO_V4L2 && (PPC_MPC512x || COMPILE_TEST) && I2C - select VIDEOBUF_DMA_CONTIG - default y - help - Support for Freescale VIU video driver. This device captures - video data, or overlays video on DIU frame buffer. - - Say Y here if you want to enable VIU device on MPC5121e Rev2+. - In doubt, say N. - -config VIDEO_MUX - tristate "Video Multiplexer" - select MULTIPLEXER - depends on VIDEO_V4L2 && OF - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select REGMAP - select V4L2_FWNODE - help - This driver provides support for N:1 video bus multiplexers. - -config VIDEO_OMAP3 - tristate "OMAP 3 Camera support" - depends on VIDEO_V4L2 && I2C - depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST - depends on COMMON_CLK && OF - select ARM_DMA_USE_IOMMU if OMAP_IOMMU - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select MFD_SYSCON - select V4L2_FWNODE - help - Driver for an OMAP 3 camera controller. - -config VIDEO_OMAP3_DEBUG - bool "OMAP 3 Camera debug messages" - depends on VIDEO_OMAP3 - help - Enable debug messages on OMAP 3 camera controller driver. - -config VIDEO_PXA27x - tristate "PXA27x Quick Capture Interface driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on PXA27x || COMPILE_TEST - select VIDEOBUF2_DMA_SG - select SG_SPLIT - select V4L2_FWNODE - help - This is a v4l2 driver for the PXA27x Quick Capture Interface - -config VIDEO_QCOM_CAMSS - tristate "Qualcomm V4L2 Camera Subsystem driver" - depends on VIDEO_V4L2 - depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_SG - select V4L2_FWNODE - -config VIDEO_S3C_CAMIF - tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" - depends on VIDEO_V4L2 && I2C && PM - depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - help - This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera - host interface (CAMIF). - - To compile this driver as a module, choose M here: the module - will be called s3c-camif. - -config VIDEO_STM32_DCMI - tristate "STM32 Digital Camera Memory Interface (DCMI) support" - depends on VIDEO_V4L2 && OF - depends on ARCH_STM32 || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select MEDIA_CONTROLLER - select V4L2_FWNODE - help - This module makes the STM32 Digital Camera Memory Interface (DCMI) - available as a v4l2 device. - - To compile this driver as a module, choose M here: the module - will be called stm32-dcmi. - -config VIDEO_RENESAS_CEU - tristate "Renesas Capture Engine Unit (CEU) driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_FWNODE +menuconfig MEDIA_PLATFORM_DRIVERS + bool "Media platform devices" + default "y" help - This is a v4l2 driver for the Renesas CEU Interface + Say Y here to enable support for platform-specific media drivers. -config VIDEO_ROCKCHIP_ISP1 - tristate "Rockchip Image Signal Processing v1 Unit driver" - depends on VIDEO_V4L2 && OF - depends on ARCH_ROCKCHIP || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_FWNODE - select GENERIC_PHY_MIPI_DPHY - default n - help - Enable this to support the Image Signal Processing (ISP) module - present in RK3399 SoCs. - - To compile this driver as a module, choose M here: the module - will be called rockchip-isp1. - -source "drivers/media/platform/exynos4-is/Kconfig" -source "drivers/media/platform/am437x/Kconfig" -source "drivers/media/platform/xilinx/Kconfig" -source "drivers/media/platform/rcar-vin/Kconfig" -source "drivers/media/platform/atmel/Kconfig" -source "drivers/media/platform/sunxi/Kconfig" +if MEDIA_PLATFORM_DRIVERS -config VIDEO_TI_CAL - tristate "TI CAL (Camera Adaptation Layer) driver" - depends on VIDEO_DEV && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - depends on SOC_DRA7XX || ARCH_K3 || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_FWNODE +config V4L_PLATFORM_DRIVERS + bool "V4L platform devices" help - Support for the TI CAL (Camera Adaptation Layer) block - found on DRA72X SoC. - In TI Technical Reference Manual this module is referred as - Camera Interface Subsystem (CAMSS). - -if VIDEO_TI_CAL + Say Y here to enable support for platform-specific V4L drivers. -config VIDEO_TI_CAL_MC - bool "Media Controller centric mode by default" - default n +config SDR_PLATFORM_DRIVERS + bool "SDR platform devices" + depends on MEDIA_SDR_SUPPORT help - Enables Media Controller centric mode by default. - - If set, CAL driver will start in Media Controller mode by - default. Note that this behavior can be overridden via - module parameter 'mc_api'. - -endif # VIDEO_TI_CAL + Say Y here to enable support for platform-specific SDR Drivers. -config VIDEO_RCAR_ISP - tristate "R-Car Image Signal Processor (ISP)" - depends on VIDEO_V4L2 && OF - depends on ARCH_RENESAS || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select RESET_CONTROLLER - select V4L2_FWNODE +config DVB_PLATFORM_DRIVERS + bool "DVB platform devices" + depends on MEDIA_DIGITAL_TV_SUPPORT help - Support for Renesas R-Car Image Signal Processor (ISP). - Enable this to support the Renesas R-Car Image Signal - Processor (ISP). - - To compile this driver as a module, choose M here: the - module will be called rcar-isp. - -endif # V4L_PLATFORM_DRIVERS + Say Y here to enable support for platform-specific Digital TV drivers. -menuconfig V4L_MEM2MEM_DRIVERS +config V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" - depends on VIDEO_V4L2 + depends on VIDEO_DEV help Say Y here to enable selecting drivers for V4L devices that use system memory for both source and destination buffers, as opposed to capture and output drivers, which use memory buffers for just one of those. -if V4L_MEM2MEM_DRIVERS - -config VIDEO_ALLEGRO_DVT - tristate "Allegro DVT Video IP Core" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_ZYNQMP || COMPILE_TEST - select V4L2_MEM2MEM_DEV - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - help - Support for the encoder video IP core by Allegro DVT. This core is - found for example on the Xilinx ZynqMP SoC in the EV family and is - called VCU in the reference manual. - - To compile this driver as a module, choose M here: the module - will be called allegro. - -config VIDEO_CODA - tristate "Chips&Media Coda multi-standard codec IP" - depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) - select SRAM - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_JPEG_HELPER - select V4L2_MEM2MEM_DEV - select GENERIC_ALLOCATOR - help - Coda is a range of video codec IPs that supports - H.264, MPEG-4, and other video formats. - -config VIDEO_IMX_VDOA - def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST - -config VIDEO_IMX_PXP - tristate "i.MX Pixel Pipeline (PXP)" - depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST) - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - The i.MX Pixel Pipeline is a memory-to-memory engine for scaling, - color space conversion, and rotation. - -source "drivers/media/platform/imx-jpeg/Kconfig" - -config VIDEO_MEDIATEK_JPEG - tristate "Mediatek JPEG Codec driver" - depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - Mediatek jpeg codec driver provides HW capability to decode - JPEG format - - To compile this driver as a module, choose M here: the - module will be called mtk-jpeg - -config VIDEO_MEDIATEK_VPU - tristate "Mediatek Video Processor Unit" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MEDIATEK || COMPILE_TEST - help - This driver provides downloading VPU firmware and - communicating with VPU. This driver for hw video - codec embedded in Mediatek's MT8173 SOCs. It is able - to handle video decoding/encoding in a range of formats. - - To compile this driver as a module, choose M here: the - module will be called mtk-vpu. - -config VIDEO_MEDIATEK_MDP - tristate "Mediatek MDP driver" - depends on MTK_IOMMU || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - select VIDEO_MEDIATEK_VPU - help - It is a v4l2 driver and present in Mediatek MT8173 SoCs. - The driver supports for scaling and color space conversion. - - To compile this driver as a module, choose M here: the - module will be called mtk-mdp. - -config VIDEO_MEDIATEK_VCODEC - tristate "Mediatek Video Codec driver" - depends on MTK_IOMMU || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on VIDEO_MEDIATEK_VPU || MTK_SCP - # The two following lines ensure we have the same state ("m" or "y") as - # our dependencies, to avoid missing symbols during link. - depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU - depends on MTK_SCP || !MTK_SCP - depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU - select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP - select V4L2_H264 - select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API - help - Mediatek video codec driver provides HW capability to - encode and decode in a range of video formats on MT8173 - and MT8183. - - Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to - also be selected. Support for MT8183 depends on MTK_SCP. - - To compile this driver as modules, choose M here: the - modules will be called mtk-vcodec-dec and mtk-vcodec-enc. - -config VIDEO_MEDIATEK_VCODEC_VPU - bool - -config VIDEO_MEDIATEK_VCODEC_SCP - bool +# Ancillary drivers config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help Generic deinterlacing V4L2 driver. -config VIDEO_MESON_GE2D - tristate "Amlogic 2D Graphic Acceleration Unit" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MESON || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Amlogic GE2D 2D graphics accelerator. - GE2D is a standalone 2D graphic acceleration unit, with color converter, - image scaling, BitBLT & alpha blending operations. - - To compile this driver as a module choose m here. - -config VIDEO_SAMSUNG_S5P_G2D - tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D - 2d graphics accelerator. - -config VIDEO_SAMSUNG_S5P_JPEG - tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Samsung S5P, EXYNOS3250 - and EXYNOS4 JPEG codec - -config VIDEO_SAMSUNG_S5P_MFC - tristate "Samsung S5P MFC Video Codec" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - help - MFC 5.1 and 6.x driver for V4L2 - -config VIDEO_MX2_EMMAPRP - tristate "MX2 eMMa-PrP support" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on SOC_IMX27 || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - MX2X chips have a PrP that can be used to process buffers from - memory to memory. Operations include resizing and format - conversion. - -config VIDEO_SAMSUNG_EXYNOS_GSC - tristate "Samsung Exynos G-Scaler driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. - -config VIDEO_STI_BDISP - tristate "STMicroelectronics BDISP 2D blitter driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_STI || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. - -config VIDEO_STI_HVA - tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_STI || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format - video encoder of STMicroelectronics SoC, allowing hardware encoding of - raw uncompressed formats in various compressed video bitstreams format. - - To compile this driver as a module, choose M here: - the module will be called st-hva. - -config VIDEO_STI_HVA_DEBUGFS - bool "Export STMicroelectronics HVA internals in debugfs" - depends on VIDEO_STI_HVA - depends on DEBUG_FS - help - Select this to see information about the internal state and the last - operation of STMicroelectronics HVA multi-format video encoder in - debugfs. - - Choose N unless you know you need this. - -config VIDEO_STI_DELTA - tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_STI || COMPILE_TEST - help - This V4L2 driver enables DELTA multi-format video decoder - of STMicroelectronics STiH4xx SoC series allowing hardware - decoding of various compressed video bitstream format in - raw uncompressed format. - - Use this option to see the decoders available for such - hardware. - - Please notice that the driver will only be built if - at least one of the DELTA decoder below is selected. - -if VIDEO_STI_DELTA - -config VIDEO_STI_DELTA_MJPEG - bool "STMicroelectronics DELTA MJPEG support" - default y - help - Enables DELTA MJPEG hardware support. - - To compile this driver as a module, choose M here: - the module will be called st-delta. - -config VIDEO_STI_DELTA_DRIVER - tristate - depends on VIDEO_STI_DELTA - depends on VIDEO_STI_DELTA_MJPEG - default VIDEO_STI_DELTA_MJPEG - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - select RPMSG - -endif # VIDEO_STI_DELTA - -config VIDEO_STM32_DMA2D - tristate "STM32 Chrom-Art Accelerator (DMA2D)" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_STM32 || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - Enables DMA2D hardware support on stm32. - - The STM32 DMA2D is a memory-to-memory engine for pixel conversion - and specialized DMA dedicated to image manipulation. - -config VIDEO_RENESAS_FDP1 - tristate "Renesas Fine Display Processor" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_RENESAS || COMPILE_TEST - depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a V4L2 driver for the Renesas Fine Display Processor - providing colour space conversion, and de-interlacing features. - - To compile this driver as a module, choose M here: the module - will be called rcar_fdp1. - -config VIDEO_RENESAS_JPU - tristate "Renesas JPEG Processing Unit" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_RENESAS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a V4L2 driver for the Renesas JPEG Processing Unit. - - 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 and RZ/G2 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 - depends on ARCH_RENESAS || COMPILE_TEST - depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP +config VIDEO_MUX + tristate "Video Multiplexer" + depends on V4L_PLATFORM_DRIVERS + select MULTIPLEXER + depends on VIDEO_DEV && OF select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - help - This is a V4L2 driver for the Renesas VSP1 video processing engine. - - To compile this driver as a module, choose M here: the module - will be called vsp1. - -config VIDEO_ROCKCHIP_RGA - tristate "Rockchip Raster 2d Graphic Acceleration Unit" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_ROCKCHIP || COMPILE_TEST - select VIDEOBUF2_DMA_SG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator. - Rockchip RGA is a separate 2D raster graphic acceleration unit. - It accelerates 2D graphics operations, such as point/line drawing, - image scaling, rotation, BitBLT, alpha blending and image blur/sharpness. - - To compile this driver as a module choose m here. - -config VIDEO_TI_VPE - tristate "TI VPE (Video Processing Engine) driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on SOC_DRA7XX || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - select VIDEO_TI_VPDMA - select VIDEO_TI_SC - select VIDEO_TI_CSC - help - Support for the TI VPE(Video Processing Engine) block - found on DRA7XX SoC. - -config VIDEO_TI_VPE_DEBUG - bool "VPE debug messages" - depends on VIDEO_TI_VPE - help - Enable debug messages on VPE driver. - -config VIDEO_QCOM_VENUS - tristate "Qualcomm Venus V4L2 encoder/decoder driver" - depends on VIDEO_DEV && VIDEO_V4L2 && QCOM_SMEM - depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST - select QCOM_MDT_LOADER if ARCH_QCOM - select QCOM_SCM - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a V4L2 driver for Qualcomm Venus video accelerator - hardware. It accelerates encoding and decoding operations - on various Qualcomm SoCs. - To compile this driver as a module choose m here. - -config VIDEO_SUN8I_DEINTERLACE - tristate "Allwinner Deinterlace driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_SUNXI || COMPILE_TEST - depends on COMMON_CLK && OF - depends on PM - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - Support for the Allwinner deinterlace unit with scaling - capability found on some SoCs, like H3. - To compile this driver as a module choose m here. - -config VIDEO_SUN8I_ROTATE - tristate "Allwinner DE2 rotation driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_SUNXI || COMPILE_TEST - depends on COMMON_CLK && OF - depends on PM - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - Support for the Allwinner DE2 rotation unit. - To compile this driver as a module choose m here. - -endif # V4L_MEM2MEM_DRIVERS - -# TI VIDEO PORT Helper Modules -# These will be selected by VPE and VIP -config VIDEO_TI_VPDMA - tristate - -config VIDEO_TI_SC - tristate - -config VIDEO_TI_CSC - tristate - -menuconfig DVB_PLATFORM_DRIVERS - bool "DVB platform devices" - depends on MEDIA_DIGITAL_TV_SUPPORT - help - Say Y here to enable support for platform-specific Digital TV drivers. - -if DVB_PLATFORM_DRIVERS -source "drivers/media/platform/sti/c8sectpfe/Kconfig" -endif #DVB_PLATFORM_DRIVERS - -menuconfig SDR_PLATFORM_DRIVERS - bool "SDR platform devices" - depends on MEDIA_SDR_SUPPORT - help - Say Y here to enable support for platform-specific SDR Drivers. - -if SDR_PLATFORM_DRIVERS - -config VIDEO_RCAR_DRIF - tristate "Renesas Digital Radio Interface (DRIF)" - depends on VIDEO_V4L2 - depends on ARCH_RENESAS || COMPILE_TEST - select VIDEOBUF2_VMALLOC - select V4L2_ASYNC + select REGMAP + select V4L2_FWNODE help - Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital - Radio Interface that interfaces with an RF front end chip. It is a - receiver of digital data which uses DMA to transfer received data to - a configured location for an application to use. + This driver provides support for N:1 video bus multiplexers. - To compile this driver as a module, choose M here; the module - will be called rcar_drif. +# Platform drivers - Please keep it alphabetically sorted +source "drivers/media/platform/allegro-dvt/Kconfig" +source "drivers/media/platform/amlogic/Kconfig" +source "drivers/media/platform/amphion/Kconfig" +source "drivers/media/platform/aspeed/Kconfig" +source "drivers/media/platform/atmel/Kconfig" +source "drivers/media/platform/cadence/Kconfig" +source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/intel/Kconfig" +source "drivers/media/platform/marvell/Kconfig" +source "drivers/media/platform/mediatek/Kconfig" +source "drivers/media/platform/nvidia/Kconfig" +source "drivers/media/platform/nxp/Kconfig" +source "drivers/media/platform/qcom/Kconfig" +source "drivers/media/platform/renesas/Kconfig" +source "drivers/media/platform/rockchip/Kconfig" +source "drivers/media/platform/samsung/Kconfig" +source "drivers/media/platform/st/Kconfig" +source "drivers/media/platform/sunxi/Kconfig" +source "drivers/media/platform/ti/Kconfig" +source "drivers/media/platform/via/Kconfig" +source "drivers/media/platform/xilinx/Kconfig" -endif # SDR_PLATFORM_DRIVERS +endif # MEDIA_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 19bcbced7382..a881e97bae95 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,88 +3,32 @@ # Makefile for the video capture/playback device drivers. # -obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ -obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o -obj-$(CONFIG_VIDEO_CADENCE) += cadence/ -obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o -obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ -obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ - -obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o - -obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o - -obj-y += ti-vpe/ - -obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o -obj-$(CONFIG_VIDEO_CODA) += coda/ - -obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o -obj-$(CONFIG_VIDEO_IMX8_JPEG) += imx-jpeg/ - +# Place here, alphabetically sorted by directory +# (e. g. LC_ALL=C sort Makefile) +obj-y += allegro-dvt/ +obj-y += amlogic/ +obj-y += amphion/ +obj-y += aspeed/ +obj-y += atmel/ +obj-y += cadence/ +obj-y += chips-media/ +obj-y += intel/ +obj-y += marvell/ +obj-y += mediatek/ +obj-y += nvidia/ +obj-y += nxp/ +obj-y += qcom/ +obj-y += renesas/ +obj-y += rockchip/ +obj-y += samsung/ +obj-y += st/ +obj-y += sunxi/ +obj-y += ti/ +obj-y += via/ +obj-y += xilinx/ + +# Please place here only ancillary drivers that aren't SoC-specific +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o - obj-$(CONFIG_VIDEO_MUX) += video-mux.o - -obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ -obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ - -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ -obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ - -obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ -obj-$(CONFIG_VIDEO_STI_HVA) += sti/hva/ -obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ - -obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/ - -obj-y += stm32/ - -obj-y += davinci/ - -obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o - -obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o -obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o -obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o -obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o -obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o -obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ - -obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip/rkisp1/ -obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/ - -obj-y += omap/ - -obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ - -obj-$(CONFIG_VIDEO_XILINX) += xilinx/ - -obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o -obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ - -obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/ -obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/ -obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel/ - -obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/ -obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/ - -obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ - -obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/ - -obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/ - -obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/ - -obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/ - -obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ - -obj-y += sunxi/ - -obj-$(CONFIG_VIDEO_MESON_GE2D) += meson/ge2d/ diff --git a/drivers/media/platform/allegro-dvt/Kconfig b/drivers/media/platform/allegro-dvt/Kconfig new file mode 100644 index 000000000000..2182e1277568 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Allegro DVT media platform drivers" + +config VIDEO_ALLEGRO_DVT + tristate "Allegro DVT Video IP Core" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_ZYNQMP || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + Support for the encoder video IP core by Allegro DVT. This core is + found for example on the Xilinx ZynqMP SoC in the EV family and is + called VCU in the reference manual. + + To compile this driver as a module, choose M here: the module + will be called allegro. diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig new file mode 100644 index 000000000000..5014957404e9 --- /dev/null +++ b/drivers/media/platform/amlogic/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Amlogic media platform drivers" + +source "drivers/media/platform/amlogic/meson-ge2d/Kconfig" diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile new file mode 100644 index 000000000000..d3cdb8fa4ddb --- /dev/null +++ b/drivers/media/platform/amlogic/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += meson-ge2d/ diff --git a/drivers/media/platform/amlogic/meson-ge2d/Kconfig b/drivers/media/platform/amlogic/meson-ge2d/Kconfig new file mode 100644 index 000000000000..312c4169e3c2 --- /dev/null +++ b/drivers/media/platform/amlogic/meson-ge2d/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_MESON_GE2D + tristate "Amlogic 2D Graphic Acceleration Unit" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_MESON || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Amlogic GE2D 2D graphics accelerator. + GE2D is a standalone 2D graphic acceleration unit, with color converter, + image scaling, BitBLT & alpha blending operations. + + To compile this driver as a module choose m here. + diff --git a/drivers/media/platform/meson/ge2d/Makefile b/drivers/media/platform/amlogic/meson-ge2d/Makefile index 450586df27d7..450586df27d7 100644 --- a/drivers/media/platform/meson/ge2d/Makefile +++ b/drivers/media/platform/amlogic/meson-ge2d/Makefile diff --git a/drivers/media/platform/meson/ge2d/ge2d-regs.h b/drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h index 2a76dd4c0ccb..2a76dd4c0ccb 100644 --- a/drivers/media/platform/meson/ge2d/ge2d-regs.h +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c index ccda18e5a377..5e7b319f300d 100644 --- a/drivers/media/platform/meson/ge2d/ge2d.c +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c @@ -215,35 +215,35 @@ static void ge2d_hw_start(struct meson_ge2d *ge2d) regmap_write(ge2d->map, GE2D_SRC1_CLIPY_START_END, FIELD_PREP(GE2D_START, ctx->in.crop.top) | - FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height)); + FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height - 1)); regmap_write(ge2d->map, GE2D_SRC1_CLIPX_START_END, FIELD_PREP(GE2D_START, ctx->in.crop.left) | - FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width)); + FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width - 1)); regmap_write(ge2d->map, GE2D_SRC2_CLIPY_START_END, FIELD_PREP(GE2D_START, ctx->out.crop.top) | - FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height)); + FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1)); regmap_write(ge2d->map, GE2D_SRC2_CLIPX_START_END, FIELD_PREP(GE2D_START, ctx->out.crop.left) | - FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width)); + FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1)); regmap_write(ge2d->map, GE2D_DST_CLIPY_START_END, FIELD_PREP(GE2D_START, ctx->out.crop.top) | - FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height)); + FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1)); regmap_write(ge2d->map, GE2D_DST_CLIPX_START_END, FIELD_PREP(GE2D_START, ctx->out.crop.left) | - FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width)); + FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1)); regmap_write(ge2d->map, GE2D_SRC1_Y_START_END, - FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height)); + FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height - 1)); regmap_write(ge2d->map, GE2D_SRC1_X_START_END, - FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width)); + FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width - 1)); regmap_write(ge2d->map, GE2D_SRC2_Y_START_END, - FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height)); + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1)); regmap_write(ge2d->map, GE2D_SRC2_X_START_END, - FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width)); + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1)); regmap_write(ge2d->map, GE2D_DST_Y_START_END, - FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height)); + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1)); regmap_write(ge2d->map, GE2D_DST_X_START_END, - FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width)); + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1)); /* Color, no blend, use source color */ reg = GE2D_ALU_DO_COLOR_OPERATION_LOGIC(LOGIC_OPERATION_COPY, diff --git a/drivers/media/platform/amphion/Kconfig b/drivers/media/platform/amphion/Kconfig new file mode 100644 index 000000000000..4a363e07ccc9 --- /dev/null +++ b/drivers/media/platform/amphion/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Amphion drivers" + +config VIDEO_AMPHION_VPU + tristate "Amphion VPU (Video Processing Unit) Codec IP" + depends on V4L_MEM2MEM_DRIVERS + depends on ARCH_MXC || COMPILE_TEST + depends on MEDIA_SUPPORT + depends on VIDEO_DEV && MAILBOX + select MEDIA_CONTROLLER + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + help + Amphion VPU Codec IP contains two parts: Windsor and Malone. + Windsor is encoder that supports H.264, and Malone is decoder + that supports H.264, HEVC, and other video formats. + This is a V4L2 driver for NXP MXC 8Q video accelerator hardware. + It accelerates encoding and decoding operations on + various NXP SoCs. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/amphion/Makefile b/drivers/media/platform/amphion/Makefile new file mode 100644 index 000000000000..80717312835f --- /dev/null +++ b/drivers/media/platform/amphion/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for NXP VPU driver + +amphion-vpu-objs += vpu_drv.o \ + vpu_core.o \ + vpu_mbox.o \ + vpu_v4l2.o \ + vpu_helpers.o \ + vpu_cmds.o \ + vpu_msgs.o \ + vpu_rpc.o \ + vpu_imx8q.o \ + vpu_windsor.o \ + vpu_malone.o \ + vpu_color.o \ + vdec.o \ + venc.o \ + vpu_dbg.o + +obj-$(CONFIG_VIDEO_AMPHION_VPU) += amphion-vpu.o diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c new file mode 100644 index 000000000000..8f8dfd6ce2c6 --- /dev/null +++ b/drivers/media/platform/amphion/vdec.c @@ -0,0 +1,1656 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> +#include "vpu.h" +#include "vpu_defs.h" +#include "vpu_core.h" +#include "vpu_helpers.h" +#include "vpu_v4l2.h" +#include "vpu_cmds.h" +#include "vpu_rpc.h" + +#define VDEC_FRAME_DEPTH 256 +#define VDEC_MIN_BUFFER_CAP 8 + +struct vdec_fs_info { + char name[8]; + u32 type; + u32 max_count; + u32 req_count; + u32 count; + u32 index; + u32 size; + struct vpu_buffer buffer[32]; + u32 tag; +}; + +struct vdec_t { + u32 seq_hdr_found; + struct vpu_buffer udata; + struct vpu_decode_params params; + struct vpu_dec_codec_info codec_info; + enum vpu_codec_state state; + + struct vpu_vb2_buffer *slots[VB2_MAX_FRAME]; + u32 req_frame_count; + struct vdec_fs_info mbi; + struct vdec_fs_info dcp; + u32 seq_tag; + + bool reset_codec; + bool fixed_fmt; + u32 decoded_frame_count; + u32 display_frame_count; + u32 sequence; + u32 eos_received; + bool is_source_changed; + u32 source_change; + u32 drain; + u32 ts_pre_count; + u32 frame_depth; +}; + +static const struct vpu_format vdec_formats[] = { + { + .pixfmt = V4L2_PIX_FMT_NV12M_8L128, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, + { + .pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, + { + .pixfmt = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_H264_MVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_XVID, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + { + .pixfmt = V4L2_PIX_FMT_H263, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION + }, + {0, 0, 0, 0}, +}; + +static const struct v4l2_ctrl_ops vdec_ctrl_ops = { + .g_volatile_ctrl = vpu_helper_g_volatile_ctrl, +}; + +static int vdec_ctrl_init(struct vpu_inst *inst) +{ + struct v4l2_ctrl *ctrl; + int ret; + + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20); + if (ret) + return ret; + + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler); + if (ret) { + dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret); + v4l2_ctrl_handler_free(&inst->ctrl_handler); + return ret; + } + + return 0; +} + +static void vdec_set_last_buffer_dequeued(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + if (vdec->eos_received) { + if (!vpu_set_last_buffer_dequeued(inst)) + vdec->eos_received--; + } +} + +static void vdec_handle_resolution_change(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + struct vb2_queue *q; + + if (!inst->fh.m2m_ctx) + return; + + if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + return; + if (!vdec->source_change) + return; + + q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + if (!list_empty(&q->done_list)) + return; + + vdec->source_change--; + vpu_notify_source_change(inst); +} + +static int vdec_update_state(struct vpu_inst *inst, enum vpu_codec_state state, u32 force) +{ + struct vdec_t *vdec = inst->priv; + enum vpu_codec_state pre_state = inst->state; + + if (state == VPU_CODEC_STATE_SEEK) { + if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + vdec->state = inst->state; + else + vdec->state = VPU_CODEC_STATE_ACTIVE; + } + if (inst->state != VPU_CODEC_STATE_SEEK || force) + inst->state = state; + else if (state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + vdec->state = VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE; + + if (inst->state != pre_state) + vpu_trace(inst->dev, "[%d] %d -> %d\n", inst->id, pre_state, inst->state); + + if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + vdec_handle_resolution_change(inst); + + return 0; +} + +static int vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver)); + strscpy(cap->card, "amphion vpu decoder", sizeof(cap->card)); + strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info)); + + return 0; +} + +static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_inst *inst = to_inst(file); + struct vdec_t *vdec = inst->priv; + const struct vpu_format *fmt; + int ret = -EINVAL; + + vpu_inst_lock(inst); + if (!V4L2_TYPE_IS_OUTPUT(f->type) && vdec->fixed_fmt) { + if (f->index == 0) { + f->pixelformat = inst->cap_format.pixfmt; + f->flags = inst->cap_format.flags; + ret = 0; + } + } else { + fmt = vpu_helper_enum_format(inst, f->type, f->index); + memset(f->reserved, 0, sizeof(f->reserved)); + if (!fmt) + goto exit; + + f->pixelformat = fmt->pixfmt; + f->flags = fmt->flags; + ret = 0; + } + +exit: + vpu_inst_unlock(inst); + return ret; +} + +static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + struct vdec_t *vdec = inst->priv; + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct vpu_format *cur_fmt; + int i; + + cur_fmt = vpu_get_format(inst, f->type); + + pixmp->pixelformat = cur_fmt->pixfmt; + pixmp->num_planes = cur_fmt->num_planes; + pixmp->width = cur_fmt->width; + pixmp->height = cur_fmt->height; + pixmp->field = cur_fmt->field; + pixmp->flags = cur_fmt->flags; + for (i = 0; i < pixmp->num_planes; i++) { + pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i]; + pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i]; + } + + f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries; + f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars; + f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs; + f->fmt.pix_mp.quantization = vdec->codec_info.full_range; + + return 0; +} + +static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + struct vdec_t *vdec = inst->priv; + + vpu_try_fmt_common(inst, f); + + vpu_inst_lock(inst); + if (vdec->fixed_fmt) { + f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries; + f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars; + f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs; + f->fmt.pix_mp.quantization = vdec->codec_info.full_range; + } else { + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + } + vpu_inst_unlock(inst); + + return 0; +} + +static int vdec_s_fmt_common(struct vpu_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + const struct vpu_format *fmt; + struct vpu_format *cur_fmt; + struct vb2_queue *q; + struct vdec_t *vdec = inst->priv; + int i; + + if (!inst->fh.m2m_ctx) + return -EINVAL; + + q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type); + if (!q) + return -EINVAL; + if (vb2_is_busy(q)) + return -EBUSY; + + fmt = vpu_try_fmt_common(inst, f); + if (!fmt) + return -EINVAL; + + cur_fmt = vpu_get_format(inst, f->type); + if (V4L2_TYPE_IS_OUTPUT(f->type) && inst->state != VPU_CODEC_STATE_DEINIT) { + if (cur_fmt->pixfmt != fmt->pixfmt) { + vdec->reset_codec = true; + vdec->fixed_fmt = false; + } + } + cur_fmt->pixfmt = fmt->pixfmt; + if (V4L2_TYPE_IS_OUTPUT(f->type) || !vdec->fixed_fmt) { + cur_fmt->num_planes = fmt->num_planes; + cur_fmt->flags = fmt->flags; + cur_fmt->width = pixmp->width; + cur_fmt->height = pixmp->height; + for (i = 0; i < fmt->num_planes; i++) { + cur_fmt->sizeimage[i] = pixmp->plane_fmt[i].sizeimage; + cur_fmt->bytesperline[i] = pixmp->plane_fmt[i].bytesperline; + } + if (pixmp->field != V4L2_FIELD_ANY) + cur_fmt->field = pixmp->field; + } else { + pixmp->num_planes = cur_fmt->num_planes; + pixmp->width = cur_fmt->width; + pixmp->height = cur_fmt->height; + for (i = 0; i < pixmp->num_planes; i++) { + pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i]; + pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i]; + } + pixmp->field = cur_fmt->field; + } + + if (!vdec->fixed_fmt) { + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + vdec->params.codec_format = cur_fmt->pixfmt; + vdec->codec_info.color_primaries = f->fmt.pix_mp.colorspace; + vdec->codec_info.transfer_chars = f->fmt.pix_mp.xfer_func; + vdec->codec_info.matrix_coeffs = f->fmt.pix_mp.ycbcr_enc; + vdec->codec_info.full_range = f->fmt.pix_mp.quantization; + } else { + vdec->params.output_format = cur_fmt->pixfmt; + inst->crop.left = 0; + inst->crop.top = 0; + inst->crop.width = cur_fmt->width; + inst->crop.height = cur_fmt->height; + } + } + + vpu_trace(inst->dev, "[%d] %c%c%c%c %dx%d\n", inst->id, + f->fmt.pix_mp.pixelformat, + f->fmt.pix_mp.pixelformat >> 8, + f->fmt.pix_mp.pixelformat >> 16, + f->fmt.pix_mp.pixelformat >> 24, + f->fmt.pix_mp.width, + f->fmt.pix_mp.height); + + return 0; +} + +static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct vdec_t *vdec = inst->priv; + int ret = 0; + + vpu_inst_lock(inst); + ret = vdec_s_fmt_common(inst, f); + if (ret) + goto exit; + + if (V4L2_TYPE_IS_OUTPUT(f->type) && !vdec->fixed_fmt) { + struct v4l2_format fc; + + memset(&fc, 0, sizeof(fc)); + fc.type = inst->cap_format.type; + fc.fmt.pix_mp.pixelformat = inst->cap_format.pixfmt; + fc.fmt.pix_mp.width = pixmp->width; + fc.fmt.pix_mp.height = pixmp->height; + vdec_s_fmt_common(inst, &fc); + } + + f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries; + f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars; + f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs; + f->fmt.pix_mp.quantization = vdec->codec_info.full_range; + +exit: + vpu_inst_unlock(inst); + return ret; +} + +static int vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_inst *inst = to_inst(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r = inst->crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->cap_format.width; + s->r.height = inst->cap_format.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vdec_drain(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + if (!inst->fh.m2m_ctx) + return 0; + + if (!vdec->drain) + return 0; + + if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx)) + return 0; + + if (!vdec->params.frame_count) { + vpu_set_last_buffer_dequeued(inst); + return 0; + } + + vpu_iface_add_scode(inst, SCODE_PADDING_EOS); + vdec->params.end_flag = 1; + vpu_iface_set_decode_params(inst, &vdec->params, 1); + vdec->drain = 0; + vpu_trace(inst->dev, "[%d] frame_count = %d\n", inst->id, vdec->params.frame_count); + + return 0; +} + +static int vdec_cmd_start(struct vpu_inst *inst) +{ + switch (inst->state) { + case VPU_CODEC_STATE_STARTED: + case VPU_CODEC_STATE_DRAIN: + case VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE: + vdec_update_state(inst, VPU_CODEC_STATE_ACTIVE, 0); + break; + default: + break; + } + vpu_process_capture_buffer(inst); + return 0; +} + +static int vdec_cmd_stop(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + + if (inst->state == VPU_CODEC_STATE_DEINIT) { + vpu_set_last_buffer_dequeued(inst); + } else { + vdec->drain = 1; + vdec_drain(inst); + } + + return 0; +} + +static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + struct vpu_inst *inst = to_inst(file); + int ret; + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd); + if (ret) + return ret; + + vpu_inst_lock(inst); + switch (cmd->cmd) { + case V4L2_DEC_CMD_START: + vdec_cmd_start(inst); + break; + case V4L2_DEC_CMD_STOP: + vdec_cmd_stop(inst); + break; + default: + break; + } + vpu_inst_unlock(inst); + + return 0; +} + +static int vdec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vdec_ioctl_ops = { + .vidioc_querycap = vdec_querycap, + .vidioc_enum_fmt_vid_cap = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out = vdec_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, + .vidioc_g_selection = vdec_g_selection, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = vdec_decoder_cmd, + .vidioc_subscribe_event = vdec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, +}; + +static bool vdec_check_ready(struct vpu_inst *inst, unsigned int type) +{ + struct vdec_t *vdec = inst->priv; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (vdec->ts_pre_count >= vdec->frame_depth) + return false; + return true; + } + + if (vdec->req_frame_count) + return true; + + return false; +} + +static int vdec_frame_decoded(struct vpu_inst *inst, void *arg) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_dec_pic_info *info = arg; + struct vpu_vb2_buffer *vpu_buf; + struct vb2_v4l2_buffer *vbuf; + int ret = 0; + + if (!info || info->id >= ARRAY_SIZE(vdec->slots)) + return -EINVAL; + + vpu_inst_lock(inst); + vpu_buf = vdec->slots[info->id]; + if (!vpu_buf) { + dev_err(inst->dev, "[%d] decoded invalid frame[%d]\n", inst->id, info->id); + ret = -EINVAL; + goto exit; + } + vbuf = &vpu_buf->m2m_buf.vb; + if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) + dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED); + vdec->decoded_frame_count++; + if (vdec->ts_pre_count >= info->consumed_count) + vdec->ts_pre_count -= info->consumed_count; + else + vdec->ts_pre_count = 0; +exit: + vpu_inst_unlock(inst); + + return ret; +} + +static struct vpu_vb2_buffer *vdec_find_buffer(struct vpu_inst *inst, u32 luma) +{ + struct vdec_t *vdec = inst->priv; + int i; + + for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) { + if (!vdec->slots[i]) + continue; + if (luma == vdec->slots[i]->luma) + return vdec->slots[i]; + } + + return NULL; +} + +static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_vb2_buffer *vpu_buf; + struct vb2_v4l2_buffer *vbuf; + u32 sequence; + + if (!frame) + return; + + vpu_inst_lock(inst); + sequence = vdec->sequence++; + vpu_buf = vdec_find_buffer(inst, frame->luma); + vpu_inst_unlock(inst); + if (!vpu_buf) { + dev_err(inst->dev, "[%d] can't find buffer, id = %d, addr = 0x%x\n", + inst->id, frame->id, frame->luma); + return; + } + if (frame->skipped) { + dev_dbg(inst->dev, "[%d] frame skip\n", inst->id); + return; + } + + vbuf = &vpu_buf->m2m_buf.vb; + if (vbuf->vb2_buf.index != frame->id) + dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n", + inst->id, vbuf->vb2_buf.index, frame->id); + + if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED) + dev_err(inst->dev, "[%d] buffer(%d) ready without decoded\n", inst->id, frame->id); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, inst->cap_format.sizeimage[0]); + vb2_set_plane_payload(&vbuf->vb2_buf, 1, inst->cap_format.sizeimage[1]); + vbuf->vb2_buf.timestamp = frame->timestamp; + vbuf->field = inst->cap_format.field; + vbuf->sequence = sequence; + dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->timestamp); + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + vpu_inst_lock(inst); + vdec->display_frame_count++; + vpu_inst_unlock(inst); + dev_dbg(inst->dev, "[%d] decoded : %d, display : %d, sequence : %d\n", + inst->id, vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence); +} + +static void vdec_stop_done(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vpu_inst_lock(inst); + vdec_update_state(inst, VPU_CODEC_STATE_DEINIT, 0); + vdec->seq_hdr_found = 0; + vdec->req_frame_count = 0; + vdec->reset_codec = false; + vdec->fixed_fmt = false; + vdec->params.end_flag = 0; + vdec->drain = 0; + vdec->ts_pre_count = 0; + vdec->params.frame_count = 0; + vdec->decoded_frame_count = 0; + vdec->display_frame_count = 0; + vdec->sequence = 0; + vdec->eos_received = 0; + vdec->is_source_changed = false; + vdec->source_change = 0; + vpu_inst_unlock(inst); +} + +static bool vdec_check_source_change(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + const struct vpu_format *fmt; + int i; + + if (!inst->fh.m2m_ctx) + return false; + + if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx))) + return true; + fmt = vpu_helper_find_format(inst, inst->cap_format.type, vdec->codec_info.pixfmt); + if (inst->cap_format.pixfmt != vdec->codec_info.pixfmt) + return true; + if (inst->cap_format.width != vdec->codec_info.decoded_width) + return true; + if (inst->cap_format.height != vdec->codec_info.decoded_height) + return true; + if (vpu_get_num_buffers(inst, inst->cap_format.type) < inst->min_buffer_cap) + return true; + if (inst->crop.left != vdec->codec_info.offset_x) + return true; + if (inst->crop.top != vdec->codec_info.offset_y) + return true; + if (inst->crop.width != vdec->codec_info.width) + return true; + if (inst->crop.height != vdec->codec_info.height) + return true; + if (fmt && inst->cap_format.num_planes != fmt->num_planes) + return true; + for (i = 0; i < inst->cap_format.num_planes; i++) { + if (inst->cap_format.bytesperline[i] != vdec->codec_info.bytesperline[i]) + return true; + if (inst->cap_format.sizeimage[i] != vdec->codec_info.sizeimage[i]) + return true; + } + + return false; +} + +static void vdec_init_fmt(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + const struct vpu_format *fmt; + int i; + + fmt = vpu_helper_find_format(inst, inst->cap_format.type, vdec->codec_info.pixfmt); + inst->out_format.width = vdec->codec_info.width; + inst->out_format.height = vdec->codec_info.height; + inst->cap_format.width = vdec->codec_info.decoded_width; + inst->cap_format.height = vdec->codec_info.decoded_height; + inst->cap_format.pixfmt = vdec->codec_info.pixfmt; + if (fmt) { + inst->cap_format.num_planes = fmt->num_planes; + inst->cap_format.flags = fmt->flags; + } + for (i = 0; i < inst->cap_format.num_planes; i++) { + inst->cap_format.bytesperline[i] = vdec->codec_info.bytesperline[i]; + inst->cap_format.sizeimage[i] = vdec->codec_info.sizeimage[i]; + } + if (vdec->codec_info.progressive) + inst->cap_format.field = V4L2_FIELD_NONE; + else + inst->cap_format.field = V4L2_FIELD_SEQ_BT; + if (vdec->codec_info.color_primaries == V4L2_COLORSPACE_DEFAULT) + vdec->codec_info.color_primaries = V4L2_COLORSPACE_REC709; + if (vdec->codec_info.transfer_chars == V4L2_XFER_FUNC_DEFAULT) + vdec->codec_info.transfer_chars = V4L2_XFER_FUNC_709; + if (vdec->codec_info.matrix_coeffs == V4L2_YCBCR_ENC_DEFAULT) + vdec->codec_info.matrix_coeffs = V4L2_YCBCR_ENC_709; + if (vdec->codec_info.full_range == V4L2_QUANTIZATION_DEFAULT) + vdec->codec_info.full_range = V4L2_QUANTIZATION_LIM_RANGE; +} + +static void vdec_init_crop(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + inst->crop.left = vdec->codec_info.offset_x; + inst->crop.top = vdec->codec_info.offset_y; + inst->crop.width = vdec->codec_info.width; + inst->crop.height = vdec->codec_info.height; +} + +static void vdec_init_mbi(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vdec->mbi.size = vdec->codec_info.mbi_size; + vdec->mbi.max_count = ARRAY_SIZE(vdec->mbi.buffer); + scnprintf(vdec->mbi.name, sizeof(vdec->mbi.name), "mbi"); + vdec->mbi.type = MEM_RES_MBI; + vdec->mbi.tag = vdec->seq_tag; +} + +static void vdec_init_dcp(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vdec->dcp.size = vdec->codec_info.dcp_size; + vdec->dcp.max_count = ARRAY_SIZE(vdec->dcp.buffer); + scnprintf(vdec->dcp.name, sizeof(vdec->dcp.name), "dcp"); + vdec->dcp.type = MEM_RES_DCP; + vdec->dcp.tag = vdec->seq_tag; +} + +static void vdec_request_one_fs(struct vdec_fs_info *fs) +{ + fs->req_count++; + if (fs->req_count > fs->max_count) + fs->req_count = fs->max_count; +} + +static int vdec_alloc_fs_buffer(struct vpu_inst *inst, struct vdec_fs_info *fs) +{ + struct vpu_buffer *buffer; + + if (!fs->size) + return -EINVAL; + + if (fs->count >= fs->req_count) + return -EINVAL; + + buffer = &fs->buffer[fs->count]; + if (buffer->virt && buffer->length >= fs->size) + return 0; + + vpu_free_dma(buffer); + buffer->length = fs->size; + return vpu_alloc_dma(inst->core, buffer); +} + +static void vdec_alloc_fs(struct vpu_inst *inst, struct vdec_fs_info *fs) +{ + int ret; + + while (fs->count < fs->req_count) { + ret = vdec_alloc_fs_buffer(inst, fs); + if (ret) + break; + fs->count++; + } +} + +static void vdec_clear_fs(struct vdec_fs_info *fs) +{ + u32 i; + + if (!fs) + return; + + for (i = 0; i < ARRAY_SIZE(fs->buffer); i++) + vpu_free_dma(&fs->buffer[i]); + memset(fs, 0, sizeof(*fs)); +} + +static int vdec_response_fs(struct vpu_inst *inst, struct vdec_fs_info *fs) +{ + struct vpu_fs_info info; + int ret; + + if (fs->index >= fs->count) + return 0; + + memset(&info, 0, sizeof(info)); + info.id = fs->index; + info.type = fs->type; + info.tag = fs->tag; + info.luma_addr = fs->buffer[fs->index].phys; + info.luma_size = fs->buffer[fs->index].length; + ret = vpu_session_alloc_fs(inst, &info); + if (ret) + return ret; + + fs->index++; + return 0; +} + +static int vdec_response_frame_abnormal(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_fs_info info; + + if (!vdec->req_frame_count) + return 0; + + memset(&info, 0, sizeof(info)); + info.type = MEM_RES_FRAME; + info.tag = vdec->seq_tag + 0xf0; + vpu_session_alloc_fs(inst, &info); + vdec->req_frame_count--; + + return 0; +} + +static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vbuf) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_vb2_buffer *vpu_buf; + struct vpu_fs_info info; + int ret; + + if (inst->state != VPU_CODEC_STATE_ACTIVE) + return -EINVAL; + + if (!vdec->req_frame_count) + return -EINVAL; + + if (!vbuf) + return -EINVAL; + + if (vdec->slots[vbuf->vb2_buf.index]) { + dev_err(inst->dev, "[%d] repeat alloc fs %d\n", + inst->id, vbuf->vb2_buf.index); + return -EINVAL; + } + + dev_dbg(inst->dev, "[%d] state = %d, alloc fs %d, tag = 0x%x\n", + inst->id, inst->state, vbuf->vb2_buf.index, vdec->seq_tag); + vpu_buf = to_vpu_vb2_buffer(vbuf); + + memset(&info, 0, sizeof(info)); + info.id = vbuf->vb2_buf.index; + info.type = MEM_RES_FRAME; + info.tag = vdec->seq_tag; + info.luma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 0); + info.luma_size = inst->cap_format.sizeimage[0]; + info.chroma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 1); + info.chromau_size = inst->cap_format.sizeimage[1]; + info.bytesperline = inst->cap_format.bytesperline[0]; + ret = vpu_session_alloc_fs(inst, &info); + if (ret) + return ret; + + vpu_buf->tag = info.tag; + vpu_buf->luma = info.luma_addr; + vpu_buf->chroma_u = info.chromau_size; + vpu_buf->chroma_v = 0; + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE); + vdec->slots[info.id] = vpu_buf; + vdec->req_frame_count--; + + return 0; +} + +static void vdec_response_fs_request(struct vpu_inst *inst, bool force) +{ + struct vdec_t *vdec = inst->priv; + int i; + int ret; + + if (force) { + for (i = vdec->req_frame_count; i > 0; i--) + vdec_response_frame_abnormal(inst); + return; + } + + for (i = vdec->req_frame_count; i > 0; i--) { + ret = vpu_process_capture_buffer(inst); + if (ret) + break; + if (vdec->eos_received) + break; + } + + for (i = vdec->mbi.index; i < vdec->mbi.count; i++) { + if (vdec_response_fs(inst, &vdec->mbi)) + break; + if (vdec->eos_received) + break; + } + for (i = vdec->dcp.index; i < vdec->dcp.count; i++) { + if (vdec_response_fs(inst, &vdec->dcp)) + break; + if (vdec->eos_received) + break; + } +} + +static void vdec_response_fs_release(struct vpu_inst *inst, u32 id, u32 tag) +{ + struct vpu_fs_info info; + + memset(&info, 0, sizeof(info)); + info.id = id; + info.tag = tag; + vpu_session_release_fs(inst, &info); +} + +static void vdec_recycle_buffer(struct vpu_inst *inst, struct vb2_v4l2_buffer *vbuf) +{ + if (!inst->fh.m2m_ctx) + return; + if (vbuf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) + return; + if (vpu_find_buf_by_idx(inst, vbuf->vb2_buf.type, vbuf->vb2_buf.index)) + return; + v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf); +} + +static void vdec_clear_slots(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_vb2_buffer *vpu_buf; + struct vb2_v4l2_buffer *vbuf; + int i; + + for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) { + if (!vdec->slots[i]) + continue; + + vpu_buf = vdec->slots[i]; + vbuf = &vpu_buf->m2m_buf.vb; + + vdec_recycle_buffer(inst, vbuf); + vdec->slots[i]->state = VPU_BUF_STATE_IDLE; + vdec->slots[i] = NULL; + } +} + +static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr) +{ + struct vdec_t *vdec = inst->priv; + + vpu_inst_lock(inst); + memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info)); + + vpu_trace(inst->dev, "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d\n", + inst->id, + vdec->codec_info.decoded_width, + vdec->codec_info.decoded_height, + vdec->codec_info.offset_x, + vdec->codec_info.offset_y, + vdec->codec_info.width, + vdec->codec_info.height, + hdr->num_ref_frms, + hdr->num_dpb_frms); + inst->min_buffer_cap = hdr->num_ref_frms + hdr->num_dpb_frms; + vdec->is_source_changed = vdec_check_source_change(inst); + vdec_init_fmt(inst); + vdec_init_crop(inst); + vdec_init_mbi(inst); + vdec_init_dcp(inst); + if (!vdec->seq_hdr_found) { + vdec->seq_tag = vdec->codec_info.tag; + if (vdec->is_source_changed) { + vdec_update_state(inst, VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, 0); + vpu_notify_source_change(inst); + vdec->is_source_changed = false; + } + } + if (vdec->seq_tag != vdec->codec_info.tag) { + vdec_response_fs_request(inst, true); + vpu_trace(inst->dev, "[%d] seq tag change: %d -> %d\n", + inst->id, vdec->seq_tag, vdec->codec_info.tag); + } + vdec->seq_hdr_found++; + vdec->fixed_fmt = true; + vpu_inst_unlock(inst); +} + +static void vdec_event_resolution_change(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + vpu_inst_lock(inst); + vdec->seq_tag = vdec->codec_info.tag; + vdec_clear_fs(&vdec->mbi); + vdec_clear_fs(&vdec->dcp); + vdec_clear_slots(inst); + vdec_init_mbi(inst); + vdec_init_dcp(inst); + if (vdec->is_source_changed) { + vdec_update_state(inst, VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, 0); + vdec->source_change++; + vdec_handle_resolution_change(inst); + vdec->is_source_changed = false; + } + vpu_inst_unlock(inst); +} + +static void vdec_event_req_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) +{ + struct vdec_t *vdec = inst->priv; + + if (!fs) + return; + + vpu_inst_lock(inst); + + switch (fs->type) { + case MEM_RES_FRAME: + vdec->req_frame_count++; + break; + case MEM_RES_MBI: + vdec_request_one_fs(&vdec->mbi); + break; + case MEM_RES_DCP: + vdec_request_one_fs(&vdec->dcp); + break; + default: + break; + } + + vdec_alloc_fs(inst, &vdec->mbi); + vdec_alloc_fs(inst, &vdec->dcp); + + vdec_response_fs_request(inst, false); + + vpu_inst_unlock(inst); +} + +static void vdec_evnet_rel_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_vb2_buffer *vpu_buf; + struct vb2_v4l2_buffer *vbuf; + + if (!fs || fs->id >= ARRAY_SIZE(vdec->slots)) + return; + if (fs->type != MEM_RES_FRAME) + return; + + if (fs->id >= vpu_get_num_buffers(inst, inst->cap_format.type)) { + dev_err(inst->dev, "[%d] invalid fs(%d) to release\n", inst->id, fs->id); + return; + } + + vpu_inst_lock(inst); + vpu_buf = vdec->slots[fs->id]; + vdec->slots[fs->id] = NULL; + + if (!vpu_buf) { + dev_dbg(inst->dev, "[%d] fs[%d] has bee released\n", inst->id, fs->id); + goto exit; + } + + vbuf = &vpu_buf->m2m_buf.vb; + if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) { + dev_dbg(inst->dev, "[%d] frame skip\n", inst->id); + vdec->sequence++; + } + + vdec_response_fs_release(inst, fs->id, vpu_buf->tag); + if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_READY) + vdec_recycle_buffer(inst, vbuf); + + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + vpu_process_capture_buffer(inst); + +exit: + vpu_inst_unlock(inst); +} + +static void vdec_event_eos(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n", + inst->id, + vdec->params.frame_count, + vdec->decoded_frame_count, + vdec->display_frame_count, + vdec->sequence); + vpu_inst_lock(inst); + vdec->eos_received++; + vdec->fixed_fmt = false; + inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP; + vdec_update_state(inst, VPU_CODEC_STATE_DRAIN, 0); + vdec_set_last_buffer_dequeued(inst); + vpu_inst_unlock(inst); +} + +static void vdec_event_notify(struct vpu_inst *inst, u32 event, void *data) +{ + switch (event) { + case VPU_MSG_ID_SEQ_HDR_FOUND: + vdec_event_seq_hdr(inst, data); + break; + case VPU_MSG_ID_RES_CHANGE: + vdec_event_resolution_change(inst); + break; + case VPU_MSG_ID_FRAME_REQ: + vdec_event_req_fs(inst, data); + break; + case VPU_MSG_ID_FRAME_RELEASE: + vdec_evnet_rel_fs(inst, data); + break; + case VPU_MSG_ID_PIC_EOS: + vdec_event_eos(inst); + break; + default: + break; + } +} + +static int vdec_process_output(struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct vdec_t *vdec = inst->priv; + struct vb2_v4l2_buffer *vbuf; + struct vpu_rpc_buffer_desc desc; + u32 free_space; + int ret; + + vbuf = to_vb2_v4l2_buffer(vb); + dev_dbg(inst->dev, "[%d] dec output [%d] %d : %ld\n", + inst->id, vbuf->sequence, vb->index, vb2_get_plane_payload(vb, 0)); + + if (inst->state == VPU_CODEC_STATE_DEINIT) + return -EINVAL; + if (vdec->reset_codec) + return -EINVAL; + + if (inst->state == VPU_CODEC_STATE_STARTED) + vdec_update_state(inst, VPU_CODEC_STATE_ACTIVE, 0); + + ret = vpu_iface_get_stream_buffer_desc(inst, &desc); + if (ret) + return ret; + + free_space = vpu_helper_get_free_space(inst); + if (free_space < vb2_get_plane_payload(vb, 0) + 0x40000) + return -ENOMEM; + + ret = vpu_iface_input_frame(inst, vb); + if (ret < 0) + return -ENOMEM; + + dev_dbg(inst->dev, "[%d][INPUT TS]%32lld\n", inst->id, vb->timestamp); + vdec->ts_pre_count++; + vdec->params.frame_count++; + + v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + + if (vdec->drain) + vdec_drain(inst); + + return 0; +} + +static int vdec_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct vdec_t *vdec = inst->priv; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + int ret; + + if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + return -EINVAL; + if (vdec->reset_codec) + return -EINVAL; + + ret = vdec_response_frame(inst, vbuf); + if (ret) + return ret; + v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf); + return 0; +} + +static void vdec_on_queue_empty(struct vpu_inst *inst, u32 type) +{ + struct vdec_t *vdec = inst->priv; + + if (V4L2_TYPE_IS_OUTPUT(type)) + return; + + vdec_handle_resolution_change(inst); + if (vdec->eos_received) + vdec_set_last_buffer_dequeued(inst); +} + +static void vdec_abort(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + struct vpu_rpc_buffer_desc desc; + int ret; + + vpu_trace(inst->dev, "[%d] state = %d\n", inst->id, inst->state); + vpu_iface_add_scode(inst, SCODE_PADDING_ABORT); + vdec->params.end_flag = 1; + vpu_iface_set_decode_params(inst, &vdec->params, 1); + + vpu_session_abort(inst); + + ret = vpu_iface_get_stream_buffer_desc(inst, &desc); + if (!ret) + vpu_iface_update_stream_buffer(inst, desc.rptr, 1); + + vpu_session_rst_buf(inst); + vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n", + inst->id, + vdec->params.frame_count, + vdec->decoded_frame_count, + vdec->display_frame_count, + vdec->sequence); + vdec->params.end_flag = 0; + vdec->drain = 0; + vdec->ts_pre_count = 0; + vdec->params.frame_count = 0; + vdec->decoded_frame_count = 0; + vdec->display_frame_count = 0; + vdec->sequence = 0; +} + +static void vdec_stop(struct vpu_inst *inst, bool free) +{ + struct vdec_t *vdec = inst->priv; + + vdec_clear_slots(inst); + if (inst->state != VPU_CODEC_STATE_DEINIT) + vpu_session_stop(inst); + vdec_clear_fs(&vdec->mbi); + vdec_clear_fs(&vdec->dcp); + if (free) { + vpu_free_dma(&vdec->udata); + vpu_free_dma(&inst->stream_buffer); + } + vdec_update_state(inst, VPU_CODEC_STATE_DEINIT, 1); + vdec->reset_codec = false; +} + +static void vdec_release(struct vpu_inst *inst) +{ + if (inst->id != VPU_INST_NULL_ID) + vpu_trace(inst->dev, "[%d]\n", inst->id); + vpu_inst_lock(inst); + vdec_stop(inst, true); + vpu_inst_unlock(inst); +} + +static void vdec_cleanup(struct vpu_inst *inst) +{ + struct vdec_t *vdec; + + if (!inst) + return; + + vdec = inst->priv; + if (vdec) + vfree(vdec); + inst->priv = NULL; + vfree(inst); +} + +static void vdec_init_params(struct vdec_t *vdec) +{ + vdec->params.frame_count = 0; + vdec->params.end_flag = 0; +} + +static int vdec_start(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + int stream_buffer_size; + int ret; + + if (inst->state != VPU_CODEC_STATE_DEINIT) + return 0; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + if (!vdec->udata.virt) { + vdec->udata.length = 0x1000; + ret = vpu_alloc_dma(inst->core, &vdec->udata); + if (ret) { + dev_err(inst->dev, "[%d] alloc udata fail\n", inst->id); + goto error; + } + } + + if (!inst->stream_buffer.virt) { + stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core); + if (stream_buffer_size > 0) { + inst->stream_buffer.length = stream_buffer_size; + ret = vpu_alloc_dma(inst->core, &inst->stream_buffer); + if (ret) { + dev_err(inst->dev, "[%d] alloc stream buffer fail\n", inst->id); + goto error; + } + inst->use_stream_buffer = true; + } + } + + if (inst->use_stream_buffer) + vpu_iface_config_stream_buffer(inst, &inst->stream_buffer); + vpu_iface_init_instance(inst); + vdec->params.udata.base = vdec->udata.phys; + vdec->params.udata.size = vdec->udata.length; + ret = vpu_iface_set_decode_params(inst, &vdec->params, 0); + if (ret) { + dev_err(inst->dev, "[%d] set decode params fail\n", inst->id); + goto error; + } + + vdec_init_params(vdec); + ret = vpu_session_start(inst); + if (ret) { + dev_err(inst->dev, "[%d] start fail\n", inst->id); + goto error; + } + + vdec_update_state(inst, VPU_CODEC_STATE_STARTED, 0); + + return 0; +error: + vpu_free_dma(&vdec->udata); + vpu_free_dma(&inst->stream_buffer); + return ret; +} + +static int vdec_start_session(struct vpu_inst *inst, u32 type) +{ + struct vdec_t *vdec = inst->priv; + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (vdec->reset_codec) + vdec_stop(inst, false); + if (inst->state == VPU_CODEC_STATE_DEINIT) { + ret = vdec_start(inst); + if (ret) + return ret; + } + } + + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (inst->state == VPU_CODEC_STATE_SEEK) + vdec_update_state(inst, vdec->state, 1); + vdec->eos_received = 0; + vpu_process_output_buffer(inst); + } else { + vdec_cmd_start(inst); + } + if (inst->state == VPU_CODEC_STATE_ACTIVE) + vdec_response_fs_request(inst, false); + + return ret; +} + +static int vdec_stop_session(struct vpu_inst *inst, u32 type) +{ + struct vdec_t *vdec = inst->priv; + + if (inst->state == VPU_CODEC_STATE_DEINIT) + return 0; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + vdec_update_state(inst, VPU_CODEC_STATE_SEEK, 0); + vdec->drain = 0; + } else { + if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + vdec_abort(inst); + + vdec->eos_received = 0; + vdec_clear_slots(inst); + } + + return 0; +} + +static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i) +{ + struct vdec_t *vdec = inst->priv; + int num = -1; + + switch (i) { + case 0: + num = scnprintf(str, size, + "req_frame_count = %d\ninterlaced = %d\n", + vdec->req_frame_count, + vdec->codec_info.progressive ? 0 : 1); + break; + case 1: + num = scnprintf(str, size, + "mbi: size = 0x%x request = %d, alloc = %d, response = %d\n", + vdec->mbi.size, + vdec->mbi.req_count, + vdec->mbi.count, + vdec->mbi.index); + break; + case 2: + num = scnprintf(str, size, + "dcp: size = 0x%x request = %d, alloc = %d, response = %d\n", + vdec->dcp.size, + vdec->dcp.req_count, + vdec->dcp.count, + vdec->dcp.index); + break; + case 3: + num = scnprintf(str, size, "input_frame_count = %d\n", vdec->params.frame_count); + break; + case 4: + num = scnprintf(str, size, "decoded_frame_count = %d\n", vdec->decoded_frame_count); + break; + case 5: + num = scnprintf(str, size, "display_frame_count = %d\n", vdec->display_frame_count); + break; + case 6: + num = scnprintf(str, size, "sequence = %d\n", vdec->sequence); + break; + case 7: + num = scnprintf(str, size, "drain = %d, eos = %d, source_change = %d\n", + vdec->drain, vdec->eos_received, vdec->source_change); + break; + case 8: + num = scnprintf(str, size, "ts_pre_count = %d, frame_depth = %d\n", + vdec->ts_pre_count, vdec->frame_depth); + break; + case 9: + num = scnprintf(str, size, "fps = %d/%d\n", + vdec->codec_info.frame_rate.numerator, + vdec->codec_info.frame_rate.denominator); + break; + default: + break; + } + + return num; +} + +static struct vpu_inst_ops vdec_inst_ops = { + .ctrl_init = vdec_ctrl_init, + .check_ready = vdec_check_ready, + .buf_done = vdec_buf_done, + .get_one_frame = vdec_frame_decoded, + .stop_done = vdec_stop_done, + .event_notify = vdec_event_notify, + .release = vdec_release, + .cleanup = vdec_cleanup, + .start = vdec_start_session, + .stop = vdec_stop_session, + .process_output = vdec_process_output, + .process_capture = vdec_process_capture, + .on_queue_empty = vdec_on_queue_empty, + .get_debug_info = vdec_get_debug_info, + .wait_prepare = vpu_inst_unlock, + .wait_finish = vpu_inst_lock, +}; + +static void vdec_init(struct file *file) +{ + struct vpu_inst *inst = to_inst(file); + struct vdec_t *vdec; + struct v4l2_format f; + + vdec = inst->priv; + vdec->frame_depth = VDEC_FRAME_DEPTH; + + memset(&f, 0, sizeof(f)); + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; + f.fmt.pix_mp.width = 1280; + f.fmt.pix_mp.height = 720; + f.fmt.pix_mp.field = V4L2_FIELD_NONE; + vdec_s_fmt(file, &inst->fh, &f); + + memset(&f, 0, sizeof(f)); + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M_8L128; + f.fmt.pix_mp.width = 1280; + f.fmt.pix_mp.height = 720; + f.fmt.pix_mp.field = V4L2_FIELD_NONE; + vdec_s_fmt(file, &inst->fh, &f); +} + +static int vdec_open(struct file *file) +{ + struct vpu_inst *inst; + struct vdec_t *vdec; + int ret; + + inst = vzalloc(sizeof(*inst)); + if (!inst) + return -ENOMEM; + + vdec = vzalloc(sizeof(*vdec)); + if (!vdec) { + vfree(inst); + return -ENOMEM; + } + + inst->ops = &vdec_inst_ops; + inst->formats = vdec_formats; + inst->type = VPU_CORE_TYPE_DEC; + inst->priv = vdec; + + ret = vpu_v4l2_open(file, inst); + if (ret) + return ret; + + vdec->fixed_fmt = false; + inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP; + vdec_init(file); + + return 0; +} + +static __poll_t vdec_poll(struct file *file, poll_table *wait) +{ + struct vpu_inst *inst = to_inst(file); + struct vb2_queue *src_q, *dst_q; + __poll_t ret; + + ret = v4l2_m2m_fop_poll(file, wait); + src_q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + if (vb2_is_streaming(src_q) && !vb2_is_streaming(dst_q)) + ret &= (~EPOLLERR); + if (!src_q->error && !dst_q->error && + (vb2_is_streaming(src_q) && list_empty(&src_q->queued_list)) && + (vb2_is_streaming(dst_q) && list_empty(&dst_q->queued_list))) + ret &= (~EPOLLERR); + + return ret; +} + +static const struct v4l2_file_operations vdec_fops = { + .owner = THIS_MODULE, + .open = vdec_open, + .release = vpu_v4l2_close, + .unlocked_ioctl = video_ioctl2, + .poll = vdec_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +const struct v4l2_ioctl_ops *vdec_get_ioctl_ops(void) +{ + return &vdec_ioctl_ops; +} + +const struct v4l2_file_operations *vdec_get_fops(void) +{ + return &vdec_fops; +} diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c new file mode 100644 index 000000000000..d33c2748e4b7 --- /dev/null +++ b/drivers/media/platform/amphion/venc.c @@ -0,0 +1,1358 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <linux/ktime.h> +#include <linux/rational.h> +#include <linux/vmalloc.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> +#include "vpu.h" +#include "vpu_defs.h" +#include "vpu_core.h" +#include "vpu_helpers.h" +#include "vpu_v4l2.h" +#include "vpu_cmds.h" +#include "vpu_rpc.h" + +#define VENC_OUTPUT_ENABLE BIT(0) +#define VENC_CAPTURE_ENABLE BIT(1) +#define VENC_ENABLE_MASK (VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE) +#define VENC_MAX_BUF_CNT 8 + +struct venc_t { + struct vpu_encode_params params; + u32 request_key_frame; + u32 input_ready; + u32 cpb_size; + bool bitrate_change; + + struct vpu_buffer enc[VENC_MAX_BUF_CNT]; + struct vpu_buffer ref[VENC_MAX_BUF_CNT]; + struct vpu_buffer act[VENC_MAX_BUF_CNT]; + struct list_head frames; + u32 frame_count; + u32 encode_count; + u32 ready_count; + u32 enable; + u32 stopped; + + u32 skipped_count; + u32 skipped_bytes; + + wait_queue_head_t wq; +}; + +struct venc_frame_t { + struct list_head list; + struct vpu_enc_pic_info info; + u32 bytesused; + s64 timestamp; +}; + +static const struct vpu_format venc_formats[] = { + { + .pixfmt = V4L2_PIX_FMT_NV12M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + { + .pixfmt = V4L2_PIX_FMT_H264, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, + {0, 0, 0, 0}, +}; + +static int venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver)); + strscpy(cap->card, "amphion vpu encoder", sizeof(cap->card)); + strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info)); + + return 0; +} + +static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct vpu_inst *inst = to_inst(file); + const struct vpu_format *fmt; + + memset(f->reserved, 0, sizeof(f->reserved)); + fmt = vpu_helper_enum_format(inst, f->type, f->index); + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixfmt; + f->flags = fmt->flags; + + return 0; +} + +static int venc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) +{ + struct vpu_inst *inst = to_inst(file); + const struct vpu_core_resources *res; + + if (!fsize || fsize->index) + return -EINVAL; + + if (!vpu_helper_find_format(inst, 0, fsize->pixel_format)) + return -EINVAL; + + res = vpu_get_resource(inst); + if (!res) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.max_width = res->max_width; + fsize->stepwise.max_height = res->max_height; + fsize->stepwise.min_width = res->min_width; + fsize->stepwise.min_height = res->min_height; + fsize->stepwise.step_width = res->step_width; + fsize->stepwise.step_height = res->step_height; + + return 0; +} + +static int venc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) +{ + struct vpu_inst *inst = to_inst(file); + const struct vpu_core_resources *res; + + if (!fival || fival->index) + return -EINVAL; + + if (!vpu_helper_find_format(inst, 0, fival->pixel_format)) + return -EINVAL; + + if (!fival->width || !fival->height) + return -EINVAL; + + res = vpu_get_resource(inst); + if (!res) + return -EINVAL; + if (fival->width < res->min_width || fival->width > res->max_width || + fival->height < res->min_height || fival->height > res->max_height) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.min.numerator = 1; + fival->stepwise.min.denominator = USHRT_MAX; + fival->stepwise.max.numerator = USHRT_MAX; + fival->stepwise.max.denominator = 1; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = 1; + + return 0; +} + +static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + struct venc_t *venc = inst->priv; + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct vpu_format *cur_fmt; + int i; + + cur_fmt = vpu_get_format(inst, f->type); + + pixmp->pixelformat = cur_fmt->pixfmt; + pixmp->num_planes = cur_fmt->num_planes; + pixmp->width = cur_fmt->width; + pixmp->height = cur_fmt->height; + pixmp->field = cur_fmt->field; + pixmp->flags = cur_fmt->flags; + for (i = 0; i < pixmp->num_planes; i++) { + pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i]; + pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i]; + } + + f->fmt.pix_mp.colorspace = venc->params.color.primaries; + f->fmt.pix_mp.xfer_func = venc->params.color.transfer; + f->fmt.pix_mp.ycbcr_enc = venc->params.color.matrix; + f->fmt.pix_mp.quantization = venc->params.color.full_range; + + return 0; +} + +static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + + vpu_try_fmt_common(inst, f); + + return 0; +} + +static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vpu_inst *inst = to_inst(file); + const struct vpu_format *fmt; + struct vpu_format *cur_fmt; + struct vb2_queue *q; + struct venc_t *venc = inst->priv; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + int i; + + q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type); + if (!q) + return -EINVAL; + if (vb2_is_busy(q)) + return -EBUSY; + + fmt = vpu_try_fmt_common(inst, f); + if (!fmt) + return -EINVAL; + + cur_fmt = vpu_get_format(inst, f->type); + + cur_fmt->pixfmt = fmt->pixfmt; + cur_fmt->num_planes = fmt->num_planes; + cur_fmt->flags = fmt->flags; + cur_fmt->width = pix_mp->width; + cur_fmt->height = pix_mp->height; + for (i = 0; i < fmt->num_planes; i++) { + cur_fmt->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage; + cur_fmt->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline; + } + + if (pix_mp->field != V4L2_FIELD_ANY) + cur_fmt->field = pix_mp->field; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + venc->params.input_format = cur_fmt->pixfmt; + venc->params.src_stride = cur_fmt->bytesperline[0]; + venc->params.src_width = cur_fmt->width; + venc->params.src_height = cur_fmt->height; + venc->params.crop.left = 0; + venc->params.crop.top = 0; + venc->params.crop.width = cur_fmt->width; + venc->params.crop.height = cur_fmt->height; + } else { + venc->params.codec_format = cur_fmt->pixfmt; + venc->params.out_width = cur_fmt->width; + venc->params.out_height = cur_fmt->height; + } + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!vpu_color_check_primaries(pix_mp->colorspace)) { + venc->params.color.primaries = pix_mp->colorspace; + vpu_color_get_default(venc->params.color.primaries, + &venc->params.color.transfer, + &venc->params.color.matrix, + &venc->params.color.full_range); + } + if (!vpu_color_check_transfers(pix_mp->xfer_func)) + venc->params.color.transfer = pix_mp->xfer_func; + if (!vpu_color_check_matrix(pix_mp->ycbcr_enc)) + venc->params.color.matrix = pix_mp->ycbcr_enc; + if (!vpu_color_check_full_range(pix_mp->quantization)) + venc->params.color.full_range = pix_mp->quantization; + } + + pix_mp->colorspace = venc->params.color.primaries; + pix_mp->xfer_func = venc->params.color.transfer; + pix_mp->ycbcr_enc = venc->params.color.matrix; + pix_mp->quantization = venc->params.color.full_range; + + return 0; +} + +static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm) +{ + struct vpu_inst *inst = to_inst(file); + struct venc_t *venc = inst->priv; + struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe; + + if (!parm) + return -EINVAL; + + if (!vpu_helper_check_type(inst, parm->type)) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.readbuffers = 0; + timeperframe->numerator = venc->params.frame_rate.numerator; + timeperframe->denominator = venc->params.frame_rate.denominator; + + return 0; +} + +static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm) +{ + struct vpu_inst *inst = to_inst(file); + struct venc_t *venc = inst->priv; + struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe; + unsigned long n, d; + + if (!parm) + return -EINVAL; + + if (!vpu_helper_check_type(inst, parm->type)) + return -EINVAL; + + if (!timeperframe->numerator) + timeperframe->numerator = venc->params.frame_rate.numerator; + if (!timeperframe->denominator) + timeperframe->denominator = venc->params.frame_rate.denominator; + + venc->params.frame_rate.numerator = timeperframe->numerator; + venc->params.frame_rate.denominator = timeperframe->denominator; + + rational_best_approximation(venc->params.frame_rate.numerator, + venc->params.frame_rate.denominator, + venc->params.frame_rate.numerator, + venc->params.frame_rate.denominator, + &n, &d); + venc->params.frame_rate.numerator = n; + venc->params.frame_rate.denominator = d; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + memset(parm->parm.capture.reserved, 0, sizeof(parm->parm.capture.reserved)); + + return 0; +} + +static int venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_inst *inst = to_inst(file); + struct venc_t *venc = inst->priv; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = inst->out_format.width; + s->r.height = inst->out_format.height; + break; + case V4L2_SEL_TGT_CROP: + s->r = venc->params.crop; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int venc_valid_crop(struct venc_t *venc, const struct vpu_core_resources *res) +{ + struct v4l2_rect *rect = NULL; + u32 min_width; + u32 min_height; + u32 src_width; + u32 src_height; + + rect = &venc->params.crop; + min_width = res->min_width; + min_height = res->min_height; + src_width = venc->params.src_width; + src_height = venc->params.src_height; + + if (rect->width == 0 || rect->height == 0) + return -EINVAL; + if (rect->left > src_width - min_width || rect->top > src_height - min_height) + return -EINVAL; + + rect->width = min(rect->width, src_width - rect->left); + rect->width = max_t(u32, rect->width, min_width); + + rect->height = min(rect->height, src_height - rect->top); + rect->height = max_t(u32, rect->height, min_height); + + return 0; +} + +static int venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpu_inst *inst = to_inst(file); + const struct vpu_core_resources *res; + struct venc_t *venc = inst->priv; + + res = vpu_get_resource(inst); + if (!res) + return -EINVAL; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + venc->params.crop.left = ALIGN(s->r.left, res->step_width); + venc->params.crop.top = ALIGN(s->r.top, res->step_height); + venc->params.crop.width = ALIGN(s->r.width, res->step_width); + venc->params.crop.height = ALIGN(s->r.height, res->step_height); + if (venc_valid_crop(venc, res)) { + venc->params.crop.left = 0; + venc->params.crop.top = 0; + venc->params.crop.width = venc->params.src_width; + venc->params.crop.height = venc->params.src_height; + } + + inst->crop = venc->params.crop; + + return 0; +} + +static int venc_drain(struct vpu_inst *inst) +{ + struct venc_t *venc = inst->priv; + int ret; + + if (!inst->fh.m2m_ctx) + return 0; + + if (inst->state != VPU_CODEC_STATE_DRAIN) + return 0; + + if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx)) + return 0; + + if (!venc->input_ready) + return 0; + + venc->input_ready = false; + vpu_trace(inst->dev, "[%d]\n", inst->id); + ret = vpu_session_stop(inst); + if (ret) + return ret; + inst->state = VPU_CODEC_STATE_STOP; + wake_up_all(&venc->wq); + + return 0; +} + +static int venc_request_eos(struct vpu_inst *inst) +{ + inst->state = VPU_CODEC_STATE_DRAIN; + venc_drain(inst); + + return 0; +} + +static int venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd) +{ + struct vpu_inst *inst = to_inst(file); + int ret; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); + if (ret) + return ret; + + vpu_inst_lock(inst); + if (cmd->cmd == V4L2_ENC_CMD_STOP) { + if (inst->state == VPU_CODEC_STATE_DEINIT) + vpu_set_last_buffer_dequeued(inst); + else + venc_request_eos(inst); + } + vpu_inst_unlock(inst); + + return 0; +} + +static int venc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops venc_ioctl_ops = { + .vidioc_querycap = venc_querycap, + .vidioc_enum_fmt_vid_cap = venc_enum_fmt, + .vidioc_enum_fmt_vid_out = venc_enum_fmt, + .vidioc_enum_framesizes = venc_enum_framesizes, + .vidioc_enum_frameintervals = venc_enum_frameintervals, + .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = venc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt, + .vidioc_try_fmt_vid_out_mplane = venc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt, + .vidioc_s_fmt_vid_out_mplane = venc_s_fmt, + .vidioc_g_parm = venc_g_parm, + .vidioc_s_parm = venc_s_parm, + .vidioc_g_selection = venc_g_selection, + .vidioc_s_selection = venc_s_selection, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = venc_encoder_cmd, + .vidioc_subscribe_event = venc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, +}; + +static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_inst *inst = ctrl_to_inst(ctrl); + struct venc_t *venc = inst->priv; + int ret = 0; + + vpu_inst_lock(inst); + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + venc->params.profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + venc->params.level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + venc->params.rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + venc->params.rc_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + if (ctrl->val != venc->params.bitrate) + venc->bitrate_change = true; + venc->params.bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + venc->params.bitrate_max = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + venc->params.gop_length = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + venc->params.bframes = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + venc->params.i_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + venc->params.p_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + venc->params.b_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + venc->request_key_frame = 1; + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + venc->cpb_size = ctrl->val * 1024; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: + venc->params.sar.enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: + venc->params.sar.idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: + venc->params.sar.width = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: + venc->params.sar.height = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + break; + default: + ret = -EINVAL; + break; + } + vpu_inst_unlock(inst); + + return ret; +} + +static const struct v4l2_ctrl_ops venc_ctrl_ops = { + .s_ctrl = venc_op_s_ctrl, + .g_volatile_ctrl = vpu_helper_g_volatile_ctrl, +}; + +static int venc_ctrl_init(struct vpu_inst *inst) +{ + struct v4l2_ctrl *ctrl; + int ret; + + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20); + if (ret) + return ret; + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + 0x0, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)), + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + BITRATE_MIN, + BITRATE_MAX, + BITRATE_STEP, + BITRATE_DEFAULT); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + BITRATE_MIN, BITRATE_MAX, + BITRATE_STEP, + BITRATE_DEFAULT_PEAK); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0); + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, 64, 10240, 1, 1024); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED, + 0x0, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH, + 0, USHRT_MAX, 1, 1); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT, + 0, USHRT_MAX, 1, 1); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + + ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler); + if (ret) { + dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret); + v4l2_ctrl_handler_free(&inst->ctrl_handler); + return ret; + } + + return 0; +} + +static bool venc_check_ready(struct vpu_inst *inst, unsigned int type) +{ + struct venc_t *venc = inst->priv; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (vpu_helper_get_free_space(inst) < venc->cpb_size) + return false; + return venc->input_ready; + } + + if (list_empty(&venc->frames)) + return false; + return true; +} + +static u32 venc_get_enable_mask(u32 type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return VENC_OUTPUT_ENABLE; + else + return VENC_CAPTURE_ENABLE; +} + +static void venc_set_enable(struct venc_t *venc, u32 type, int enable) +{ + u32 mask = venc_get_enable_mask(type); + + if (enable) + venc->enable |= mask; + else + venc->enable &= ~mask; +} + +static u32 venc_get_enable(struct venc_t *venc, u32 type) +{ + return venc->enable & venc_get_enable_mask(type); +} + +static void venc_input_done(struct vpu_inst *inst) +{ + struct venc_t *venc = inst->priv; + + vpu_inst_lock(inst); + venc->input_ready = true; + vpu_process_output_buffer(inst); + if (inst->state == VPU_CODEC_STATE_DRAIN) + venc_drain(inst); + vpu_inst_unlock(inst); +} + +/* + * It's hardware limitation, that there may be several bytes + * redundant data at the beginning of frame. + * For android platform, the redundant data may cause cts test fail + * So driver will strip them + */ +static int venc_precheck_encoded_frame(struct vpu_inst *inst, struct venc_frame_t *frame) +{ + struct venc_t *venc; + int skipped; + + if (!frame || !frame->bytesused) + return -EINVAL; + + venc = inst->priv; + skipped = vpu_helper_find_startcode(&inst->stream_buffer, + inst->cap_format.pixfmt, + frame->info.wptr - inst->stream_buffer.phys, + frame->bytesused); + if (skipped > 0) { + frame->bytesused -= skipped; + frame->info.wptr = vpu_helper_step_walk(&inst->stream_buffer, + frame->info.wptr, skipped); + venc->skipped_bytes += skipped; + venc->skipped_count++; + } + + return 0; +} + +static int venc_get_one_encoded_frame(struct vpu_inst *inst, + struct venc_frame_t *frame, + struct vb2_v4l2_buffer *vbuf) +{ + struct venc_t *venc = inst->priv; + + if (!vbuf) + return -EAGAIN; + + if (!venc_get_enable(inst->priv, vbuf->vb2_buf.type)) { + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + return 0; + } + if (frame->bytesused > vbuf->vb2_buf.planes[0].length) { + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + return -ENOMEM; + } + + venc_precheck_encoded_frame(inst, frame); + + if (frame->bytesused) { + u32 rptr = frame->info.wptr; + void *dst = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + + vpu_helper_copy_from_stream_buffer(&inst->stream_buffer, + &rptr, frame->bytesused, dst); + vpu_iface_update_stream_buffer(inst, rptr, 0); + } + vb2_set_plane_payload(&vbuf->vb2_buf, 0, frame->bytesused); + vbuf->sequence = frame->info.frame_id; + vbuf->vb2_buf.timestamp = frame->info.timestamp; + vbuf->field = inst->cap_format.field; + vbuf->flags |= frame->info.pic_type; + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->info.timestamp); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + venc->ready_count++; + + if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME) + dev_dbg(inst->dev, "[%d][%d]key frame\n", inst->id, frame->info.frame_id); + + return 0; +} + +static int venc_get_encoded_frames(struct vpu_inst *inst) +{ + struct venc_t *venc; + struct venc_frame_t *frame; + struct venc_frame_t *tmp; + + if (!inst->fh.m2m_ctx) + return 0; + venc = inst->priv; + list_for_each_entry_safe(frame, tmp, &venc->frames, list) { + if (venc_get_one_encoded_frame(inst, frame, + v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx))) + break; + list_del_init(&frame->list); + vfree(frame); + } + + return 0; +} + +static int venc_frame_encoded(struct vpu_inst *inst, void *arg) +{ + struct vpu_enc_pic_info *info = arg; + struct venc_frame_t *frame; + struct venc_t *venc; + int ret = 0; + + if (!info) + return -EINVAL; + venc = inst->priv; + frame = vzalloc(sizeof(*frame)); + if (!frame) + return -ENOMEM; + + memcpy(&frame->info, info, sizeof(frame->info)); + frame->bytesused = info->frame_size; + + vpu_inst_lock(inst); + list_add_tail(&frame->list, &venc->frames); + venc->encode_count++; + venc_get_encoded_frames(inst); + vpu_inst_unlock(inst); + + return ret; +} + +static void venc_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) +{ + struct vb2_v4l2_buffer *vbuf; + + if (!inst->fh.m2m_ctx) + return; + + vpu_inst_lock(inst); + if (!venc_get_enable(inst->priv, frame->type)) + goto exit; + vbuf = vpu_find_buf_by_sequence(inst, frame->type, frame->sequence); + if (!vbuf) { + dev_err(inst->dev, "[%d] can't find buf: type %d, sequence %d\n", + inst->id, frame->type, frame->sequence); + goto exit; + } + + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + if (V4L2_TYPE_IS_OUTPUT(frame->type)) + v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf); + else + v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); +exit: + vpu_inst_unlock(inst); +} + +static void venc_set_last_buffer_dequeued(struct vpu_inst *inst) +{ + struct venc_t *venc = inst->priv; + + if (venc->stopped && list_empty(&venc->frames)) + vpu_set_last_buffer_dequeued(inst); +} + +static void venc_stop_done(struct vpu_inst *inst) +{ + struct venc_t *venc = inst->priv; + + vpu_inst_lock(inst); + venc->stopped = true; + venc_set_last_buffer_dequeued(inst); + vpu_inst_unlock(inst); + + wake_up_all(&venc->wq); +} + +static void venc_event_notify(struct vpu_inst *inst, u32 event, void *data) +{ +} + +static void venc_release(struct vpu_inst *inst) +{ +} + +static void venc_cleanup(struct vpu_inst *inst) +{ + struct venc_t *venc; + + if (!inst) + return; + + venc = inst->priv; + if (venc) + vfree(venc); + inst->priv = NULL; + vfree(inst); +} + +static int venc_start_session(struct vpu_inst *inst, u32 type) +{ + struct venc_t *venc = inst->priv; + int stream_buffer_size; + int ret; + + venc_set_enable(venc, type, 1); + if ((venc->enable & VENC_ENABLE_MASK) != VENC_ENABLE_MASK) + return 0; + + vpu_iface_init_instance(inst); + stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core); + if (stream_buffer_size > 0) { + inst->stream_buffer.length = max_t(u32, stream_buffer_size, venc->cpb_size * 3); + ret = vpu_alloc_dma(inst->core, &inst->stream_buffer); + if (ret) + goto error; + + inst->use_stream_buffer = true; + vpu_iface_config_stream_buffer(inst, &inst->stream_buffer); + } + + ret = vpu_iface_set_encode_params(inst, &venc->params, 0); + if (ret) + goto error; + ret = vpu_session_configure_codec(inst); + if (ret) + goto error; + + inst->state = VPU_CODEC_STATE_CONFIGURED; + /*vpu_iface_config_memory_resource*/ + + /*config enc expert mode parameter*/ + ret = vpu_iface_set_encode_params(inst, &venc->params, 1); + if (ret) + goto error; + + ret = vpu_session_start(inst); + if (ret) + goto error; + inst->state = VPU_CODEC_STATE_STARTED; + + venc->bitrate_change = false; + venc->input_ready = true; + venc->frame_count = 0; + venc->encode_count = 0; + venc->ready_count = 0; + venc->stopped = false; + vpu_process_output_buffer(inst); + if (venc->frame_count == 0) + dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id); + + return 0; +error: + venc_set_enable(venc, type, 0); + inst->state = VPU_CODEC_STATE_DEINIT; + + vpu_free_dma(&inst->stream_buffer); + return ret; +} + +static void venc_cleanup_mem_resource(struct vpu_inst *inst) +{ + struct venc_t *venc; + u32 i; + + venc = inst->priv; + + for (i = 0; i < ARRAY_SIZE(venc->enc); i++) + vpu_free_dma(&venc->enc[i]); + for (i = 0; i < ARRAY_SIZE(venc->ref); i++) + vpu_free_dma(&venc->ref[i]); +} + +static void venc_request_mem_resource(struct vpu_inst *inst, + u32 enc_frame_size, + u32 enc_frame_num, + u32 ref_frame_size, + u32 ref_frame_num, + u32 act_frame_size, + u32 act_frame_num) +{ + struct venc_t *venc; + u32 i; + int ret; + + venc = inst->priv; + if (enc_frame_num > ARRAY_SIZE(venc->enc)) { + dev_err(inst->dev, "[%d] enc num(%d) is out of range\n", inst->id, enc_frame_num); + return; + } + if (ref_frame_num > ARRAY_SIZE(venc->ref)) { + dev_err(inst->dev, "[%d] ref num(%d) is out of range\n", inst->id, ref_frame_num); + return; + } + if (act_frame_num > ARRAY_SIZE(venc->act)) { + dev_err(inst->dev, "[%d] act num(%d) is out of range\n", inst->id, act_frame_num); + return; + } + + for (i = 0; i < enc_frame_num; i++) { + venc->enc[i].length = enc_frame_size; + ret = vpu_alloc_dma(inst->core, &venc->enc[i]); + if (ret) { + venc_cleanup_mem_resource(inst); + return; + } + } + for (i = 0; i < ref_frame_num; i++) { + venc->ref[i].length = ref_frame_size; + ret = vpu_alloc_dma(inst->core, &venc->ref[i]); + if (ret) { + venc_cleanup_mem_resource(inst); + return; + } + } + if (act_frame_num != 1 || act_frame_size > inst->act.length) { + venc_cleanup_mem_resource(inst); + return; + } + venc->act[0].length = act_frame_size; + venc->act[0].phys = inst->act.phys; + venc->act[0].virt = inst->act.virt; + + for (i = 0; i < enc_frame_num; i++) + vpu_iface_config_memory_resource(inst, MEM_RES_ENC, i, &venc->enc[i]); + for (i = 0; i < ref_frame_num; i++) + vpu_iface_config_memory_resource(inst, MEM_RES_REF, i, &venc->ref[i]); + for (i = 0; i < act_frame_num; i++) + vpu_iface_config_memory_resource(inst, MEM_RES_ACT, i, &venc->act[i]); +} + +static void venc_cleanup_frames(struct venc_t *venc) +{ + struct venc_frame_t *frame; + struct venc_frame_t *tmp; + + list_for_each_entry_safe(frame, tmp, &venc->frames, list) { + list_del_init(&frame->list); + vfree(frame); + } +} + +static int venc_stop_session(struct vpu_inst *inst, u32 type) +{ + struct venc_t *venc = inst->priv; + + venc_set_enable(venc, type, 0); + if (venc->enable & VENC_ENABLE_MASK) + return 0; + + if (inst->state == VPU_CODEC_STATE_DEINIT) + return 0; + + if (inst->state != VPU_CODEC_STATE_STOP) + venc_request_eos(inst); + + call_void_vop(inst, wait_prepare); + if (!wait_event_timeout(venc->wq, venc->stopped, VPU_TIMEOUT)) { + set_bit(inst->id, &inst->core->hang_mask); + vpu_session_debug(inst); + } + call_void_vop(inst, wait_finish); + + inst->state = VPU_CODEC_STATE_DEINIT; + venc_cleanup_frames(inst->priv); + vpu_free_dma(&inst->stream_buffer); + venc_cleanup_mem_resource(inst); + + return 0; +} + +static int venc_process_output(struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct venc_t *venc = inst->priv; + struct vb2_v4l2_buffer *vbuf; + u32 flags; + + if (inst->state == VPU_CODEC_STATE_DEINIT) + return -EINVAL; + + vbuf = to_vb2_v4l2_buffer(vb); + if (inst->state == VPU_CODEC_STATE_STARTED) + inst->state = VPU_CODEC_STATE_ACTIVE; + + flags = vbuf->flags; + if (venc->request_key_frame) { + vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME; + venc->request_key_frame = 0; + } + if (venc->bitrate_change) { + vpu_session_update_parameters(inst, &venc->params); + venc->bitrate_change = false; + } + dev_dbg(inst->dev, "[%d][INPUT TS]%32lld\n", inst->id, vb->timestamp); + vpu_iface_input_frame(inst, vb); + vbuf->flags = flags; + venc->input_ready = false; + venc->frame_count++; + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE); + + return 0; +} + +static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct venc_t *venc; + struct venc_frame_t *frame = NULL; + struct vb2_v4l2_buffer *vbuf; + int ret; + + venc = inst->priv; + if (list_empty(&venc->frames)) + return -EINVAL; + + frame = list_first_entry(&venc->frames, struct venc_frame_t, list); + vbuf = to_vb2_v4l2_buffer(vb); + v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf); + ret = venc_get_one_encoded_frame(inst, frame, vbuf); + if (ret) + return ret; + + list_del_init(&frame->list); + vfree(frame); + return 0; +} + +static void venc_on_queue_empty(struct vpu_inst *inst, u32 type) +{ + struct venc_t *venc = inst->priv; + + if (V4L2_TYPE_IS_OUTPUT(type)) + return; + + if (venc->stopped) + venc_set_last_buffer_dequeued(inst); +} + +static int venc_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i) +{ + struct venc_t *venc = inst->priv; + int num = -1; + + switch (i) { + case 0: + num = scnprintf(str, size, "profile = %d\n", venc->params.profile); + break; + case 1: + num = scnprintf(str, size, "level = %d\n", venc->params.level); + break; + case 2: + num = scnprintf(str, size, "fps = %d/%d\n", + venc->params.frame_rate.numerator, + venc->params.frame_rate.denominator); + break; + case 3: + num = scnprintf(str, size, "%d x %d -> %d x %d\n", + venc->params.src_width, + venc->params.src_height, + venc->params.out_width, + venc->params.out_height); + break; + case 4: + num = scnprintf(str, size, "(%d, %d) %d x %d\n", + venc->params.crop.left, + venc->params.crop.top, + venc->params.crop.width, + venc->params.crop.height); + break; + case 5: + num = scnprintf(str, size, + "enable = 0x%x, input = %d, encode = %d, ready = %d, stopped = %d\n", + venc->enable, + venc->frame_count, venc->encode_count, + venc->ready_count, + venc->stopped); + break; + case 6: + num = scnprintf(str, size, "gop = %d\n", venc->params.gop_length); + break; + case 7: + num = scnprintf(str, size, "bframes = %d\n", venc->params.bframes); + break; + case 8: + num = scnprintf(str, size, "rc: %s, mode = %d, bitrate = %d(%d), qp = %d\n", + venc->params.rc_enable ? "enable" : "disable", + venc->params.rc_mode, + venc->params.bitrate, + venc->params.bitrate_max, + venc->params.i_frame_qp); + break; + case 9: + num = scnprintf(str, size, "sar: enable = %d, idc = %d, %d x %d\n", + venc->params.sar.enable, + venc->params.sar.idc, + venc->params.sar.width, + venc->params.sar.height); + + break; + case 10: + num = scnprintf(str, size, + "colorspace: primaries = %d, transfer = %d, matrix = %d, full_range = %d\n", + venc->params.color.primaries, + venc->params.color.transfer, + venc->params.color.matrix, + venc->params.color.full_range); + break; + case 11: + num = scnprintf(str, size, "skipped: count = %d, bytes = %d\n", + venc->skipped_count, venc->skipped_bytes); + break; + default: + break; + } + + return num; +} + +static struct vpu_inst_ops venc_inst_ops = { + .ctrl_init = venc_ctrl_init, + .check_ready = venc_check_ready, + .input_done = venc_input_done, + .get_one_frame = venc_frame_encoded, + .buf_done = venc_buf_done, + .stop_done = venc_stop_done, + .event_notify = venc_event_notify, + .release = venc_release, + .cleanup = venc_cleanup, + .start = venc_start_session, + .mem_request = venc_request_mem_resource, + .stop = venc_stop_session, + .process_output = venc_process_output, + .process_capture = venc_process_capture, + .on_queue_empty = venc_on_queue_empty, + .get_debug_info = venc_get_debug_info, + .wait_prepare = vpu_inst_unlock, + .wait_finish = vpu_inst_lock, +}; + +static void venc_init(struct file *file) +{ + struct vpu_inst *inst = to_inst(file); + struct venc_t *venc; + struct v4l2_format f; + struct v4l2_streamparm parm; + + venc = inst->priv; + venc->params.qp_min = 1; + venc->params.qp_max = 51; + venc->params.qp_min_i = 1; + venc->params.qp_max_i = 51; + venc->params.bitrate_min = BITRATE_MIN; + + memset(&f, 0, sizeof(f)); + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; + f.fmt.pix_mp.width = 1280; + f.fmt.pix_mp.height = 720; + f.fmt.pix_mp.field = V4L2_FIELD_NONE; + f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + venc_s_fmt(file, &inst->fh, &f); + + memset(&f, 0, sizeof(f)); + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; + f.fmt.pix_mp.width = 1280; + f.fmt.pix_mp.height = 720; + f.fmt.pix_mp.field = V4L2_FIELD_NONE; + venc_s_fmt(file, &inst->fh, &f); + + memset(&parm, 0, sizeof(parm)); + parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + parm.parm.capture.timeperframe.numerator = 1; + parm.parm.capture.timeperframe.denominator = 30; + venc_s_parm(file, &inst->fh, &parm); +} + +static int venc_open(struct file *file) +{ + struct vpu_inst *inst; + struct venc_t *venc; + int ret; + + inst = vzalloc(sizeof(*inst)); + if (!inst) + return -ENOMEM; + + venc = vzalloc(sizeof(*venc)); + if (!venc) { + vfree(inst); + return -ENOMEM; + } + + inst->ops = &venc_inst_ops; + inst->formats = venc_formats; + inst->type = VPU_CORE_TYPE_ENC; + inst->priv = venc; + INIT_LIST_HEAD(&venc->frames); + init_waitqueue_head(&venc->wq); + + ret = vpu_v4l2_open(file, inst); + if (ret) + return ret; + + venc_init(file); + + return 0; +} + +static const struct v4l2_file_operations venc_fops = { + .owner = THIS_MODULE, + .open = venc_open, + .release = vpu_v4l2_close, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void) +{ + return &venc_ioctl_ops; +} + +const struct v4l2_file_operations *venc_get_fops(void) +{ + return &venc_fops; +} diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h new file mode 100644 index 000000000000..e56b96a7e5d3 --- /dev/null +++ b/drivers/media/platform/amphion/vpu.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_H +#define _AMPHION_VPU_H + +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-mem2mem.h> +#include <linux/mailbox_client.h> +#include <linux/mailbox_controller.h> +#include <linux/kfifo.h> + +#define VPU_TIMEOUT msecs_to_jiffies(1000) +#define VPU_INST_NULL_ID (-1L) +#define VPU_MSG_BUFFER_SIZE (8192) + +enum imx_plat_type { + IMX8QXP = 0, + IMX8QM = 1, + IMX8DM, + IMX8DX, + PLAT_TYPE_RESERVED +}; + +enum vpu_core_type { + VPU_CORE_TYPE_ENC = 0, + VPU_CORE_TYPE_DEC = 0x10, +}; + +struct vpu_dev; +struct vpu_resources { + enum imx_plat_type plat_type; + u32 mreg_base; + int (*setup)(struct vpu_dev *vpu); + int (*setup_encoder)(struct vpu_dev *vpu); + int (*setup_decoder)(struct vpu_dev *vpu); + int (*reset)(struct vpu_dev *vpu); +}; + +struct vpu_buffer { + void *virt; + dma_addr_t phys; + u32 length; + u32 bytesused; + struct device *dev; +}; + +struct vpu_func { + struct video_device *vfd; + struct v4l2_m2m_dev *m2m_dev; + enum vpu_core_type type; + int function; +}; + +struct vpu_dev { + void __iomem *base; + struct platform_device *pdev; + struct device *dev; + struct mutex lock; /* protect vpu device */ + const struct vpu_resources *res; + struct list_head cores; + + struct v4l2_device v4l2_dev; + struct vpu_func encoder; + struct vpu_func decoder; + struct media_device mdev; + + struct delayed_work watchdog_work; + void (*get_vpu)(struct vpu_dev *vpu); + void (*put_vpu)(struct vpu_dev *vpu); + void (*get_enc)(struct vpu_dev *vpu); + void (*put_enc)(struct vpu_dev *vpu); + void (*get_dec)(struct vpu_dev *vpu); + void (*put_dec)(struct vpu_dev *vpu); + atomic_t ref_vpu; + atomic_t ref_enc; + atomic_t ref_dec; + + struct dentry *debugfs; +}; + +struct vpu_format { + u32 pixfmt; + unsigned int num_planes; + u32 type; + u32 flags; + u32 width; + u32 height; + u32 sizeimage[VIDEO_MAX_PLANES]; + u32 bytesperline[VIDEO_MAX_PLANES]; + u32 field; +}; + +struct vpu_core_resources { + enum vpu_core_type type; + const char *fwname; + u32 stride; + u32 max_width; + u32 min_width; + u32 step_width; + u32 max_height; + u32 min_height; + u32 step_height; + u32 rpc_size; + u32 fwlog_size; + u32 act_size; +}; + +struct vpu_mbox { + char name[20]; + struct mbox_client cl; + struct mbox_chan *ch; + bool block; +}; + +enum vpu_core_state { + VPU_CORE_DEINIT = 0, + VPU_CORE_ACTIVE, + VPU_CORE_SNAPSHOT, + VPU_CORE_HANG +}; + +struct vpu_core { + void __iomem *base; + struct platform_device *pdev; + struct device *dev; + struct device *parent; + struct device *pd; + struct device_link *pd_link; + struct mutex lock; /* protect vpu core */ + struct mutex cmd_lock; /* Lock vpu command */ + struct list_head list; + enum vpu_core_type type; + int id; + const struct vpu_core_resources *res; + unsigned long instance_mask; + u32 supported_instance_count; + unsigned long hang_mask; + u32 request_count; + struct list_head instances; + enum vpu_core_state state; + u32 fw_version; + + struct vpu_buffer fw; + struct vpu_buffer rpc; + struct vpu_buffer log; + struct vpu_buffer act; + + struct vpu_mbox tx_type; + struct vpu_mbox tx_data; + struct vpu_mbox rx; + unsigned long cmd_seq; + + wait_queue_head_t ack_wq; + struct completion cmp; + struct workqueue_struct *workqueue; + struct work_struct msg_work; + struct delayed_work msg_delayed_work; + struct kfifo msg_fifo; + void *msg_buffer; + unsigned int msg_buffer_size; + + struct vpu_dev *vpu; + void *iface; + + struct dentry *debugfs; + struct dentry *debugfs_fwlog; +}; + +enum vpu_codec_state { + VPU_CODEC_STATE_DEINIT = 1, + VPU_CODEC_STATE_CONFIGURED, + VPU_CODEC_STATE_START, + VPU_CODEC_STATE_STARTED, + VPU_CODEC_STATE_ACTIVE, + VPU_CODEC_STATE_SEEK, + VPU_CODEC_STATE_STOP, + VPU_CODEC_STATE_DRAIN, + VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, +}; + +struct vpu_frame_info { + u32 type; + u32 id; + u32 sequence; + u32 luma; + u32 chroma_u; + u32 chroma_v; + u32 data_offset; + u32 flags; + u32 skipped; + s64 timestamp; +}; + +struct vpu_inst; +struct vpu_inst_ops { + int (*ctrl_init)(struct vpu_inst *inst); + int (*start)(struct vpu_inst *inst, u32 type); + int (*stop)(struct vpu_inst *inst, u32 type); + int (*abort)(struct vpu_inst *inst); + bool (*check_ready)(struct vpu_inst *inst, unsigned int type); + void (*buf_done)(struct vpu_inst *inst, struct vpu_frame_info *frame); + void (*event_notify)(struct vpu_inst *inst, u32 event, void *data); + void (*release)(struct vpu_inst *inst); + void (*cleanup)(struct vpu_inst *inst); + void (*mem_request)(struct vpu_inst *inst, + u32 enc_frame_size, + u32 enc_frame_num, + u32 ref_frame_size, + u32 ref_frame_num, + u32 act_frame_size, + u32 act_frame_num); + void (*input_done)(struct vpu_inst *inst); + void (*stop_done)(struct vpu_inst *inst); + int (*process_output)(struct vpu_inst *inst, struct vb2_buffer *vb); + int (*process_capture)(struct vpu_inst *inst, struct vb2_buffer *vb); + int (*get_one_frame)(struct vpu_inst *inst, void *info); + void (*on_queue_empty)(struct vpu_inst *inst, u32 type); + int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i); + void (*wait_prepare)(struct vpu_inst *inst); + void (*wait_finish)(struct vpu_inst *inst); +}; + +struct vpu_inst { + struct list_head list; + struct mutex lock; /* v4l2 and videobuf2 lock */ + struct vpu_dev *vpu; + struct vpu_core *core; + struct device *dev; + int id; + + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + atomic_t ref_count; + int (*release)(struct vpu_inst *inst); + + enum vpu_codec_state state; + enum vpu_core_type type; + + struct workqueue_struct *workqueue; + struct work_struct msg_work; + struct kfifo msg_fifo; + u8 msg_buffer[VPU_MSG_BUFFER_SIZE]; + + struct vpu_buffer stream_buffer; + bool use_stream_buffer; + struct vpu_buffer act; + + struct list_head cmd_q; + void *pending; + + struct vpu_inst_ops *ops; + const struct vpu_format *formats; + struct vpu_format out_format; + struct vpu_format cap_format; + u32 min_buffer_cap; + u32 min_buffer_out; + + struct v4l2_rect crop; + u32 colorspace; + u8 ycbcr_enc; + u8 quantization; + u8 xfer_func; + u32 sequence; + u32 extra_size; + + u32 flows[16]; + u32 flow_idx; + + pid_t pid; + pid_t tgid; + struct dentry *debugfs; + + void *priv; +}; + +#define call_vop(inst, op, args...) \ + ((inst)->ops->op ? (inst)->ops->op(inst, ##args) : 0) \ + +#define call_void_vop(inst, op, args...) \ + do { \ + if ((inst)->ops->op) \ + (inst)->ops->op(inst, ##args); \ + } while (0) + +enum { + VPU_BUF_STATE_IDLE = 0, + VPU_BUF_STATE_INUSE, + VPU_BUF_STATE_DECODED, + VPU_BUF_STATE_READY, + VPU_BUF_STATE_SKIP, + VPU_BUF_STATE_ERROR +}; + +struct vpu_vb2_buffer { + struct v4l2_m2m_buffer m2m_buf; + dma_addr_t luma; + dma_addr_t chroma_u; + dma_addr_t chroma_v; + unsigned int state; + u32 tag; +}; + +void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val); +u32 vpu_readl(struct vpu_dev *vpu, u32 reg); + +static inline struct vpu_vb2_buffer *to_vpu_vb2_buffer(struct vb2_v4l2_buffer *vbuf) +{ + struct v4l2_m2m_buffer *m2m_buf = container_of(vbuf, struct v4l2_m2m_buffer, vb); + + return container_of(m2m_buf, struct vpu_vb2_buffer, m2m_buf); +} + +static inline const char *vpu_core_type_desc(enum vpu_core_type type) +{ + return type == VPU_CORE_TYPE_ENC ? "encoder" : "decoder"; +} + +static inline struct vpu_inst *to_inst(struct file *filp) +{ + return container_of(filp->private_data, struct vpu_inst, fh); +} + +#define ctrl_to_inst(ctrl) \ + container_of((ctrl)->handler, struct vpu_inst, ctrl_handler) + +const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void); +const struct v4l2_file_operations *venc_get_fops(void); +const struct v4l2_ioctl_ops *vdec_get_ioctl_ops(void); +const struct v4l2_file_operations *vdec_get_fops(void); + +int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func); +void vpu_remove_func(struct vpu_func *func); + +struct vpu_inst *vpu_inst_get(struct vpu_inst *inst); +void vpu_inst_put(struct vpu_inst *inst); +struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type); +void vpu_release_core(struct vpu_core *core); +int vpu_inst_register(struct vpu_inst *inst); +int vpu_inst_unregister(struct vpu_inst *inst); +const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst); + +int vpu_inst_create_dbgfs_file(struct vpu_inst *inst); +int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst); +int vpu_core_create_dbgfs_file(struct vpu_core *core); +int vpu_core_remove_dbgfs_file(struct vpu_core *core); +void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow); + +int vpu_core_driver_init(void); +void vpu_core_driver_exit(void); + +extern bool debug; +#define vpu_trace(dev, fmt, arg...) \ + do { \ + if (debug) \ + dev_info(dev, "%s: " fmt, __func__, ## arg); \ + } while (0) + +#endif diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c new file mode 100644 index 000000000000..9b39d77a178d --- /dev/null +++ b/drivers/media/platform/amphion/vpu_cmds.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include "vpu.h" +#include "vpu_defs.h" +#include "vpu_cmds.h" +#include "vpu_rpc.h" +#include "vpu_mbox.h" + +struct vpu_cmd_request { + u32 request; + u32 response; + u32 handled; +}; + +struct vpu_cmd_t { + struct list_head list; + u32 id; + struct vpu_cmd_request *request; + struct vpu_rpc_event *pkt; + unsigned long key; +}; + +static struct vpu_cmd_request vpu_cmd_requests[] = { + { + .request = VPU_CMD_ID_CONFIGURE_CODEC, + .response = VPU_MSG_ID_MEM_REQUEST, + .handled = 1, + }, + { + .request = VPU_CMD_ID_START, + .response = VPU_MSG_ID_START_DONE, + .handled = 0, + }, + { + .request = VPU_CMD_ID_STOP, + .response = VPU_MSG_ID_STOP_DONE, + .handled = 0, + }, + { + .request = VPU_CMD_ID_ABORT, + .response = VPU_MSG_ID_ABORT_DONE, + .handled = 0, + }, + { + .request = VPU_CMD_ID_RST_BUF, + .response = VPU_MSG_ID_BUF_RST, + .handled = 1, + }, +}; + +static int vpu_cmd_send(struct vpu_core *core, struct vpu_rpc_event *pkt) +{ + int ret = 0; + + ret = vpu_iface_send_cmd(core, pkt); + if (ret) + return ret; + + /*write cmd data to cmd buffer before trigger a cmd interrupt*/ + mb(); + vpu_mbox_send_type(core, COMMAND); + + return ret; +} + +static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data) +{ + struct vpu_cmd_t *cmd; + int i; + int ret; + + cmd = vzalloc(sizeof(*cmd)); + if (!cmd) + return NULL; + + cmd->pkt = vzalloc(sizeof(*cmd->pkt)); + if (!cmd->pkt) { + vfree(cmd); + return NULL; + } + + cmd->id = id; + ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data); + if (ret) { + dev_err(inst->dev, "iface pack cmd(%d) fail\n", id); + vfree(cmd->pkt); + vfree(cmd); + return NULL; + } + for (i = 0; i < ARRAY_SIZE(vpu_cmd_requests); i++) { + if (vpu_cmd_requests[i].request == id) { + cmd->request = &vpu_cmd_requests[i]; + break; + } + } + + return cmd; +} + +static void vpu_free_cmd(struct vpu_cmd_t *cmd) +{ + if (!cmd) + return; + if (cmd->pkt) + vfree(cmd->pkt); + vfree(cmd); +} + +static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd) +{ + int ret; + + dev_dbg(inst->dev, "[%d]send cmd(0x%x)\n", inst->id, cmd->id); + vpu_iface_pre_send_cmd(inst); + ret = vpu_cmd_send(inst->core, cmd->pkt); + if (!ret) { + vpu_iface_post_send_cmd(inst); + vpu_inst_record_flow(inst, cmd->id); + } else { + dev_err(inst->dev, "[%d] iface send cmd(0x%x) fail\n", inst->id, cmd->id); + } + + return ret; +} + +static void vpu_process_cmd_request(struct vpu_inst *inst) +{ + struct vpu_cmd_t *cmd; + struct vpu_cmd_t *tmp; + + if (!inst || inst->pending) + return; + + list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) { + list_del_init(&cmd->list); + if (vpu_session_process_cmd(inst, cmd)) + dev_err(inst->dev, "[%d] process cmd(%d) fail\n", inst->id, cmd->id); + if (cmd->request) { + inst->pending = (void *)cmd; + break; + } + vpu_free_cmd(cmd); + } +} + +static int vpu_request_cmd(struct vpu_inst *inst, u32 id, void *data, + unsigned long *key, int *sync) +{ + struct vpu_core *core; + struct vpu_cmd_t *cmd; + + if (!inst || !inst->core) + return -EINVAL; + + core = inst->core; + cmd = vpu_alloc_cmd(inst, id, data); + if (!cmd) + return -ENOMEM; + + mutex_lock(&core->cmd_lock); + cmd->key = core->cmd_seq++; + if (key) + *key = cmd->key; + if (sync) + *sync = cmd->request ? true : false; + list_add_tail(&cmd->list, &inst->cmd_q); + vpu_process_cmd_request(inst); + mutex_unlock(&core->cmd_lock); + + return 0; +} + +static void vpu_clear_pending(struct vpu_inst *inst) +{ + if (!inst || !inst->pending) + return; + + vpu_free_cmd(inst->pending); + wake_up_all(&inst->core->ack_wq); + inst->pending = NULL; +} + +static bool vpu_check_response(struct vpu_cmd_t *cmd, u32 response, u32 handled) +{ + struct vpu_cmd_request *request; + + if (!cmd || !cmd->request) + return false; + + request = cmd->request; + if (request->response != response) + return false; + if (request->handled != handled) + return false; + + return true; +} + +int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled) +{ + struct vpu_core *core; + + if (!inst || !inst->core) + return -EINVAL; + + core = inst->core; + mutex_lock(&core->cmd_lock); + if (vpu_check_response(inst->pending, response, handled)) + vpu_clear_pending(inst); + + vpu_process_cmd_request(inst); + mutex_unlock(&core->cmd_lock); + + return 0; +} + +void vpu_clear_request(struct vpu_inst *inst) +{ + struct vpu_cmd_t *cmd; + struct vpu_cmd_t *tmp; + + mutex_lock(&inst->core->cmd_lock); + if (inst->pending) + vpu_clear_pending(inst); + + list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) { + list_del_init(&cmd->list); + vpu_free_cmd(cmd); + } + mutex_unlock(&inst->core->cmd_lock); +} + +static bool check_is_responsed(struct vpu_inst *inst, unsigned long key) +{ + struct vpu_core *core = inst->core; + struct vpu_cmd_t *cmd; + bool flag = true; + + mutex_lock(&core->cmd_lock); + cmd = inst->pending; + if (cmd && key == cmd->key) { + flag = false; + goto exit; + } + list_for_each_entry(cmd, &inst->cmd_q, list) { + if (key == cmd->key) { + flag = false; + break; + } + } +exit: + mutex_unlock(&core->cmd_lock); + + return flag; +} + +static int sync_session_response(struct vpu_inst *inst, unsigned long key) +{ + struct vpu_core *core; + + if (!inst || !inst->core) + return -EINVAL; + + core = inst->core; + + call_void_vop(inst, wait_prepare); + wait_event_timeout(core->ack_wq, check_is_responsed(inst, key), VPU_TIMEOUT); + call_void_vop(inst, wait_finish); + + if (!check_is_responsed(inst, key)) { + dev_err(inst->dev, "[%d] sync session timeout\n", inst->id); + set_bit(inst->id, &core->hang_mask); + mutex_lock(&inst->core->cmd_lock); + vpu_clear_pending(inst); + mutex_unlock(&inst->core->cmd_lock); + return -EINVAL; + } + + return 0; +} + +static int vpu_session_send_cmd(struct vpu_inst *inst, u32 id, void *data) +{ + unsigned long key; + int sync = false; + int ret = -EINVAL; + + if (inst->id < 0) + return -EINVAL; + + ret = vpu_request_cmd(inst, id, data, &key, &sync); + if (!ret && sync) + ret = sync_session_response(inst, key); + + if (ret) + dev_err(inst->dev, "[%d] send cmd(0x%x) fail\n", inst->id, id); + + return ret; +} + +int vpu_session_configure_codec(struct vpu_inst *inst) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_CONFIGURE_CODEC, NULL); +} + +int vpu_session_start(struct vpu_inst *inst) +{ + vpu_trace(inst->dev, "[%d]\n", inst->id); + + return vpu_session_send_cmd(inst, VPU_CMD_ID_START, NULL); +} + +int vpu_session_stop(struct vpu_inst *inst) +{ + int ret; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + + ret = vpu_session_send_cmd(inst, VPU_CMD_ID_STOP, NULL); + /* workaround for a firmware bug, + * if the next command is too close after stop cmd, + * the firmware may enter wfi wrongly. + */ + usleep_range(3000, 5000); + return ret; +} + +int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_FRAME_ENCODE, ×tamp); +} + +int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_ALLOC, fs); +} + +int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_RELEASE, fs); +} + +int vpu_session_abort(struct vpu_inst *inst) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_ABORT, NULL); +} + +int vpu_session_rst_buf(struct vpu_inst *inst) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_RST_BUF, NULL); +} + +int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_TIMESTAMP, info); +} + +int vpu_session_update_parameters(struct vpu_inst *inst, void *arg) +{ + if (inst->type & VPU_CORE_TYPE_DEC) + vpu_iface_set_decode_params(inst, arg, 1); + else + vpu_iface_set_encode_params(inst, arg, 1); + + return vpu_session_send_cmd(inst, VPU_CMD_ID_UPDATE_PARAMETER, arg); +} + +int vpu_session_debug(struct vpu_inst *inst) +{ + return vpu_session_send_cmd(inst, VPU_CMD_ID_DEBUG, NULL); +} + +int vpu_core_snapshot(struct vpu_core *core) +{ + struct vpu_inst *inst; + int ret; + + if (!core || list_empty(&core->instances)) + return 0; + + inst = list_first_entry(&core->instances, struct vpu_inst, list); + + reinit_completion(&core->cmp); + ret = vpu_session_send_cmd(inst, VPU_CMD_ID_SNAPSHOT, NULL); + if (ret) + return ret; + ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT); + if (!ret) { + dev_err(core->dev, "snapshot timeout\n"); + return -EINVAL; + } + + return 0; +} + +int vpu_core_sw_reset(struct vpu_core *core) +{ + struct vpu_rpc_event pkt; + int ret; + + memset(&pkt, 0, sizeof(pkt)); + vpu_iface_pack_cmd(core, &pkt, 0, VPU_CMD_ID_FIRM_RESET, NULL); + + reinit_completion(&core->cmp); + mutex_lock(&core->cmd_lock); + ret = vpu_cmd_send(core, &pkt); + mutex_unlock(&core->cmd_lock); + if (ret) + return ret; + ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT); + if (!ret) { + dev_err(core->dev, "sw reset timeout\n"); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/platform/amphion/vpu_cmds.h b/drivers/media/platform/amphion/vpu_cmds.h new file mode 100644 index 000000000000..bc538d277bc9 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_cmds.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_CMDS_H +#define _AMPHION_VPU_CMDS_H + +int vpu_session_configure_codec(struct vpu_inst *inst); +int vpu_session_start(struct vpu_inst *inst); +int vpu_session_stop(struct vpu_inst *inst); +int vpu_session_abort(struct vpu_inst *inst); +int vpu_session_rst_buf(struct vpu_inst *inst); +int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp); +int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs); +int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs); +int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info); +int vpu_session_update_parameters(struct vpu_inst *inst, void *arg); +int vpu_core_snapshot(struct vpu_core *core); +int vpu_core_sw_reset(struct vpu_core *core); +int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled); +void vpu_clear_request(struct vpu_inst *inst); +int vpu_session_debug(struct vpu_inst *inst); + +#endif diff --git a/drivers/media/platform/amphion/vpu_codec.h b/drivers/media/platform/amphion/vpu_codec.h new file mode 100644 index 000000000000..528a93f08ecd --- /dev/null +++ b/drivers/media/platform/amphion/vpu_codec.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_CODEC_H +#define _AMPHION_VPU_CODEC_H + +struct vpu_encode_params { + u32 input_format; + u32 codec_format; + u32 profile; + u32 tier; + u32 level; + struct v4l2_fract frame_rate; + u32 src_stride; + u32 src_width; + u32 src_height; + struct v4l2_rect crop; + u32 out_width; + u32 out_height; + + u32 gop_length; + u32 bframes; + + u32 rc_enable; + u32 rc_mode; + u32 bitrate; + u32 bitrate_min; + u32 bitrate_max; + + u32 i_frame_qp; + u32 p_frame_qp; + u32 b_frame_qp; + u32 qp_min; + u32 qp_max; + u32 qp_min_i; + u32 qp_max_i; + + struct { + u32 enable; + u32 idc; + u32 width; + u32 height; + } sar; + + struct { + u32 primaries; + u32 transfer; + u32 matrix; + u32 full_range; + } color; +}; + +struct vpu_decode_params { + u32 codec_format; + u32 output_format; + u32 b_dis_reorder; + u32 b_non_frame; + u32 frame_count; + u32 end_flag; + struct { + u32 base; + u32 size; + } udata; +}; + +#endif diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c new file mode 100644 index 000000000000..80b9a53fd1c1 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_color.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <media/v4l2-device.h> +#include "vpu.h" +#include "vpu_helpers.h" + +static const u8 colorprimaries[] = { + 0, + V4L2_COLORSPACE_REC709, /*Rec. ITU-R BT.709-6*/ + 0, + 0, + V4L2_COLORSPACE_470_SYSTEM_M, /*Rec. ITU-R BT.470-6 System M*/ + V4L2_COLORSPACE_470_SYSTEM_BG, /*Rec. ITU-R BT.470-6 System B, G*/ + V4L2_COLORSPACE_SMPTE170M, /*SMPTE170M*/ + V4L2_COLORSPACE_SMPTE240M, /*SMPTE240M*/ + 0, /*Generic film*/ + V4L2_COLORSPACE_BT2020, /*Rec. ITU-R BT.2020-2*/ + 0, /*SMPTE ST 428-1*/ +}; + +static const u8 colortransfers[] = { + 0, + V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.709-6*/ + 0, + 0, + 0, /*Rec. ITU-R BT.470-6 System M*/ + 0, /*Rec. ITU-R BT.470-6 System B, G*/ + V4L2_XFER_FUNC_709, /*SMPTE170M*/ + V4L2_XFER_FUNC_SMPTE240M, /*SMPTE240M*/ + V4L2_XFER_FUNC_NONE, /*Linear transfer characteristics*/ + 0, + 0, + 0, /*IEC 61966-2-4*/ + 0, /*Rec. ITU-R BT.1361-0 extended colour gamut*/ + V4L2_XFER_FUNC_SRGB, /*IEC 61966-2-1 sRGB or sYCC*/ + V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (10 bit system)*/ + V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (12 bit system)*/ + V4L2_XFER_FUNC_SMPTE2084, /*SMPTE ST 2084*/ + 0, /*SMPTE ST 428-1*/ + 0 /*Rec. ITU-R BT.2100-0 hybrid log-gamma (HLG)*/ +}; + +static const u8 colormatrixcoefs[] = { + 0, + V4L2_YCBCR_ENC_709, /*Rec. ITU-R BT.709-6*/ + 0, + 0, + 0, /*Title 47 Code of Federal Regulations*/ + V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 625*/ + V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 525*/ + V4L2_YCBCR_ENC_SMPTE240M, /*SMPTE240M*/ + 0, + V4L2_YCBCR_ENC_BT2020, /*Rec. ITU-R BT.2020-2*/ + V4L2_YCBCR_ENC_BT2020_CONST_LUM /*Rec. ITU-R BT.2020-2 constant*/ +}; + +u32 vpu_color_cvrt_primaries_v2i(u32 primaries) +{ + return vpu_helper_find_in_array_u8(colorprimaries, ARRAY_SIZE(colorprimaries), primaries); +} + +u32 vpu_color_cvrt_primaries_i2v(u32 primaries) +{ + return primaries < ARRAY_SIZE(colorprimaries) ? colorprimaries[primaries] : 0; +} + +u32 vpu_color_cvrt_transfers_v2i(u32 transfers) +{ + return vpu_helper_find_in_array_u8(colortransfers, ARRAY_SIZE(colortransfers), transfers); +} + +u32 vpu_color_cvrt_transfers_i2v(u32 transfers) +{ + return transfers < ARRAY_SIZE(colortransfers) ? colortransfers[transfers] : 0; +} + +u32 vpu_color_cvrt_matrix_v2i(u32 matrix) +{ + return vpu_helper_find_in_array_u8(colormatrixcoefs, ARRAY_SIZE(colormatrixcoefs), matrix); +} + +u32 vpu_color_cvrt_matrix_i2v(u32 matrix) +{ + return matrix < ARRAY_SIZE(colormatrixcoefs) ? colormatrixcoefs[matrix] : 0; +} + +u32 vpu_color_cvrt_full_range_v2i(u32 full_range) +{ + return (full_range == V4L2_QUANTIZATION_FULL_RANGE); +} + +u32 vpu_color_cvrt_full_range_i2v(u32 full_range) +{ + if (full_range) + return V4L2_QUANTIZATION_FULL_RANGE; + + return V4L2_QUANTIZATION_LIM_RANGE; +} + +int vpu_color_check_primaries(u32 primaries) +{ + return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL; +} + +int vpu_color_check_transfers(u32 transfers) +{ + return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL; +} + +int vpu_color_check_matrix(u32 matrix) +{ + return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL; +} + +int vpu_color_check_full_range(u32 full_range) +{ + int ret = -EINVAL; + + switch (full_range) { + case V4L2_QUANTIZATION_FULL_RANGE: + case V4L2_QUANTIZATION_LIM_RANGE: + ret = 0; + break; + default: + break; + } + + return ret; +} + +int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range) +{ + u32 transfers; + u32 matrix; + u32 full_range; + + switch (primaries) { + case V4L2_COLORSPACE_REC709: + transfers = V4L2_XFER_FUNC_709; + matrix = V4L2_YCBCR_ENC_709; + break; + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + case V4L2_COLORSPACE_SMPTE170M: + transfers = V4L2_XFER_FUNC_709; + matrix = V4L2_YCBCR_ENC_601; + break; + case V4L2_COLORSPACE_SMPTE240M: + transfers = V4L2_XFER_FUNC_SMPTE240M; + matrix = V4L2_YCBCR_ENC_SMPTE240M; + break; + case V4L2_COLORSPACE_BT2020: + transfers = V4L2_XFER_FUNC_709; + matrix = V4L2_YCBCR_ENC_BT2020; + break; + default: + transfers = V4L2_XFER_FUNC_DEFAULT; + matrix = V4L2_YCBCR_ENC_DEFAULT; + break; + } + full_range = V4L2_QUANTIZATION_LIM_RANGE; + + if (ptransfers) + *ptransfers = transfers; + if (pmatrix) + *pmatrix = matrix; + if (pfull_range) + *pfull_range = full_range; + + return 0; +} diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c new file mode 100644 index 000000000000..68ad183925fd --- /dev/null +++ b/drivers/media/platform/amphion/vpu_core.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/pm_runtime.h> +#include <linux/pm_domain.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> +#include "vpu.h" +#include "vpu_defs.h" +#include "vpu_core.h" +#include "vpu_mbox.h" +#include "vpu_msgs.h" +#include "vpu_rpc.h" +#include "vpu_cmds.h" + +void csr_writel(struct vpu_core *core, u32 reg, u32 val) +{ + writel(val, core->base + reg); +} + +u32 csr_readl(struct vpu_core *core, u32 reg) +{ + return readl(core->base + reg); +} + +static int vpu_core_load_firmware(struct vpu_core *core) +{ + const struct firmware *pfw = NULL; + int ret = 0; + + if (!core->fw.virt) { + dev_err(core->dev, "firmware buffer is not ready\n"); + return -EINVAL; + } + + ret = request_firmware(&pfw, core->res->fwname, core->dev); + dev_dbg(core->dev, "request_firmware %s : %d\n", core->res->fwname, ret); + if (ret) { + dev_err(core->dev, "request firmware %s failed, ret = %d\n", + core->res->fwname, ret); + return ret; + } + + if (core->fw.length < pfw->size) { + dev_err(core->dev, "firmware buffer size want %zu, but %d\n", + pfw->size, core->fw.length); + ret = -EINVAL; + goto exit; + } + + memset(core->fw.virt, 0, core->fw.length); + memcpy(core->fw.virt, pfw->data, pfw->size); + core->fw.bytesused = pfw->size; + ret = vpu_iface_on_firmware_loaded(core); +exit: + release_firmware(pfw); + pfw = NULL; + + return ret; +} + +static int vpu_core_boot_done(struct vpu_core *core) +{ + u32 fw_version; + + fw_version = vpu_iface_get_version(core); + dev_info(core->dev, "%s firmware version : %d.%d.%d\n", + vpu_core_type_desc(core->type), + (fw_version >> 16) & 0xff, + (fw_version >> 8) & 0xff, + fw_version & 0xff); + core->supported_instance_count = vpu_iface_get_max_instance_count(core); + if (core->res->act_size) { + u32 count = core->act.length / core->res->act_size; + + core->supported_instance_count = min(core->supported_instance_count, count); + } + core->fw_version = fw_version; + core->state = VPU_CORE_ACTIVE; + + return 0; +} + +static int vpu_core_wait_boot_done(struct vpu_core *core) +{ + int ret; + + ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT); + if (!ret) { + dev_err(core->dev, "boot timeout\n"); + return -EINVAL; + } + return vpu_core_boot_done(core); +} + +static int vpu_core_boot(struct vpu_core *core, bool load) +{ + int ret; + + reinit_completion(&core->cmp); + if (load) { + ret = vpu_core_load_firmware(core); + if (ret) + return ret; + } + + vpu_iface_boot_core(core); + return vpu_core_wait_boot_done(core); +} + +static int vpu_core_shutdown(struct vpu_core *core) +{ + return vpu_iface_shutdown_core(core); +} + +static int vpu_core_restore(struct vpu_core *core) +{ + int ret; + + ret = vpu_core_sw_reset(core); + if (ret) + return ret; + + vpu_core_boot_done(core); + return vpu_iface_restore_core(core); +} + +static int __vpu_alloc_dma(struct device *dev, struct vpu_buffer *buf) +{ + gfp_t gfp = GFP_KERNEL | GFP_DMA32; + + if (!buf->length) + return 0; + + buf->virt = dma_alloc_coherent(dev, buf->length, &buf->phys, gfp); + if (!buf->virt) + return -ENOMEM; + + buf->dev = dev; + + return 0; +} + +void vpu_free_dma(struct vpu_buffer *buf) +{ + if (!buf->virt || !buf->dev) + return; + + dma_free_coherent(buf->dev, buf->length, buf->virt, buf->phys); + buf->virt = NULL; + buf->phys = 0; + buf->length = 0; + buf->bytesused = 0; + buf->dev = NULL; +} + +int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf) +{ + return __vpu_alloc_dma(core->dev, buf); +} + +static void vpu_core_check_hang(struct vpu_core *core) +{ + if (core->hang_mask) + core->state = VPU_CORE_HANG; +} + +static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 type) +{ + struct vpu_core *core = NULL; + int request_count = INT_MAX; + struct vpu_core *c; + + list_for_each_entry(c, &vpu->cores, list) { + dev_dbg(c->dev, "instance_mask = 0x%lx, state = %d\n", c->instance_mask, c->state); + if (c->type != type) + continue; + if (c->state == VPU_CORE_DEINIT) { + core = c; + break; + } + vpu_core_check_hang(c); + if (c->state != VPU_CORE_ACTIVE) + continue; + if (c->request_count < request_count) { + request_count = c->request_count; + core = c; + } + if (!request_count) + break; + } + + return core; +} + +static bool vpu_core_is_exist(struct vpu_dev *vpu, struct vpu_core *core) +{ + struct vpu_core *c; + + list_for_each_entry(c, &vpu->cores, list) { + if (c == core) + return true; + } + + return false; +} + +static void vpu_core_get_vpu(struct vpu_core *core) +{ + core->vpu->get_vpu(core->vpu); + if (core->type == VPU_CORE_TYPE_ENC) + core->vpu->get_enc(core->vpu); + if (core->type == VPU_CORE_TYPE_DEC) + core->vpu->get_dec(core->vpu); +} + +static int vpu_core_register(struct device *dev, struct vpu_core *core) +{ + struct vpu_dev *vpu = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type)); + if (vpu_core_is_exist(vpu, core)) + return 0; + + core->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!core->workqueue) { + dev_err(core->dev, "fail to alloc workqueue\n"); + return -ENOMEM; + } + INIT_WORK(&core->msg_work, vpu_msg_run_work); + INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work); + core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE); + core->msg_buffer = vzalloc(core->msg_buffer_size); + if (!core->msg_buffer) { + dev_err(core->dev, "failed allocate buffer for fifo\n"); + ret = -ENOMEM; + goto error; + } + ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size); + if (ret) { + dev_err(core->dev, "failed init kfifo\n"); + goto error; + } + + list_add_tail(&core->list, &vpu->cores); + + vpu_core_get_vpu(core); + + if (vpu_iface_get_power_state(core)) + ret = vpu_core_restore(core); + if (ret) + goto error; + + return 0; +error: + if (core->msg_buffer) { + vfree(core->msg_buffer); + core->msg_buffer = NULL; + } + if (core->workqueue) { + destroy_workqueue(core->workqueue); + core->workqueue = NULL; + } + return ret; +} + +static void vpu_core_put_vpu(struct vpu_core *core) +{ + if (core->type == VPU_CORE_TYPE_ENC) + core->vpu->put_enc(core->vpu); + if (core->type == VPU_CORE_TYPE_DEC) + core->vpu->put_dec(core->vpu); + core->vpu->put_vpu(core->vpu); +} + +static int vpu_core_unregister(struct device *dev, struct vpu_core *core) +{ + list_del_init(&core->list); + + vpu_core_put_vpu(core); + core->vpu = NULL; + vfree(core->msg_buffer); + core->msg_buffer = NULL; + + if (core->workqueue) { + cancel_work_sync(&core->msg_work); + cancel_delayed_work_sync(&core->msg_delayed_work); + destroy_workqueue(core->workqueue); + core->workqueue = NULL; + } + + return 0; +} + +static int vpu_core_acquire_instance(struct vpu_core *core) +{ + int id; + + id = ffz(core->instance_mask); + if (id >= core->supported_instance_count) + return -EINVAL; + + set_bit(id, &core->instance_mask); + + return id; +} + +static void vpu_core_release_instance(struct vpu_core *core, int id) +{ + if (id < 0 || id >= core->supported_instance_count) + return; + + clear_bit(id, &core->instance_mask); +} + +struct vpu_inst *vpu_inst_get(struct vpu_inst *inst) +{ + if (!inst) + return NULL; + + atomic_inc(&inst->ref_count); + + return inst; +} + +void vpu_inst_put(struct vpu_inst *inst) +{ + if (!inst) + return; + if (atomic_dec_and_test(&inst->ref_count)) { + if (inst->release) + inst->release(inst); + } +} + +struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type) +{ + struct vpu_core *core = NULL; + int ret; + + mutex_lock(&vpu->lock); + + core = vpu_core_find_proper_by_type(vpu, type); + if (!core) + goto exit; + + mutex_lock(&core->lock); + pm_runtime_resume_and_get(core->dev); + + if (core->state == VPU_CORE_DEINIT) { + ret = vpu_core_boot(core, true); + if (ret) { + pm_runtime_put_sync(core->dev); + mutex_unlock(&core->lock); + core = NULL; + goto exit; + } + } + + core->request_count++; + + mutex_unlock(&core->lock); +exit: + mutex_unlock(&vpu->lock); + + return core; +} + +void vpu_release_core(struct vpu_core *core) +{ + if (!core) + return; + + mutex_lock(&core->lock); + pm_runtime_put_sync(core->dev); + if (core->request_count) + core->request_count--; + mutex_unlock(&core->lock); +} + +int vpu_inst_register(struct vpu_inst *inst) +{ + struct vpu_dev *vpu; + struct vpu_core *core; + int ret = 0; + + vpu = inst->vpu; + core = inst->core; + if (!core) { + core = vpu_request_core(vpu, inst->type); + if (!core) { + dev_err(vpu->dev, "there is no vpu core for %s\n", + vpu_core_type_desc(inst->type)); + return -EINVAL; + } + inst->core = core; + inst->dev = get_device(core->dev); + } + + mutex_lock(&core->lock); + if (inst->id >= 0 && inst->id < core->supported_instance_count) + goto exit; + + ret = vpu_core_acquire_instance(core); + if (ret < 0) + goto exit; + + vpu_trace(inst->dev, "[%d] %p\n", ret, inst); + inst->id = ret; + list_add_tail(&inst->list, &core->instances); + ret = 0; + if (core->res->act_size) { + inst->act.phys = core->act.phys + core->res->act_size * inst->id; + inst->act.virt = core->act.virt + core->res->act_size * inst->id; + inst->act.length = core->res->act_size; + } + vpu_inst_create_dbgfs_file(inst); +exit: + mutex_unlock(&core->lock); + + if (ret) + dev_err(core->dev, "register instance fail\n"); + return ret; +} + +int vpu_inst_unregister(struct vpu_inst *inst) +{ + struct vpu_core *core; + + if (!inst->core) + return 0; + + core = inst->core; + vpu_clear_request(inst); + mutex_lock(&core->lock); + if (inst->id >= 0 && inst->id < core->supported_instance_count) { + vpu_inst_remove_dbgfs_file(inst); + list_del_init(&inst->list); + vpu_core_release_instance(core, inst->id); + inst->id = VPU_INST_NULL_ID; + } + vpu_core_check_hang(core); + if (core->state == VPU_CORE_HANG && !core->instance_mask) { + dev_info(core->dev, "reset hang core\n"); + if (!vpu_core_sw_reset(core)) { + core->state = VPU_CORE_ACTIVE; + core->hang_mask = 0; + } + } + mutex_unlock(&core->lock); + + return 0; +} + +struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index) +{ + struct vpu_inst *inst = NULL; + struct vpu_inst *tmp; + + mutex_lock(&core->lock); + if (index >= core->supported_instance_count || !test_bit(index, &core->instance_mask)) + goto exit; + list_for_each_entry(tmp, &core->instances, list) { + if (tmp->id == index) { + inst = vpu_inst_get(tmp); + break; + } + } +exit: + mutex_unlock(&core->lock); + + return inst; +} + +const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst) +{ + struct vpu_dev *vpu; + struct vpu_core *core = NULL; + const struct vpu_core_resources *res = NULL; + + if (!inst || !inst->vpu) + return NULL; + + if (inst->core && inst->core->res) + return inst->core->res; + + vpu = inst->vpu; + mutex_lock(&vpu->lock); + list_for_each_entry(core, &vpu->cores, list) { + if (core->type == inst->type) { + res = core->res; + break; + } + } + mutex_unlock(&vpu->lock); + + return res; +} + +static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np) +{ + struct device_node *node; + struct resource res; + int ret; + + if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) { + dev_err(core->dev, "need 2 memory-region for boot and rpc\n"); + return -ENODEV; + } + + node = of_parse_phandle(np, "memory-region", 0); + if (!node) { + dev_err(core->dev, "boot-region of_parse_phandle error\n"); + return -ENODEV; + } + if (of_address_to_resource(node, 0, &res)) { + dev_err(core->dev, "boot-region of_address_to_resource error\n"); + of_node_put(node); + return -EINVAL; + } + core->fw.phys = res.start; + core->fw.length = resource_size(&res); + + of_node_put(node); + + node = of_parse_phandle(np, "memory-region", 1); + if (!node) { + dev_err(core->dev, "rpc-region of_parse_phandle error\n"); + return -ENODEV; + } + if (of_address_to_resource(node, 0, &res)) { + dev_err(core->dev, "rpc-region of_address_to_resource error\n"); + of_node_put(node); + return -EINVAL; + } + core->rpc.phys = res.start; + core->rpc.length = resource_size(&res); + + if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) { + dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n", + &core->rpc.phys, core->rpc.length); + of_node_put(node); + return -EINVAL; + } + + core->fw.virt = memremap(core->fw.phys, core->fw.length, MEMREMAP_WC); + core->rpc.virt = memremap(core->rpc.phys, core->rpc.length, MEMREMAP_WC); + memset(core->rpc.virt, 0, core->rpc.length); + + ret = vpu_iface_check_memory_region(core, core->rpc.phys, core->rpc.length); + if (ret != VPU_CORE_MEMORY_UNCACHED) { + dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n", + &core->rpc.phys, core->rpc.length); + of_node_put(node); + return -EINVAL; + } + + core->log.phys = core->rpc.phys + core->res->rpc_size; + core->log.virt = core->rpc.virt + core->res->rpc_size; + core->log.length = core->res->fwlog_size; + core->act.phys = core->log.phys + core->log.length; + core->act.virt = core->log.virt + core->log.length; + core->act.length = core->rpc.length - core->res->rpc_size - core->log.length; + core->rpc.length = core->res->rpc_size; + + of_node_put(node); + + return 0; +} + +static int vpu_core_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vpu_core *core; + struct vpu_dev *vpu = dev_get_drvdata(dev->parent); + struct vpu_shared_addr *iface; + u32 iface_data_size; + int ret; + + dev_dbg(dev, "probe\n"); + if (!vpu) + return -EINVAL; + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->pdev = pdev; + core->dev = dev; + platform_set_drvdata(pdev, core); + core->vpu = vpu; + INIT_LIST_HEAD(&core->instances); + mutex_init(&core->lock); + mutex_init(&core->cmd_lock); + init_completion(&core->cmp); + init_waitqueue_head(&core->ack_wq); + core->state = VPU_CORE_DEINIT; + + core->res = of_device_get_match_data(dev); + if (!core->res) + return -ENODEV; + + core->type = core->res->type; + core->id = of_alias_get_id(dev->of_node, "vpu_core"); + if (core->id < 0) { + dev_err(dev, "can't get vpu core id\n"); + return core->id; + } + dev_info(core->dev, "[%d] = %s\n", core->id, vpu_core_type_desc(core->type)); + ret = vpu_core_parse_dt(core, dev->of_node); + if (ret) + return ret; + + core->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + if (!vpu_iface_check_codec(core)) { + dev_err(core->dev, "is not supported\n"); + return -EINVAL; + } + + ret = vpu_mbox_init(core); + if (ret) + return ret; + + iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); + if (!iface) + return -ENOMEM; + + iface_data_size = vpu_iface_get_data_size(core); + if (iface_data_size) { + iface->priv = devm_kzalloc(dev, iface_data_size, GFP_KERNEL); + if (!iface->priv) + return -ENOMEM; + } + + ret = vpu_iface_init(core, iface, &core->rpc, core->fw.phys); + if (ret) { + dev_err(core->dev, "init iface fail, ret = %d\n", ret); + return ret; + } + + vpu_iface_config_system(core, vpu->res->mreg_base, vpu->base); + vpu_iface_set_log_buf(core, &core->log); + + pm_runtime_enable(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) { + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + goto err_runtime_disable; + } + + ret = vpu_core_register(dev->parent, core); + if (ret) + goto err_core_register; + core->parent = dev->parent; + + pm_runtime_put_sync(dev); + vpu_core_create_dbgfs_file(core); + + return 0; + +err_core_register: + pm_runtime_put_sync(dev); +err_runtime_disable: + pm_runtime_disable(dev); + + return ret; +} + +static int vpu_core_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vpu_core *core = platform_get_drvdata(pdev); + int ret; + + vpu_core_remove_dbgfs_file(core); + ret = pm_runtime_resume_and_get(dev); + WARN_ON(ret < 0); + + vpu_core_shutdown(core); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + vpu_core_unregister(core->parent, core); + memunmap(core->fw.virt); + memunmap(core->rpc.virt); + mutex_destroy(&core->lock); + mutex_destroy(&core->cmd_lock); + + return 0; +} + +static int __maybe_unused vpu_core_runtime_resume(struct device *dev) +{ + struct vpu_core *core = dev_get_drvdata(dev); + + return vpu_mbox_request(core); +} + +static int __maybe_unused vpu_core_runtime_suspend(struct device *dev) +{ + struct vpu_core *core = dev_get_drvdata(dev); + + vpu_mbox_free(core); + return 0; +} + +static void vpu_core_cancel_work(struct vpu_core *core) +{ + struct vpu_inst *inst = NULL; + + cancel_work_sync(&core->msg_work); + cancel_delayed_work_sync(&core->msg_delayed_work); + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) + cancel_work_sync(&inst->msg_work); + mutex_unlock(&core->lock); +} + +static void vpu_core_resume_work(struct vpu_core *core) +{ + struct vpu_inst *inst = NULL; + unsigned long delay = msecs_to_jiffies(10); + + queue_work(core->workqueue, &core->msg_work); + queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay); + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) + queue_work(inst->workqueue, &inst->msg_work); + mutex_unlock(&core->lock); +} + +static int __maybe_unused vpu_core_resume(struct device *dev) +{ + struct vpu_core *core = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&core->lock); + pm_runtime_resume_and_get(dev); + vpu_core_get_vpu(core); + if (core->state != VPU_CORE_SNAPSHOT) + goto exit; + + if (!vpu_iface_get_power_state(core)) { + if (!list_empty(&core->instances)) { + ret = vpu_core_boot(core, false); + if (ret) { + dev_err(core->dev, "%s boot fail\n", __func__); + core->state = VPU_CORE_DEINIT; + goto exit; + } + } else { + core->state = VPU_CORE_DEINIT; + } + } else { + if (!list_empty(&core->instances)) { + ret = vpu_core_sw_reset(core); + if (ret) { + dev_err(core->dev, "%s sw_reset fail\n", __func__); + core->state = VPU_CORE_HANG; + goto exit; + } + } + core->state = VPU_CORE_ACTIVE; + } + +exit: + pm_runtime_put_sync(dev); + mutex_unlock(&core->lock); + + vpu_core_resume_work(core); + return ret; +} + +static int __maybe_unused vpu_core_suspend(struct device *dev) +{ + struct vpu_core *core = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&core->lock); + if (core->state == VPU_CORE_ACTIVE) { + if (!list_empty(&core->instances)) { + ret = vpu_core_snapshot(core); + if (ret) { + mutex_unlock(&core->lock); + return ret; + } + } + + core->state = VPU_CORE_SNAPSHOT; + } + mutex_unlock(&core->lock); + + vpu_core_cancel_work(core); + + mutex_lock(&core->lock); + vpu_core_put_vpu(core); + mutex_unlock(&core->lock); + return ret; +} + +static const struct dev_pm_ops vpu_core_pm_ops = { + SET_RUNTIME_PM_OPS(vpu_core_runtime_suspend, vpu_core_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(vpu_core_suspend, vpu_core_resume) +}; + +static struct vpu_core_resources imx8q_enc = { + .type = VPU_CORE_TYPE_ENC, + .fwname = "vpu/vpu_fw_imx8_enc.bin", + .stride = 16, + .max_width = 1920, + .max_height = 1920, + .min_width = 64, + .min_height = 48, + .step_width = 2, + .step_height = 2, + .rpc_size = 0x80000, + .fwlog_size = 0x80000, + .act_size = 0xc0000, +}; + +static struct vpu_core_resources imx8q_dec = { + .type = VPU_CORE_TYPE_DEC, + .fwname = "vpu/vpu_fw_imx8_dec.bin", + .stride = 256, + .max_width = 8188, + .max_height = 8188, + .min_width = 16, + .min_height = 16, + .step_width = 1, + .step_height = 1, + .rpc_size = 0x80000, + .fwlog_size = 0x80000, +}; + +static const struct of_device_id vpu_core_dt_match[] = { + { .compatible = "nxp,imx8q-vpu-encoder", .data = &imx8q_enc }, + { .compatible = "nxp,imx8q-vpu-decoder", .data = &imx8q_dec }, + {} +}; +MODULE_DEVICE_TABLE(of, vpu_core_dt_match); + +static struct platform_driver amphion_vpu_core_driver = { + .probe = vpu_core_probe, + .remove = vpu_core_remove, + .driver = { + .name = "amphion-vpu-core", + .of_match_table = vpu_core_dt_match, + .pm = &vpu_core_pm_ops, + }, +}; + +int __init vpu_core_driver_init(void) +{ + return platform_driver_register(&hion_vpu_core_driver); +} + +void __exit vpu_core_driver_exit(void) +{ + platform_driver_unregister(&hion_vpu_core_driver); +} diff --git a/drivers/media/platform/amphion/vpu_core.h b/drivers/media/platform/amphion/vpu_core.h new file mode 100644 index 000000000000..00a662997da4 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_core.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_CORE_H +#define _AMPHION_VPU_CORE_H + +void csr_writel(struct vpu_core *core, u32 reg, u32 val); +u32 csr_readl(struct vpu_core *core, u32 reg); +int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf); +void vpu_free_dma(struct vpu_buffer *buf); +struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index); + +#endif diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c new file mode 100644 index 000000000000..376196bea178 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-device.h> +#include <linux/debugfs.h> +#include "vpu.h" +#include "vpu_defs.h" +#include "vpu_helpers.h" +#include "vpu_cmds.h" +#include "vpu_rpc.h" +#include "vpu_v4l2.h" + +struct print_buf_desc { + u32 start_h_phy; + u32 start_h_vir; + u32 start_m; + u32 bytes; + u32 read; + u32 write; + char buffer[0]; +}; + +static char *vb2_stat_name[] = { + [VB2_BUF_STATE_DEQUEUED] = "dequeued", + [VB2_BUF_STATE_IN_REQUEST] = "in_request", + [VB2_BUF_STATE_PREPARING] = "preparing", + [VB2_BUF_STATE_QUEUED] = "queued", + [VB2_BUF_STATE_ACTIVE] = "active", + [VB2_BUF_STATE_DONE] = "done", + [VB2_BUF_STATE_ERROR] = "error", +}; + +static char *vpu_stat_name[] = { + [VPU_BUF_STATE_IDLE] = "idle", + [VPU_BUF_STATE_INUSE] = "inuse", + [VPU_BUF_STATE_DECODED] = "decoded", + [VPU_BUF_STATE_READY] = "ready", + [VPU_BUF_STATE_SKIP] = "skip", + [VPU_BUF_STATE_ERROR] = "error", +}; + +static int vpu_dbg_instance(struct seq_file *s, void *data) +{ + struct vpu_inst *inst = s->private; + char str[128]; + int num; + struct vb2_queue *vq; + int i; + + if (!inst->fh.m2m_ctx) + return 0; + num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(inst->type)); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "tgig = %d,pid = %d\n", inst->tgid, inst->pid); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), "state = %d\n", inst->state); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), + "min_buffer_out = %d, min_buffer_cap = %d\n", + inst->min_buffer_out, inst->min_buffer_cap); + if (seq_write(s, str, num)) + return 0; + + vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); + num = scnprintf(str, sizeof(str), + "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", + vb2_is_streaming(vq), + vq->num_buffers, + inst->out_format.pixfmt, + inst->out_format.pixfmt >> 8, + inst->out_format.pixfmt >> 16, + inst->out_format.pixfmt >> 24, + inst->out_format.width, + inst->out_format.height, + vq->last_buffer_dequeued); + if (seq_write(s, str, num)) + return 0; + for (i = 0; i < inst->out_format.num_planes; i++) { + num = scnprintf(str, sizeof(str), " %d(%d)", + inst->out_format.sizeimage[i], + inst->out_format.bytesperline[i]); + if (seq_write(s, str, num)) + return 0; + } + if (seq_write(s, "\n", 1)) + return 0; + + vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + num = scnprintf(str, sizeof(str), + "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;", + vb2_is_streaming(vq), + vq->num_buffers, + inst->cap_format.pixfmt, + inst->cap_format.pixfmt >> 8, + inst->cap_format.pixfmt >> 16, + inst->cap_format.pixfmt >> 24, + inst->cap_format.width, + inst->cap_format.height, + vq->last_buffer_dequeued); + if (seq_write(s, str, num)) + return 0; + for (i = 0; i < inst->cap_format.num_planes; i++) { + num = scnprintf(str, sizeof(str), " %d(%d)", + inst->cap_format.sizeimage[i], + inst->cap_format.bytesperline[i]); + if (seq_write(s, str, num)) + return 0; + } + if (seq_write(s, "\n", 1)) + return 0; + num = scnprintf(str, sizeof(str), "crop: (%d, %d) %d x %d\n", + inst->crop.left, + inst->crop.top, + inst->crop.width, + inst->crop.height); + if (seq_write(s, str, num)) + return 0; + + vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); + for (i = 0; i < vq->num_buffers; i++) { + struct vb2_buffer *vb = vq->bufs[i]; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (vb->state == VB2_BUF_STATE_DEQUEUED) + continue; + num = scnprintf(str, sizeof(str), + "output [%2d] state = %10s, %8s\n", + i, vb2_stat_name[vb->state], + vpu_stat_name[vpu_get_buffer_state(vbuf)]); + if (seq_write(s, str, num)) + return 0; + } + + vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + for (i = 0; i < vq->num_buffers; i++) { + struct vb2_buffer *vb = vq->bufs[i]; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (vb->state == VB2_BUF_STATE_DEQUEUED) + continue; + num = scnprintf(str, sizeof(str), + "capture[%2d] state = %10s, %8s\n", + i, vb2_stat_name[vb->state], + vpu_stat_name[vpu_get_buffer_state(vbuf)]); + if (seq_write(s, str, num)) + return 0; + } + + num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence); + if (seq_write(s, str, num)) + return 0; + + if (inst->use_stream_buffer) { + num = scnprintf(str, sizeof(str), "stream_buffer = %d / %d, <%pad, 0x%x>\n", + vpu_helper_get_used_space(inst), + inst->stream_buffer.length, + &inst->stream_buffer.phys, + inst->stream_buffer.length); + if (seq_write(s, str, num)) + return 0; + } + num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&inst->msg_fifo)); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "flow :\n"); + if (seq_write(s, str, num)) + return 0; + + mutex_lock(&inst->core->cmd_lock); + for (i = 0; i < ARRAY_SIZE(inst->flows); i++) { + u32 idx = (inst->flow_idx + i) % (ARRAY_SIZE(inst->flows)); + + if (!inst->flows[idx]) + continue; + num = scnprintf(str, sizeof(str), "\t[%s]0x%x\n", + inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C", + inst->flows[idx]); + if (seq_write(s, str, num)) { + mutex_unlock(&inst->core->cmd_lock); + return 0; + } + } + mutex_unlock(&inst->core->cmd_lock); + + i = 0; + while (true) { + num = call_vop(inst, get_debug_info, str, sizeof(str), i++); + if (num <= 0) + break; + if (seq_write(s, str, num)) + return 0; + } + + return 0; +} + +static int vpu_dbg_core(struct seq_file *s, void *data) +{ + struct vpu_core *core = s->private; + struct vpu_shared_addr *iface = core->iface; + char str[128]; + int num; + + num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(core->type)); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "boot_region = <%pad, 0x%x>\n", + &core->fw.phys, core->fw.length); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), "rpc_region = <%pad, 0x%x> used = 0x%x\n", + &core->rpc.phys, core->rpc.length, core->rpc.bytesused); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), "fwlog_region = <%pad, 0x%x>\n", + &core->log.phys, core->log.length); + if (seq_write(s, str, num)) + return 0; + + num = scnprintf(str, sizeof(str), "state = %d\n", core->state); + if (seq_write(s, str, num)) + return 0; + if (core->state == VPU_CORE_DEINIT) + return 0; + num = scnprintf(str, sizeof(str), "fw version = %d.%d.%d\n", + (core->fw_version >> 16) & 0xff, + (core->fw_version >> 8) & 0xff, + core->fw_version & 0xff); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), "instances = %d/%d (0x%02lx), %d\n", + hweight32(core->instance_mask), + core->supported_instance_count, + core->instance_mask, + core->request_count); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&core->msg_fifo)); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), + "cmd_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n", + iface->cmd_desc->start, + iface->cmd_desc->end, + iface->cmd_desc->wptr, + iface->cmd_desc->rptr); + if (seq_write(s, str, num)) + return 0; + num = scnprintf(str, sizeof(str), + "msg_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n", + iface->msg_desc->start, + iface->msg_desc->end, + iface->msg_desc->wptr, + iface->msg_desc->rptr); + if (seq_write(s, str, num)) + return 0; + + return 0; +} + +static int vpu_dbg_fwlog(struct seq_file *s, void *data) +{ + struct vpu_core *core = s->private; + struct print_buf_desc *print_buf; + int length; + u32 rptr; + u32 wptr; + int ret = 0; + + if (!core->log.virt || core->state == VPU_CORE_DEINIT) + return 0; + + print_buf = core->log.virt; + rptr = print_buf->read; + wptr = print_buf->write; + + if (rptr == wptr) + return 0; + else if (rptr < wptr) + length = wptr - rptr; + else + length = print_buf->bytes + wptr - rptr; + + if (s->count + length >= s->size) { + s->count = s->size; + return 0; + } + + if (rptr + length >= print_buf->bytes) { + int num = print_buf->bytes - rptr; + + if (seq_write(s, print_buf->buffer + rptr, num)) + ret = -1; + length -= num; + rptr = 0; + } + + if (length) { + if (seq_write(s, print_buf->buffer + rptr, length)) + ret = -1; + rptr += length; + } + if (!ret) + print_buf->read = rptr; + + return 0; +} + +static int vpu_dbg_inst_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, vpu_dbg_instance, inode->i_private); +} + +static ssize_t vpu_dbg_inst_write(struct file *file, + const char __user *user_buf, size_t size, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct vpu_inst *inst = s->private; + + vpu_session_debug(inst); + + return size; +} + +static ssize_t vpu_dbg_core_write(struct file *file, + const char __user *user_buf, size_t size, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct vpu_core *core = s->private; + + pm_runtime_resume_and_get(core->dev); + mutex_lock(&core->lock); + if (core->state != VPU_CORE_DEINIT && !core->instance_mask) { + dev_info(core->dev, "reset\n"); + if (!vpu_core_sw_reset(core)) { + core->state = VPU_CORE_ACTIVE; + core->hang_mask = 0; + } + } + mutex_unlock(&core->lock); + pm_runtime_put_sync(core->dev); + + return size; +} + +static int vpu_dbg_core_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, vpu_dbg_core, inode->i_private); +} + +static int vpu_dbg_fwlog_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, vpu_dbg_fwlog, inode->i_private); +} + +static const struct file_operations vpu_dbg_inst_fops = { + .owner = THIS_MODULE, + .open = vpu_dbg_inst_open, + .release = single_release, + .read = seq_read, + .write = vpu_dbg_inst_write, +}; + +static const struct file_operations vpu_dbg_core_fops = { + .owner = THIS_MODULE, + .open = vpu_dbg_core_open, + .release = single_release, + .read = seq_read, + .write = vpu_dbg_core_write, +}; + +static const struct file_operations vpu_dbg_fwlog_fops = { + .owner = THIS_MODULE, + .open = vpu_dbg_fwlog_open, + .release = single_release, + .read = seq_read, +}; + +int vpu_inst_create_dbgfs_file(struct vpu_inst *inst) +{ + struct vpu_dev *vpu; + char name[64]; + + if (!inst || !inst->core || !inst->core->vpu) + return -EINVAL; + + vpu = inst->core->vpu; + if (!vpu->debugfs) + return -EINVAL; + + if (inst->debugfs) + return 0; + + scnprintf(name, sizeof(name), "instance.%d.%d", inst->core->id, inst->id); + inst->debugfs = debugfs_create_file((const char *)name, + VERIFY_OCTAL_PERMISSIONS(0644), + vpu->debugfs, + inst, + &vpu_dbg_inst_fops); + if (!inst->debugfs) { + dev_err(inst->dev, "vpu create debugfs %s fail\n", name); + return -EINVAL; + } + + return 0; +} + +int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst) +{ + if (!inst) + return 0; + + debugfs_remove(inst->debugfs); + inst->debugfs = NULL; + + return 0; +} + +int vpu_core_create_dbgfs_file(struct vpu_core *core) +{ + struct vpu_dev *vpu; + char name[64]; + + if (!core || !core->vpu) + return -EINVAL; + + vpu = core->vpu; + if (!vpu->debugfs) + return -EINVAL; + + if (!core->debugfs) { + scnprintf(name, sizeof(name), "core.%d", core->id); + core->debugfs = debugfs_create_file((const char *)name, + VERIFY_OCTAL_PERMISSIONS(0644), + vpu->debugfs, + core, + &vpu_dbg_core_fops); + if (!core->debugfs) { + dev_err(core->dev, "vpu create debugfs %s fail\n", name); + return -EINVAL; + } + } + if (!core->debugfs_fwlog) { + scnprintf(name, sizeof(name), "fwlog.%d", core->id); + core->debugfs_fwlog = debugfs_create_file((const char *)name, + VERIFY_OCTAL_PERMISSIONS(0444), + vpu->debugfs, + core, + &vpu_dbg_fwlog_fops); + if (!core->debugfs_fwlog) { + dev_err(core->dev, "vpu create debugfs %s fail\n", name); + return -EINVAL; + } + } + + return 0; +} + +int vpu_core_remove_dbgfs_file(struct vpu_core *core) +{ + if (!core) + return 0; + debugfs_remove(core->debugfs); + core->debugfs = NULL; + debugfs_remove(core->debugfs_fwlog); + core->debugfs_fwlog = NULL; + + return 0; +} + +void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow) +{ + if (!inst) + return; + + inst->flows[inst->flow_idx] = flow; + inst->flow_idx = (inst->flow_idx + 1) % (ARRAY_SIZE(inst->flows)); +} diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h new file mode 100644 index 000000000000..282664202dcf --- /dev/null +++ b/drivers/media/platform/amphion/vpu_defs.h @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_DEFS_H +#define _AMPHION_VPU_DEFS_H + +enum MSG_TYPE { + INIT_DONE = 1, + PRC_BUF_OFFSET, + BOOT_ADDRESS, + COMMAND, + EVENT, +}; + +enum { + VPU_IRQ_CODE_BOOT_DONE = 0x55, + VPU_IRQ_CODE_SNAPSHOT_DONE = 0xa5, + VPU_IRQ_CODE_SYNC = 0xaa, +}; + +enum { + VPU_CMD_ID_NOOP = 0x0, + VPU_CMD_ID_CONFIGURE_CODEC, + VPU_CMD_ID_START, + VPU_CMD_ID_STOP, + VPU_CMD_ID_ABORT, + VPU_CMD_ID_RST_BUF, + VPU_CMD_ID_SNAPSHOT, + VPU_CMD_ID_FIRM_RESET, + VPU_CMD_ID_UPDATE_PARAMETER, + VPU_CMD_ID_FRAME_ENCODE, + VPU_CMD_ID_SKIP, + VPU_CMD_ID_PARSE_NEXT_SEQ, + VPU_CMD_ID_PARSE_NEXT_I, + VPU_CMD_ID_PARSE_NEXT_IP, + VPU_CMD_ID_PARSE_NEXT_ANY, + VPU_CMD_ID_DEC_PIC, + VPU_CMD_ID_FS_ALLOC, + VPU_CMD_ID_FS_RELEASE, + VPU_CMD_ID_TIMESTAMP, + VPU_CMD_ID_DEBUG +}; + +enum { + VPU_MSG_ID_NOOP = 0x100, + VPU_MSG_ID_RESET_DONE, + VPU_MSG_ID_START_DONE, + VPU_MSG_ID_STOP_DONE, + VPU_MSG_ID_ABORT_DONE, + VPU_MSG_ID_BUF_RST, + VPU_MSG_ID_MEM_REQUEST, + VPU_MSG_ID_PARAM_UPD_DONE, + VPU_MSG_ID_FRAME_INPUT_DONE, + VPU_MSG_ID_ENC_DONE, + VPU_MSG_ID_DEC_DONE, + VPU_MSG_ID_FRAME_REQ, + VPU_MSG_ID_FRAME_RELEASE, + VPU_MSG_ID_SEQ_HDR_FOUND, + VPU_MSG_ID_RES_CHANGE, + VPU_MSG_ID_PIC_HDR_FOUND, + VPU_MSG_ID_PIC_DECODED, + VPU_MSG_ID_PIC_EOS, + VPU_MSG_ID_FIFO_LOW, + VPU_MSG_ID_FIFO_HIGH, + VPU_MSG_ID_FIFO_EMPTY, + VPU_MSG_ID_FIFO_FULL, + VPU_MSG_ID_BS_ERROR, + VPU_MSG_ID_UNSUPPORTED, + VPU_MSG_ID_TIMESTAMP_INFO, + + VPU_MSG_ID_FIRMWARE_XCPT, +}; + +enum VPU_ENC_MEMORY_RESOURSE { + MEM_RES_ENC, + MEM_RES_REF, + MEM_RES_ACT +}; + +enum VPU_DEC_MEMORY_RESOURCE { + MEM_RES_FRAME, + MEM_RES_MBI, + MEM_RES_DCP +}; + +enum VPU_SCODE_TYPE { + SCODE_PADDING_EOS = 1, + SCODE_PADDING_BUFFLUSH = 2, + SCODE_PADDING_ABORT = 3, + SCODE_SEQUENCE = 0x31, + SCODE_PICTURE = 0x32, + SCODE_SLICE = 0x33 +}; + +struct vpu_pkt_mem_req_data { + u32 enc_frame_size; + u32 enc_frame_num; + u32 ref_frame_size; + u32 ref_frame_num; + u32 act_buf_size; + u32 act_buf_num; +}; + +struct vpu_enc_pic_info { + u32 frame_id; + u32 pic_type; + u32 skipped_frame; + u32 error_flag; + u32 psnr; + u32 frame_size; + u32 wptr; + u32 crc; + s64 timestamp; +}; + +struct vpu_dec_codec_info { + u32 pixfmt; + u32 num_ref_frms; + u32 num_dpb_frms; + u32 num_dfe_area; + u32 color_primaries; + u32 transfer_chars; + u32 matrix_coeffs; + u32 full_range; + u32 vui_present; + u32 progressive; + u32 width; + u32 height; + u32 decoded_width; + u32 decoded_height; + struct v4l2_fract frame_rate; + u32 dsp_asp_ratio; + u32 level_idc; + u32 bit_depth_luma; + u32 bit_depth_chroma; + u32 chroma_fmt; + u32 mvc_num_views; + u32 offset_x; + u32 offset_y; + u32 tag; + u32 sizeimage[VIDEO_MAX_PLANES]; + u32 bytesperline[VIDEO_MAX_PLANES]; + u32 mbi_size; + u32 dcp_size; + u32 stride; +}; + +struct vpu_dec_pic_info { + u32 id; + u32 luma; + u32 start; + u32 end; + u32 pic_size; + u32 stride; + u32 skipped; + s64 timestamp; + u32 consumed_count; +}; + +struct vpu_fs_info { + u32 id; + u32 type; + u32 tag; + u32 luma_addr; + u32 luma_size; + u32 chroma_addr; + u32 chromau_size; + u32 chromav_addr; + u32 chromav_size; + u32 bytesperline; + u32 not_displayed; +}; + +struct vpu_ts_info { + s64 timestamp; + u32 size; +}; + +#define BITRATE_STEP (1024) +#define BITRATE_MIN (16 * BITRATE_STEP) +#define BITRATE_MAX (240 * 1024 * BITRATE_STEP) +#define BITRATE_DEFAULT (2 * 1024 * BITRATE_STEP) +#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2) + +#endif diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c new file mode 100644 index 000000000000..9d5a5075343d --- /dev/null +++ b/drivers/media/platform/amphion/vpu_drv.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/dma-map-ops.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/pm_runtime.h> +#include <linux/videodev2.h> +#include <linux/of_reserved_mem.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ioctl.h> +#include <linux/debugfs.h> +#include "vpu.h" +#include "vpu_imx8q.h" + +bool debug; +module_param(debug, bool, 0644); + +void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val) +{ + writel(val, vpu->base + reg); +} + +u32 vpu_readl(struct vpu_dev *vpu, u32 reg) +{ + return readl(vpu->base + reg); +} + +static void vpu_dev_get(struct vpu_dev *vpu) +{ + if (atomic_inc_return(&vpu->ref_vpu) == 1 && vpu->res->setup) + vpu->res->setup(vpu); +} + +static void vpu_dev_put(struct vpu_dev *vpu) +{ + atomic_dec(&vpu->ref_vpu); +} + +static void vpu_enc_get(struct vpu_dev *vpu) +{ + if (atomic_inc_return(&vpu->ref_enc) == 1 && vpu->res->setup_encoder) + vpu->res->setup_encoder(vpu); +} + +static void vpu_enc_put(struct vpu_dev *vpu) +{ + atomic_dec(&vpu->ref_enc); +} + +static void vpu_dec_get(struct vpu_dev *vpu) +{ + if (atomic_inc_return(&vpu->ref_dec) == 1 && vpu->res->setup_decoder) + vpu->res->setup_decoder(vpu); +} + +static void vpu_dec_put(struct vpu_dev *vpu) +{ + atomic_dec(&vpu->ref_dec); +} + +static int vpu_init_media_device(struct vpu_dev *vpu) +{ + vpu->mdev.dev = vpu->dev; + strscpy(vpu->mdev.model, "amphion-vpu", sizeof(vpu->mdev.model)); + strscpy(vpu->mdev.bus_info, "platform: amphion-vpu", sizeof(vpu->mdev.bus_info)); + media_device_init(&vpu->mdev); + vpu->v4l2_dev.mdev = &vpu->mdev; + + return 0; +} + +static int vpu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vpu_dev *vpu; + int ret; + + dev_dbg(dev, "probe\n"); + vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL); + if (!vpu) + return -ENOMEM; + + vpu->pdev = pdev; + vpu->dev = dev; + mutex_init(&vpu->lock); + INIT_LIST_HEAD(&vpu->cores); + platform_set_drvdata(pdev, vpu); + atomic_set(&vpu->ref_vpu, 0); + atomic_set(&vpu->ref_enc, 0); + atomic_set(&vpu->ref_dec, 0); + vpu->get_vpu = vpu_dev_get; + vpu->put_vpu = vpu_dev_put; + vpu->get_enc = vpu_enc_get; + vpu->put_enc = vpu_enc_put; + vpu->get_dec = vpu_dec_get; + vpu->put_dec = vpu_dec_put; + + vpu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vpu->base)) + return PTR_ERR(vpu->base); + + vpu->res = of_device_get_match_data(dev); + if (!vpu->res) + return -ENODEV; + + pm_runtime_enable(dev); + + ret = v4l2_device_register(dev, &vpu->v4l2_dev); + if (ret) + goto err_vpu_deinit; + + vpu_init_media_device(vpu); + vpu->encoder.type = VPU_CORE_TYPE_ENC; + vpu->encoder.function = MEDIA_ENT_F_PROC_VIDEO_ENCODER; + vpu->decoder.type = VPU_CORE_TYPE_DEC; + vpu->decoder.function = MEDIA_ENT_F_PROC_VIDEO_DECODER; + ret = vpu_add_func(vpu, &vpu->decoder); + if (ret) + goto err_add_decoder; + ret = vpu_add_func(vpu, &vpu->encoder); + if (ret) + goto err_add_encoder; + ret = media_device_register(&vpu->mdev); + if (ret) + goto err_vpu_media; + vpu->debugfs = debugfs_create_dir("amphion_vpu", NULL); + + of_platform_populate(dev->of_node, NULL, NULL, dev); + + return 0; + +err_vpu_media: + vpu_remove_func(&vpu->encoder); +err_add_encoder: + vpu_remove_func(&vpu->decoder); +err_add_decoder: + media_device_cleanup(&vpu->mdev); + v4l2_device_unregister(&vpu->v4l2_dev); +err_vpu_deinit: + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + return ret; +} + +static int vpu_remove(struct platform_device *pdev) +{ + struct vpu_dev *vpu = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + debugfs_remove_recursive(vpu->debugfs); + vpu->debugfs = NULL; + + pm_runtime_disable(dev); + + media_device_unregister(&vpu->mdev); + vpu_remove_func(&vpu->decoder); + vpu_remove_func(&vpu->encoder); + media_device_cleanup(&vpu->mdev); + v4l2_device_unregister(&vpu->v4l2_dev); + mutex_destroy(&vpu->lock); + + return 0; +} + +static int __maybe_unused vpu_runtime_resume(struct device *dev) +{ + return 0; +} + +static int __maybe_unused vpu_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused vpu_resume(struct device *dev) +{ + return 0; +} + +static int __maybe_unused vpu_suspend(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops vpu_pm_ops = { + SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume) +}; + +static struct vpu_resources imx8qxp_res = { + .plat_type = IMX8QXP, + .mreg_base = 0x40000000, + .setup = vpu_imx8q_setup, + .setup_encoder = vpu_imx8q_setup_enc, + .setup_decoder = vpu_imx8q_setup_dec, + .reset = vpu_imx8q_reset +}; + +static struct vpu_resources imx8qm_res = { + .plat_type = IMX8QM, + .mreg_base = 0x40000000, + .setup = vpu_imx8q_setup, + .setup_encoder = vpu_imx8q_setup_enc, + .setup_decoder = vpu_imx8q_setup_dec, + .reset = vpu_imx8q_reset +}; + +static const struct of_device_id vpu_dt_match[] = { + { .compatible = "nxp,imx8qxp-vpu", .data = &imx8qxp_res }, + { .compatible = "nxp,imx8qm-vpu", .data = &imx8qm_res }, + {} +}; +MODULE_DEVICE_TABLE(of, vpu_dt_match); + +static struct platform_driver amphion_vpu_driver = { + .probe = vpu_probe, + .remove = vpu_remove, + .driver = { + .name = "amphion-vpu", + .of_match_table = vpu_dt_match, + .pm = &vpu_pm_ops, + }, +}; + +static int __init vpu_driver_init(void) +{ + int ret; + + ret = platform_driver_register(&hion_vpu_driver); + if (ret) + return ret; + + return vpu_core_driver_init(); +} + +static void __exit vpu_driver_exit(void) +{ + vpu_core_driver_exit(); + platform_driver_unregister(&hion_vpu_driver); +} +module_init(vpu_driver_init); +module_exit(vpu_driver_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX8Q"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c new file mode 100644 index 000000000000..e9aeb3453dfc --- /dev/null +++ b/drivers/media/platform/amphion/vpu_helpers.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include "vpu.h" +#include "vpu_core.h" +#include "vpu_rpc.h" +#include "vpu_helpers.h" + +int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x) +{ + int i; + + for (i = 0; i < size; i++) { + if (array[i] == x) + return i; + } + + return 0; +} + +bool vpu_helper_check_type(struct vpu_inst *inst, u32 type) +{ + const struct vpu_format *pfmt; + + for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) { + if (!vpu_iface_check_format(inst, pfmt->pixfmt)) + continue; + if (pfmt->type == type) + return true; + } + + return false; +} + +const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt) +{ + const struct vpu_format *pfmt; + + if (!inst || !inst->formats) + return NULL; + + if (!vpu_iface_check_format(inst, pixelfmt)) + return NULL; + + for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) { + if (pfmt->pixfmt == pixelfmt && (!type || type == pfmt->type)) + return pfmt; + } + + return NULL; +} + +const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index) +{ + const struct vpu_format *pfmt; + int i = 0; + + if (!inst || !inst->formats) + return NULL; + + for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) { + if (!vpu_iface_check_format(inst, pfmt->pixfmt)) + continue; + + if (pfmt->type == type) { + if (index == i) + return pfmt; + i++; + } + } + + return NULL; +} + +u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width) +{ + const struct vpu_core_resources *res; + + if (!inst) + return width; + + res = vpu_get_resource(inst); + if (!res) + return width; + if (res->max_width) + width = clamp(width, res->min_width, res->max_width); + if (res->step_width) + width = ALIGN(width, res->step_width); + + return width; +} + +u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height) +{ + const struct vpu_core_resources *res; + + if (!inst) + return height; + + res = vpu_get_resource(inst); + if (!res) + return height; + if (res->max_height) + height = clamp(height, res->min_height, res->max_height); + if (res->step_height) + height = ALIGN(height, res->step_height); + + return height; +} + +static u32 get_nv12_plane_size(u32 width, u32 height, int plane_no, + u32 stride, u32 interlaced, u32 *pbl) +{ + u32 bytesperline; + u32 size = 0; + + bytesperline = ALIGN(width, stride); + if (pbl) + bytesperline = max(bytesperline, *pbl); + height = ALIGN(height, 2); + if (plane_no == 0) + size = bytesperline * height; + else if (plane_no == 1) + size = bytesperline * height >> 1; + if (pbl) + *pbl = bytesperline; + + return size; +} + +static u32 get_tiled_8l128_plane_size(u32 fmt, u32 width, u32 height, int plane_no, + u32 stride, u32 interlaced, u32 *pbl) +{ + u32 ws = 3; + u32 hs = 7; + u32 bitdepth = 8; + u32 bytesperline; + u32 size = 0; + + if (interlaced) + hs++; + if (fmt == V4L2_PIX_FMT_NV12M_10BE_8L128) + bitdepth = 10; + bytesperline = DIV_ROUND_UP(width * bitdepth, BITS_PER_BYTE); + bytesperline = ALIGN(bytesperline, 1 << ws); + bytesperline = ALIGN(bytesperline, stride); + if (pbl) + bytesperline = max(bytesperline, *pbl); + height = ALIGN(height, 1 << hs); + if (plane_no == 0) + size = bytesperline * height; + else if (plane_no == 1) + size = (bytesperline * ALIGN(height, 1 << (hs + 1))) >> 1; + if (pbl) + *pbl = bytesperline; + + return size; +} + +static u32 get_default_plane_size(u32 width, u32 height, int plane_no, + u32 stride, u32 interlaced, u32 *pbl) +{ + u32 bytesperline; + u32 size = 0; + + bytesperline = ALIGN(width, stride); + if (pbl) + bytesperline = max(bytesperline, *pbl); + if (plane_no == 0) + size = bytesperline * height; + if (pbl) + *pbl = bytesperline; + + return size; +} + +u32 vpu_helper_get_plane_size(u32 fmt, u32 w, u32 h, int plane_no, + u32 stride, u32 interlaced, u32 *pbl) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12M: + return get_nv12_plane_size(w, h, plane_no, stride, interlaced, pbl); + case V4L2_PIX_FMT_NV12M_8L128: + case V4L2_PIX_FMT_NV12M_10BE_8L128: + return get_tiled_8l128_plane_size(fmt, w, h, plane_no, stride, interlaced, pbl); + default: + return get_default_plane_size(w, h, plane_no, stride, interlaced, pbl); + } +} + +int vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *rptr, u32 size, void *dst) +{ + u32 offset; + u32 start; + u32 end; + void *virt; + + if (!stream_buffer || !rptr || !dst) + return -EINVAL; + + if (!size) + return 0; + + offset = *rptr; + start = stream_buffer->phys; + end = start + stream_buffer->length; + virt = stream_buffer->virt; + + if (offset < start || offset > end) + return -EINVAL; + + if (offset + size <= end) { + memcpy(dst, virt + (offset - start), size); + } else { + memcpy(dst, virt + (offset - start), end - offset); + memcpy(dst + end - offset, virt, size + offset - end); + } + + *rptr = vpu_helper_step_walk(stream_buffer, offset, size); + + return 0; +} + +int vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *wptr, u32 size, void *src) +{ + u32 offset; + u32 start; + u32 end; + void *virt; + + if (!stream_buffer || !wptr || !src) + return -EINVAL; + + if (!size) + return 0; + + offset = *wptr; + start = stream_buffer->phys; + end = start + stream_buffer->length; + virt = stream_buffer->virt; + if (offset < start || offset > end) + return -EINVAL; + + if (offset + size <= end) { + memcpy(virt + (offset - start), src, size); + } else { + memcpy(virt + (offset - start), src, end - offset); + memcpy(virt, src + end - offset, size + offset - end); + } + + *wptr = vpu_helper_step_walk(stream_buffer, offset, size); + + return 0; +} + +int vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *wptr, u8 val, u32 size) +{ + u32 offset; + u32 start; + u32 end; + void *virt; + + if (!stream_buffer || !wptr) + return -EINVAL; + + if (!size) + return 0; + + offset = *wptr; + start = stream_buffer->phys; + end = start + stream_buffer->length; + virt = stream_buffer->virt; + if (offset < start || offset > end) + return -EINVAL; + + if (offset + size <= end) { + memset(virt + (offset - start), val, size); + } else { + memset(virt + (offset - start), val, end - offset); + memset(virt, val, size + offset - end); + } + + offset += size; + if (offset >= end) + offset -= stream_buffer->length; + + *wptr = offset; + + return 0; +} + +u32 vpu_helper_get_free_space(struct vpu_inst *inst) +{ + struct vpu_rpc_buffer_desc desc; + + if (vpu_iface_get_stream_buffer_desc(inst, &desc)) + return 0; + + if (desc.rptr > desc.wptr) + return desc.rptr - desc.wptr; + else if (desc.rptr < desc.wptr) + return (desc.end - desc.start + desc.rptr - desc.wptr); + else + return desc.end - desc.start; +} + +u32 vpu_helper_get_used_space(struct vpu_inst *inst) +{ + struct vpu_rpc_buffer_desc desc; + + if (vpu_iface_get_stream_buffer_desc(inst, &desc)) + return 0; + + if (desc.wptr > desc.rptr) + return desc.wptr - desc.rptr; + else if (desc.wptr < desc.rptr) + return (desc.end - desc.start + desc.wptr - desc.rptr); + else + return 0; +} + +int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpu_inst *inst = ctrl_to_inst(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + ctrl->val = inst->min_buffer_cap; + break; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: + ctrl->val = inst->min_buffer_out; + break; + default: + return -EINVAL; + } + + return 0; +} + +int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer, + u32 pixelformat, u32 offset, u32 bytesused) +{ + u32 start_code; + int start_code_size; + u32 val = 0; + int i; + int ret = -EINVAL; + + if (!stream_buffer || !stream_buffer->virt) + return -EINVAL; + + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + start_code_size = 4; + start_code = 0x00000001; + break; + default: + return 0; + } + + for (i = 0; i < bytesused; i++) { + val = (val << 8) | vpu_helper_read_byte(stream_buffer, offset + i); + if (i < start_code_size - 1) + continue; + if (val == start_code) { + ret = i + 1 - start_code_size; + break; + } + } + + return ret; +} + +int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src) +{ + u32 i; + + if (!pairs || !cnt) + return -EINVAL; + + for (i = 0; i < cnt; i++) { + if (pairs[i].src == src) + return pairs[i].dst; + } + + return -EINVAL; +} + +int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst) +{ + u32 i; + + if (!pairs || !cnt) + return -EINVAL; + + for (i = 0; i < cnt; i++) { + if (pairs[i].dst == dst) + return pairs[i].src; + } + + return -EINVAL; +} diff --git a/drivers/media/platform/amphion/vpu_helpers.h b/drivers/media/platform/amphion/vpu_helpers.h new file mode 100644 index 000000000000..bc28350958be --- /dev/null +++ b/drivers/media/platform/amphion/vpu_helpers.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_HELPERS_H +#define _AMPHION_VPU_HELPERS_H + +struct vpu_pair { + u32 src; + u32 dst; +}; + +int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x); +bool vpu_helper_check_type(struct vpu_inst *inst, u32 type); +const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt); +const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index); +u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width); +u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height); +u32 vpu_helper_get_plane_size(u32 fmt, u32 width, u32 height, int plane_no, + u32 stride, u32 interlaced, u32 *pbl); +int vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *rptr, u32 size, void *dst); +int vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *wptr, u32 size, void *src); +int vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer, + u32 *wptr, u8 val, u32 size); +u32 vpu_helper_get_free_space(struct vpu_inst *inst); +u32 vpu_helper_get_used_space(struct vpu_inst *inst); +int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl); +void vpu_helper_get_kmp_next(const u8 *pattern, int *next, int size); +int vpu_helper_kmp_search(u8 *s, int s_len, const u8 *p, int p_len, int *next); +int vpu_helper_kmp_search_in_stream_buffer(struct vpu_buffer *stream_buffer, + u32 offset, int bytesused, + const u8 *p, int p_len, int *next); +int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer, + u32 pixelformat, u32 offset, u32 bytesused); + +static inline u32 vpu_helper_step_walk(struct vpu_buffer *stream_buffer, u32 pos, u32 step) +{ + pos += step; + if (pos > stream_buffer->phys + stream_buffer->length) + pos -= stream_buffer->length; + + return pos; +} + +static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos) +{ + u8 *pdata = (u8 *)stream_buffer->virt; + + return pdata[pos % stream_buffer->length]; +} + +int vpu_color_check_primaries(u32 primaries); +int vpu_color_check_transfers(u32 transfers); +int vpu_color_check_matrix(u32 matrix); +int vpu_color_check_full_range(u32 full_range); +u32 vpu_color_cvrt_primaries_v2i(u32 primaries); +u32 vpu_color_cvrt_primaries_i2v(u32 primaries); +u32 vpu_color_cvrt_transfers_v2i(u32 transfers); +u32 vpu_color_cvrt_transfers_i2v(u32 transfers); +u32 vpu_color_cvrt_matrix_v2i(u32 matrix); +u32 vpu_color_cvrt_matrix_i2v(u32 matrix); +u32 vpu_color_cvrt_full_range_v2i(u32 full_range); +u32 vpu_color_cvrt_full_range_i2v(u32 full_range); +int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range); + +int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src); +int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst); +#endif diff --git a/drivers/media/platform/amphion/vpu_imx8q.c b/drivers/media/platform/amphion/vpu_imx8q.c new file mode 100644 index 000000000000..f14c2b8312a8 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_imx8q.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/types.h> +#include "vpu.h" +#include "vpu_core.h" +#include "vpu_imx8q.h" +#include "vpu_rpc.h" + +#define IMX8Q_CSR_CM0Px_ADDR_OFFSET 0x00000000 +#define IMX8Q_CSR_CM0Px_CPUWAIT 0x00000004 + +#ifdef CONFIG_IMX_SCU +#include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/svc/misc.h> + +#define VPU_DISABLE_BITS 0x7 +#define VPU_IMX_DECODER_FUSE_OFFSET 14 +#define VPU_ENCODER_MASK 0x1 +#define VPU_DECODER_MASK 0x3UL +#define VPU_DECODER_H264_MASK 0x2UL +#define VPU_DECODER_HEVC_MASK 0x1UL + +static u32 imx8q_fuse; + +struct vpu_sc_msg_misc { + struct imx_sc_rpc_msg hdr; + u32 word; +} __packed; +#endif + +int vpu_imx8q_setup_dec(struct vpu_dev *vpu) +{ + const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL; + + vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET, 0x1f); + vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_SET, 0xffffffff); + + return 0; +} + +int vpu_imx8q_setup_enc(struct vpu_dev *vpu) +{ + return 0; +} + +int vpu_imx8q_setup(struct vpu_dev *vpu) +{ + const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL; + + vpu_readl(vpu, offset + 0x108); + + vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0x1); + vpu_writel(vpu, offset + 0x190, 0xffffffff); + vpu_writel(vpu, offset + SCB_BLK_CTRL_XMEM_RESET_SET, 0xffffffff); + vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0xE); + vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_SET, 0x7); + vpu_writel(vpu, XMEM_CONTROL, 0x102); + + vpu_readl(vpu, offset + 0x108); + + return 0; +} + +static int vpu_imx8q_reset_enc(struct vpu_dev *vpu) +{ + return 0; +} + +static int vpu_imx8q_reset_dec(struct vpu_dev *vpu) +{ + const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL; + + vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_CLR, 0xffffffff); + + return 0; +} + +int vpu_imx8q_reset(struct vpu_dev *vpu) +{ + const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL; + + vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_CLR, 0x7); + vpu_imx8q_reset_enc(vpu); + vpu_imx8q_reset_dec(vpu); + + return 0; +} + +int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id) +{ + if (!config) + return -EINVAL; + + switch (core_id) { + case 0: + config->malone_base_addr[0] = regs + DEC_MFD_XREG_SLV_BASE; + config->num_malones = 1; + config->num_windsors = 0; + break; + case 1: + config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_0_BASE; + config->num_windsors = 1; + config->num_malones = 0; + break; + case 2: + config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_1_BASE; + config->num_windsors = 1; + config->num_malones = 0; + break; + default: + return -EINVAL; + } + if (config->num_windsors) { + config->windsor_irq_pin[0x0][0x0] = WINDSOR_PAL_IRQ_PIN_L; + config->windsor_irq_pin[0x0][0x1] = WINDSOR_PAL_IRQ_PIN_H; + } + + config->malone_base_addr[0x1] = 0x0; + config->hif_offset[0x0] = MFD_HIF; + config->hif_offset[0x1] = 0x0; + + config->dpv_base_addr = 0x0; + config->dpv_irq_pin = 0x0; + config->pixif_base_addr = regs + DEC_MFD_XREG_SLV_BASE + MFD_PIX_IF; + config->cache_base_addr[0] = regs + MC_CACHE_0_BASE; + config->cache_base_addr[1] = regs + MC_CACHE_1_BASE; + + return 0; +} + +int vpu_imx8q_boot_core(struct vpu_core *core) +{ + csr_writel(core, IMX8Q_CSR_CM0Px_ADDR_OFFSET, core->fw.phys); + csr_writel(core, IMX8Q_CSR_CM0Px_CPUWAIT, 0); + return 0; +} + +int vpu_imx8q_get_power_state(struct vpu_core *core) +{ + if (csr_readl(core, IMX8Q_CSR_CM0Px_CPUWAIT) == 1) + return 0; + return 1; +} + +int vpu_imx8q_on_firmware_loaded(struct vpu_core *core) +{ + u8 *p; + + p = core->fw.virt; + p[16] = core->vpu->res->plat_type; + p[17] = core->id; + p[18] = 1; + + return 0; +} + +int vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size) +{ + const struct vpu_rpc_region_t imx8q_regions[] = { + {0x00000000, 0x08000000, VPU_CORE_MEMORY_CACHED}, + {0x08000000, 0x10000000, VPU_CORE_MEMORY_UNCACHED}, + {0x10000000, 0x20000000, VPU_CORE_MEMORY_CACHED}, + {0x20000000, 0x40000000, VPU_CORE_MEMORY_UNCACHED} + }; + int i; + + if (addr < base) + return VPU_CORE_MEMORY_INVALID; + + addr -= base; + for (i = 0; i < ARRAY_SIZE(imx8q_regions); i++) { + const struct vpu_rpc_region_t *region = &imx8q_regions[i]; + + if (addr >= region->start && addr + size < region->end) + return region->type; + } + + return VPU_CORE_MEMORY_INVALID; +} + +#ifdef CONFIG_IMX_SCU +static u32 vpu_imx8q_get_fuse(void) +{ + static u32 fuse_got; + struct imx_sc_ipc *ipc; + struct vpu_sc_msg_misc msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + if (fuse_got) + return imx8q_fuse; + + ret = imx_scu_get_handle(&ipc); + if (ret) { + pr_err("error: get sct handle fail: %d\n", ret); + return 0; + } + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_MISC; + hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ; + hdr->size = 2; + + msg.word = VPU_DISABLE_BITS; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return 0; + + imx8q_fuse = msg.word; + fuse_got = 1; + return imx8q_fuse; +} + +bool vpu_imx8q_check_codec(enum vpu_core_type type) +{ + u32 fuse = vpu_imx8q_get_fuse(); + + if (type == VPU_CORE_TYPE_ENC) { + if (fuse & VPU_ENCODER_MASK) + return false; + } else if (type == VPU_CORE_TYPE_DEC) { + fuse >>= VPU_IMX_DECODER_FUSE_OFFSET; + fuse &= VPU_DECODER_MASK; + + if (fuse == VPU_DECODER_MASK) + return false; + } + return true; +} + +bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt) +{ + u32 fuse = vpu_imx8q_get_fuse(); + + if (type == VPU_CORE_TYPE_DEC) { + fuse >>= VPU_IMX_DECODER_FUSE_OFFSET; + fuse &= VPU_DECODER_MASK; + + if (fuse == VPU_DECODER_HEVC_MASK && pixelfmt == V4L2_PIX_FMT_HEVC) + return false; + if (fuse == VPU_DECODER_H264_MASK && pixelfmt == V4L2_PIX_FMT_H264) + return false; + if (fuse == VPU_DECODER_MASK) + return false; + } + + return true; +} +#else +bool vpu_imx8q_check_codec(enum vpu_core_type type) +{ + return true; +} + +bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt) +{ + return true; +} +#endif diff --git a/drivers/media/platform/amphion/vpu_imx8q.h b/drivers/media/platform/amphion/vpu_imx8q.h new file mode 100644 index 000000000000..9deffd7dde42 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_imx8q.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_IMX8Q_H +#define _AMPHION_VPU_IMX8Q_H + +#define SCB_XREG_SLV_BASE 0x00000000 +#define SCB_SCB_BLK_CTRL 0x00070000 +#define SCB_BLK_CTRL_XMEM_RESET_SET 0x00000090 +#define SCB_BLK_CTRL_CACHE_RESET_SET 0x000000A0 +#define SCB_BLK_CTRL_CACHE_RESET_CLR 0x000000A4 +#define SCB_BLK_CTRL_SCB_CLK_ENABLE_SET 0x00000100 + +#define XMEM_CONTROL 0x00041000 + +#define MC_CACHE_0_BASE 0x00060000 +#define MC_CACHE_1_BASE 0x00068000 + +#define DEC_MFD_XREG_SLV_BASE 0x00180000 +#define ENC_MFD_XREG_SLV_0_BASE 0x00800000 +#define ENC_MFD_XREG_SLV_1_BASE 0x00A00000 + +#define MFD_HIF 0x0001C000 +#define MFD_HIF_MSD_REG_INTERRUPT_STATUS 0x00000018 +#define MFD_SIF 0x0001D000 +#define MFD_SIF_CTRL_STATUS 0x000000F0 +#define MFD_SIF_INTR_STATUS 0x000000F4 +#define MFD_MCX 0x00020800 +#define MFD_MCX_OFF 0x00000020 +#define MFD_PIX_IF 0x00020000 + +#define MFD_BLK_CTRL 0x00030000 +#define MFD_BLK_CTRL_MFD_SYS_RESET_SET 0x00000000 +#define MFD_BLK_CTRL_MFD_SYS_RESET_CLR 0x00000004 +#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET 0x00000100 +#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_CLR 0x00000104 + +#define VID_API_NUM_STREAMS 8 +#define VID_API_MAX_BUF_PER_STR 3 +#define VID_API_MAX_NUM_MVC_VIEWS 4 +#define MEDIAIP_MAX_NUM_MALONES 2 +#define MEDIAIP_MAX_NUM_MALONE_IRQ_PINS 2 +#define MEDIAIP_MAX_NUM_WINDSORS 1 +#define MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS 2 +#define MEDIAIP_MAX_NUM_CMD_IRQ_PINS 2 +#define MEDIAIP_MAX_NUM_MSG_IRQ_PINS 1 +#define MEDIAIP_MAX_NUM_TIMER_IRQ_PINS 4 +#define MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS 4 + +#define WINDSOR_PAL_IRQ_PIN_L 0x4 +#define WINDSOR_PAL_IRQ_PIN_H 0x5 + +struct vpu_rpc_system_config { + u32 cfg_cookie; + + u32 num_malones; + u32 malone_base_addr[MEDIAIP_MAX_NUM_MALONES]; + u32 hif_offset[MEDIAIP_MAX_NUM_MALONES]; + u32 malone_irq_pin[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS]; + u32 malone_irq_target[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS]; + + u32 num_windsors; + u32 windsor_base_addr[MEDIAIP_MAX_NUM_WINDSORS]; + u32 windsor_irq_pin[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS]; + u32 windsor_irq_target[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS]; + + u32 cmd_irq_pin[MEDIAIP_MAX_NUM_CMD_IRQ_PINS]; + u32 cmd_irq_target[MEDIAIP_MAX_NUM_CMD_IRQ_PINS]; + + u32 msg_irq_pin[MEDIAIP_MAX_NUM_MSG_IRQ_PINS]; + u32 msg_irq_target[MEDIAIP_MAX_NUM_MSG_IRQ_PINS]; + + u32 sys_clk_freq; + u32 num_timers; + u32 timer_base_addr; + u32 timer_irq_pin[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS]; + u32 timer_irq_target[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS]; + u32 timer_slots[MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS]; + + u32 gic_base_addr; + u32 uart_base_addr; + + u32 dpv_base_addr; + u32 dpv_irq_pin; + u32 dpv_irq_target; + + u32 pixif_base_addr; + + u32 pal_trace_level; + u32 pal_trace_destination; + + u32 pal_trace_level1; + u32 pal_trace_destination1; + + u32 heap_base; + u32 heap_size; + + u32 cache_base_addr[2]; +}; + +int vpu_imx8q_setup_dec(struct vpu_dev *vpu); +int vpu_imx8q_setup_enc(struct vpu_dev *vpu); +int vpu_imx8q_setup(struct vpu_dev *vpu); +int vpu_imx8q_reset(struct vpu_dev *vpu); +int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id); +int vpu_imx8q_boot_core(struct vpu_core *core); +int vpu_imx8q_get_power_state(struct vpu_core *core); +int vpu_imx8q_on_firmware_loaded(struct vpu_core *core); +int vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size); +bool vpu_imx8q_check_codec(enum vpu_core_type type); +bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt); + +#endif diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c new file mode 100644 index 000000000000..446a9de0cc11 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -0,0 +1,1644 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/rational.h> +#include <linux/time64.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include <linux/videodev2.h> +#include "vpu.h" +#include "vpu_rpc.h" +#include "vpu_defs.h" +#include "vpu_helpers.h" +#include "vpu_v4l2.h" +#include "vpu_cmds.h" +#include "vpu_imx8q.h" +#include "vpu_malone.h" + +#define CMD_SIZE 25600 +#define MSG_SIZE 25600 +#define CODEC_SIZE 0x1000 +#define JPEG_SIZE 0x1000 +#define SEQ_SIZE 0x1000 +#define GOP_SIZE 0x1000 +#define PIC_SIZE 0x1000 +#define QMETER_SIZE 0x1000 +#define DBGLOG_SIZE 0x10000 +#define DEBUG_SIZE 0x80000 +#define ENG_SIZE 0x1000 +#define MALONE_SKIPPED_FRAME_ID 0x555 + +#define MALONE_ALIGN_MBI 0x800 +#define MALONE_DCP_CHUNK_BIT 16 +#define MALONE_DCP_SIZE_MAX 0x3000000 +#define MALONE_DCP_SIZE_MIN 0x100000 +#define MALONE_DCP_FIXED_MB_ALLOC 250 + +#define CONFIG_SET(val, cfg, pos, mask) \ + (*(cfg) |= (((val) << (pos)) & (mask))) +//x means source data , y means destination data +#define STREAM_CONFIG_FORMAT_SET(x, y) CONFIG_SET(x, y, 0, 0x0000000F) +#define STREAM_CONFIG_STRBUFIDX_SET(x, y) CONFIG_SET(x, y, 8, 0x00000300) +#define STREAM_CONFIG_NOSEQ_SET(x, y) CONFIG_SET(x, y, 10, 0x00000400) +#define STREAM_CONFIG_DEBLOCK_SET(x, y) CONFIG_SET(x, y, 11, 0x00000800) +#define STREAM_CONFIG_DERING_SET(x, y) CONFIG_SET(x, y, 12, 0x00001000) +#define STREAM_CONFIG_IBWAIT_SET(x, y) CONFIG_SET(x, y, 13, 0x00002000) +#define STREAM_CONFIG_FBC_SET(x, y) CONFIG_SET(x, y, 14, 0x00004000) +#define STREAM_CONFIG_PLAY_MODE_SET(x, y) CONFIG_SET(x, y, 16, 0x00030000) +#define STREAM_CONFIG_ENABLE_DCP_SET(x, y) CONFIG_SET(x, y, 20, 0x00100000) +#define STREAM_CONFIG_NUM_STR_BUF_SET(x, y) CONFIG_SET(x, y, 21, 0x00600000) +#define STREAM_CONFIG_MALONE_USAGE_SET(x, y) CONFIG_SET(x, y, 23, 0x01800000) +#define STREAM_CONFIG_MULTI_VID_SET(x, y) CONFIG_SET(x, y, 25, 0x02000000) +#define STREAM_CONFIG_OBFUSC_EN_SET(x, y) CONFIG_SET(x, y, 26, 0x04000000) +#define STREAM_CONFIG_RC4_EN_SET(x, y) CONFIG_SET(x, y, 27, 0x08000000) +#define STREAM_CONFIG_MCX_SET(x, y) CONFIG_SET(x, y, 28, 0x10000000) +#define STREAM_CONFIG_PES_SET(x, y) CONFIG_SET(x, y, 29, 0x20000000) +#define STREAM_CONFIG_NUM_DBE_SET(x, y) CONFIG_SET(x, y, 30, 0x40000000) +#define STREAM_CONFIG_FS_CTRL_MODE_SET(x, y) CONFIG_SET(x, y, 31, 0x80000000) + +enum vpu_malone_stream_input_mode { + INVALID_MODE = 0, + FRAME_LVL, + NON_FRAME_LVL +}; + +enum vpu_malone_format { + MALONE_FMT_NULL = 0x0, + MALONE_FMT_AVC = 0x1, + MALONE_FMT_MP2 = 0x2, + MALONE_FMT_VC1 = 0x3, + MALONE_FMT_AVS = 0x4, + MALONE_FMT_ASP = 0x5, + MALONE_FMT_JPG = 0x6, + MALONE_FMT_RV = 0x7, + MALONE_FMT_VP6 = 0x8, + MALONE_FMT_SPK = 0x9, + MALONE_FMT_VP8 = 0xA, + MALONE_FMT_HEVC = 0xB, + MALONE_FMT_LAST = MALONE_FMT_HEVC +}; + +enum { + VID_API_CMD_NULL = 0x00, + VID_API_CMD_PARSE_NEXT_SEQ = 0x01, + VID_API_CMD_PARSE_NEXT_I = 0x02, + VID_API_CMD_PARSE_NEXT_IP = 0x03, + VID_API_CMD_PARSE_NEXT_ANY = 0x04, + VID_API_CMD_DEC_PIC = 0x05, + VID_API_CMD_UPDATE_ES_WR_PTR = 0x06, + VID_API_CMD_UPDATE_ES_RD_PTR = 0x07, + VID_API_CMD_UPDATE_UDATA = 0x08, + VID_API_CMD_GET_FSINFO = 0x09, + VID_API_CMD_SKIP_PIC = 0x0a, + VID_API_CMD_DEC_CHUNK = 0x0b, + VID_API_CMD_START = 0x10, + VID_API_CMD_STOP = 0x11, + VID_API_CMD_ABORT = 0x12, + VID_API_CMD_RST_BUF = 0x13, + VID_API_CMD_FS_RELEASE = 0x15, + VID_API_CMD_MEM_REGION_ATTACH = 0x16, + VID_API_CMD_MEM_REGION_DETACH = 0x17, + VID_API_CMD_MVC_VIEW_SELECT = 0x18, + VID_API_CMD_FS_ALLOC = 0x19, + VID_API_CMD_DBG_GET_STATUS = 0x1C, + VID_API_CMD_DBG_START_LOG = 0x1D, + VID_API_CMD_DBG_STOP_LOG = 0x1E, + VID_API_CMD_DBG_DUMP_LOG = 0x1F, + VID_API_CMD_YUV_READY = 0x20, + VID_API_CMD_TS = 0x21, + + VID_API_CMD_FIRM_RESET = 0x40, + + VID_API_CMD_SNAPSHOT = 0xAA, + VID_API_CMD_ROLL_SNAPSHOT = 0xAB, + VID_API_CMD_LOCK_SCHEDULER = 0xAC, + VID_API_CMD_UNLOCK_SCHEDULER = 0xAD, + VID_API_CMD_CQ_FIFO_DUMP = 0xAE, + VID_API_CMD_DBG_FIFO_DUMP = 0xAF, + VID_API_CMD_SVC_ILP = 0xBB, + VID_API_CMD_FW_STATUS = 0xF0, + VID_API_CMD_INVALID = 0xFF +}; + +enum { + VID_API_EVENT_NULL = 0x00, + VID_API_EVENT_RESET_DONE = 0x01, + VID_API_EVENT_SEQ_HDR_FOUND = 0x02, + VID_API_EVENT_PIC_HDR_FOUND = 0x03, + VID_API_EVENT_PIC_DECODED = 0x04, + VID_API_EVENT_FIFO_LOW = 0x05, + VID_API_EVENT_FIFO_HIGH = 0x06, + VID_API_EVENT_FIFO_EMPTY = 0x07, + VID_API_EVENT_FIFO_FULL = 0x08, + VID_API_EVENT_BS_ERROR = 0x09, + VID_API_EVENT_UDATA_FIFO_UPTD = 0x0A, + VID_API_EVENT_RES_CHANGE = 0x0B, + VID_API_EVENT_FIFO_OVF = 0x0C, + VID_API_EVENT_CHUNK_DECODED = 0x0D, + VID_API_EVENT_REQ_FRAME_BUFF = 0x10, + VID_API_EVENT_FRAME_BUFF_RDY = 0x11, + VID_API_EVENT_REL_FRAME_BUFF = 0x12, + VID_API_EVENT_STR_BUF_RST = 0x13, + VID_API_EVENT_RET_PING = 0x14, + VID_API_EVENT_QMETER = 0x15, + VID_API_EVENT_STR_FMT_CHANGE = 0x16, + VID_API_EVENT_FIRMWARE_XCPT = 0x17, + VID_API_EVENT_START_DONE = 0x18, + VID_API_EVENT_STOPPED = 0x19, + VID_API_EVENT_ABORT_DONE = 0x1A, + VID_API_EVENT_FINISHED = 0x1B, + VID_API_EVENT_DBG_STAT_UPDATE = 0x1C, + VID_API_EVENT_DBG_LOG_STARTED = 0x1D, + VID_API_EVENT_DBG_LOG_STOPPED = 0x1E, + VID_API_EVENT_DBG_LOG_UPDATED = 0x1F, + VID_API_EVENT_DBG_MSG_DEC = 0x20, + VID_API_EVENT_DEC_SC_ERR = 0x21, + VID_API_EVENT_CQ_FIFO_DUMP = 0x22, + VID_API_EVENT_DBG_FIFO_DUMP = 0x23, + VID_API_EVENT_DEC_CHECK_RES = 0x24, + VID_API_EVENT_DEC_CFG_INFO = 0x25, + VID_API_EVENT_UNSUPPORTED_STREAM = 0x26, + VID_API_EVENT_STR_SUSPENDED = 0x30, + VID_API_EVENT_SNAPSHOT_DONE = 0x40, + VID_API_EVENT_FW_STATUS = 0xF0, + VID_API_EVENT_INVALID = 0xFF +}; + +struct vpu_malone_buffer_desc { + struct vpu_rpc_buffer_desc buffer; + u32 low; + u32 high; +}; + +struct vpu_malone_str_buffer { + u32 wptr; + u32 rptr; + u32 start; + u32 end; + u32 lwm; +}; + +struct vpu_malone_picth_info { + u32 frame_pitch; +}; + +struct vpu_malone_table_desc { + u32 array_base; + u32 size; +}; + +struct vpu_malone_dbglog_desc { + u32 addr; + u32 size; + u32 level; + u32 reserved; +}; + +struct vpu_malone_frame_buffer { + u32 addr; + u32 size; +}; + +struct vpu_malone_udata { + u32 base; + u32 total_size; + u32 slot_size; +}; + +struct vpu_malone_buffer_info { + u32 stream_input_mode; + u32 stream_pic_input_count; + u32 stream_pic_parsed_count; + u32 stream_buffer_threshold; + u32 stream_pic_end_flag; +}; + +struct vpu_malone_encrypt_info { + u32 rec4key[8]; + u32 obfusc; +}; + +struct malone_iface { + u32 exec_base_addr; + u32 exec_area_size; + struct vpu_malone_buffer_desc cmd_buffer_desc; + struct vpu_malone_buffer_desc msg_buffer_desc; + u32 cmd_int_enable[VID_API_NUM_STREAMS]; + struct vpu_malone_picth_info stream_pitch_info[VID_API_NUM_STREAMS]; + u32 stream_config[VID_API_NUM_STREAMS]; + struct vpu_malone_table_desc codec_param_tab_desc; + struct vpu_malone_table_desc jpeg_param_tab_desc; + u32 stream_buffer_desc[VID_API_NUM_STREAMS][VID_API_MAX_BUF_PER_STR]; + struct vpu_malone_table_desc seq_info_tab_desc; + struct vpu_malone_table_desc pic_info_tab_desc; + struct vpu_malone_table_desc gop_info_tab_desc; + struct vpu_malone_table_desc qmeter_info_tab_desc; + u32 stream_error[VID_API_NUM_STREAMS]; + u32 fw_version; + u32 fw_offset; + u32 max_streams; + struct vpu_malone_dbglog_desc dbglog_desc; + struct vpu_rpc_buffer_desc api_cmd_buffer_desc[VID_API_NUM_STREAMS]; + struct vpu_malone_udata udata_buffer[VID_API_NUM_STREAMS]; + struct vpu_malone_buffer_desc debug_buffer_desc; + struct vpu_malone_buffer_desc eng_access_buff_desc[VID_API_NUM_STREAMS]; + u32 encrypt_info[VID_API_NUM_STREAMS]; + struct vpu_rpc_system_config system_cfg; + u32 api_version; + struct vpu_malone_buffer_info stream_buff_info[VID_API_NUM_STREAMS]; +}; + +struct malone_jpg_params { + u32 rotation_angle; + u32 horiz_scale_factor; + u32 vert_scale_factor; + u32 rotation_mode; + u32 rgb_mode; + u32 chunk_mode; /* 0 ~ 1 */ + u32 last_chunk; /* 0 ~ 1 */ + u32 chunk_rows; /* 0 ~ 255 */ + u32 num_bytes; + u32 jpg_crop_x; + u32 jpg_crop_y; + u32 jpg_crop_width; + u32 jpg_crop_height; + u32 jpg_mjpeg_mode; + u32 jpg_mjpeg_interlaced; +}; + +struct malone_codec_params { + u32 disp_imm; + u32 fourcc; + u32 codec_version; + u32 frame_rate; + u32 dbglog_enable; + u32 bsdma_lwm; + u32 bbd_coring; + u32 bbd_s_thr_row; + u32 bbd_p_thr_row; + u32 bbd_s_thr_logo_row; + u32 bbd_p_thr_logo_row; + u32 bbd_s_thr_col; + u32 bbd_p_thr_col; + u32 bbd_chr_thr_row; + u32 bbd_chr_thr_col; + u32 bbd_uv_mid_level; + u32 bbd_excl_win_mb_left; + u32 bbd_excl_win_mb_right; +}; + +struct malone_padding_scode { + u32 scode_type; + u32 pixelformat; + u32 data[2]; +}; + +struct malone_fmt_mapping { + u32 pixelformat; + enum vpu_malone_format malone_format; +}; + +struct malone_scode_t { + struct vpu_inst *inst; + struct vb2_buffer *vb; + u32 wptr; + u32 need_data; +}; + +struct malone_scode_handler { + u32 pixelformat; + int (*insert_scode_seq)(struct malone_scode_t *scode); + int (*insert_scode_pic)(struct malone_scode_t *scode); +}; + +struct vpu_dec_ctrl { + struct malone_codec_params *codec_param; + struct malone_jpg_params *jpg; + void *seq_mem; + void *pic_mem; + void *gop_mem; + void *qmeter_mem; + void *dbglog_mem; + struct vpu_malone_str_buffer __iomem *str_buf[VID_API_NUM_STREAMS]; + u32 buf_addr[VID_API_NUM_STREAMS]; +}; + +u32 vpu_malone_get_data_size(void) +{ + return sizeof(struct vpu_dec_ctrl); +} + +void vpu_malone_init_rpc(struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, dma_addr_t boot_addr) +{ + struct malone_iface *iface; + struct vpu_dec_ctrl *hc; + unsigned long base_phy_addr; + unsigned long phy_addr; + unsigned long offset; + unsigned int i; + + if (rpc->phys < boot_addr) + return; + + iface = rpc->virt; + base_phy_addr = rpc->phys - boot_addr; + hc = shared->priv; + + shared->iface = iface; + shared->boot_addr = boot_addr; + + iface->exec_base_addr = base_phy_addr; + iface->exec_area_size = rpc->length; + + offset = sizeof(struct malone_iface); + phy_addr = base_phy_addr + offset; + + shared->cmd_desc = &iface->cmd_buffer_desc.buffer; + shared->cmd_mem_vir = rpc->virt + offset; + iface->cmd_buffer_desc.buffer.start = + iface->cmd_buffer_desc.buffer.rptr = + iface->cmd_buffer_desc.buffer.wptr = phy_addr; + iface->cmd_buffer_desc.buffer.end = iface->cmd_buffer_desc.buffer.start + CMD_SIZE; + offset += CMD_SIZE; + phy_addr = base_phy_addr + offset; + + shared->msg_desc = &iface->msg_buffer_desc.buffer; + shared->msg_mem_vir = rpc->virt + offset; + iface->msg_buffer_desc.buffer.start = + iface->msg_buffer_desc.buffer.wptr = + iface->msg_buffer_desc.buffer.rptr = phy_addr; + iface->msg_buffer_desc.buffer.end = iface->msg_buffer_desc.buffer.start + MSG_SIZE; + offset += MSG_SIZE; + phy_addr = base_phy_addr + offset; + + iface->codec_param_tab_desc.array_base = phy_addr; + hc->codec_param = rpc->virt + offset; + offset += CODEC_SIZE; + phy_addr = base_phy_addr + offset; + + iface->jpeg_param_tab_desc.array_base = phy_addr; + hc->jpg = rpc->virt + offset; + offset += JPEG_SIZE; + phy_addr = base_phy_addr + offset; + + iface->seq_info_tab_desc.array_base = phy_addr; + hc->seq_mem = rpc->virt + offset; + offset += SEQ_SIZE; + phy_addr = base_phy_addr + offset; + + iface->pic_info_tab_desc.array_base = phy_addr; + hc->pic_mem = rpc->virt + offset; + offset += PIC_SIZE; + phy_addr = base_phy_addr + offset; + + iface->gop_info_tab_desc.array_base = phy_addr; + hc->gop_mem = rpc->virt + offset; + offset += GOP_SIZE; + phy_addr = base_phy_addr + offset; + + iface->qmeter_info_tab_desc.array_base = phy_addr; + hc->qmeter_mem = rpc->virt + offset; + offset += QMETER_SIZE; + phy_addr = base_phy_addr + offset; + + iface->dbglog_desc.addr = phy_addr; + iface->dbglog_desc.size = DBGLOG_SIZE; + hc->dbglog_mem = rpc->virt + offset; + offset += DBGLOG_SIZE; + phy_addr = base_phy_addr + offset; + + for (i = 0; i < VID_API_NUM_STREAMS; i++) { + iface->eng_access_buff_desc[i].buffer.start = + iface->eng_access_buff_desc[i].buffer.wptr = + iface->eng_access_buff_desc[i].buffer.rptr = phy_addr; + iface->eng_access_buff_desc[i].buffer.end = + iface->eng_access_buff_desc[i].buffer.start + ENG_SIZE; + offset += ENG_SIZE; + phy_addr = base_phy_addr + offset; + } + + for (i = 0; i < VID_API_NUM_STREAMS; i++) { + iface->encrypt_info[i] = phy_addr; + offset += sizeof(struct vpu_malone_encrypt_info); + phy_addr = base_phy_addr + offset; + } + + rpc->bytesused = offset; +} + +void vpu_malone_set_log_buf(struct vpu_shared_addr *shared, + struct vpu_buffer *log) +{ + struct malone_iface *iface = shared->iface; + + iface->debug_buffer_desc.buffer.start = + iface->debug_buffer_desc.buffer.wptr = + iface->debug_buffer_desc.buffer.rptr = log->phys - shared->boot_addr; + iface->debug_buffer_desc.buffer.end = iface->debug_buffer_desc.buffer.start + log->length; +} + +static u32 get_str_buffer_offset(u32 instance) +{ + return DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * instance; +} + +void vpu_malone_set_system_cfg(struct vpu_shared_addr *shared, + u32 regs_base, void __iomem *regs, u32 core_id) +{ + struct malone_iface *iface = shared->iface; + struct vpu_rpc_system_config *config = &iface->system_cfg; + struct vpu_dec_ctrl *hc = shared->priv; + int i; + + vpu_imx8q_set_system_cfg_common(config, regs_base, core_id); + for (i = 0; i < VID_API_NUM_STREAMS; i++) { + u32 offset = get_str_buffer_offset(i); + + hc->buf_addr[i] = regs_base + offset; + hc->str_buf[i] = regs + offset; + } +} + +u32 vpu_malone_get_version(struct vpu_shared_addr *shared) +{ + struct malone_iface *iface = shared->iface; + + return iface->fw_version; +} + +int vpu_malone_get_stream_buffer_size(struct vpu_shared_addr *shared) +{ + return 0xc00000; +} + +int vpu_malone_config_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *buf) +{ + struct malone_iface *iface = shared->iface; + struct vpu_dec_ctrl *hc = shared->priv; + struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance]; + + writel(buf->phys, &str_buf->start); + writel(buf->phys, &str_buf->rptr); + writel(buf->phys, &str_buf->wptr); + writel(buf->phys + buf->length, &str_buf->end); + writel(0x1, &str_buf->lwm); + + iface->stream_buffer_desc[instance][0] = hc->buf_addr[instance]; + + return 0; +} + +int vpu_malone_get_stream_buffer_desc(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_rpc_buffer_desc *desc) +{ + struct vpu_dec_ctrl *hc = shared->priv; + struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance]; + + if (desc) { + desc->wptr = readl(&str_buf->wptr); + desc->rptr = readl(&str_buf->rptr); + desc->start = readl(&str_buf->start); + desc->end = readl(&str_buf->end); + } + + return 0; +} + +static void vpu_malone_update_wptr(struct vpu_malone_str_buffer __iomem *str_buf, u32 wptr) +{ + /*update wptr after data is written*/ + mb(); + writel(wptr, &str_buf->wptr); +} + +static void vpu_malone_update_rptr(struct vpu_malone_str_buffer __iomem *str_buf, u32 rptr) +{ + /*update rptr after data is read*/ + mb(); + writel(rptr, &str_buf->rptr); +} + +int vpu_malone_update_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, u32 ptr, bool write) +{ + struct vpu_dec_ctrl *hc = shared->priv; + struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance]; + + if (write) + vpu_malone_update_wptr(str_buf, ptr); + else + vpu_malone_update_rptr(str_buf, ptr); + + return 0; +} + +static struct malone_fmt_mapping fmt_mappings[] = { + {V4L2_PIX_FMT_H264, MALONE_FMT_AVC}, + {V4L2_PIX_FMT_H264_MVC, MALONE_FMT_AVC}, + {V4L2_PIX_FMT_HEVC, MALONE_FMT_HEVC}, + {V4L2_PIX_FMT_VC1_ANNEX_G, MALONE_FMT_VC1}, + {V4L2_PIX_FMT_VC1_ANNEX_L, MALONE_FMT_VC1}, + {V4L2_PIX_FMT_MPEG2, MALONE_FMT_MP2}, + {V4L2_PIX_FMT_MPEG4, MALONE_FMT_ASP}, + {V4L2_PIX_FMT_XVID, MALONE_FMT_ASP}, + {V4L2_PIX_FMT_H263, MALONE_FMT_ASP}, + {V4L2_PIX_FMT_JPEG, MALONE_FMT_JPG}, + {V4L2_PIX_FMT_VP8, MALONE_FMT_VP8}, +}; + +static enum vpu_malone_format vpu_malone_format_remap(u32 pixelformat) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(fmt_mappings); i++) { + if (pixelformat == fmt_mappings[i].pixelformat) + return fmt_mappings[i].malone_format; + } + + return MALONE_FMT_NULL; +} + +static void vpu_malone_set_stream_cfg(struct vpu_shared_addr *shared, + u32 instance, + enum vpu_malone_format malone_format) +{ + struct malone_iface *iface = shared->iface; + u32 *curr_str_cfg = &iface->stream_config[instance]; + + *curr_str_cfg = 0; + STREAM_CONFIG_FORMAT_SET(malone_format, curr_str_cfg); + STREAM_CONFIG_STRBUFIDX_SET(0, curr_str_cfg); + STREAM_CONFIG_NOSEQ_SET(0, curr_str_cfg); + STREAM_CONFIG_DEBLOCK_SET(0, curr_str_cfg); + STREAM_CONFIG_DERING_SET(0, curr_str_cfg); + STREAM_CONFIG_PLAY_MODE_SET(0x3, curr_str_cfg); + STREAM_CONFIG_FS_CTRL_MODE_SET(0x1, curr_str_cfg); + STREAM_CONFIG_ENABLE_DCP_SET(1, curr_str_cfg); + STREAM_CONFIG_NUM_STR_BUF_SET(1, curr_str_cfg); + STREAM_CONFIG_MALONE_USAGE_SET(1, curr_str_cfg); + STREAM_CONFIG_MULTI_VID_SET(0, curr_str_cfg); + STREAM_CONFIG_OBFUSC_EN_SET(0, curr_str_cfg); + STREAM_CONFIG_RC4_EN_SET(0, curr_str_cfg); + STREAM_CONFIG_MCX_SET(1, curr_str_cfg); + STREAM_CONFIG_PES_SET(0, curr_str_cfg); + STREAM_CONFIG_NUM_DBE_SET(1, curr_str_cfg); +} + +static int vpu_malone_set_params(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_decode_params *params) +{ + struct malone_iface *iface = shared->iface; + struct vpu_dec_ctrl *hc = shared->priv; + enum vpu_malone_format malone_format; + + malone_format = vpu_malone_format_remap(params->codec_format); + iface->udata_buffer[instance].base = params->udata.base; + iface->udata_buffer[instance].slot_size = params->udata.size; + + vpu_malone_set_stream_cfg(shared, instance, malone_format); + + if (malone_format == MALONE_FMT_JPG) { + //1:JPGD_MJPEG_MODE_A; 2:JPGD_MJPEG_MODE_B + hc->jpg[instance].jpg_mjpeg_mode = 1; + //0: JPGD_MJPEG_PROGRESSIVE + hc->jpg[instance].jpg_mjpeg_interlaced = 0; + } + + hc->codec_param[instance].disp_imm = params->b_dis_reorder ? 1 : 0; + hc->codec_param[instance].dbglog_enable = 0; + iface->dbglog_desc.level = 0; + + if (params->b_non_frame) + iface->stream_buff_info[instance].stream_input_mode = NON_FRAME_LVL; + else + iface->stream_buff_info[instance].stream_input_mode = FRAME_LVL; + iface->stream_buff_info[instance].stream_buffer_threshold = 0; + iface->stream_buff_info[instance].stream_pic_input_count = 0; + + return 0; +} + +static bool vpu_malone_is_non_frame_mode(struct vpu_shared_addr *shared, u32 instance) +{ + struct malone_iface *iface = shared->iface; + + if (iface->stream_buff_info[instance].stream_input_mode == NON_FRAME_LVL) + return true; + + return false; +} + +static int vpu_malone_update_params(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_decode_params *params) +{ + struct malone_iface *iface = shared->iface; + + if (params->end_flag) + iface->stream_buff_info[instance].stream_pic_end_flag = params->end_flag; + params->end_flag = 0; + + return 0; +} + +int vpu_malone_set_decode_params(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_decode_params *params, + u32 update) +{ + if (!params) + return -EINVAL; + + if (!update) + return vpu_malone_set_params(shared, instance, params); + else + return vpu_malone_update_params(shared, instance, params); +} + +static struct vpu_pair malone_cmds[] = { + {VPU_CMD_ID_START, VID_API_CMD_START}, + {VPU_CMD_ID_STOP, VID_API_CMD_STOP}, + {VPU_CMD_ID_ABORT, VID_API_CMD_ABORT}, + {VPU_CMD_ID_RST_BUF, VID_API_CMD_RST_BUF}, + {VPU_CMD_ID_SNAPSHOT, VID_API_CMD_SNAPSHOT}, + {VPU_CMD_ID_FIRM_RESET, VID_API_CMD_FIRM_RESET}, + {VPU_CMD_ID_FS_ALLOC, VID_API_CMD_FS_ALLOC}, + {VPU_CMD_ID_FS_RELEASE, VID_API_CMD_FS_RELEASE}, + {VPU_CMD_ID_TIMESTAMP, VID_API_CMD_TS}, + {VPU_CMD_ID_DEBUG, VID_API_CMD_FW_STATUS}, +}; + +static struct vpu_pair malone_msgs[] = { + {VPU_MSG_ID_RESET_DONE, VID_API_EVENT_RESET_DONE}, + {VPU_MSG_ID_START_DONE, VID_API_EVENT_START_DONE}, + {VPU_MSG_ID_STOP_DONE, VID_API_EVENT_STOPPED}, + {VPU_MSG_ID_ABORT_DONE, VID_API_EVENT_ABORT_DONE}, + {VPU_MSG_ID_BUF_RST, VID_API_EVENT_STR_BUF_RST}, + {VPU_MSG_ID_PIC_EOS, VID_API_EVENT_FINISHED}, + {VPU_MSG_ID_SEQ_HDR_FOUND, VID_API_EVENT_SEQ_HDR_FOUND}, + {VPU_MSG_ID_RES_CHANGE, VID_API_EVENT_RES_CHANGE}, + {VPU_MSG_ID_PIC_HDR_FOUND, VID_API_EVENT_PIC_HDR_FOUND}, + {VPU_MSG_ID_PIC_DECODED, VID_API_EVENT_PIC_DECODED}, + {VPU_MSG_ID_DEC_DONE, VID_API_EVENT_FRAME_BUFF_RDY}, + {VPU_MSG_ID_FRAME_REQ, VID_API_EVENT_REQ_FRAME_BUFF}, + {VPU_MSG_ID_FRAME_RELEASE, VID_API_EVENT_REL_FRAME_BUFF}, + {VPU_MSG_ID_FIFO_LOW, VID_API_EVENT_FIFO_LOW}, + {VPU_MSG_ID_BS_ERROR, VID_API_EVENT_BS_ERROR}, + {VPU_MSG_ID_UNSUPPORTED, VID_API_EVENT_UNSUPPORTED_STREAM}, + {VPU_MSG_ID_FIRMWARE_XCPT, VID_API_EVENT_FIRMWARE_XCPT}, +}; + +static void vpu_malone_pack_fs_alloc(struct vpu_rpc_event *pkt, + struct vpu_fs_info *fs) +{ + const u32 fs_type[] = { + [MEM_RES_FRAME] = 0, + [MEM_RES_MBI] = 1, + [MEM_RES_DCP] = 2, + }; + + pkt->hdr.num = 7; + pkt->data[0] = fs->id | (fs->tag << 24); + pkt->data[1] = fs->luma_addr; + if (fs->type == MEM_RES_FRAME) { + /* + * if luma_addr equal to chroma_addr, + * means luma(plane[0]) and chromau(plane[1]) used the + * same fd -- usage of NXP codec2. Need to manually + * offset chroma addr. + */ + if (fs->luma_addr == fs->chroma_addr) + fs->chroma_addr = fs->luma_addr + fs->luma_size; + pkt->data[2] = fs->luma_addr + fs->luma_size / 2; + pkt->data[3] = fs->chroma_addr; + pkt->data[4] = fs->chroma_addr + fs->chromau_size / 2; + pkt->data[5] = fs->bytesperline; + } else { + pkt->data[2] = fs->luma_size; + pkt->data[3] = 0; + pkt->data[4] = 0; + pkt->data[5] = 0; + } + pkt->data[6] = fs_type[fs->type]; +} + +static void vpu_malone_pack_fs_release(struct vpu_rpc_event *pkt, + struct vpu_fs_info *fs) +{ + pkt->hdr.num = 1; + pkt->data[0] = fs->id | (fs->tag << 24); +} + +static void vpu_malone_pack_timestamp(struct vpu_rpc_event *pkt, + struct vpu_ts_info *info) +{ + struct timespec64 ts = ns_to_timespec64(info->timestamp); + + pkt->hdr.num = 3; + + pkt->data[0] = ts.tv_sec; + pkt->data[1] = ts.tv_nsec; + pkt->data[2] = info->size; +} + +int vpu_malone_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data) +{ + int ret; + + ret = vpu_find_dst_by_src(malone_cmds, ARRAY_SIZE(malone_cmds), id); + if (ret < 0) + return ret; + + pkt->hdr.id = ret; + pkt->hdr.num = 0; + pkt->hdr.index = index; + + switch (id) { + case VPU_CMD_ID_FS_ALLOC: + vpu_malone_pack_fs_alloc(pkt, data); + break; + case VPU_CMD_ID_FS_RELEASE: + vpu_malone_pack_fs_release(pkt, data); + break; + case VPU_CMD_ID_TIMESTAMP: + vpu_malone_pack_timestamp(pkt, data); + break; + } + + pkt->hdr.index = index; + return 0; +} + +int vpu_malone_convert_msg_id(u32 id) +{ + return vpu_find_src_by_dst(malone_msgs, ARRAY_SIZE(malone_msgs), id); +} + +static void vpu_malone_fill_planes(struct vpu_dec_codec_info *info) +{ + u32 interlaced = info->progressive ? 0 : 1; + + info->bytesperline[0] = 0; + info->sizeimage[0] = vpu_helper_get_plane_size(info->pixfmt, + info->decoded_width, + info->decoded_height, + 0, + info->stride, + interlaced, + &info->bytesperline[0]); + info->bytesperline[1] = 0; + info->sizeimage[1] = vpu_helper_get_plane_size(info->pixfmt, + info->decoded_width, + info->decoded_height, + 1, + info->stride, + interlaced, + &info->bytesperline[1]); +} + +static void vpu_malone_init_seq_hdr(struct vpu_dec_codec_info *info) +{ + u32 chunks = info->num_dfe_area >> MALONE_DCP_CHUNK_BIT; + + vpu_malone_fill_planes(info); + + info->mbi_size = (info->sizeimage[0] + info->sizeimage[1]) >> 2; + info->mbi_size = ALIGN(info->mbi_size, MALONE_ALIGN_MBI); + + info->dcp_size = MALONE_DCP_SIZE_MAX; + if (chunks) { + u32 mb_num; + u32 mb_w; + u32 mb_h; + + mb_w = DIV_ROUND_UP(info->decoded_width, 16); + mb_h = DIV_ROUND_UP(info->decoded_height, 16); + mb_num = mb_w * mb_h; + info->dcp_size = mb_num * MALONE_DCP_FIXED_MB_ALLOC * chunks; + info->dcp_size = clamp_t(u32, info->dcp_size, + MALONE_DCP_SIZE_MIN, MALONE_DCP_SIZE_MAX); + } +} + +static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt, + struct vpu_dec_codec_info *info) +{ + info->num_ref_frms = pkt->data[0]; + info->num_dpb_frms = pkt->data[1]; + info->num_dfe_area = pkt->data[2]; + info->progressive = pkt->data[3]; + info->width = pkt->data[5]; + info->height = pkt->data[4]; + info->decoded_width = pkt->data[12]; + info->decoded_height = pkt->data[11]; + info->frame_rate.numerator = 1000; + info->frame_rate.denominator = pkt->data[8]; + info->dsp_asp_ratio = pkt->data[9]; + info->level_idc = pkt->data[10]; + info->bit_depth_luma = pkt->data[13]; + info->bit_depth_chroma = pkt->data[14]; + info->chroma_fmt = pkt->data[15]; + info->color_primaries = vpu_color_cvrt_primaries_i2v(pkt->data[16]); + info->transfer_chars = vpu_color_cvrt_transfers_i2v(pkt->data[17]); + info->matrix_coeffs = vpu_color_cvrt_matrix_i2v(pkt->data[18]); + info->full_range = vpu_color_cvrt_full_range_i2v(pkt->data[19]); + info->vui_present = pkt->data[20]; + info->mvc_num_views = pkt->data[21]; + info->offset_x = pkt->data[23]; + info->offset_y = pkt->data[25]; + info->tag = pkt->data[27]; + if (info->bit_depth_luma > 8) + info->pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128; + else + info->pixfmt = V4L2_PIX_FMT_NV12M_8L128; + if (info->frame_rate.numerator && info->frame_rate.denominator) { + unsigned long n, d; + + rational_best_approximation(info->frame_rate.numerator, + info->frame_rate.denominator, + info->frame_rate.numerator, + info->frame_rate.denominator, + &n, &d); + info->frame_rate.numerator = n; + info->frame_rate.denominator = d; + } + vpu_malone_init_seq_hdr(info); +} + +static void vpu_malone_unpack_pic_info(struct vpu_rpc_event *pkt, + struct vpu_dec_pic_info *info) +{ + info->id = pkt->data[7]; + info->luma = pkt->data[0]; + info->start = pkt->data[10]; + info->end = pkt->data[12]; + info->pic_size = pkt->data[11]; + info->stride = pkt->data[5]; + info->consumed_count = pkt->data[13]; + if (info->id == MALONE_SKIPPED_FRAME_ID) + info->skipped = 1; + else + info->skipped = 0; +} + +static void vpu_malone_unpack_req_frame(struct vpu_rpc_event *pkt, + struct vpu_fs_info *info) +{ + info->type = pkt->data[1]; +} + +static void vpu_malone_unpack_rel_frame(struct vpu_rpc_event *pkt, + struct vpu_fs_info *info) +{ + info->id = pkt->data[0]; + info->type = pkt->data[1]; + info->not_displayed = pkt->data[2]; +} + +static void vpu_malone_unpack_buff_rdy(struct vpu_rpc_event *pkt, + struct vpu_dec_pic_info *info) +{ + struct timespec64 ts = { pkt->data[9], pkt->data[10] }; + + info->id = pkt->data[0]; + info->luma = pkt->data[1]; + info->stride = pkt->data[3]; + if (info->id == MALONE_SKIPPED_FRAME_ID) + info->skipped = 1; + else + info->skipped = 0; + + info->timestamp = timespec64_to_ns(&ts); +} + +int vpu_malone_unpack_msg_data(struct vpu_rpc_event *pkt, void *data) +{ + if (!pkt || !data) + return -EINVAL; + + switch (pkt->hdr.id) { + case VID_API_EVENT_SEQ_HDR_FOUND: + vpu_malone_unpack_seq_hdr(pkt, data); + break; + case VID_API_EVENT_PIC_DECODED: + vpu_malone_unpack_pic_info(pkt, data); + break; + case VID_API_EVENT_REQ_FRAME_BUFF: + vpu_malone_unpack_req_frame(pkt, data); + break; + case VID_API_EVENT_REL_FRAME_BUFF: + vpu_malone_unpack_rel_frame(pkt, data); + break; + case VID_API_EVENT_FRAME_BUFF_RDY: + vpu_malone_unpack_buff_rdy(pkt, data); + break; + } + + return 0; +} + +static const struct malone_padding_scode padding_scodes[] = { + {SCODE_PADDING_EOS, V4L2_PIX_FMT_H264, {0x0B010000, 0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_H264_MVC, {0x0B010000, 0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_HEVC, {0x4A010000, 0x20}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_VC1_ANNEX_G, {0x0a010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_VC1_ANNEX_L, {0x0a010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_MPEG2, {0xCC010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_MPEG4, {0xb1010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0xefff0000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264, {0x0B010000, 0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264_MVC, {0x0B010000, 0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_HEVC, {0x4A010000, 0x20}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VC1_ANNEX_G, {0x0a010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VC1_ANNEX_L, {0x0a010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_MPEG2, {0xb7010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_MPEG4, {0xb1010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}}, + {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}}, + {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0x0, 0x0}}, + {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264, {0x15010000, 0x0}}, + {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264_MVC, {0x15010000, 0x0}}, +}; + +static const struct malone_padding_scode padding_scode_dft = {0x0, 0x0}; + +static const struct malone_padding_scode *get_padding_scode(u32 type, u32 fmt) +{ + const struct malone_padding_scode *s; + int i; + + for (i = 0; i < ARRAY_SIZE(padding_scodes); i++) { + s = &padding_scodes[i]; + + if (s->scode_type == type && s->pixelformat == fmt) + return s; + } + + if (type != SCODE_PADDING_BUFFLUSH) + return &padding_scode_dft; + + return NULL; +} + +static int vpu_malone_add_padding_scode(struct vpu_buffer *stream_buffer, + struct vpu_malone_str_buffer __iomem *str_buf, + u32 pixelformat, u32 scode_type) +{ + u32 wptr; + int size; + int total_size = 0; + const struct malone_padding_scode *ps; + const u32 padding_size = 4096; + int ret; + + ps = get_padding_scode(scode_type, pixelformat); + if (!ps) + return -EINVAL; + + wptr = readl(&str_buf->wptr); + if (wptr < stream_buffer->phys || wptr > stream_buffer->phys + stream_buffer->length) + return -EINVAL; + if (wptr == stream_buffer->phys + stream_buffer->length) + wptr = stream_buffer->phys; + size = ALIGN(wptr, 4) - wptr; + if (size) + vpu_helper_memset_stream_buffer(stream_buffer, &wptr, 0, size); + total_size += size; + + size = sizeof(ps->data); + ret = vpu_helper_copy_to_stream_buffer(stream_buffer, &wptr, size, (void *)ps->data); + if (ret < 0) + return -EINVAL; + total_size += size; + + size = padding_size - sizeof(ps->data); + vpu_helper_memset_stream_buffer(stream_buffer, &wptr, 0, size); + total_size += size; + + vpu_malone_update_wptr(str_buf, wptr); + return total_size; +} + +int vpu_malone_add_scode(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *stream_buffer, + u32 pixelformat, + u32 scode_type) +{ + struct vpu_dec_ctrl *hc = shared->priv; + struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance]; + int ret = -EINVAL; + + switch (scode_type) { + case SCODE_PADDING_EOS: + case SCODE_PADDING_ABORT: + case SCODE_PADDING_BUFFLUSH: + ret = vpu_malone_add_padding_scode(stream_buffer, str_buf, pixelformat, scode_type); + break; + default: + break; + } + + return ret; +} + +#define MALONE_PAYLOAD_HEADER_SIZE 16 +#define MALONE_CODEC_VERSION_ID 0x1 +#define MALONE_CODEC_ID_VC1_SIMPLE 0x10 +#define MALONE_CODEC_ID_VC1_MAIN 0x11 +#define MALONE_CODEC_ID_ARV8 0x28 +#define MALONE_CODEC_ID_ARV9 0x29 +#define MALONE_CODEC_ID_VP6 0x36 +#define MALONE_CODEC_ID_VP8 0x36 +#define MALONE_CODEC_ID_DIVX3 0x38 +#define MALONE_CODEC_ID_SPK 0x39 + +#define MALONE_VP8_IVF_SEQ_HEADER_LEN 32 +#define MALONE_VP8_IVF_FRAME_HEADER_LEN 8 + +#define MALONE_VC1_RCV_CODEC_V1_VERSION 0x85 +#define MALONE_VC1_RCV_CODEC_V2_VERSION 0xC5 +#define MALONE_VC1_RCV_NUM_FRAMES 0xFF +#define MALONE_VC1_RCV_SEQ_EXT_DATA_SIZE 4 +#define MALONE_VC1_RCV_SEQ_HEADER_LEN 20 +#define MALONE_VC1_RCV_PIC_HEADER_LEN 4 +#define MALONE_VC1_NAL_HEADER_LEN 4 +#define MALONE_VC1_CONTAIN_NAL(data) (((data) & 0x00FFFFFF) == 0x00010000) + +static void set_payload_hdr(u8 *dst, u32 scd_type, u32 codec_id, + u32 buffer_size, u32 width, u32 height) +{ + unsigned int payload_size; + /* payload_size = buffer_size + itself_size(16) - start_code(4) */ + payload_size = buffer_size + 12; + + dst[0] = 0x00; + dst[1] = 0x00; + dst[2] = 0x01; + dst[3] = scd_type; + + /* length */ + dst[4] = ((payload_size >> 16) & 0xff); + dst[5] = ((payload_size >> 8) & 0xff); + dst[6] = 0x4e; + dst[7] = ((payload_size >> 0) & 0xff); + + /* Codec ID and Version */ + dst[8] = codec_id; + dst[9] = MALONE_CODEC_VERSION_ID; + + /* width */ + dst[10] = ((width >> 8) & 0xff); + dst[11] = ((width >> 0) & 0xff); + dst[12] = 0x58; + + /* height */ + dst[13] = ((height >> 8) & 0xff); + dst[14] = ((height >> 0) & 0xff); + dst[15] = 0x50; +} + +static void set_vp8_ivf_seqhdr(u8 *dst, u32 width, u32 height) +{ + /* 0-3byte signature "DKIF" */ + dst[0] = 0x44; + dst[1] = 0x4b; + dst[2] = 0x49; + dst[3] = 0x46; + /* 4-5byte version: should be 0*/ + dst[4] = 0x00; + dst[5] = 0x00; + /* 6-7 length of Header */ + dst[6] = MALONE_VP8_IVF_SEQ_HEADER_LEN; + dst[7] = MALONE_VP8_IVF_SEQ_HEADER_LEN >> 8; + /* 8-11 VP8 fourcc */ + dst[8] = 0x56; + dst[9] = 0x50; + dst[10] = 0x38; + dst[11] = 0x30; + /* 12-13 width in pixels */ + dst[12] = width; + dst[13] = width >> 8; + /* 14-15 height in pixels */ + dst[14] = height; + dst[15] = height >> 8; + /* 16-19 frame rate */ + dst[16] = 0xe8; + dst[17] = 0x03; + dst[18] = 0x00; + dst[19] = 0x00; + /* 20-23 time scale */ + dst[20] = 0x01; + dst[21] = 0x00; + dst[22] = 0x00; + dst[23] = 0x00; + /* 24-27 number frames */ + dst[24] = 0xdf; + dst[25] = 0xf9; + dst[26] = 0x09; + dst[27] = 0x00; + /* 28-31 reserved */ +} + +static void set_vp8_ivf_pichdr(u8 *dst, u32 frame_size) +{ + /* + * firmware just parse 64-bit timestamp(8 bytes). + * As not transfer timestamp to firmware, use default value(ZERO). + * No need to do anything here + */ +} + +static void set_vc1_rcv_seqhdr(u8 *dst, u8 *src, u32 width, u32 height) +{ + u32 frames = MALONE_VC1_RCV_NUM_FRAMES; + u32 ext_data_size = MALONE_VC1_RCV_SEQ_EXT_DATA_SIZE; + + /* 0-2 Number of frames, used default value 0xFF */ + dst[0] = frames; + dst[1] = frames >> 8; + dst[2] = frames >> 16; + + /* 3 RCV version, used V1 */ + dst[3] = MALONE_VC1_RCV_CODEC_V1_VERSION; + + /* 4-7 extension data size */ + dst[4] = ext_data_size; + dst[5] = ext_data_size >> 8; + dst[6] = ext_data_size >> 16; + dst[7] = ext_data_size >> 24; + /* 8-11 extension data */ + dst[8] = src[0]; + dst[9] = src[1]; + dst[10] = src[2]; + dst[11] = src[3]; + + /* height */ + dst[12] = height; + dst[13] = (height >> 8) & 0xff; + dst[14] = (height >> 16) & 0xff; + dst[15] = (height >> 24) & 0xff; + /* width */ + dst[16] = width; + dst[17] = (width >> 8) & 0xff; + dst[18] = (width >> 16) & 0xff; + dst[19] = (width >> 24) & 0xff; +} + +static void set_vc1_rcv_pichdr(u8 *dst, u32 buffer_size) +{ + dst[0] = buffer_size; + dst[1] = buffer_size >> 8; + dst[2] = buffer_size >> 16; + dst[3] = buffer_size >> 24; +} + +static void create_vc1_nal_pichdr(u8 *dst) +{ + /* need insert nal header: special ID */ + dst[0] = 0x0; + dst[1] = 0x0; + dst[2] = 0x01; + dst[3] = 0x0D; +} + +static int vpu_malone_insert_scode_seq(struct malone_scode_t *scode, u32 codec_id, u32 ext_size) +{ + u8 hdr[MALONE_PAYLOAD_HEADER_SIZE]; + int ret; + + set_payload_hdr(hdr, + SCODE_SEQUENCE, + codec_id, + ext_size, + scode->inst->out_format.width, + scode->inst->out_format.height); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(hdr), + hdr); + if (ret < 0) + return ret; + return sizeof(hdr); +} + +static int vpu_malone_insert_scode_pic(struct malone_scode_t *scode, u32 codec_id, u32 ext_size) +{ + u8 hdr[MALONE_PAYLOAD_HEADER_SIZE]; + int ret; + + set_payload_hdr(hdr, + SCODE_PICTURE, + codec_id, + ext_size + vb2_get_plane_payload(scode->vb, 0), + scode->inst->out_format.width, + scode->inst->out_format.height); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(hdr), + hdr); + if (ret < 0) + return ret; + return sizeof(hdr); +} + +static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode) +{ + struct vb2_v4l2_buffer *vbuf; + u8 nal_hdr[MALONE_VC1_NAL_HEADER_LEN]; + u32 *data = NULL; + int ret; + + vbuf = to_vb2_v4l2_buffer(scode->vb); + data = vb2_plane_vaddr(scode->vb, 0); + + if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf)) + return 0; + if (MALONE_VC1_CONTAIN_NAL(*data)) + return 0; + + create_vc1_nal_pichdr(nal_hdr); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(nal_hdr), + nal_hdr); + if (ret < 0) + return ret; + return sizeof(nal_hdr); +} + +static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode) +{ + int ret; + int size = 0; + u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN]; + + scode->need_data = 0; + + ret = vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_VC1_SIMPLE, sizeof(rcv_seqhdr)); + if (ret < 0) + return ret; + size = ret; + + set_vc1_rcv_seqhdr(rcv_seqhdr, + vb2_plane_vaddr(scode->vb, 0), + scode->inst->out_format.width, + scode->inst->out_format.height); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(rcv_seqhdr), + rcv_seqhdr); + + if (ret < 0) + return ret; + size += sizeof(rcv_seqhdr); + return size; +} + +static int vpu_malone_insert_scode_vc1_l_pic(struct malone_scode_t *scode) +{ + int ret; + int size = 0; + u8 rcv_pichdr[MALONE_VC1_RCV_PIC_HEADER_LEN]; + + ret = vpu_malone_insert_scode_pic(scode, MALONE_CODEC_ID_VC1_SIMPLE, + sizeof(rcv_pichdr)); + if (ret < 0) + return ret; + size = ret; + + set_vc1_rcv_pichdr(rcv_pichdr, vb2_get_plane_payload(scode->vb, 0)); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(rcv_pichdr), + rcv_pichdr); + if (ret < 0) + return ret; + size += sizeof(rcv_pichdr); + return size; +} + +static int vpu_malone_insert_scode_vp8_seq(struct malone_scode_t *scode) +{ + int ret; + int size = 0; + u8 ivf_hdr[MALONE_VP8_IVF_SEQ_HEADER_LEN]; + + ret = vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_VP8, sizeof(ivf_hdr)); + if (ret < 0) + return ret; + size = ret; + + set_vp8_ivf_seqhdr(ivf_hdr, + scode->inst->out_format.width, + scode->inst->out_format.height); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(ivf_hdr), + ivf_hdr); + if (ret < 0) + return ret; + size += sizeof(ivf_hdr); + + return size; +} + +static int vpu_malone_insert_scode_vp8_pic(struct malone_scode_t *scode) +{ + int ret; + int size = 0; + u8 ivf_hdr[MALONE_VP8_IVF_FRAME_HEADER_LEN] = {0}; + + ret = vpu_malone_insert_scode_pic(scode, MALONE_CODEC_ID_VP8, sizeof(ivf_hdr)); + if (ret < 0) + return ret; + size = ret; + + set_vp8_ivf_pichdr(ivf_hdr, vb2_get_plane_payload(scode->vb, 0)); + ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer, + &scode->wptr, + sizeof(ivf_hdr), + ivf_hdr); + if (ret < 0) + return ret; + size += sizeof(ivf_hdr); + + return size; +} + +static const struct malone_scode_handler scode_handlers[] = { + { + /* fix me, need to swap return operation after gstreamer swap */ + .pixelformat = V4L2_PIX_FMT_VC1_ANNEX_L, + .insert_scode_seq = vpu_malone_insert_scode_vc1_l_seq, + .insert_scode_pic = vpu_malone_insert_scode_vc1_l_pic, + }, + { + .pixelformat = V4L2_PIX_FMT_VC1_ANNEX_G, + .insert_scode_pic = vpu_malone_insert_scode_vc1_g_pic, + }, + { + .pixelformat = V4L2_PIX_FMT_VP8, + .insert_scode_seq = vpu_malone_insert_scode_vp8_seq, + .insert_scode_pic = vpu_malone_insert_scode_vp8_pic, + }, +}; + +static const struct malone_scode_handler *get_scode_handler(u32 pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(scode_handlers); i++) { + if (scode_handlers[i].pixelformat == pixelformat) + return &scode_handlers[i]; + } + + return NULL; +} + +static int vpu_malone_insert_scode(struct malone_scode_t *scode, u32 type) +{ + const struct malone_scode_handler *handler; + int ret = 0; + + if (!scode || !scode->inst || !scode->vb) + return 0; + + scode->need_data = 1; + handler = get_scode_handler(scode->inst->out_format.pixfmt); + if (!handler) + return 0; + + switch (type) { + case SCODE_SEQUENCE: + if (handler->insert_scode_seq) + ret = handler->insert_scode_seq(scode); + break; + case SCODE_PICTURE: + if (handler->insert_scode_pic) + ret = handler->insert_scode_pic(scode); + break; + default: + break; + } + + return ret; +} + +static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str_buf, + struct vpu_inst *inst, struct vb2_buffer *vb, + u32 disp_imm) +{ + struct malone_scode_t scode; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + u32 wptr = readl(&str_buf->wptr); + int size = 0; + int ret = 0; + + /*add scode: SCODE_SEQUENCE, SCODE_PICTURE, SCODE_SLICE*/ + scode.inst = inst; + scode.vb = vb; + scode.wptr = wptr; + scode.need_data = 1; + if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf)) + ret = vpu_malone_insert_scode(&scode, SCODE_SEQUENCE); + + if (ret < 0) + return -ENOMEM; + size += ret; + wptr = scode.wptr; + if (!scode.need_data) { + vpu_malone_update_wptr(str_buf, wptr); + return size; + } + + ret = vpu_malone_insert_scode(&scode, SCODE_PICTURE); + if (ret < 0) + return -ENOMEM; + size += ret; + wptr = scode.wptr; + + ret = vpu_helper_copy_to_stream_buffer(&inst->stream_buffer, + &wptr, + vb2_get_plane_payload(vb, 0), + vb2_plane_vaddr(vb, 0)); + if (ret < 0) + return -ENOMEM; + size += vb2_get_plane_payload(vb, 0); + + vpu_malone_update_wptr(str_buf, wptr); + + if (disp_imm && !vpu_vb_is_codecconfig(vbuf)) { + ret = vpu_malone_add_scode(inst->core->iface, + inst->id, + &inst->stream_buffer, + inst->out_format.pixfmt, + SCODE_PADDING_BUFFLUSH); + if (ret < 0) + return ret; + size += ret; + } + + return size; +} + +static int vpu_malone_input_stream_data(struct vpu_malone_str_buffer __iomem *str_buf, + struct vpu_inst *inst, struct vb2_buffer *vb) +{ + u32 wptr = readl(&str_buf->wptr); + int ret = 0; + + ret = vpu_helper_copy_to_stream_buffer(&inst->stream_buffer, + &wptr, + vb2_get_plane_payload(vb, 0), + vb2_plane_vaddr(vb, 0)); + if (ret < 0) + return -ENOMEM; + + vpu_malone_update_wptr(str_buf, wptr); + + return ret; +} + +static int vpu_malone_input_ts(struct vpu_inst *inst, s64 timestamp, u32 size) +{ + struct vpu_ts_info info; + + memset(&info, 0, sizeof(info)); + info.timestamp = timestamp; + info.size = size; + + return vpu_session_fill_timestamp(inst, &info); +} + +int vpu_malone_input_frame(struct vpu_shared_addr *shared, + struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct vpu_dec_ctrl *hc = shared->priv; + struct vb2_v4l2_buffer *vbuf; + struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[inst->id]; + u32 disp_imm = hc->codec_param[inst->id].disp_imm; + u32 size; + int ret; + + if (vpu_malone_is_non_frame_mode(shared, inst->id)) + ret = vpu_malone_input_stream_data(str_buf, inst, vb); + else + ret = vpu_malone_input_frame_data(str_buf, inst, vb, disp_imm); + if (ret < 0) + return ret; + size = ret; + + /* + * if buffer only contain codec data, and the timestamp is invalid, + * don't put the invalid timestamp to resync + * merge the data to next frame + */ + vbuf = to_vb2_v4l2_buffer(vb); + if (vpu_vb_is_codecconfig(vbuf) && (s64)vb->timestamp < 0) { + inst->extra_size += size; + return 0; + } + if (inst->extra_size) { + size += inst->extra_size; + inst->extra_size = 0; + } + + ret = vpu_malone_input_ts(inst, vb->timestamp, size); + if (ret) + return ret; + + return 0; +} + +static bool vpu_malone_check_ready(struct vpu_shared_addr *shared, u32 instance) +{ + struct malone_iface *iface = shared->iface; + struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance]; + u32 size = desc->end - desc->start; + u32 rptr = desc->rptr; + u32 wptr = desc->wptr; + u32 used; + + if (!size) + return true; + + used = (wptr + size - rptr) % size; + if (used < (size / 2)) + return true; + + return false; +} + +bool vpu_malone_is_ready(struct vpu_shared_addr *shared, u32 instance) +{ + u32 cnt = 0; + + while (!vpu_malone_check_ready(shared, instance)) { + if (cnt > 30) + return false; + mdelay(1); + cnt++; + } + return true; +} + +int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance) +{ + if (!vpu_malone_is_ready(shared, instance)) + return -EINVAL; + + return 0; +} + +int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance) +{ + struct malone_iface *iface = shared->iface; + struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance]; + + desc->wptr++; + if (desc->wptr == desc->end) + desc->wptr = desc->start; + + return 0; +} + +int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance) +{ + struct malone_iface *iface = shared->iface; + struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance]; + + desc->wptr = desc->rptr; + if (desc->wptr == desc->end) + desc->wptr = desc->start; + + return 0; +} + +u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared) +{ + struct malone_iface *iface = shared->iface; + + return iface->max_streams; +} diff --git a/drivers/media/platform/amphion/vpu_malone.h b/drivers/media/platform/amphion/vpu_malone.h new file mode 100644 index 000000000000..e5a5cbe9843e --- /dev/null +++ b/drivers/media/platform/amphion/vpu_malone.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_MALONE_H +#define _AMPHION_VPU_MALONE_H + +u32 vpu_malone_get_data_size(void); +void vpu_malone_init_rpc(struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, dma_addr_t boot_addr); +void vpu_malone_set_log_buf(struct vpu_shared_addr *shared, + struct vpu_buffer *log); +void vpu_malone_set_system_cfg(struct vpu_shared_addr *shared, + u32 regs_base, void __iomem *regs, u32 core_id); +u32 vpu_malone_get_version(struct vpu_shared_addr *shared); +int vpu_malone_get_stream_buffer_size(struct vpu_shared_addr *shared); +int vpu_malone_config_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, struct vpu_buffer *buf); +int vpu_malone_get_stream_buffer_desc(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_rpc_buffer_desc *desc); +int vpu_malone_update_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, u32 ptr, bool write); +int vpu_malone_set_decode_params(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_decode_params *params, u32 update); +int vpu_malone_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data); +int vpu_malone_convert_msg_id(u32 msg_id); +int vpu_malone_unpack_msg_data(struct vpu_rpc_event *pkt, void *data); +int vpu_malone_add_scode(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *stream_buffer, + u32 pixelformat, + u32 scode_type); +int vpu_malone_input_frame(struct vpu_shared_addr *shared, + struct vpu_inst *inst, struct vb2_buffer *vb); +bool vpu_malone_is_ready(struct vpu_shared_addr *shared, u32 instance); +int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance); +int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance); +int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance); +u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared); + +#endif diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c new file mode 100644 index 000000000000..bf759eb2fd46 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_mbox.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include "vpu.h" +#include "vpu_mbox.h" +#include "vpu_msgs.h" + +static void vpu_mbox_rx_callback(struct mbox_client *cl, void *msg) +{ + struct vpu_mbox *rx = container_of(cl, struct vpu_mbox, cl); + struct vpu_core *core = container_of(rx, struct vpu_core, rx); + + vpu_isr(core, *(u32 *)msg); +} + +static int vpu_mbox_request_channel(struct device *dev, struct vpu_mbox *mbox) +{ + struct mbox_chan *ch; + struct mbox_client *cl; + + if (!dev || !mbox) + return -EINVAL; + if (mbox->ch) + return 0; + + cl = &mbox->cl; + cl->dev = dev; + if (mbox->block) { + cl->tx_block = true; + cl->tx_tout = 1000; + } else { + cl->tx_block = false; + } + cl->knows_txdone = false; + cl->rx_callback = vpu_mbox_rx_callback; + + ch = mbox_request_channel_byname(cl, mbox->name); + if (IS_ERR(ch)) { + dev_err(dev, "Failed to request mbox chan %s, ret : %ld\n", + mbox->name, PTR_ERR(ch)); + return PTR_ERR(ch); + } + + mbox->ch = ch; + return 0; +} + +int vpu_mbox_init(struct vpu_core *core) +{ + scnprintf(core->tx_type.name, sizeof(core->tx_type.name) - 1, "tx0"); + core->tx_type.block = true; + + scnprintf(core->tx_data.name, sizeof(core->tx_data.name) - 1, "tx1"); + core->tx_data.block = false; + + scnprintf(core->rx.name, sizeof(core->rx.name) - 1, "rx"); + core->rx.block = true; + + return 0; +} + +int vpu_mbox_request(struct vpu_core *core) +{ + int ret; + + ret = vpu_mbox_request_channel(core->dev, &core->tx_type); + if (ret) + goto error; + ret = vpu_mbox_request_channel(core->dev, &core->tx_data); + if (ret) + goto error; + ret = vpu_mbox_request_channel(core->dev, &core->rx); + if (ret) + goto error; + + dev_dbg(core->dev, "%s request mbox\n", vpu_core_type_desc(core->type)); + return 0; +error: + vpu_mbox_free(core); + return ret; +} + +void vpu_mbox_free(struct vpu_core *core) +{ + mbox_free_channel(core->tx_type.ch); + mbox_free_channel(core->tx_data.ch); + mbox_free_channel(core->rx.ch); + core->tx_type.ch = NULL; + core->tx_data.ch = NULL; + core->rx.ch = NULL; + dev_dbg(core->dev, "%s free mbox\n", vpu_core_type_desc(core->type)); +} + +void vpu_mbox_send_type(struct vpu_core *core, u32 type) +{ + mbox_send_message(core->tx_type.ch, &type); +} + +void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data) +{ + mbox_send_message(core->tx_data.ch, &data); + mbox_send_message(core->tx_type.ch, &type); +} + +void vpu_mbox_enable_rx(struct vpu_dev *dev) +{ +} diff --git a/drivers/media/platform/amphion/vpu_mbox.h b/drivers/media/platform/amphion/vpu_mbox.h new file mode 100644 index 000000000000..79cfd874e92b --- /dev/null +++ b/drivers/media/platform/amphion/vpu_mbox.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_MBOX_H +#define _AMPHION_VPU_MBOX_H + +int vpu_mbox_init(struct vpu_core *core); +int vpu_mbox_request(struct vpu_core *core); +void vpu_mbox_free(struct vpu_core *core); +void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data); +void vpu_mbox_send_type(struct vpu_core *core, u32 type); +void vpu_mbox_enable_rx(struct vpu_dev *dev); + +#endif diff --git a/drivers/media/platform/amphion/vpu_msgs.c b/drivers/media/platform/amphion/vpu_msgs.c new file mode 100644 index 000000000000..58502c51ddb3 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_msgs.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "vpu.h" +#include "vpu_core.h" +#include "vpu_rpc.h" +#include "vpu_mbox.h" +#include "vpu_defs.h" +#include "vpu_cmds.h" +#include "vpu_msgs.h" +#include "vpu_v4l2.h" + +#define VPU_PKT_HEADER_LENGTH 3 + +struct vpu_msg_handler { + u32 id; + void (*done)(struct vpu_inst *inst, struct vpu_rpc_event *pkt); +}; + +static void vpu_session_handle_start_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + vpu_trace(inst->dev, "[%d]\n", inst->id); +} + +static void vpu_session_handle_mem_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_pkt_mem_req_data req_data; + + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&req_data); + vpu_trace(inst->dev, "[%d] %d:%d %d:%d %d:%d\n", + inst->id, + req_data.enc_frame_size, + req_data.enc_frame_num, + req_data.ref_frame_size, + req_data.ref_frame_num, + req_data.act_buf_size, + req_data.act_buf_num); + call_void_vop(inst, mem_request, + req_data.enc_frame_size, + req_data.enc_frame_num, + req_data.ref_frame_size, + req_data.ref_frame_num, + req_data.act_buf_size, + req_data.act_buf_num); +} + +static void vpu_session_handle_stop_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + vpu_trace(inst->dev, "[%d]\n", inst->id); + + call_void_vop(inst, stop_done); +} + +static void vpu_session_handle_seq_hdr(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_dec_codec_info info; + const struct vpu_core_resources *res; + + memset(&info, 0, sizeof(info)); + res = vpu_get_resource(inst); + info.stride = res ? res->stride : 1; + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); + call_void_vop(inst, event_notify, VPU_MSG_ID_SEQ_HDR_FOUND, &info); +} + +static void vpu_session_handle_resolution_change(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + call_void_vop(inst, event_notify, VPU_MSG_ID_RES_CHANGE, NULL); +} + +static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_enc_pic_info info; + + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); + dev_dbg(inst->dev, "[%d] frame id = %d, wptr = 0x%x, size = %d\n", + inst->id, info.frame_id, info.wptr, info.frame_size); + call_void_vop(inst, get_one_frame, &info); +} + +static void vpu_session_handle_frame_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_fs_info fs; + + vpu_iface_unpack_msg_data(inst->core, pkt, &fs); + call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_REQ, &fs); +} + +static void vpu_session_handle_frame_release(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + if (inst->core->type == VPU_CORE_TYPE_ENC) { + struct vpu_frame_info info; + + memset(&info, 0, sizeof(info)); + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info.sequence); + dev_dbg(inst->dev, "[%d] %d\n", inst->id, info.sequence); + info.type = inst->out_format.type; + call_void_vop(inst, buf_done, &info); + } else if (inst->core->type == VPU_CORE_TYPE_DEC) { + struct vpu_fs_info fs; + + vpu_iface_unpack_msg_data(inst->core, pkt, &fs); + call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_RELEASE, &fs); + } +} + +static void vpu_session_handle_input_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + dev_dbg(inst->dev, "[%d]\n", inst->id); + call_void_vop(inst, input_done); +} + +static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_dec_pic_info info; + + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); + call_void_vop(inst, get_one_frame, &info); +} + +static void vpu_session_handle_pic_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + struct vpu_dec_pic_info info; + struct vpu_frame_info frame; + + memset(&frame, 0, sizeof(frame)); + vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); + if (inst->core->type == VPU_CORE_TYPE_DEC) + frame.type = inst->cap_format.type; + frame.id = info.id; + frame.luma = info.luma; + frame.skipped = info.skipped; + frame.timestamp = info.timestamp; + + call_void_vop(inst, buf_done, &frame); +} + +static void vpu_session_handle_eos(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + call_void_vop(inst, event_notify, VPU_MSG_ID_PIC_EOS, NULL); +} + +static void vpu_session_handle_error(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + dev_err(inst->dev, "unsupported stream\n"); + call_void_vop(inst, event_notify, VPU_MSG_ID_UNSUPPORTED, NULL); + vpu_v4l2_set_error(inst); +} + +static void vpu_session_handle_firmware_xcpt(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + char *str = (char *)pkt->data; + + dev_err(inst->dev, "%s firmware xcpt: %s\n", + vpu_core_type_desc(inst->core->type), str); + call_void_vop(inst, event_notify, VPU_MSG_ID_FIRMWARE_XCPT, NULL); + set_bit(inst->id, &inst->core->hang_mask); + vpu_v4l2_set_error(inst); +} + +static struct vpu_msg_handler handlers[] = { + {VPU_MSG_ID_START_DONE, vpu_session_handle_start_done}, + {VPU_MSG_ID_STOP_DONE, vpu_session_handle_stop_done}, + {VPU_MSG_ID_MEM_REQUEST, vpu_session_handle_mem_request}, + {VPU_MSG_ID_SEQ_HDR_FOUND, vpu_session_handle_seq_hdr}, + {VPU_MSG_ID_RES_CHANGE, vpu_session_handle_resolution_change}, + {VPU_MSG_ID_FRAME_INPUT_DONE, vpu_session_handle_input_done}, + {VPU_MSG_ID_FRAME_REQ, vpu_session_handle_frame_request}, + {VPU_MSG_ID_FRAME_RELEASE, vpu_session_handle_frame_release}, + {VPU_MSG_ID_ENC_DONE, vpu_session_handle_enc_frame_done}, + {VPU_MSG_ID_PIC_DECODED, vpu_session_handle_pic_decoded}, + {VPU_MSG_ID_DEC_DONE, vpu_session_handle_pic_done}, + {VPU_MSG_ID_PIC_EOS, vpu_session_handle_eos}, + {VPU_MSG_ID_UNSUPPORTED, vpu_session_handle_error}, + {VPU_MSG_ID_FIRMWARE_XCPT, vpu_session_handle_firmware_xcpt}, +}; + +static int vpu_session_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *msg) +{ + int ret; + u32 msg_id; + struct vpu_msg_handler *handler = NULL; + unsigned int i; + + ret = vpu_iface_convert_msg_id(inst->core, msg->hdr.id); + if (ret < 0) + return -EINVAL; + + msg_id = ret; + dev_dbg(inst->dev, "[%d] receive event(0x%x)\n", inst->id, msg_id); + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (handlers[i].id == msg_id) { + handler = &handlers[i]; + break; + } + } + + if (handler && handler->done) + handler->done(inst, msg); + + vpu_response_cmd(inst, msg_id, 1); + + return 0; +} + +static bool vpu_inst_receive_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + unsigned long bytes = sizeof(struct vpu_rpc_event_header); + u32 ret; + + memset(pkt, 0, sizeof(*pkt)); + if (kfifo_len(&inst->msg_fifo) < bytes) + return false; + + ret = kfifo_out(&inst->msg_fifo, pkt, bytes); + if (ret != bytes) + return false; + + if (pkt->hdr.num > 0) { + bytes = pkt->hdr.num * sizeof(u32); + ret = kfifo_out(&inst->msg_fifo, pkt->data, bytes); + if (ret != bytes) + return false; + } + + return true; +} + +void vpu_inst_run_work(struct work_struct *work) +{ + struct vpu_inst *inst = container_of(work, struct vpu_inst, msg_work); + struct vpu_rpc_event pkt; + + while (vpu_inst_receive_msg(inst, &pkt)) + vpu_session_handle_msg(inst, &pkt); +} + +static void vpu_inst_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt) +{ + unsigned long bytes; + u32 id = pkt->hdr.id; + int ret; + + if (!inst->workqueue) + return; + + bytes = sizeof(pkt->hdr) + pkt->hdr.num * sizeof(u32); + ret = kfifo_in(&inst->msg_fifo, pkt, bytes); + if (ret != bytes) + dev_err(inst->dev, "[%d:%d]overflow: %d\n", inst->core->id, inst->id, id); + queue_work(inst->workqueue, &inst->msg_work); +} + +static int vpu_handle_msg(struct vpu_core *core) +{ + struct vpu_rpc_event pkt; + struct vpu_inst *inst; + int ret; + + memset(&pkt, 0, sizeof(pkt)); + while (!vpu_iface_receive_msg(core, &pkt)) { + dev_dbg(core->dev, "event index = %d, id = %d, num = %d\n", + pkt.hdr.index, pkt.hdr.id, pkt.hdr.num); + + ret = vpu_iface_convert_msg_id(core, pkt.hdr.id); + if (ret < 0) + continue; + + inst = vpu_core_find_instance(core, pkt.hdr.index); + if (inst) { + vpu_response_cmd(inst, ret, 0); + mutex_lock(&core->cmd_lock); + vpu_inst_record_flow(inst, ret); + mutex_unlock(&core->cmd_lock); + + vpu_inst_handle_msg(inst, &pkt); + vpu_inst_put(inst); + } + memset(&pkt, 0, sizeof(pkt)); + } + + return 0; +} + +static int vpu_isr_thread(struct vpu_core *core, u32 irq_code) +{ + dev_dbg(core->dev, "irq code = 0x%x\n", irq_code); + switch (irq_code) { + case VPU_IRQ_CODE_SYNC: + vpu_mbox_send_msg(core, PRC_BUF_OFFSET, core->rpc.phys - core->fw.phys); + vpu_mbox_send_msg(core, BOOT_ADDRESS, core->fw.phys); + vpu_mbox_send_msg(core, INIT_DONE, 2); + break; + case VPU_IRQ_CODE_BOOT_DONE: + break; + case VPU_IRQ_CODE_SNAPSHOT_DONE: + break; + default: + vpu_handle_msg(core); + break; + } + + return 0; +} + +static void vpu_core_run_msg_work(struct vpu_core *core) +{ + const unsigned int SIZE = sizeof(u32); + + while (kfifo_len(&core->msg_fifo) >= SIZE) { + u32 data = 0; + + if (kfifo_out(&core->msg_fifo, &data, SIZE) == SIZE) + vpu_isr_thread(core, data); + } +} + +void vpu_msg_run_work(struct work_struct *work) +{ + struct vpu_core *core = container_of(work, struct vpu_core, msg_work); + unsigned long delay = msecs_to_jiffies(10); + + vpu_core_run_msg_work(core); + queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay); +} + +void vpu_msg_delayed_work(struct work_struct *work) +{ + struct vpu_core *core; + struct delayed_work *dwork; + unsigned long bytes = sizeof(u32); + u32 i; + + if (!work) + return; + + dwork = to_delayed_work(work); + core = container_of(dwork, struct vpu_core, msg_delayed_work); + if (kfifo_len(&core->msg_fifo) >= bytes) + vpu_core_run_msg_work(core); + + bytes = sizeof(struct vpu_rpc_event_header); + for (i = 0; i < core->supported_instance_count; i++) { + struct vpu_inst *inst = vpu_core_find_instance(core, i); + + if (!inst) + continue; + + if (inst->workqueue && kfifo_len(&inst->msg_fifo) >= bytes) + queue_work(inst->workqueue, &inst->msg_work); + + vpu_inst_put(inst); + } +} + +int vpu_isr(struct vpu_core *core, u32 irq) +{ + switch (irq) { + case VPU_IRQ_CODE_SYNC: + break; + case VPU_IRQ_CODE_BOOT_DONE: + complete(&core->cmp); + break; + case VPU_IRQ_CODE_SNAPSHOT_DONE: + complete(&core->cmp); + break; + default: + break; + } + + if (kfifo_in(&core->msg_fifo, &irq, sizeof(irq)) != sizeof(irq)) + dev_err(core->dev, "[%d]overflow: %d\n", core->id, irq); + queue_work(core->workqueue, &core->msg_work); + + return 0; +} diff --git a/drivers/media/platform/amphion/vpu_msgs.h b/drivers/media/platform/amphion/vpu_msgs.h new file mode 100644 index 000000000000..c466b4f62aad --- /dev/null +++ b/drivers/media/platform/amphion/vpu_msgs.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_MSGS_H +#define _AMPHION_VPU_MSGS_H + +int vpu_isr(struct vpu_core *core, u32 irq); +void vpu_inst_run_work(struct work_struct *work); +void vpu_msg_run_work(struct work_struct *work); +void vpu_msg_delayed_work(struct work_struct *work); + +#endif diff --git a/drivers/media/platform/amphion/vpu_rpc.c b/drivers/media/platform/amphion/vpu_rpc.c new file mode 100644 index 000000000000..18a164766409 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_rpc.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/svc/misc.h> +#include "vpu.h" +#include "vpu_rpc.h" +#include "vpu_imx8q.h" +#include "vpu_windsor.h" +#include "vpu_malone.h" + +int vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->check_memory_region) + return VPU_CORE_MEMORY_INVALID; + + return ops->check_memory_region(core->fw.phys, addr, size); +} + +static u32 vpu_rpc_check_buffer_space(struct vpu_rpc_buffer_desc *desc, bool write) +{ + u32 ptr1; + u32 ptr2; + u32 size; + + size = desc->end - desc->start; + if (write) { + ptr1 = desc->wptr; + ptr2 = desc->rptr; + } else { + ptr1 = desc->rptr; + ptr2 = desc->wptr; + } + + if (ptr1 == ptr2) { + if (!write) + return 0; + else + return size; + } + + return (ptr2 + size - ptr1) % size; +} + +static int vpu_rpc_send_cmd_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *cmd) +{ + struct vpu_rpc_buffer_desc *desc; + u32 space = 0; + u32 *data; + u32 wptr; + u32 i; + + if (cmd->hdr.num > 0xff || cmd->hdr.num >= ARRAY_SIZE(cmd->data)) + return -EINVAL; + desc = shared->cmd_desc; + space = vpu_rpc_check_buffer_space(desc, true); + if (space < (((cmd->hdr.num + 1) << 2) + 16)) + return -EINVAL; + wptr = desc->wptr; + data = (u32 *)(shared->cmd_mem_vir + desc->wptr - desc->start); + *data = 0; + *data |= ((cmd->hdr.index & 0xff) << 24); + *data |= ((cmd->hdr.num & 0xff) << 16); + *data |= (cmd->hdr.id & 0x3fff); + wptr += 4; + data++; + if (wptr >= desc->end) { + wptr = desc->start; + data = shared->cmd_mem_vir; + } + + for (i = 0; i < cmd->hdr.num; i++) { + *data = cmd->data[i]; + wptr += 4; + data++; + if (wptr >= desc->end) { + wptr = desc->start; + data = shared->cmd_mem_vir; + } + } + + /*update wptr after data is written*/ + mb(); + desc->wptr = wptr; + + return 0; +} + +static bool vpu_rpc_check_msg(struct vpu_shared_addr *shared) +{ + struct vpu_rpc_buffer_desc *desc; + u32 space = 0; + u32 msgword; + u32 msgnum; + + desc = shared->msg_desc; + space = vpu_rpc_check_buffer_space(desc, 0); + space = (space >> 2); + + if (space) { + msgword = *(u32 *)(shared->msg_mem_vir + desc->rptr - desc->start); + msgnum = (msgword & 0xff0000) >> 16; + if (msgnum <= space) + return true; + } + + return false; +} + +static int vpu_rpc_receive_msg_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *msg) +{ + struct vpu_rpc_buffer_desc *desc; + u32 *data; + u32 msgword; + u32 rptr; + u32 i; + + if (!vpu_rpc_check_msg(shared)) + return -EINVAL; + + desc = shared->msg_desc; + data = (u32 *)(shared->msg_mem_vir + desc->rptr - desc->start); + rptr = desc->rptr; + msgword = *data; + data++; + rptr += 4; + if (rptr >= desc->end) { + rptr = desc->start; + data = shared->msg_mem_vir; + } + + msg->hdr.index = (msgword >> 24) & 0xff; + msg->hdr.num = (msgword >> 16) & 0xff; + msg->hdr.id = msgword & 0x3fff; + + if (msg->hdr.num > ARRAY_SIZE(msg->data)) + return -EINVAL; + + for (i = 0; i < msg->hdr.num; i++) { + msg->data[i] = *data; + data++; + rptr += 4; + if (rptr >= desc->end) { + rptr = desc->start; + data = shared->msg_mem_vir; + } + } + + /*update rptr after data is read*/ + mb(); + desc->rptr = rptr; + + return 0; +} + +static struct vpu_iface_ops imx8q_rpc_ops[] = { + [VPU_CORE_TYPE_ENC] = { + .check_codec = vpu_imx8q_check_codec, + .check_fmt = vpu_imx8q_check_fmt, + .boot_core = vpu_imx8q_boot_core, + .get_power_state = vpu_imx8q_get_power_state, + .on_firmware_loaded = vpu_imx8q_on_firmware_loaded, + .get_data_size = vpu_windsor_get_data_size, + .check_memory_region = vpu_imx8q_check_memory_region, + .init_rpc = vpu_windsor_init_rpc, + .set_log_buf = vpu_windsor_set_log_buf, + .set_system_cfg = vpu_windsor_set_system_cfg, + .get_version = vpu_windsor_get_version, + .send_cmd_buf = vpu_rpc_send_cmd_buf, + .receive_msg_buf = vpu_rpc_receive_msg_buf, + .pack_cmd = vpu_windsor_pack_cmd, + .convert_msg_id = vpu_windsor_convert_msg_id, + .unpack_msg_data = vpu_windsor_unpack_msg_data, + .config_memory_resource = vpu_windsor_config_memory_resource, + .get_stream_buffer_size = vpu_windsor_get_stream_buffer_size, + .config_stream_buffer = vpu_windsor_config_stream_buffer, + .get_stream_buffer_desc = vpu_windsor_get_stream_buffer_desc, + .update_stream_buffer = vpu_windsor_update_stream_buffer, + .set_encode_params = vpu_windsor_set_encode_params, + .input_frame = vpu_windsor_input_frame, + .get_max_instance_count = vpu_windsor_get_max_instance_count, + }, + [VPU_CORE_TYPE_DEC] = { + .check_codec = vpu_imx8q_check_codec, + .check_fmt = vpu_imx8q_check_fmt, + .boot_core = vpu_imx8q_boot_core, + .get_power_state = vpu_imx8q_get_power_state, + .on_firmware_loaded = vpu_imx8q_on_firmware_loaded, + .get_data_size = vpu_malone_get_data_size, + .check_memory_region = vpu_imx8q_check_memory_region, + .init_rpc = vpu_malone_init_rpc, + .set_log_buf = vpu_malone_set_log_buf, + .set_system_cfg = vpu_malone_set_system_cfg, + .get_version = vpu_malone_get_version, + .send_cmd_buf = vpu_rpc_send_cmd_buf, + .receive_msg_buf = vpu_rpc_receive_msg_buf, + .get_stream_buffer_size = vpu_malone_get_stream_buffer_size, + .config_stream_buffer = vpu_malone_config_stream_buffer, + .set_decode_params = vpu_malone_set_decode_params, + .pack_cmd = vpu_malone_pack_cmd, + .convert_msg_id = vpu_malone_convert_msg_id, + .unpack_msg_data = vpu_malone_unpack_msg_data, + .get_stream_buffer_desc = vpu_malone_get_stream_buffer_desc, + .update_stream_buffer = vpu_malone_update_stream_buffer, + .add_scode = vpu_malone_add_scode, + .input_frame = vpu_malone_input_frame, + .pre_send_cmd = vpu_malone_pre_cmd, + .post_send_cmd = vpu_malone_post_cmd, + .init_instance = vpu_malone_init_instance, + .get_max_instance_count = vpu_malone_get_max_instance_count, + }, +}; + +static struct vpu_iface_ops *vpu_get_iface(struct vpu_dev *vpu, enum vpu_core_type type) +{ + struct vpu_iface_ops *rpc_ops = NULL; + u32 size = 0; + + switch (vpu->res->plat_type) { + case IMX8QXP: + case IMX8QM: + rpc_ops = imx8q_rpc_ops; + size = ARRAY_SIZE(imx8q_rpc_ops); + break; + default: + return NULL; + } + + if (type >= size) + return NULL; + + return &rpc_ops[type]; +} + +struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core) +{ + return vpu_get_iface(core->vpu, core->type); +} + +struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst) +{ + if (inst->core) + return vpu_core_get_iface(inst->core); + + return vpu_get_iface(inst->vpu, inst->type); +} diff --git a/drivers/media/platform/amphion/vpu_rpc.h b/drivers/media/platform/amphion/vpu_rpc.h new file mode 100644 index 000000000000..25119e5e807e --- /dev/null +++ b/drivers/media/platform/amphion/vpu_rpc.h @@ -0,0 +1,461 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_RPC_H +#define _AMPHION_VPU_RPC_H + +#include <media/videobuf2-core.h> +#include "vpu_codec.h" + +struct vpu_rpc_buffer_desc { + u32 wptr; + u32 rptr; + u32 start; + u32 end; +}; + +struct vpu_shared_addr { + void *iface; + struct vpu_rpc_buffer_desc *cmd_desc; + void *cmd_mem_vir; + struct vpu_rpc_buffer_desc *msg_desc; + void *msg_mem_vir; + + unsigned long boot_addr; + struct vpu_core *core; + void *priv; +}; + +struct vpu_rpc_event_header { + u32 index; + u32 id; + u32 num; +}; + +struct vpu_rpc_event { + struct vpu_rpc_event_header hdr; + u32 data[128]; +}; + +struct vpu_iface_ops { + bool (*check_codec)(enum vpu_core_type type); + bool (*check_fmt)(enum vpu_core_type type, u32 pixelfmt); + u32 (*get_data_size)(void); + int (*check_memory_region)(dma_addr_t base, dma_addr_t addr, u32 size); + int (*boot_core)(struct vpu_core *core); + int (*shutdown_core)(struct vpu_core *core); + int (*restore_core)(struct vpu_core *core); + int (*get_power_state)(struct vpu_core *core); + int (*on_firmware_loaded)(struct vpu_core *core); + void (*init_rpc)(struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, dma_addr_t boot_addr); + void (*set_log_buf)(struct vpu_shared_addr *shared, + struct vpu_buffer *log); + void (*set_system_cfg)(struct vpu_shared_addr *shared, + u32 regs_base, void __iomem *regs, u32 index); + void (*set_stream_cfg)(struct vpu_shared_addr *shared, u32 index); + u32 (*get_version)(struct vpu_shared_addr *shared); + u32 (*get_max_instance_count)(struct vpu_shared_addr *shared); + int (*get_stream_buffer_size)(struct vpu_shared_addr *shared); + int (*send_cmd_buf)(struct vpu_shared_addr *shared, + struct vpu_rpc_event *cmd); + int (*receive_msg_buf)(struct vpu_shared_addr *shared, + struct vpu_rpc_event *msg); + int (*pack_cmd)(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data); + int (*convert_msg_id)(u32 msg_id); + int (*unpack_msg_data)(struct vpu_rpc_event *pkt, void *data); + int (*input_frame)(struct vpu_shared_addr *shared, + struct vpu_inst *inst, struct vb2_buffer *vb); + int (*config_memory_resource)(struct vpu_shared_addr *shared, + u32 instance, + u32 type, + u32 index, + struct vpu_buffer *buf); + int (*config_stream_buffer)(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *buf); + int (*update_stream_buffer)(struct vpu_shared_addr *shared, + u32 instance, u32 ptr, bool write); + int (*get_stream_buffer_desc)(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_rpc_buffer_desc *desc); + int (*set_encode_params)(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_encode_params *params, + u32 update); + int (*set_decode_params)(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_decode_params *params, + u32 update); + int (*add_scode)(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *stream_buffer, + u32 pixelformat, + u32 scode_type); + int (*pre_send_cmd)(struct vpu_shared_addr *shared, u32 instance); + int (*post_send_cmd)(struct vpu_shared_addr *shared, u32 instance); + int (*init_instance)(struct vpu_shared_addr *shared, u32 instance); +}; + +enum { + VPU_CORE_MEMORY_INVALID = 0, + VPU_CORE_MEMORY_CACHED, + VPU_CORE_MEMORY_UNCACHED +}; + +struct vpu_rpc_region_t { + dma_addr_t start; + dma_addr_t end; + dma_addr_t type; +}; + +struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core); +struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst); +int vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size); + +static inline bool vpu_iface_check_codec(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->check_codec) + return ops->check_codec(core->type); + + return true; +} + +static inline bool vpu_iface_check_format(struct vpu_inst *inst, u32 pixelfmt) +{ + struct vpu_iface_ops *ops = vpu_inst_get_iface(inst); + + if (ops && ops->check_fmt) + return ops->check_fmt(inst->type, pixelfmt); + + return true; +} + +static inline int vpu_iface_boot_core(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->boot_core) + return ops->boot_core(core); + return 0; +} + +static inline int vpu_iface_get_power_state(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->get_power_state) + return ops->get_power_state(core); + return 1; +} + +static inline int vpu_iface_shutdown_core(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->shutdown_core) + return ops->shutdown_core(core); + return 0; +} + +static inline int vpu_iface_restore_core(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->restore_core) + return ops->restore_core(core); + return 0; +} + +static inline int vpu_iface_on_firmware_loaded(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (ops && ops->on_firmware_loaded) + return ops->on_firmware_loaded(core); + + return 0; +} + +static inline u32 vpu_iface_get_data_size(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->get_data_size) + return 0; + + return ops->get_data_size(); +} + +static inline int vpu_iface_init(struct vpu_core *core, + struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, + dma_addr_t boot_addr) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->init_rpc) + return -EINVAL; + + ops->init_rpc(shared, rpc, boot_addr); + core->iface = shared; + shared->core = core; + if (rpc->bytesused > rpc->length) + return -ENOSPC; + return 0; +} + +static inline int vpu_iface_set_log_buf(struct vpu_core *core, + struct vpu_buffer *log) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops) + return -EINVAL; + + if (ops->set_log_buf) + ops->set_log_buf(core->iface, log); + + return 0; +} + +static inline int vpu_iface_config_system(struct vpu_core *core, u32 regs_base, void __iomem *regs) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops) + return -EINVAL; + if (ops->set_system_cfg) + ops->set_system_cfg(core->iface, regs_base, regs, core->id); + + return 0; +} + +static inline int vpu_iface_get_stream_buffer_size(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->get_stream_buffer_size) + return 0; + + return ops->get_stream_buffer_size(core->iface); +} + +static inline int vpu_iface_config_stream(struct vpu_inst *inst) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || inst->id < 0) + return -EINVAL; + if (ops->set_stream_cfg) + ops->set_stream_cfg(inst->core->iface, inst->id); + return 0; +} + +static inline int vpu_iface_send_cmd(struct vpu_core *core, struct vpu_rpc_event *cmd) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->send_cmd_buf) + return -EINVAL; + + return ops->send_cmd_buf(core->iface, cmd); +} + +static inline int vpu_iface_receive_msg(struct vpu_core *core, struct vpu_rpc_event *msg) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->receive_msg_buf) + return -EINVAL; + + return ops->receive_msg_buf(core->iface, msg); +} + +static inline int vpu_iface_pack_cmd(struct vpu_core *core, + struct vpu_rpc_event *pkt, + u32 index, u32 id, void *data) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->pack_cmd) + return -EINVAL; + return ops->pack_cmd(pkt, index, id, data); +} + +static inline int vpu_iface_convert_msg_id(struct vpu_core *core, u32 msg_id) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->convert_msg_id) + return -EINVAL; + + return ops->convert_msg_id(msg_id); +} + +static inline int vpu_iface_unpack_msg_data(struct vpu_core *core, + struct vpu_rpc_event *pkt, void *data) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->unpack_msg_data) + return -EINVAL; + + return ops->unpack_msg_data(pkt, data); +} + +static inline int vpu_iface_input_frame(struct vpu_inst *inst, + struct vb2_buffer *vb) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->input_frame) + return -EINVAL; + + return ops->input_frame(inst->core->iface, inst, vb); +} + +static inline int vpu_iface_config_memory_resource(struct vpu_inst *inst, + u32 type, + u32 index, + struct vpu_buffer *buf) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->config_memory_resource || inst->id < 0) + return -EINVAL; + + return ops->config_memory_resource(inst->core->iface, + inst->id, + type, index, buf); +} + +static inline int vpu_iface_config_stream_buffer(struct vpu_inst *inst, + struct vpu_buffer *buf) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->config_stream_buffer || inst->id < 0) + return -EINVAL; + + if ((buf->phys % 4) || (buf->length % 4)) + return -EINVAL; + if (buf->phys + buf->length > (u64)UINT_MAX) + return -EINVAL; + + return ops->config_stream_buffer(inst->core->iface, inst->id, buf); +} + +static inline int vpu_iface_update_stream_buffer(struct vpu_inst *inst, + u32 ptr, bool write) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->update_stream_buffer || inst->id < 0) + return -EINVAL; + + return ops->update_stream_buffer(inst->core->iface, inst->id, ptr, write); +} + +static inline int vpu_iface_get_stream_buffer_desc(struct vpu_inst *inst, + struct vpu_rpc_buffer_desc *desc) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->get_stream_buffer_desc || inst->id < 0) + return -EINVAL; + + if (!desc) + return 0; + + return ops->get_stream_buffer_desc(inst->core->iface, inst->id, desc); +} + +static inline u32 vpu_iface_get_version(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->get_version) + return 0; + + return ops->get_version(core->iface); +} + +static inline u32 vpu_iface_get_max_instance_count(struct vpu_core *core) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(core); + + if (!ops || !ops->get_max_instance_count) + return 0; + + return ops->get_max_instance_count(core->iface); +} + +static inline int vpu_iface_set_encode_params(struct vpu_inst *inst, + struct vpu_encode_params *params, u32 update) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->set_encode_params || inst->id < 0) + return -EINVAL; + + return ops->set_encode_params(inst->core->iface, inst->id, params, update); +} + +static inline int vpu_iface_set_decode_params(struct vpu_inst *inst, + struct vpu_decode_params *params, u32 update) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->set_decode_params || inst->id < 0) + return -EINVAL; + + return ops->set_decode_params(inst->core->iface, inst->id, params, update); +} + +static inline int vpu_iface_add_scode(struct vpu_inst *inst, u32 scode_type) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (!ops || !ops->add_scode || inst->id < 0) + return -EINVAL; + + return ops->add_scode(inst->core->iface, inst->id, + &inst->stream_buffer, + inst->out_format.pixfmt, + scode_type); +} + +static inline int vpu_iface_pre_send_cmd(struct vpu_inst *inst) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (ops && ops->pre_send_cmd && inst->id >= 0) + return ops->pre_send_cmd(inst->core->iface, inst->id); + return 0; +} + +static inline int vpu_iface_post_send_cmd(struct vpu_inst *inst) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (ops && ops->post_send_cmd && inst->id >= 0) + return ops->post_send_cmd(inst->core->iface, inst->id); + return 0; +} + +static inline int vpu_iface_init_instance(struct vpu_inst *inst) +{ + struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + + if (ops && ops->init_instance && inst->id >= 0) + return ops->init_instance(inst->core->iface, inst->id); + + return 0; +} + +#endif diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c new file mode 100644 index 000000000000..9c0704cd5766 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> +#include "vpu.h" +#include "vpu_core.h" +#include "vpu_v4l2.h" +#include "vpu_msgs.h" +#include "vpu_helpers.h" + +void vpu_inst_lock(struct vpu_inst *inst) +{ + mutex_lock(&inst->lock); +} + +void vpu_inst_unlock(struct vpu_inst *inst) +{ + mutex_unlock(&inst->lock); +} + +dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no) +{ + if (plane_no >= vb->num_planes) + return 0; + return vb2_dma_contig_plane_dma_addr(vb, plane_no) + + vb->planes[plane_no].data_offset; +} + +unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no) +{ + if (plane_no >= vb->num_planes) + return 0; + return vb2_plane_size(vb, plane_no) - vb->planes[plane_no].data_offset; +} + +void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state) +{ + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + + vpu_buf->state = state; +} + +unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf) +{ + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + + return vpu_buf->state; +} + +void vpu_v4l2_set_error(struct vpu_inst *inst) +{ + struct vb2_queue *src_q; + struct vb2_queue *dst_q; + + vpu_inst_lock(inst); + dev_err(inst->dev, "some error occurs in codec\n"); + if (inst->fh.m2m_ctx) { + src_q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + if (src_q) + src_q->error = 1; + if (dst_q) + dst_q->error = 1; + } + vpu_inst_unlock(inst); +} + +int vpu_notify_eos(struct vpu_inst *inst) +{ + static const struct v4l2_event ev = { + .id = 0, + .type = V4L2_EVENT_EOS + }; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + v4l2_event_queue_fh(&inst->fh, &ev); + + return 0; +} + +int vpu_notify_source_change(struct vpu_inst *inst) +{ + static const struct v4l2_event ev = { + .id = 0, + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION + }; + + vpu_trace(inst->dev, "[%d]\n", inst->id); + v4l2_event_queue_fh(&inst->fh, &ev); + return 0; +} + +int vpu_set_last_buffer_dequeued(struct vpu_inst *inst) +{ + struct vb2_queue *q; + + if (!inst || !inst->fh.m2m_ctx) + return -EINVAL; + + q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + if (!list_empty(&q->done_list)) + return -EINVAL; + + if (q->last_buffer_dequeued) + return 0; + vpu_trace(inst->dev, "last buffer dequeued\n"); + q->last_buffer_dequeued = true; + wake_up(&q->done_wq); + vpu_notify_eos(inst); + return 0; +} + +const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + u32 type = f->type; + u32 stride = 1; + u32 bytesperline; + u32 sizeimage; + const struct vpu_format *fmt; + const struct vpu_core_resources *res; + int i; + + fmt = vpu_helper_find_format(inst, type, pixmp->pixelformat); + if (!fmt) { + fmt = vpu_helper_enum_format(inst, type, 0); + if (!fmt) + return NULL; + pixmp->pixelformat = fmt->pixfmt; + } + + res = vpu_get_resource(inst); + if (res) + stride = res->stride; + if (pixmp->width) + pixmp->width = vpu_helper_valid_frame_width(inst, pixmp->width); + if (pixmp->height) + pixmp->height = vpu_helper_valid_frame_height(inst, pixmp->height); + pixmp->flags = fmt->flags; + pixmp->num_planes = fmt->num_planes; + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + for (i = 0; i < pixmp->num_planes; i++) { + bytesperline = max_t(s32, pixmp->plane_fmt[i].bytesperline, 0); + sizeimage = vpu_helper_get_plane_size(pixmp->pixelformat, + pixmp->width, + pixmp->height, + i, + stride, + pixmp->field > V4L2_FIELD_NONE ? 1 : 0, + &bytesperline); + sizeimage = max_t(s32, pixmp->plane_fmt[i].sizeimage, sizeimage); + pixmp->plane_fmt[i].bytesperline = bytesperline; + pixmp->plane_fmt[i].sizeimage = sizeimage; + } + + return fmt; +} + +static bool vpu_check_ready(struct vpu_inst *inst, u32 type) +{ + if (!inst) + return false; + if (inst->state == VPU_CODEC_STATE_DEINIT || inst->id < 0) + return false; + if (!inst->ops->check_ready) + return true; + return call_vop(inst, check_ready, type); +} + +int vpu_process_output_buffer(struct vpu_inst *inst) +{ + struct v4l2_m2m_buffer *buf = NULL; + struct vb2_v4l2_buffer *vbuf = NULL; + + if (!inst || !inst->fh.m2m_ctx) + return -EINVAL; + + if (!vpu_check_ready(inst, inst->out_format.type)) + return -EINVAL; + + v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE) + break; + vbuf = NULL; + } + + if (!vbuf) + return -EINVAL; + + dev_dbg(inst->dev, "[%d]frame id = %d / %d\n", + inst->id, vbuf->sequence, inst->sequence); + return call_vop(inst, process_output, &vbuf->vb2_buf); +} + +int vpu_process_capture_buffer(struct vpu_inst *inst) +{ + struct v4l2_m2m_buffer *buf = NULL; + struct vb2_v4l2_buffer *vbuf = NULL; + + if (!inst || !inst->fh.m2m_ctx) + return -EINVAL; + + if (!vpu_check_ready(inst, inst->cap_format.type)) + return -EINVAL; + + v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE) + break; + vbuf = NULL; + } + if (!vbuf) + return -EINVAL; + + return call_vop(inst, process_capture, &vbuf->vb2_buf); +} + +struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence) +{ + struct v4l2_m2m_buffer *buf = NULL; + struct vb2_v4l2_buffer *vbuf = NULL; + + if (!inst || !inst->fh.m2m_ctx) + return NULL; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vbuf->sequence == sequence) + break; + vbuf = NULL; + } + } else { + v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vbuf->sequence == sequence) + break; + vbuf = NULL; + } + } + + return vbuf; +} + +struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx) +{ + struct v4l2_m2m_buffer *buf = NULL; + struct vb2_v4l2_buffer *vbuf = NULL; + + if (!inst || !inst->fh.m2m_ctx) + return NULL; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vbuf->vb2_buf.index == idx) + break; + vbuf = NULL; + } + } else { + v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) { + vbuf = &buf->vb; + if (vbuf->vb2_buf.index == idx) + break; + vbuf = NULL; + } + } + + return vbuf; +} + +int vpu_get_num_buffers(struct vpu_inst *inst, u32 type) +{ + struct vb2_queue *q; + + if (!inst || !inst->fh.m2m_ctx) + return -EINVAL; + + if (V4L2_TYPE_IS_OUTPUT(type)) + q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx); + else + q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx); + + return q->num_buffers; +} + +static void vpu_m2m_device_run(void *priv) +{ +} + +static void vpu_m2m_job_abort(void *priv) +{ + struct vpu_inst *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx; + + v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx); +} + +static const struct v4l2_m2m_ops vpu_m2m_ops = { + .device_run = vpu_m2m_device_run, + .job_abort = vpu_m2m_job_abort +}; + +static int vpu_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *buf_count, + unsigned int *plane_count, + unsigned int psize[], + struct device *allocators[]) +{ + struct vpu_inst *inst = vb2_get_drv_priv(vq); + struct vpu_format *cur_fmt; + int i; + + cur_fmt = vpu_get_format(inst, vq->type); + + if (*plane_count) { + if (*plane_count != cur_fmt->num_planes) + return -EINVAL; + for (i = 0; i < cur_fmt->num_planes; i++) { + if (psize[i] < cur_fmt->sizeimage[i]) + return -EINVAL; + } + return 0; + } + + *plane_count = cur_fmt->num_planes; + for (i = 0; i < cur_fmt->num_planes; i++) + psize[i] = cur_fmt->sizeimage[i]; + + return 0; +} + +static int vpu_vb2_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + return 0; +} + +static int vpu_vb2_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int vpu_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_format *cur_fmt; + u32 i; + + cur_fmt = vpu_get_format(inst, vb->type); + for (i = 0; i < cur_fmt->num_planes; i++) { + if (vpu_get_vb_length(vb, i) < cur_fmt->sizeimage[i]) { + dev_dbg(inst->dev, "[%d] %s buf[%d] is invalid\n", + inst->id, vpu_type_name(vb->type), vb->index); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR); + } + } + + return 0; +} + +static void vpu_vb2_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *q = vb->vb2_queue; + + if (vbuf->flags & V4L2_BUF_FLAG_LAST) + vpu_notify_eos(inst); + + if (list_empty(&q->done_list)) + call_void_vop(inst, on_queue_empty, q->type); +} + +void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *buf; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + while ((buf = v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx))) { + vpu_set_buffer_state(buf, VPU_BUF_STATE_IDLE); + v4l2_m2m_buf_done(buf, state); + } + } else { + while ((buf = v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx))) { + vpu_set_buffer_state(buf, VPU_BUF_STATE_IDLE); + v4l2_m2m_buf_done(buf, state); + } + } +} + +static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct vpu_inst *inst = vb2_get_drv_priv(q); + struct vpu_format *fmt = vpu_get_format(inst, q->type); + int ret; + + vpu_inst_unlock(inst); + ret = vpu_inst_register(inst); + vpu_inst_lock(inst); + if (ret) { + vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_QUEUED); + return ret; + } + + vpu_trace(inst->dev, "[%d] %s %c%c%c%c %dx%d %u(%u) %u(%u) %u(%u) %d\n", + inst->id, vpu_type_name(q->type), + fmt->pixfmt, + fmt->pixfmt >> 8, + fmt->pixfmt >> 16, + fmt->pixfmt >> 24, + fmt->width, fmt->height, + fmt->sizeimage[0], fmt->bytesperline[0], + fmt->sizeimage[1], fmt->bytesperline[1], + fmt->sizeimage[2], fmt->bytesperline[2], + q->num_buffers); + call_void_vop(inst, start, q->type); + vb2_clear_last_buffer_dequeued(q); + + return 0; +} + +static void vpu_vb2_stop_streaming(struct vb2_queue *q) +{ + struct vpu_inst *inst = vb2_get_drv_priv(q); + + vpu_trace(inst->dev, "[%d] %s\n", inst->id, vpu_type_name(q->type)); + + call_void_vop(inst, stop, q->type); + vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_ERROR); + if (V4L2_TYPE_IS_OUTPUT(q->type)) + inst->sequence = 0; +} + +static void vpu_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + vbuf->sequence = inst->sequence++; + + v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf); + vpu_process_output_buffer(inst); + vpu_process_capture_buffer(inst); +} + +static const struct vb2_ops vpu_vb2_ops = { + .queue_setup = vpu_vb2_queue_setup, + .buf_init = vpu_vb2_buf_init, + .buf_out_validate = vpu_vb2_buf_out_validate, + .buf_prepare = vpu_vb2_buf_prepare, + .buf_finish = vpu_vb2_buf_finish, + .start_streaming = vpu_vb2_start_streaming, + .stop_streaming = vpu_vb2_stop_streaming, + .buf_queue = vpu_vb2_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct vpu_inst *inst = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + inst->out_format.type = src_vq->type; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &vpu_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + if (inst->type == VPU_CORE_TYPE_DEC && inst->use_stream_buffer) + src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->drv_priv = inst; + src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); + src_vq->min_buffers_needed = 1; + src_vq->dev = inst->vpu->dev; + src_vq->lock = &inst->lock; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + inst->cap_format.type = dst_vq->type; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &vpu_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + if (inst->type == VPU_CORE_TYPE_ENC && inst->use_stream_buffer) + dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->drv_priv = inst; + dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); + dst_vq->min_buffers_needed = 1; + dst_vq->dev = inst->vpu->dev; + dst_vq->lock = &inst->lock; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int vpu_v4l2_release(struct vpu_inst *inst) +{ + vpu_trace(inst->vpu->dev, "%p\n", inst); + + vpu_release_core(inst->core); + put_device(inst->dev); + + if (inst->workqueue) { + cancel_work_sync(&inst->msg_work); + destroy_workqueue(inst->workqueue); + inst->workqueue = NULL; + } + + v4l2_ctrl_handler_free(&inst->ctrl_handler); + mutex_destroy(&inst->lock); + v4l2_fh_del(&inst->fh); + v4l2_fh_exit(&inst->fh); + + call_void_vop(inst, cleanup); + + return 0; +} + +int vpu_v4l2_open(struct file *file, struct vpu_inst *inst) +{ + struct vpu_dev *vpu = video_drvdata(file); + struct vpu_func *func; + int ret = 0; + + if (!inst || !inst->ops) + return -EINVAL; + + if (inst->type == VPU_CORE_TYPE_ENC) + func = &vpu->encoder; + else + func = &vpu->decoder; + + atomic_set(&inst->ref_count, 0); + vpu_inst_get(inst); + inst->vpu = vpu; + inst->core = vpu_request_core(vpu, inst->type); + if (inst->core) + inst->dev = get_device(inst->core->dev); + mutex_init(&inst->lock); + INIT_LIST_HEAD(&inst->cmd_q); + inst->id = VPU_INST_NULL_ID; + inst->release = vpu_v4l2_release; + inst->pid = current->pid; + inst->tgid = current->tgid; + inst->min_buffer_cap = 2; + inst->min_buffer_out = 2; + v4l2_fh_init(&inst->fh, func->vfd); + v4l2_fh_add(&inst->fh); + + ret = call_vop(inst, ctrl_init); + if (ret) + goto error; + + inst->fh.m2m_ctx = v4l2_m2m_ctx_init(func->m2m_dev, inst, vpu_m2m_queue_init); + if (IS_ERR(inst->fh.m2m_ctx)) { + dev_err(vpu->dev, "v4l2_m2m_ctx_init fail\n"); + ret = PTR_ERR(inst->fh.m2m_ctx); + goto error; + } + + inst->fh.ctrl_handler = &inst->ctrl_handler; + file->private_data = &inst->fh; + inst->state = VPU_CODEC_STATE_DEINIT; + inst->workqueue = alloc_workqueue("vpu_inst", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (inst->workqueue) { + INIT_WORK(&inst->msg_work, vpu_inst_run_work); + ret = kfifo_init(&inst->msg_fifo, + inst->msg_buffer, + rounddown_pow_of_two(sizeof(inst->msg_buffer))); + if (ret) { + destroy_workqueue(inst->workqueue); + inst->workqueue = NULL; + } + } + vpu_trace(vpu->dev, "tgid = %d, pid = %d, type = %s, inst = %p\n", + inst->tgid, inst->pid, vpu_core_type_desc(inst->type), inst); + + return 0; +error: + vpu_inst_put(inst); + return ret; +} + +int vpu_v4l2_close(struct file *file) +{ + struct vpu_dev *vpu = video_drvdata(file); + struct vpu_inst *inst = to_inst(file); + + vpu_trace(vpu->dev, "tgid = %d, pid = %d, inst = %p\n", inst->tgid, inst->pid, inst); + + vpu_inst_lock(inst); + if (inst->fh.m2m_ctx) { + v4l2_m2m_ctx_release(inst->fh.m2m_ctx); + inst->fh.m2m_ctx = NULL; + } + vpu_inst_unlock(inst); + + call_void_vop(inst, release); + vpu_inst_unregister(inst); + vpu_inst_put(inst); + + return 0; +} + +int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func) +{ + struct video_device *vfd; + int ret; + + if (!vpu || !func) + return -EINVAL; + + if (func->vfd) + return 0; + + func->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops); + if (IS_ERR(func->m2m_dev)) { + dev_err(vpu->dev, "v4l2_m2m_init fail\n"); + func->vfd = NULL; + return PTR_ERR(func->m2m_dev); + } + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_m2m_release(func->m2m_dev); + dev_err(vpu->dev, "alloc vpu decoder video device fail\n"); + return -ENOMEM; + } + vfd->release = video_device_release; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->v4l2_dev = &vpu->v4l2_dev; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + if (func->type == VPU_CORE_TYPE_ENC) { + strscpy(vfd->name, "amphion-vpu-encoder", sizeof(vfd->name)); + vfd->fops = venc_get_fops(); + vfd->ioctl_ops = venc_get_ioctl_ops(); + } else { + strscpy(vfd->name, "amphion-vpu-decoder", sizeof(vfd->name)); + vfd->fops = vdec_get_fops(); + vfd->ioctl_ops = vdec_get_ioctl_ops(); + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); + if (ret) { + video_device_release(vfd); + v4l2_m2m_release(func->m2m_dev); + return ret; + } + video_set_drvdata(vfd, vpu); + func->vfd = vfd; + + ret = v4l2_m2m_register_media_controller(func->m2m_dev, func->vfd, func->function); + if (ret) { + v4l2_m2m_release(func->m2m_dev); + func->m2m_dev = NULL; + video_unregister_device(func->vfd); + func->vfd = NULL; + return ret; + } + + return 0; +} + +void vpu_remove_func(struct vpu_func *func) +{ + if (!func) + return; + + if (func->m2m_dev) { + v4l2_m2m_unregister_media_controller(func->m2m_dev); + v4l2_m2m_release(func->m2m_dev); + func->m2m_dev = NULL; + } + if (func->vfd) { + video_unregister_device(func->vfd); + func->vfd = NULL; + } +} diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h new file mode 100644 index 000000000000..90fa7ea67495 --- /dev/null +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_V4L2_H +#define _AMPHION_VPU_V4L2_H + +#include <linux/videodev2.h> + +void vpu_inst_lock(struct vpu_inst *inst); +void vpu_inst_unlock(struct vpu_inst *inst); +void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state); +unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf); + +int vpu_v4l2_open(struct file *file, struct vpu_inst *inst); +int vpu_v4l2_close(struct file *file); + +const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f); +int vpu_process_output_buffer(struct vpu_inst *inst); +int vpu_process_capture_buffer(struct vpu_inst *inst); +struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence); +struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx); +void vpu_v4l2_set_error(struct vpu_inst *inst); +int vpu_notify_eos(struct vpu_inst *inst); +int vpu_notify_source_change(struct vpu_inst *inst); +int vpu_set_last_buffer_dequeued(struct vpu_inst *inst); +void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state); +int vpu_get_num_buffers(struct vpu_inst *inst, u32 type); + +dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no); +unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no); +static inline struct vpu_format *vpu_get_format(struct vpu_inst *inst, u32 type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &inst->out_format; + else + return &inst->cap_format; +} + +static inline char *vpu_type_name(u32 type) +{ + return V4L2_TYPE_IS_OUTPUT(type) ? "output" : "capture"; +} + +static inline int vpu_vb_is_codecconfig(struct vb2_v4l2_buffer *vbuf) +{ +#ifdef V4L2_BUF_FLAG_CODECCONFIG + return (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG) ? 1 : 0; +#else + return 0; +#endif +} + +#endif diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c new file mode 100644 index 000000000000..1526af2ef9da --- /dev/null +++ b/drivers/media/platform/amphion/vpu_windsor.c @@ -0,0 +1,1173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP + */ + +#include <linux/init.h> +#include <linux/interconnect.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/time64.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> +#include "vpu.h" +#include "vpu_rpc.h" +#include "vpu_defs.h" +#include "vpu_helpers.h" +#include "vpu_cmds.h" +#include "vpu_v4l2.h" +#include "vpu_imx8q.h" +#include "vpu_windsor.h" + +#define CMD_SIZE 2560 +#define MSG_SIZE 25600 +#define WINDSOR_USER_DATA_WORDS 16 +#define WINDSOR_MAX_SRC_FRAMES 0x6 +#define WINDSOR_MAX_REF_FRAMES 0x3 +#define WINDSOR_BITRATE_UNIT 1024 +#define WINDSOR_H264_EXTENDED_SAR 255 + +enum { + GTB_ENC_CMD_NOOP = 0x0, + GTB_ENC_CMD_STREAM_START, + GTB_ENC_CMD_FRAME_ENCODE, + GTB_ENC_CMD_FRAME_SKIP, + GTB_ENC_CMD_STREAM_STOP, + GTB_ENC_CMD_PARAMETER_UPD, + GTB_ENC_CMD_TERMINATE, + GTB_ENC_CMD_SNAPSHOT, + GTB_ENC_CMD_ROLL_SNAPSHOT, + GTB_ENC_CMD_LOCK_SCHEDULER, + GTB_ENC_CMD_UNLOCK_SCHEDULER, + GTB_ENC_CMD_CONFIGURE_CODEC, + GTB_ENC_CMD_DEAD_MARK, + GTB_ENC_CMD_FIRM_RESET, + GTB_ENC_CMD_FW_STATUS, + GTB_ENC_CMD_RESERVED +}; + +enum { + VID_API_EVENT_UNDEFINED = 0x0, + VID_API_ENC_EVENT_RESET_DONE = 0x1, + VID_API_ENC_EVENT_START_DONE, + VID_API_ENC_EVENT_STOP_DONE, + VID_API_ENC_EVENT_TERMINATE_DONE, + VID_API_ENC_EVENT_FRAME_INPUT_DONE, + VID_API_ENC_EVENT_FRAME_DONE, + VID_API_ENC_EVENT_FRAME_RELEASE, + VID_API_ENC_EVENT_PARA_UPD_DONE, + VID_API_ENC_EVENT_MEM_REQUEST, + VID_API_ENC_EVENT_FIRMWARE_XCPT, + VID_API_ENC_EVENT_RESERVED +}; + +enum { + MEDIAIP_ENC_PIC_TYPE_B_FRAME = 0, + MEDIAIP_ENC_PIC_TYPE_P_FRAME, + MEDIAIP_ENC_PIC_TYPE_I_FRAME, + MEDIAIP_ENC_PIC_TYPE_IDR_FRAME, + MEDIAIP_ENC_PIC_TYPE_BI_FRAME +}; + +struct windsor_iface { + u32 exec_base_addr; + u32 exec_area_size; + struct vpu_rpc_buffer_desc cmd_buffer_desc; + struct vpu_rpc_buffer_desc msg_buffer_desc; + u32 cmd_int_enable[VID_API_NUM_STREAMS]; + u32 fw_version; + u32 mvd_fw_offset; + u32 max_streams; + u32 ctrl_iface[VID_API_NUM_STREAMS]; + struct vpu_rpc_system_config system_config; + u32 api_version; + struct vpu_rpc_buffer_desc log_buffer_desc; +}; + +struct windsor_ctrl_iface { + u32 enc_yuv_buffer_desc; + u32 enc_stream_buffer_desc; + u32 enc_expert_mode_param; + u32 enc_param; + u32 enc_mem_pool; + u32 enc_encoding_status; + u32 enc_dsa_status; +}; + +struct vpu_enc_yuv_desc { + u32 frame_id; + u32 luma_base; + u32 chroma_base; + u32 param_idx; + u32 key_frame; +}; + +struct vpu_enc_calib_params { + u32 use_ame; + + u32 cme_mvx_max; + u32 cme_mvy_max; + u32 ame_prefresh_y0; + u32 ame_prefresh_y1; + u32 fme_min_sad; + u32 cme_min_sad; + + u32 fme_pred_int_weight; + u32 fme_pred_hp_weight; + u32 fme_pred_qp_weight; + u32 fme_cost_weight; + u32 fme_act_thold; + u32 fme_sad_thold; + u32 fme_zero_sad_thold; + + u32 fme_lrg_mvx_lmt; + u32 fme_lrg_mvy_lmt; + u32 fme_force_mode; + u32 fme_force4mvcost; + u32 fme_force2mvcost; + + u32 h264_inter_thrd; + + u32 i16x16_mode_cost; + u32 i4x4_mode_lambda; + u32 i8x8_mode_lambda; + + u32 inter_mod_mult; + u32 inter_sel_mult; + u32 inter_bid_cost; + u32 inter_bwd_cost; + u32 inter_4mv_cost; + s32 one_mv_i16_cost; + s32 one_mv_i4x4_cost; + s32 one_mv_i8x8_cost; + s32 two_mv_i16_cost; + s32 two_mv_i4x4_cost; + s32 two_mv_i8x8_cost; + s32 four_mv_i16_cost; + s32 four_mv_i4x4_cost; + s32 four_mv_i8x8_cost; + + u32 intra_pred_enab; + u32 intra_chr_pred; + u32 intra16_pred; + u32 intra4x4_pred; + u32 intra8x8_pred; + + u32 cb_base; + u32 cb_size; + u32 cb_head_room; + + u32 mem_page_width; + u32 mem_page_height; + u32 mem_total_size; + u32 mem_chunk_phys_addr; + u32 mem_chunk_virt_addr; + u32 mem_chunk_size; + u32 mem_y_stride; + u32 mem_uv_stride; + + u32 split_wr_enab; + u32 split_wr_req_size; + u32 split_rd_enab; + u32 split_rd_req_size; +}; + +struct vpu_enc_config_params { + u32 param_change; + u32 start_frame; + u32 end_frame; + u32 userdata_enable; + u32 userdata_id[4]; + u32 userdata_message[WINDSOR_USER_DATA_WORDS]; + u32 userdata_length; + u32 h264_profile_idc; + u32 h264_level_idc; + u32 h264_au_delimiter; + u32 h264_seq_end_code; + u32 h264_recovery_points; + u32 h264_vui_parameters; + u32 h264_aspect_ratio_present; + u32 h264_aspect_ratio_sar_width; + u32 h264_aspect_ratio_sar_height; + u32 h264_overscan_present; + u32 h264_video_type_present; + u32 h264_video_format; + u32 h264_video_full_range; + u32 h264_video_colour_descriptor; + u32 h264_video_colour_primaries; + u32 h264_video_transfer_char; + u32 h264_video_matrix_coeff; + u32 h264_chroma_loc_info_present; + u32 h264_chroma_loc_type_top; + u32 h264_chroma_loc_type_bot; + u32 h264_timing_info_present; + u32 h264_buffering_period_present; + u32 h264_low_delay_hrd_flag; + u32 aspect_ratio; + u32 test_mode; // Automated firmware test mode + u32 dsa_test_mode; // Automated test mode for the DSA. + u32 fme_test_mode; // Automated test mode for the fme + u32 cbr_row_mode; //0: FW mode; 1: HW mode + u32 windsor_mode; //0: normal mode; 1: intra only mode; 2: intra+0MV mode + u32 encode_mode; // H264, VC1, MPEG2, DIVX + u32 frame_width; // display width + u32 frame_height; // display height + u32 enc_frame_width; // encoding width, should be 16-pix align + u32 enc_frame_height; // encoding height, should be 16-pix aligned + u32 frame_rate_num; + u32 frame_rate_den; + u32 vi_field_source; + u32 vi_frame_width; + u32 vi_frame_height; + u32 crop_frame_width; + u32 crop_frame_height; + u32 crop_x_start_posn; + u32 crop_y_start_posn; + u32 mode422; + u32 mode_yuy2; + u32 dsa_luma_en; + u32 dsa_chroma_en; + u32 dsa_ext_hfilt_en; + u32 dsa_di_en; + u32 dsa_di_top_ref; + u32 dsa_vertf_disable; + u32 dsa_disable_pwb; + u32 dsa_hor_phase; + u32 dsa_ver_phase; + u32 dsa_iac_enable; + u32 iac_sc_threshold; + u32 iac_vm_threshold; + u32 iac_skip_mode; + u32 iac_grp_width; + u32 iac_grp_height; + u32 rate_control_mode; + u32 rate_control_resolution; + u32 buffer_size; + u32 buffer_level_init; + u32 buffer_I_bit_budget; + u32 top_field_first; + u32 intra_lum_qoffset; + u32 intra_chr_qoffset; + u32 inter_lum_qoffset; + u32 inter_chr_qoffset; + u32 use_def_scaling_mtx; + u32 inter_8x8_enab; + u32 inter_4x4_enab; + u32 fme_enable_qpel; + u32 fme_enable_hpel; + u32 fme_nozeromv; + u32 fme_predmv_en; + u32 fme_pred_2mv4mv; + u32 fme_smallsadthresh; + u32 ame_en_lmvc; + u32 ame_x_mult; + u32 cme_enable_4mv; + u32 cme_enable_1mv; + u32 hme_enable_16x8mv; + u32 hme_enable_8x16mv; + u32 cme_mv_weight; + u32 cme_mv_cost; + u32 ame_mult_mv; + u32 ame_shift_mv; + u32 hme_forceto1mv_en; + u32 hme_2mv_cost; + u32 hme_pred_mode; + u32 hme_sc_rnge; + u32 hme_sw_rnge; + u32 output_format; + u32 timestamp_enab; + u32 initial_pts_enab; + u32 initial_pts; +}; + +struct vpu_enc_static_params { + u32 param_change; + u32 gop_length; + u32 rate_control_bitrate; + u32 rate_control_bitrate_min; + u32 rate_control_bitrate_max; + u32 rate_control_content_models; + u32 rate_control_iframe_maxsize; + u32 rate_control_qp_init; + u32 rate_control_islice_qp; + u32 rate_control_pslice_qp; + u32 rate_control_bslice_qp; + u32 adaptive_quantization; + u32 aq_variance; + u32 cost_optimization; + u32 fdlp_mode; + u32 enable_isegbframes; + u32 enable_adaptive_keyratio; + u32 keyratio_imin; + u32 keyratio_imax; + u32 keyratio_pmin; + u32 keyratio_pmax; + u32 keyratio_bmin; + u32 keyratio_bmax; + s32 keyratio_istep; + s32 keyratio_pstep; + s32 keyratio_bstep; + u32 enable_paff; + u32 enable_b_frame_ref; + u32 enable_adaptive_gop; + u32 enable_closed_gop; + u32 open_gop_refresh_freq; + u32 enable_adaptive_sc; + u32 enable_fade_detection; + s32 fade_detection_threshold; + u32 enable_repeat_b; + u32 enable_low_delay_b; +}; + +struct vpu_enc_dynamic_params { + u32 param_change; + u32 rows_per_slice; + u32 mbaff_enable; + u32 dbf_enable; + u32 field_source; + u32 gop_b_length; + u32 mb_group_size; + u32 cbr_rows_per_group; + u32 skip_enable; + u32 pts_bits_0_to_31; + u32 pts_bit_32; + u32 rm_expsv_cff; + u32 const_ipred; + s32 chr_qp_offset; + u32 intra_mb_qp_offset; + u32 h264_cabac_init_method; + u32 h264_cabac_init_idc; + u32 h264_cabac_enable; + s32 alpha_c0_offset_div2; + s32 beta_offset_div2; + u32 intra_prefresh_y0; + u32 intra_prefresh_y1; + u32 dbg_dump_rec_src; +}; + +struct vpu_enc_expert_mode_param { + struct vpu_enc_calib_params calib_param; + struct vpu_enc_config_params config_param; + struct vpu_enc_static_params static_param; + struct vpu_enc_dynamic_params dynamic_param; +}; + +enum MEDIAIP_ENC_FMT { + MEDIAIP_ENC_FMT_H264 = 0, + MEDIAIP_ENC_FMT_VC1, + MEDIAIP_ENC_FMT_MPEG2, + MEDIAIP_ENC_FMT_MPEG4SP, + MEDIAIP_ENC_FMT_H263, + MEDIAIP_ENC_FMT_MPEG1, + MEDIAIP_ENC_FMT_SHORT_HEADER, + MEDIAIP_ENC_FMT_NULL +}; + +enum MEDIAIP_ENC_PROFILE { + MEDIAIP_ENC_PROF_MPEG2_SP = 0, + MEDIAIP_ENC_PROF_MPEG2_MP, + MEDIAIP_ENC_PROF_MPEG2_HP, + MEDIAIP_ENC_PROF_H264_BP, + MEDIAIP_ENC_PROF_H264_MP, + MEDIAIP_ENC_PROF_H264_HP, + MEDIAIP_ENC_PROF_MPEG4_SP, + MEDIAIP_ENC_PROF_MPEG4_ASP, + MEDIAIP_ENC_PROF_VC1_SP, + MEDIAIP_ENC_PROF_VC1_MP, + MEDIAIP_ENC_PROF_VC1_AP +}; + +enum MEDIAIP_ENC_BITRATE_MODE { + MEDIAIP_ENC_BITRATE_MODE_VBR = 0x00000001, + MEDIAIP_ENC_BITRATE_MODE_CBR = 0x00000002, + MEDIAIP_ENC_BITRATE_MODE_CONSTANT_QP = 0x00000004 +}; + +struct vpu_enc_memory_resource { + u32 phys; + u32 virt; + u32 size; +}; + +struct vpu_enc_param { + enum MEDIAIP_ENC_FMT codec_mode; + enum MEDIAIP_ENC_PROFILE profile; + u32 level; + + struct vpu_enc_memory_resource enc_mem_desc; + + u32 frame_rate; + u32 src_stride; + u32 src_width; + u32 src_height; + u32 src_offset_x; + u32 src_offset_y; + u32 src_crop_width; + u32 src_crop_height; + u32 out_width; + u32 out_height; + u32 iframe_interval; + u32 bframes; + u32 low_latency_mode; + + enum MEDIAIP_ENC_BITRATE_MODE bitrate_mode; + u32 target_bitrate; + u32 max_bitrate; + u32 min_bitrate; + u32 init_slice_qp; +}; + +struct vpu_enc_mem_pool { + struct vpu_enc_memory_resource enc_frames[WINDSOR_MAX_SRC_FRAMES]; + struct vpu_enc_memory_resource ref_frames[WINDSOR_MAX_REF_FRAMES]; + struct vpu_enc_memory_resource act_frame; +}; + +struct vpu_enc_encoding_status { + u32 frame_id; + u32 error_flag; //Error type + u32 mb_y; + u32 mb_x; + u32 reserved[12]; + +}; + +struct vpu_enc_dsa_status { + u32 frame_id; + u32 dsa_cyle; + u32 mb_y; + u32 mb_x; + u32 reserved[4]; +}; + +struct vpu_enc_ctrl { + struct vpu_enc_yuv_desc *yuv_desc; + struct vpu_rpc_buffer_desc *stream_desc; + struct vpu_enc_expert_mode_param *expert; + struct vpu_enc_param *param; + struct vpu_enc_mem_pool *pool; + struct vpu_enc_encoding_status *status; + struct vpu_enc_dsa_status *dsa; +}; + +struct vpu_enc_host_ctrls { + struct vpu_enc_ctrl ctrls[VID_API_NUM_STREAMS]; +}; + +struct windsor_pic_info { + u32 frame_id; + u32 pic_encod_done; + u32 pic_type; + u32 skipped_frame; + u32 error_flag; + u32 psnr; + u32 flush_done; + u32 mb_y; + u32 mb_x; + u32 frame_size; + u32 frame_enc_ttl_cycles; + u32 frame_enc_ttl_frm_cycles; + u32 frame_enc_ttl_slc_cycles; + u32 frame_enc_ttl_enc_cycles; + u32 frame_enc_ttl_hme_cycles; + u32 frame_enc_ttl_dsa_cycles; + u32 frame_enc_fw_cycles; + u32 frame_crc; + u32 num_interrupts_1; + u32 num_interrupts_2; + u32 poc; + u32 ref_info; + u32 pic_num; + u32 pic_activity; + u32 scene_change; + u32 mb_stats; + u32 enc_cache_count0; + u32 enc_cache_count1; + u32 mtl_wr_strb_cnt; + u32 mtl_rd_strb_cnt; + u32 str_buff_wptr; + u32 diagnosticEvents; + u32 proc_iacc_tot_rd_cnt; + u32 proc_dacc_tot_rd_cnt; + u32 proc_dacc_tot_wr_cnt; + u32 proc_dacc_reg_rd_cnt; + u32 proc_dacc_reg_wr_cnt; + u32 proc_dacc_rng_rd_cnt; + u32 proc_dacc_rng_wr_cnt; + s32 tv_s; + u32 tv_ns; +}; + +u32 vpu_windsor_get_data_size(void) +{ + return sizeof(struct vpu_enc_host_ctrls); +} + +static struct vpu_enc_yuv_desc *get_yuv_desc(struct vpu_shared_addr *shared, + u32 instance) +{ + struct vpu_enc_host_ctrls *hcs = shared->priv; + + return hcs->ctrls[instance].yuv_desc; +} + +static struct vpu_enc_mem_pool *get_mem_pool(struct vpu_shared_addr *shared, + u32 instance) +{ + struct vpu_enc_host_ctrls *hcs = shared->priv; + + return hcs->ctrls[instance].pool; +} + +static struct vpu_rpc_buffer_desc *get_stream_buf_desc(struct vpu_shared_addr *shared, + u32 instance) +{ + struct vpu_enc_host_ctrls *hcs = shared->priv; + + return hcs->ctrls[instance].stream_desc; +} + +static struct vpu_enc_expert_mode_param *get_expert_param(struct vpu_shared_addr *shared, + u32 instance) +{ + struct vpu_enc_host_ctrls *hcs = shared->priv; + + return hcs->ctrls[instance].expert; +} + +static struct vpu_enc_param *get_enc_param(struct vpu_shared_addr *shared, u32 instance) +{ + struct vpu_enc_host_ctrls *hcs = shared->priv; + + return hcs->ctrls[instance].param; +} + +static u32 get_ptr(u32 ptr) +{ + return (ptr | 0x80000000); +} + +void vpu_windsor_init_rpc(struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, dma_addr_t boot_addr) +{ + unsigned long base_phy_addr; + unsigned long phy_addr; + unsigned long offset; + struct windsor_iface *iface; + struct windsor_ctrl_iface *ctrl; + struct vpu_enc_host_ctrls *hcs; + unsigned int i; + + if (rpc->phys < boot_addr) + return; + + base_phy_addr = rpc->phys - boot_addr; + iface = rpc->virt; + shared->iface = iface; + shared->boot_addr = boot_addr; + hcs = shared->priv; + + iface->exec_base_addr = base_phy_addr; + iface->exec_area_size = rpc->length; + + offset = sizeof(struct windsor_iface); + phy_addr = base_phy_addr + offset; + shared->cmd_desc = &iface->cmd_buffer_desc; + shared->cmd_mem_vir = rpc->virt + offset; + iface->cmd_buffer_desc.start = + iface->cmd_buffer_desc.rptr = + iface->cmd_buffer_desc.wptr = phy_addr; + iface->cmd_buffer_desc.end = iface->cmd_buffer_desc.start + CMD_SIZE; + + offset += CMD_SIZE; + phy_addr = base_phy_addr + offset; + shared->msg_desc = &iface->msg_buffer_desc; + shared->msg_mem_vir = rpc->virt + offset; + iface->msg_buffer_desc.start = + iface->msg_buffer_desc.wptr = + iface->msg_buffer_desc.rptr = phy_addr; + iface->msg_buffer_desc.end = iface->msg_buffer_desc.start + MSG_SIZE; + + offset += MSG_SIZE; + for (i = 0; i < ARRAY_SIZE(iface->ctrl_iface); i++) { + iface->ctrl_iface[i] = base_phy_addr + offset; + offset += sizeof(struct windsor_ctrl_iface); + } + for (i = 0; i < ARRAY_SIZE(iface->ctrl_iface); i++) { + ctrl = rpc->virt + (iface->ctrl_iface[i] - base_phy_addr); + + ctrl->enc_yuv_buffer_desc = base_phy_addr + offset; + hcs->ctrls[i].yuv_desc = rpc->virt + offset; + offset += sizeof(struct vpu_enc_yuv_desc); + + ctrl->enc_stream_buffer_desc = base_phy_addr + offset; + hcs->ctrls[i].stream_desc = rpc->virt + offset; + offset += sizeof(struct vpu_rpc_buffer_desc); + + ctrl->enc_expert_mode_param = base_phy_addr + offset; + hcs->ctrls[i].expert = rpc->virt + offset; + offset += sizeof(struct vpu_enc_expert_mode_param); + + ctrl->enc_param = base_phy_addr + offset; + hcs->ctrls[i].param = rpc->virt + offset; + offset += sizeof(struct vpu_enc_param); + + ctrl->enc_mem_pool = base_phy_addr + offset; + hcs->ctrls[i].pool = rpc->virt + offset; + offset += sizeof(struct vpu_enc_mem_pool); + + ctrl->enc_encoding_status = base_phy_addr + offset; + hcs->ctrls[i].status = rpc->virt + offset; + offset += sizeof(struct vpu_enc_encoding_status); + + ctrl->enc_dsa_status = base_phy_addr + offset; + hcs->ctrls[i].dsa = rpc->virt + offset; + offset += sizeof(struct vpu_enc_dsa_status); + } + + rpc->bytesused = offset; +} + +void vpu_windsor_set_log_buf(struct vpu_shared_addr *shared, struct vpu_buffer *log) +{ + struct windsor_iface *iface = shared->iface; + + iface->log_buffer_desc.start = + iface->log_buffer_desc.wptr = + iface->log_buffer_desc.rptr = log->phys - shared->boot_addr; + iface->log_buffer_desc.end = iface->log_buffer_desc.start + log->length; +} + +void vpu_windsor_set_system_cfg(struct vpu_shared_addr *shared, + u32 regs_base, void __iomem *regs, u32 core_id) +{ + struct windsor_iface *iface = shared->iface; + struct vpu_rpc_system_config *config = &iface->system_config; + + vpu_imx8q_set_system_cfg_common(config, regs_base, core_id); +} + +int vpu_windsor_get_stream_buffer_size(struct vpu_shared_addr *shared) +{ + return 0x300000; +} + +static struct vpu_pair windsor_cmds[] = { + {VPU_CMD_ID_CONFIGURE_CODEC, GTB_ENC_CMD_CONFIGURE_CODEC}, + {VPU_CMD_ID_START, GTB_ENC_CMD_STREAM_START}, + {VPU_CMD_ID_STOP, GTB_ENC_CMD_STREAM_STOP}, + {VPU_CMD_ID_FRAME_ENCODE, GTB_ENC_CMD_FRAME_ENCODE}, + {VPU_CMD_ID_SNAPSHOT, GTB_ENC_CMD_SNAPSHOT}, + {VPU_CMD_ID_FIRM_RESET, GTB_ENC_CMD_FIRM_RESET}, + {VPU_CMD_ID_UPDATE_PARAMETER, GTB_ENC_CMD_PARAMETER_UPD}, + {VPU_CMD_ID_DEBUG, GTB_ENC_CMD_FW_STATUS} +}; + +static struct vpu_pair windsor_msgs[] = { + {VPU_MSG_ID_RESET_DONE, VID_API_ENC_EVENT_RESET_DONE}, + {VPU_MSG_ID_START_DONE, VID_API_ENC_EVENT_START_DONE}, + {VPU_MSG_ID_STOP_DONE, VID_API_ENC_EVENT_STOP_DONE}, + {VPU_MSG_ID_FRAME_INPUT_DONE, VID_API_ENC_EVENT_FRAME_INPUT_DONE}, + {VPU_MSG_ID_ENC_DONE, VID_API_ENC_EVENT_FRAME_DONE}, + {VPU_MSG_ID_FRAME_RELEASE, VID_API_ENC_EVENT_FRAME_RELEASE}, + {VPU_MSG_ID_MEM_REQUEST, VID_API_ENC_EVENT_MEM_REQUEST}, + {VPU_MSG_ID_PARAM_UPD_DONE, VID_API_ENC_EVENT_PARA_UPD_DONE}, + {VPU_MSG_ID_FIRMWARE_XCPT, VID_API_ENC_EVENT_FIRMWARE_XCPT}, +}; + +int vpu_windsor_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data) +{ + int ret; + + ret = vpu_find_dst_by_src(windsor_cmds, ARRAY_SIZE(windsor_cmds), id); + if (ret < 0) + return ret; + pkt->hdr.id = ret; + pkt->hdr.num = 0; + pkt->hdr.index = index; + if (id == VPU_CMD_ID_FRAME_ENCODE) { + s64 timestamp = *(s64 *)data; + struct timespec64 ts = ns_to_timespec64(timestamp); + + pkt->hdr.num = 2; + pkt->data[0] = ts.tv_sec; + pkt->data[1] = ts.tv_nsec; + } + + return 0; +} + +int vpu_windsor_convert_msg_id(u32 id) +{ + return vpu_find_src_by_dst(windsor_msgs, ARRAY_SIZE(windsor_msgs), id); +} + +static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data) +{ + struct vpu_enc_pic_info *info = data; + struct windsor_pic_info *windsor = (struct windsor_pic_info *)pkt->data; + struct timespec64 ts = { windsor->tv_s, windsor->tv_ns }; + + info->frame_id = windsor->frame_id; + switch (windsor->pic_type) { + case MEDIAIP_ENC_PIC_TYPE_I_FRAME: + case MEDIAIP_ENC_PIC_TYPE_IDR_FRAME: + info->pic_type = V4L2_BUF_FLAG_KEYFRAME; + break; + case MEDIAIP_ENC_PIC_TYPE_P_FRAME: + info->pic_type = V4L2_BUF_FLAG_PFRAME; + break; + case MEDIAIP_ENC_PIC_TYPE_B_FRAME: + info->pic_type = V4L2_BUF_FLAG_BFRAME; + break; + default: + break; + } + info->skipped_frame = windsor->skipped_frame; + info->error_flag = windsor->error_flag; + info->psnr = windsor->psnr; + info->frame_size = windsor->frame_size; + info->wptr = get_ptr(windsor->str_buff_wptr); + info->crc = windsor->frame_crc; + info->timestamp = timespec64_to_ns(&ts); +} + +static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data) +{ + struct vpu_pkt_mem_req_data *req_data = data; + + req_data->enc_frame_size = pkt->data[0]; + req_data->enc_frame_num = pkt->data[1]; + req_data->ref_frame_size = pkt->data[2]; + req_data->ref_frame_num = pkt->data[3]; + req_data->act_buf_size = pkt->data[4]; + req_data->act_buf_num = 1; +} + +int vpu_windsor_unpack_msg_data(struct vpu_rpc_event *pkt, void *data) +{ + if (!pkt || !data) + return -EINVAL; + + switch (pkt->hdr.id) { + case VID_API_ENC_EVENT_FRAME_DONE: + vpu_windsor_unpack_pic_info(pkt, data); + break; + case VID_API_ENC_EVENT_MEM_REQUEST: + vpu_windsor_unpack_mem_req(pkt, data); + break; + case VID_API_ENC_EVENT_FRAME_RELEASE: + *(u32 *)data = pkt->data[0]; + break; + default: + break; + } + + return 0; +} + +static int vpu_windsor_fill_yuv_frame(struct vpu_shared_addr *shared, + u32 instance, + struct vb2_buffer *vb) +{ + struct vpu_enc_yuv_desc *desc; + struct vb2_v4l2_buffer *vbuf; + + if (instance >= VID_API_NUM_STREAMS) + return -EINVAL; + + desc = get_yuv_desc(shared, instance); + + vbuf = to_vb2_v4l2_buffer(vb); + desc->frame_id = vbuf->sequence; + if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME) + desc->key_frame = 1; + else + desc->key_frame = 0; + desc->luma_base = vpu_get_vb_phy_addr(vb, 0); + desc->chroma_base = vpu_get_vb_phy_addr(vb, 1); + + return 0; +} + +int vpu_windsor_input_frame(struct vpu_shared_addr *shared, + struct vpu_inst *inst, struct vb2_buffer *vb) +{ + vpu_windsor_fill_yuv_frame(shared, inst->id, vb); + return vpu_session_encode_frame(inst, vb->timestamp); +} + +int vpu_windsor_config_memory_resource(struct vpu_shared_addr *shared, + u32 instance, + u32 type, + u32 index, + struct vpu_buffer *buf) +{ + struct vpu_enc_mem_pool *pool; + struct vpu_enc_memory_resource *res; + + if (instance >= VID_API_NUM_STREAMS) + return -EINVAL; + + pool = get_mem_pool(shared, instance); + + switch (type) { + case MEM_RES_ENC: + if (index >= ARRAY_SIZE(pool->enc_frames)) + return -EINVAL; + res = &pool->enc_frames[index]; + break; + case MEM_RES_REF: + if (index >= ARRAY_SIZE(pool->ref_frames)) + return -EINVAL; + res = &pool->ref_frames[index]; + break; + case MEM_RES_ACT: + if (index) + return -EINVAL; + res = &pool->act_frame; + break; + default: + return -EINVAL; + } + + res->phys = buf->phys; + res->virt = buf->phys - shared->boot_addr; + res->size = buf->length; + + return 0; +} + +int vpu_windsor_config_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_buffer *buf) +{ + struct vpu_rpc_buffer_desc *desc; + struct vpu_enc_expert_mode_param *expert; + + desc = get_stream_buf_desc(shared, instance); + expert = get_expert_param(shared, instance); + + desc->start = buf->phys; + desc->wptr = buf->phys; + desc->rptr = buf->phys; + desc->end = buf->phys + buf->length; + + expert->calib_param.mem_chunk_phys_addr = 0; + expert->calib_param.mem_chunk_virt_addr = 0; + expert->calib_param.mem_chunk_size = 0; + expert->calib_param.cb_base = buf->phys; + expert->calib_param.cb_size = buf->length; + + return 0; +} + +int vpu_windsor_update_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, u32 ptr, bool write) +{ + struct vpu_rpc_buffer_desc *desc; + + desc = get_stream_buf_desc(shared, instance); + + /*update wptr/rptr after data is written or read*/ + mb(); + if (write) + desc->wptr = ptr; + else + desc->rptr = ptr; + + return 0; +} + +int vpu_windsor_get_stream_buffer_desc(struct vpu_shared_addr *shared, + u32 instance, struct vpu_rpc_buffer_desc *desc) +{ + struct vpu_rpc_buffer_desc *rpc_desc; + + rpc_desc = get_stream_buf_desc(shared, instance); + if (desc) { + desc->wptr = get_ptr(rpc_desc->wptr); + desc->rptr = get_ptr(rpc_desc->rptr); + desc->start = get_ptr(rpc_desc->start); + desc->end = get_ptr(rpc_desc->end); + } + + return 0; +} + +u32 vpu_windsor_get_version(struct vpu_shared_addr *shared) +{ + struct windsor_iface *iface = shared->iface; + + return iface->fw_version; +} + +static int vpu_windsor_set_frame_rate(struct vpu_enc_expert_mode_param *expert, + struct vpu_encode_params *params) +{ + expert->config_param.frame_rate_num = params->frame_rate.numerator; + expert->config_param.frame_rate_den = params->frame_rate.denominator; + + return 0; +} + +static int vpu_windsor_set_format(struct vpu_enc_param *param, u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + param->codec_mode = MEDIAIP_ENC_FMT_H264; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vpu_windsor_set_profile(struct vpu_enc_param *param, u32 profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + param->profile = MEDIAIP_ENC_PROF_H264_BP; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + param->profile = MEDIAIP_ENC_PROF_H264_MP; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + param->profile = MEDIAIP_ENC_PROF_H264_HP; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const u32 h264_level[] = { + [V4L2_MPEG_VIDEO_H264_LEVEL_1_0] = 10, + [V4L2_MPEG_VIDEO_H264_LEVEL_1B] = 14, + [V4L2_MPEG_VIDEO_H264_LEVEL_1_1] = 11, + [V4L2_MPEG_VIDEO_H264_LEVEL_1_2] = 12, + [V4L2_MPEG_VIDEO_H264_LEVEL_1_3] = 13, + [V4L2_MPEG_VIDEO_H264_LEVEL_2_0] = 20, + [V4L2_MPEG_VIDEO_H264_LEVEL_2_1] = 21, + [V4L2_MPEG_VIDEO_H264_LEVEL_2_2] = 22, + [V4L2_MPEG_VIDEO_H264_LEVEL_3_0] = 30, + [V4L2_MPEG_VIDEO_H264_LEVEL_3_1] = 31, + [V4L2_MPEG_VIDEO_H264_LEVEL_3_2] = 32, + [V4L2_MPEG_VIDEO_H264_LEVEL_4_0] = 40, + [V4L2_MPEG_VIDEO_H264_LEVEL_4_1] = 41, + [V4L2_MPEG_VIDEO_H264_LEVEL_4_2] = 42, + [V4L2_MPEG_VIDEO_H264_LEVEL_5_0] = 50, + [V4L2_MPEG_VIDEO_H264_LEVEL_5_1] = 51 +}; + +static int vpu_windsor_set_level(struct vpu_enc_param *param, u32 level) +{ + if (level >= ARRAY_SIZE(h264_level)) + return -EINVAL; + + param->level = h264_level[level]; + + return 0; +} + +static int vpu_windsor_set_size(struct vpu_enc_param *windsor, + struct vpu_encode_params *params) +{ + windsor->src_stride = params->src_stride; + windsor->src_width = params->src_width; + windsor->src_height = params->src_height; + windsor->src_offset_x = params->crop.left; + windsor->src_offset_y = params->crop.top; + windsor->src_crop_width = params->crop.width; + windsor->src_crop_height = params->crop.height; + windsor->out_width = params->out_width; + windsor->out_height = params->out_height; + + return 0; +} + +static int vpu_windsor_set_gop(struct vpu_enc_param *param, u32 gop) +{ + param->iframe_interval = gop; + + return 0; +} + +static int vpu_windsor_set_bframes(struct vpu_enc_param *param, u32 bframes) +{ + if (bframes) { + param->low_latency_mode = 0; + param->bframes = bframes; + } else { + param->low_latency_mode = 1; + param->bframes = 0; + } + + return 0; +} + +static int vpu_windsor_set_bitrate_mode(struct vpu_enc_param *param, u32 rc_enable, u32 mode) +{ + if (!rc_enable) + param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_CONSTANT_QP; + else if (mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_VBR; + else + param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_CBR; + + return 0; +} + +static u32 vpu_windsor_bitrate(u32 bitrate) +{ + return DIV_ROUND_CLOSEST(bitrate, WINDSOR_BITRATE_UNIT); +} + +static int vpu_windsor_set_bitrate(struct vpu_enc_param *windsor, + struct vpu_encode_params *params) +{ + windsor->target_bitrate = vpu_windsor_bitrate(params->bitrate); + windsor->min_bitrate = vpu_windsor_bitrate(params->bitrate_min); + windsor->max_bitrate = vpu_windsor_bitrate(params->bitrate_max); + + return 0; +} + +static int vpu_windsor_set_qp(struct vpu_enc_expert_mode_param *expert, + struct vpu_encode_params *params) +{ + expert->static_param.rate_control_islice_qp = params->i_frame_qp; + expert->static_param.rate_control_pslice_qp = params->p_frame_qp; + expert->static_param.rate_control_bslice_qp = params->b_frame_qp; + + return 0; +} + +static int vpu_windsor_set_sar(struct vpu_enc_expert_mode_param *expert, + struct vpu_encode_params *params) +{ + expert->config_param.h264_aspect_ratio_present = params->sar.enable; + if (params->sar.idc == V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED) + expert->config_param.aspect_ratio = WINDSOR_H264_EXTENDED_SAR; + else + expert->config_param.aspect_ratio = params->sar.idc; + expert->config_param.h264_aspect_ratio_sar_width = params->sar.width; + expert->config_param.h264_aspect_ratio_sar_height = params->sar.height; + + return 0; +} + +static int vpu_windsor_set_color(struct vpu_enc_expert_mode_param *expert, + struct vpu_encode_params *params) +{ + expert->config_param.h264_video_type_present = 1; + expert->config_param.h264_video_format = 5; + expert->config_param.h264_video_colour_descriptor = 1; + expert->config_param.h264_video_colour_primaries = + vpu_color_cvrt_primaries_v2i(params->color.primaries); + expert->config_param.h264_video_transfer_char = + vpu_color_cvrt_transfers_v2i(params->color.transfer); + expert->config_param.h264_video_matrix_coeff = + vpu_color_cvrt_matrix_v2i(params->color.matrix); + expert->config_param.h264_video_full_range = + vpu_color_cvrt_full_range_v2i(params->color.full_range); + return 0; +} + +static int vpu_windsor_update_bitrate(struct vpu_shared_addr *shared, + u32 instance, struct vpu_encode_params *params) +{ + struct vpu_enc_param *windsor; + struct vpu_enc_expert_mode_param *expert; + + windsor = get_enc_param(shared, instance); + expert = get_expert_param(shared, instance); + + if (windsor->bitrate_mode != MEDIAIP_ENC_BITRATE_MODE_CBR) + return 0; + if (!params->rc_enable) + return 0; + if (vpu_windsor_bitrate(params->bitrate) == windsor->target_bitrate) + return 0; + + vpu_windsor_set_bitrate(windsor, params); + expert->static_param.rate_control_bitrate = windsor->target_bitrate; + expert->static_param.rate_control_bitrate_min = windsor->min_bitrate; + expert->static_param.rate_control_bitrate_max = windsor->max_bitrate; + + return 0; +} + +static int vpu_windsor_set_params(struct vpu_shared_addr *shared, + u32 instance, struct vpu_encode_params *params) +{ + struct vpu_enc_param *windsor; + int ret; + + windsor = get_enc_param(shared, instance); + + if (params->input_format != V4L2_PIX_FMT_NV12 && + params->input_format != V4L2_PIX_FMT_NV12M) + return -EINVAL; + + ret = vpu_windsor_set_format(windsor, params->codec_format); + if (ret) + return ret; + vpu_windsor_set_profile(windsor, params->profile); + vpu_windsor_set_level(windsor, params->level); + vpu_windsor_set_size(windsor, params); + vpu_windsor_set_gop(windsor, params->gop_length); + vpu_windsor_set_bframes(windsor, params->bframes); + vpu_windsor_set_bitrate_mode(windsor, params->rc_enable, params->rc_mode); + vpu_windsor_set_bitrate(windsor, params); + windsor->init_slice_qp = params->i_frame_qp; + + if (!params->frame_rate.numerator) + return -EINVAL; + windsor->frame_rate = params->frame_rate.denominator / params->frame_rate.numerator; + + return 0; +} + +static int vpu_windsor_update_params(struct vpu_shared_addr *shared, + u32 instance, struct vpu_encode_params *params) +{ + struct vpu_enc_expert_mode_param *expert; + + expert = get_expert_param(shared, instance); + + vpu_windsor_set_frame_rate(expert, params); + vpu_windsor_set_qp(expert, params); + vpu_windsor_set_sar(expert, params); + vpu_windsor_set_color(expert, params); + vpu_windsor_update_bitrate(shared, instance, params); + /*expert->config_param.iac_sc_threshold = 0;*/ + + return 0; +} + +int vpu_windsor_set_encode_params(struct vpu_shared_addr *shared, + u32 instance, struct vpu_encode_params *params, u32 update) +{ + if (!params) + return -EINVAL; + + if (!update) + return vpu_windsor_set_params(shared, instance, params); + else + return vpu_windsor_update_params(shared, instance, params); +} + +u32 vpu_windsor_get_max_instance_count(struct vpu_shared_addr *shared) +{ + struct windsor_iface *iface = shared->iface; + + return iface->max_streams; +} diff --git a/drivers/media/platform/amphion/vpu_windsor.h b/drivers/media/platform/amphion/vpu_windsor.h new file mode 100644 index 000000000000..3fbb6556dbca --- /dev/null +++ b/drivers/media/platform/amphion/vpu_windsor.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020-2021 NXP + */ + +#ifndef _AMPHION_VPU_WINDSOR_H +#define _AMPHION_VPU_WINDSOR_H + +u32 vpu_windsor_get_data_size(void); +void vpu_windsor_init_rpc(struct vpu_shared_addr *shared, + struct vpu_buffer *rpc, dma_addr_t boot_addr); +void vpu_windsor_set_log_buf(struct vpu_shared_addr *shared, struct vpu_buffer *log); +void vpu_windsor_set_system_cfg(struct vpu_shared_addr *shared, + u32 regs_base, void __iomem *regs, u32 core_id); +int vpu_windsor_get_stream_buffer_size(struct vpu_shared_addr *shared); +int vpu_windsor_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data); +int vpu_windsor_convert_msg_id(u32 msg_id); +int vpu_windsor_unpack_msg_data(struct vpu_rpc_event *pkt, void *data); +int vpu_windsor_config_memory_resource(struct vpu_shared_addr *shared, + u32 instance, u32 type, u32 index, + struct vpu_buffer *buf); +int vpu_windsor_config_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, struct vpu_buffer *buf); +int vpu_windsor_update_stream_buffer(struct vpu_shared_addr *shared, + u32 instance, u32 ptr, bool write); +int vpu_windsor_get_stream_buffer_desc(struct vpu_shared_addr *shared, + u32 instance, struct vpu_rpc_buffer_desc *desc); +u32 vpu_windsor_get_version(struct vpu_shared_addr *shared); +int vpu_windsor_set_encode_params(struct vpu_shared_addr *shared, + u32 instance, + struct vpu_encode_params *params, + u32 update); +int vpu_windsor_input_frame(struct vpu_shared_addr *shared, + struct vpu_inst *inst, struct vb2_buffer *vb); +u32 vpu_windsor_get_max_instance_count(struct vpu_shared_addr *shared); + +#endif diff --git a/drivers/media/platform/aspeed/Kconfig b/drivers/media/platform/aspeed/Kconfig new file mode 100644 index 000000000000..c871eda33570 --- /dev/null +++ b/drivers/media/platform/aspeed/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Aspeed media platform drivers" + +config VIDEO_ASPEED + tristate "Aspeed AST2400 and AST2500 Video Engine driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + help + Support for the Aspeed Video Engine (VE) embedded in the Aspeed + AST2400 and AST2500 SOCs. The VE can capture and compress video data + from digital or analog sources. diff --git a/drivers/media/platform/aspeed/Makefile b/drivers/media/platform/aspeed/Makefile new file mode 100644 index 000000000000..1979af63dadd --- /dev/null +++ b/drivers/media/platform/aspeed/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 7a24daf7165a..b937dbcbe9e0 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> @@ -33,6 +34,8 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-dma-contig.h> +#define ASPEED_VIDEO_V4L2_MIN_BUF_REQ 3 + #define DEVICE_NAME "aspeed-video" #define ASPEED_VIDEO_JPEG_NUM_QUALITIES 12 @@ -85,8 +88,6 @@ #define VE_CTRL_SOURCE BIT(2) #define VE_CTRL_INT_DE BIT(4) #define VE_CTRL_DIRECT_FETCH BIT(5) -#define VE_CTRL_YUV BIT(6) -#define VE_CTRL_RGB BIT(7) #define VE_CTRL_CAPTURE_FMT GENMASK(7, 6) #define VE_CTRL_AUTO_OR_CURSOR BIT(8) #define VE_CTRL_CLK_INVERSE BIT(11) @@ -143,26 +144,29 @@ #define VE_SRC_LR_EDGE_DET_NO_H BIT(13) #define VE_SRC_LR_EDGE_DET_NO_DISP BIT(14) #define VE_SRC_LR_EDGE_DET_NO_CLK BIT(15) -#define VE_SRC_LR_EDGE_DET_RT_SHF 16 -#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF) +#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, 16) #define VE_SRC_LR_EDGE_DET_INTERLACE BIT(31) #define VE_SRC_TB_EDGE_DET 0x094 #define VE_SRC_TB_EDGE_DET_TOP GENMASK(12, 0) -#define VE_SRC_TB_EDGE_DET_BOT_SHF 16 -#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF) +#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, 16) #define VE_MODE_DETECT_STATUS 0x098 -#define VE_MODE_DETECT_H_PIXELS GENMASK(11, 0) -#define VE_MODE_DETECT_V_LINES_SHF 16 -#define VE_MODE_DETECT_V_LINES GENMASK(27, VE_MODE_DETECT_V_LINES_SHF) +#define VE_MODE_DETECT_H_PERIOD GENMASK(11, 0) +#define VE_MODE_DETECT_EXTSRC_ADC BIT(12) +#define VE_MODE_DETECT_H_STABLE BIT(13) +#define VE_MODE_DETECT_V_STABLE BIT(14) +#define VE_MODE_DETECT_V_LINES GENMASK(27, 16) #define VE_MODE_DETECT_STATUS_VSYNC BIT(28) #define VE_MODE_DETECT_STATUS_HSYNC BIT(29) +#define VE_MODE_DETECT_VSYNC_RDY BIT(30) +#define VE_MODE_DETECT_HSYNC_RDY BIT(31) #define VE_SYNC_STATUS 0x09c #define VE_SYNC_STATUS_HSYNC GENMASK(11, 0) -#define VE_SYNC_STATUS_VSYNC_SHF 16 -#define VE_SYNC_STATUS_VSYNC GENMASK(27, VE_SYNC_STATUS_VSYNC_SHF) +#define VE_SYNC_STATUS_VSYNC GENMASK(27, 16) + +#define VE_H_TOTAL_PIXELS 0x0A0 #define VE_INTERRUPT_CTRL 0x304 #define VE_INTERRUPT_STATUS 0x308 @@ -179,9 +183,24 @@ #define VE_INTERRUPT_VSYNC_DESC BIT(11) #define VE_MODE_DETECT 0x30c +#define VE_MODE_DT_HOR_TOLER GENMASK(31, 28) +#define VE_MODE_DT_VER_TOLER GENMASK(27, 24) +#define VE_MODE_DT_HOR_STABLE GENMASK(23, 20) +#define VE_MODE_DT_VER_STABLE GENMASK(19, 16) +#define VE_MODE_DT_EDG_THROD GENMASK(15, 8) + #define VE_MEM_RESTRICT_START 0x310 #define VE_MEM_RESTRICT_END 0x314 +/* + * VIDEO_MODE_DETECT_DONE: a flag raised if signal lock + * VIDEO_RES_CHANGE: a flag raised if res_change work on-going + * VIDEO_RES_DETECT: a flag raised if res. detection on-going + * VIDEO_STREAMING: a flag raised if user requires stream-on + * VIDEO_FRAME_INPRG: a flag raised if hw working on a frame + * VIDEO_STOPPED: a flag raised if device release + * VIDEO_CLOCKS_ON: a flag raised if clk is on + */ enum { VIDEO_MODE_DETECT_DONE, VIDEO_RES_CHANGE, @@ -192,6 +211,15 @@ enum { VIDEO_CLOCKS_ON, }; +// for VE_CTRL_CAPTURE_FMT +enum aspeed_video_capture_format { + VIDEO_CAP_FMT_YUV_STUDIO_SWING = 0, + VIDEO_CAP_FMT_YUV_FULL_SWING, + VIDEO_CAP_FMT_RGB, + VIDEO_CAP_FMT_GRAY, + VIDEO_CAP_FMT_MAX +}; + struct aspeed_video_addr { unsigned int size; dma_addr_t dma; @@ -214,6 +242,25 @@ struct aspeed_video_perf { #define to_aspeed_video_buffer(x) \ container_of((x), struct aspeed_video_buffer, vb) +/* + * struct aspeed_video - driver data + * + * res_work: holds the delayed_work for res-detection if unlock + * buffers: holds the list of buffer queued from user + * flags: holds the state of video + * sequence: holds the last number of frame completed + * max_compressed_size: holds max compressed stream's size + * srcs: holds the buffer information for srcs + * jpeg: holds the buffer information for jpeg header + * yuv420: a flag raised if JPEG subsampling is 420 + * frame_rate: holds the frame_rate + * jpeg_quality: holds jpeq's quality (0~11) + * frame_bottom: end position of video data in vertical direction + * frame_left: start position of video data in horizontal direction + * frame_right: end position of video data in horizontal direction + * frame_top: start position of video data in vertical direction + * perf: holds the statistics primary for debugfs + */ struct aspeed_video { void __iomem *base; struct clk *eclk; @@ -411,6 +458,8 @@ static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = { }, }; +static unsigned int debug; + static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420) { int i; @@ -458,33 +507,38 @@ static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear, t &= ~clear; t |= bits; writel(t, video->base + reg); - dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before, - readl(video->base + reg)); + v4l2_dbg(3, debug, &video->v4l2_dev, "update %03x[%08x -> %08x]\n", + reg, before, readl(video->base + reg)); } static u32 aspeed_video_read(struct aspeed_video *video, u32 reg) { u32 t = readl(video->base + reg); - dev_dbg(video->dev, "read %03x[%08x]\n", reg, t); + v4l2_dbg(3, debug, &video->v4l2_dev, "read %03x[%08x]\n", reg, t); return t; } static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val) { writel(val, video->base + reg); - dev_dbg(video->dev, "write %03x[%08x]\n", reg, - readl(video->base + reg)); + v4l2_dbg(3, debug, &video->v4l2_dev, "write %03x[%08x]\n", reg, + readl(video->base + reg)); } static void update_perf(struct aspeed_video_perf *p) { + struct aspeed_video *v = container_of(p, struct aspeed_video, + perf); + p->duration = ktime_to_ms(ktime_sub(ktime_get(), p->last_sample)); p->totaltime += p->duration; p->duration_max = max(p->duration, p->duration_max); p->duration_min = min(p->duration, p->duration_min); + v4l2_dbg(2, debug, &v->v4l2_dev, "time consumed: %d ms\n", + p->duration); } static int aspeed_video_start_frame(struct aspeed_video *video) @@ -495,13 +549,13 @@ static int aspeed_video_start_frame(struct aspeed_video *video) u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL); if (video->v4l2_input_status) { - dev_dbg(video->dev, "No signal; don't start frame\n"); + v4l2_warn(&video->v4l2_dev, "No signal; don't start frame\n"); return 0; } if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { - dev_dbg(video->dev, "Engine busy; don't start frame\n"); + v4l2_warn(&video->v4l2_dev, "Engine busy; don't start frame\n"); return -EBUSY; } @@ -510,7 +564,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video) struct aspeed_video_buffer, link); if (!buf) { spin_unlock_irqrestore(&video->lock, flags); - dev_dbg(video->dev, "No buffers; don't start frame\n"); + v4l2_warn(&video->v4l2_dev, "No buffers; don't start frame\n"); return -EPROTO; } @@ -590,7 +644,7 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay) { - dev_dbg(video->dev, "Resolution changed; resetting\n"); + v4l2_dbg(1, debug, &video->v4l2_dev, "Resolution changed; resetting\n"); set_bit(VIDEO_RES_CHANGE, &video->flags); clear_bit(VIDEO_FRAME_INPRG, &video->flags); @@ -614,6 +668,12 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) */ sts &= aspeed_video_read(video, VE_INTERRUPT_CTRL); + v4l2_dbg(2, debug, &video->v4l2_dev, "irq sts=%#x %s%s%s%s\n", sts, + sts & VE_INTERRUPT_MODE_DETECT_WD ? ", unlock" : "", + sts & VE_INTERRUPT_MODE_DETECT ? ", lock" : "", + sts & VE_INTERRUPT_CAPTURE_COMPLETE ? ", capture-done" : "", + sts & VE_INTERRUPT_COMP_COMPLETE ? ", comp-done" : ""); + /* * Resolution changed or signal was lost; reset the engine and * re-initialize @@ -787,8 +847,101 @@ static void aspeed_video_calc_compressed_size(struct aspeed_video *video, aspeed_video_write(video, VE_STREAM_BUF_SIZE, compression_buffer_size_reg); - dev_dbg(video->dev, "Max compressed size: %x\n", - video->max_compressed_size); + v4l2_dbg(1, debug, &video->v4l2_dev, "Max compressed size: %#x\n", + video->max_compressed_size); +} + +/* + * Update v4l2_bt_timings per current status. + * frame_top/frame_bottom/frame_left/frame_right need to be ready. + * + * The following registers start counting from sync's rising edge: + * 1. VR090: frame edge's left and right + * 2. VR094: frame edge's top and bottom + * 3. VR09C: counting from sync's rising edge to falling edge + * + * [Vertical timing] + * +--+ +-------------------+ +--+ + * | | | v i d e o | | | + * +--+ +-----+ +-----+ +---+ + * vsync+--+ + * frame_top+--------+ + * frame_bottom+----------------------------+ + * + * +-------------------+ + * | v i d e o | + * +--+ +-----+ +-----+ +---+ + * | | | | + * +--+ +--+ + * vsync+-------------------------------+ + * frame_top+-----+ + * frame_bottom+-------------------------+ + * + * [Horizontal timing] + * +--+ +-------------------+ +--+ + * | | | v i d e o | | | + * +--+ +-----+ +-----+ +---+ + * hsync+--+ + * frame_left+--------+ + * frame_right+----------------------------+ + * + * +-------------------+ + * | v i d e o | + * +--+ +-----+ +-----+ +---+ + * | | | | + * +--+ +--+ + * hsync+-------------------------------+ + * frame_left+-----+ + * frame_right+-------------------------+ + * + * @v: the struct of aspeed_video + * @det: v4l2_bt_timings to be updated. + */ +static void aspeed_video_get_timings(struct aspeed_video *v, + struct v4l2_bt_timings *det) +{ + u32 mds, sync, htotal, vtotal, vsync, hsync; + + mds = aspeed_video_read(v, VE_MODE_DETECT_STATUS); + sync = aspeed_video_read(v, VE_SYNC_STATUS); + htotal = aspeed_video_read(v, VE_H_TOTAL_PIXELS); + vtotal = FIELD_GET(VE_MODE_DETECT_V_LINES, mds); + vsync = FIELD_GET(VE_SYNC_STATUS_VSYNC, sync); + hsync = FIELD_GET(VE_SYNC_STATUS_HSYNC, sync); + + /* + * This is a workaround for polarity detection. + * Because ast-soc counts sync from sync's rising edge, the reg value + * of sync would be larger than video's active area if negative. + */ + if (vsync > det->height) + det->polarities &= ~V4L2_DV_VSYNC_POS_POL; + else + det->polarities |= V4L2_DV_VSYNC_POS_POL; + if (hsync > det->width) + det->polarities &= ~V4L2_DV_HSYNC_POS_POL; + else + det->polarities |= V4L2_DV_HSYNC_POS_POL; + + if (det->polarities & V4L2_DV_VSYNC_POS_POL) { + det->vbackporch = v->frame_top - vsync; + det->vfrontporch = vtotal - v->frame_bottom; + det->vsync = vsync; + } else { + det->vbackporch = v->frame_top; + det->vfrontporch = vsync - v->frame_bottom; + det->vsync = vtotal - vsync; + } + + if (det->polarities & V4L2_DV_HSYNC_POS_POL) { + det->hbackporch = v->frame_left - hsync; + det->hfrontporch = htotal - v->frame_right; + det->hsync = hsync; + } else { + det->hbackporch = v->frame_left; + det->hfrontporch = hsync - v->frame_right; + det->hsync = htotal - hsync; + } } #define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags) @@ -801,7 +954,6 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) u32 mds; u32 src_lr_edge; u32 src_tb_edge; - u32 sync; struct v4l2_bt_timings *det = &video->detected_timings; det->width = MIN_WIDTH; @@ -825,11 +977,18 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) res_check(video), MODE_DETECT_TIMEOUT); if (!rc) { - dev_dbg(video->dev, "Timed out; first mode detect\n"); + v4l2_warn(&video->v4l2_dev, "Timed out; first mode detect\n"); clear_bit(VIDEO_RES_DETECT, &video->flags); return; } + mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS); + // try detection again if current signal isn't stable + if (!(mds & VE_MODE_DETECT_H_STABLE) || + !(mds & VE_MODE_DETECT_V_STABLE) || + (mds & VE_MODE_DETECT_EXTSRC_ADC)) + continue; + aspeed_video_check_and_set_polarity(video); aspeed_video_enable_mode_detect(video); @@ -839,33 +998,22 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) MODE_DETECT_TIMEOUT); clear_bit(VIDEO_RES_DETECT, &video->flags); if (!rc) { - dev_dbg(video->dev, "Timed out; second mode detect\n"); + v4l2_warn(&video->v4l2_dev, "Timed out; second mode detect\n"); return; } src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET); src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET); - mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS); - sync = aspeed_video_read(video, VE_SYNC_STATUS); - - video->frame_bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >> - VE_SRC_TB_EDGE_DET_BOT_SHF; - video->frame_top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP; - det->vfrontporch = video->frame_top; - det->vbackporch = ((mds & VE_MODE_DETECT_V_LINES) >> - VE_MODE_DETECT_V_LINES_SHF) - video->frame_bottom; - det->vsync = (sync & VE_SYNC_STATUS_VSYNC) >> - VE_SYNC_STATUS_VSYNC_SHF; + + video->frame_bottom = FIELD_GET(VE_SRC_TB_EDGE_DET_BOT, src_tb_edge); + video->frame_top = FIELD_GET(VE_SRC_TB_EDGE_DET_TOP, src_tb_edge); + if (video->frame_top > video->frame_bottom) continue; - video->frame_right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >> - VE_SRC_LR_EDGE_DET_RT_SHF; - video->frame_left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT; - det->hfrontporch = video->frame_left; - det->hbackporch = (mds & VE_MODE_DETECT_H_PIXELS) - - video->frame_right; - det->hsync = sync & VE_SYNC_STATUS_HSYNC; + video->frame_right = FIELD_GET(VE_SRC_LR_EDGE_DET_RT, src_lr_edge); + video->frame_left = FIELD_GET(VE_SRC_LR_EDGE_DET_LEFT, src_lr_edge); + if (video->frame_left > video->frame_right) continue; @@ -873,7 +1021,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); if (invalid_resolution) { - dev_dbg(video->dev, "Invalid resolution detected\n"); + v4l2_warn(&video->v4l2_dev, "Invalid resolution detected\n"); return; } @@ -881,6 +1029,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) det->width = (video->frame_right - video->frame_left) + 1; video->v4l2_input_status = 0; + aspeed_video_get_timings(video, det); + /* * Enable mode-detect watchdog, resolution-change watchdog and * automatic compression after frame capture. @@ -890,8 +1040,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG); - dev_dbg(video->dev, "Got resolution: %dx%d\n", det->width, - det->height); + v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n", + det->width, det->height); } static void aspeed_video_set_resolution(struct aspeed_video *video) @@ -902,18 +1052,19 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) /* Set capture/compression frame sizes */ aspeed_video_calc_compressed_size(video, size); - if (video->active_timings.width == 1680) { + if (!IS_ALIGNED(act->width, 64)) { /* - * This is a workaround to fix a silicon bug on A1 and A2 - * revisions. Since it doesn't break capturing operation of + * This is a workaround to fix a AST2500 silicon bug on A1 and + * A2 revisions. Since it doesn't break capturing operation of * other revisions, use it for all revisions without checking - * the revision ID. It picked 1728 which is a very next - * 64-pixels aligned value to 1680 to minimize memory bandwidth + * the revision ID. It picked new width which is a very next + * 64-pixels aligned value to minimize memory bandwidth * and to get better access speed from video engine. */ - aspeed_video_write(video, VE_CAP_WINDOW, - 1728 << 16 | act->height); - size += (1728 - 1680) * video->active_timings.height; + u32 width = ALIGN(act->width, 64); + + aspeed_video_write(video, VE_CAP_WINDOW, width << 16 | act->height); + size = width * act->height; } else { aspeed_video_write(video, VE_CAP_WINDOW, act->width << 16 | act->height); @@ -924,6 +1075,7 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ if (size < DIRECT_FETCH_THRESHOLD) { + v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n"); aspeed_video_write(video, VE_TGS_0, FIELD_PREP(VE_TGS_FIRST, video->frame_left - 1) | @@ -935,6 +1087,7 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) video->frame_bottom + 1)); aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE); } else { + v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Direct Mode\n"); aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); } @@ -951,6 +1104,10 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) goto err_mem; + v4l2_dbg(1, debug, &video->v4l2_dev, "src buf0 addr(%pad) size(%d)\n", + &video->srcs[0].dma, video->srcs[0].size); + v4l2_dbg(1, debug, &video->v4l2_dev, "src buf1 addr(%pad) size(%d)\n", + &video->srcs[1].dma, video->srcs[1].size); aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); } @@ -969,7 +1126,8 @@ static void aspeed_video_init_regs(struct aspeed_video *video) u32 comp_ctrl = VE_COMP_CTRL_RSVD | FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); - u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; + u32 ctrl = VE_CTRL_AUTO_OR_CURSOR | + FIELD_PREP(VE_CTRL_CAPTURE_FMT, VIDEO_CAP_FMT_YUV_FULL_SWING); u32 seq_ctrl = video->jpeg_mode; if (video->frame_rate) @@ -1004,7 +1162,12 @@ static void aspeed_video_init_regs(struct aspeed_video *video) aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000); /* Set mode detection defaults */ - aspeed_video_write(video, VE_MODE_DETECT, 0x22666500); + aspeed_video_write(video, VE_MODE_DETECT, + FIELD_PREP(VE_MODE_DT_HOR_TOLER, 2) | + FIELD_PREP(VE_MODE_DT_VER_TOLER, 2) | + FIELD_PREP(VE_MODE_DT_HOR_STABLE, 6) | + FIELD_PREP(VE_MODE_DT_VER_STABLE, 6) | + FIELD_PREP(VE_MODE_DT_EDG_THROD, 0x65)); } static void aspeed_video_start(struct aspeed_video *video) @@ -1111,7 +1274,7 @@ static int aspeed_video_get_parm(struct file *file, void *fh, struct aspeed_video *video = video_drvdata(file); a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - a->parm.capture.readbuffers = 3; + a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; a->parm.capture.timeperframe.numerator = 1; if (!video->frame_rate) a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; @@ -1128,7 +1291,7 @@ static int aspeed_video_set_parm(struct file *file, void *fh, struct aspeed_video *video = video_drvdata(file); a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - a->parm.capture.readbuffers = 3; + a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; if (a->parm.capture.timeperframe.numerator) frame_rate = a->parm.capture.timeperframe.denominator / @@ -1215,6 +1378,9 @@ static int aspeed_video_set_dv_timings(struct file *file, void *fh, timings->type = V4L2_DV_BT_656_1120; + v4l2_dbg(1, debug, &video->v4l2_dev, "set new timings(%dx%d)\n", + timings->bt.width, timings->bt.height); + return 0; } @@ -1395,6 +1561,7 @@ static void aspeed_video_resolution_work(struct work_struct *work) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; + v4l2_dbg(1, debug, &video->v4l2_dev, "fire source change event\n"); v4l2_event_queue(&video->vdev, &ev); } else if (test_bit(VIDEO_STREAMING, &video->flags)) { /* No resolution change so just restart streaming */ @@ -1516,7 +1683,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q) !test_bit(VIDEO_FRAME_INPRG, &video->flags), STOP_TIMEOUT); if (!rc) { - dev_dbg(video->dev, "Timed out when stopping streaming\n"); + v4l2_warn(&video->v4l2_dev, "Timed out when stopping streaming\n"); /* * Need to force stop any DMA and try and get HW into a good @@ -1676,7 +1843,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video) vbq->drv_priv = video; vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = 3; + vbq->min_buffers_needed = ASPEED_VIDEO_V4L2_MIN_BUF_REQ; rc = vb2_queue_init(vbq); if (rc) { @@ -1730,6 +1897,7 @@ static int aspeed_video_init(struct aspeed_video *video) dev_err(dev, "Unable to request IRQ %d\n", irq); return rc; } + dev_info(video->dev, "irq %d\n", irq); video->eclk = devm_clk_get(dev, "eclk"); if (IS_ERR(video->eclk)) { @@ -1766,6 +1934,8 @@ static int aspeed_video_init(struct aspeed_video *video) rc = -ENOMEM; goto err_release_reserved_mem; } + dev_info(video->dev, "alloc mem size(%d) at %pad for jpeg header\n", + VE_JPEG_HEADER_SIZE, &video->jpeg.dma); aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); @@ -1791,7 +1961,6 @@ MODULE_DEVICE_TABLE(of, aspeed_video_of_match); static int aspeed_video_probe(struct platform_device *pdev) { const struct aspeed_video_config *config; - const struct of_device_id *match; struct aspeed_video *video; int rc; @@ -1803,11 +1972,10 @@ static int aspeed_video_probe(struct platform_device *pdev) if (IS_ERR(video->base)) return PTR_ERR(video->base); - match = of_match_node(aspeed_video_of_match, pdev->dev.of_node); - if (!match) - return -EINVAL; + config = of_device_get_match_data(&pdev->dev); + if (!config) + return -ENODEV; - config = match->data; video->jpeg_mode = config->jpeg_mode; video->comp_size_read = config->comp_size_read; @@ -1875,6 +2043,9 @@ static struct platform_driver aspeed_video_driver = { module_platform_driver(aspeed_video_driver); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0=off,1=info,2=debug,3=reg ops)"); + MODULE_DESCRIPTION("ASPEED Video Engine Driver"); MODULE_AUTHOR("Eddie James"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig index dda2f27da317..83aebee0c8eb 100644 --- a/drivers/media/platform/atmel/Kconfig +++ b/drivers/media/platform/atmel/Kconfig @@ -1,7 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only + +comment "Atmel media platform drivers" + config VIDEO_ATMEL_ISC tristate "ATMEL Image Sensor Controller (ISC) support" - depends on VIDEO_V4L2 && COMMON_CLK + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK depends on ARCH_AT91 || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API @@ -15,7 +19,8 @@ config VIDEO_ATMEL_ISC config VIDEO_ATMEL_XISC tristate "ATMEL eXtended Image Sensor Controller (XISC) support" - depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK && VIDEO_V4L2_SUBDEV_API depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO @@ -33,10 +38,27 @@ config VIDEO_ATMEL_ISC_BASE config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" - depends on VIDEO_V4L2 && OF + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help This module makes the ATMEL Image Sensor Interface available as a v4l2 device. + +config VIDEO_MICROCHIP_CSI2DC + tristate "Microchip CSI2 Demux Controller" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK && OF + depends on ARCH_AT91 || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + CSI2 Demux Controller driver. CSI2DC is a helper chip + that converts IDI interface byte stream to a parallel pixel stream. + It supports various RAW formats as input. + + To compile this driver as a module, choose M here: the + module will be called microchip-csi2dc. diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile index 46d264ab7948..794e8f739287 100644 --- a/drivers/media/platform/atmel/Makefile +++ b/drivers/media/platform/atmel/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only atmel-isc-objs = atmel-sama5d2-isc.o atmel-xisc-objs = atmel-sama7g5-isc.o +atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-base.o +obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o +obj-$(CONFIG_VIDEO_MICROCHIP_CSI2DC) += microchip-csi2dc.o diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index 660cd0ab6749..db15770d5b88 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -8,10 +8,6 @@ * Author: Eugen Hristev <eugen.hristev@microchip.com> * */ - -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/math64.h> @@ -100,297 +96,6 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc) } } -static int isc_wait_clk_stable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - struct regmap *regmap = isc_clk->regmap; - unsigned long timeout = jiffies + usecs_to_jiffies(1000); - unsigned int status; - - while (time_before(jiffies, timeout)) { - regmap_read(regmap, ISC_CLKSR, &status); - if (!(status & ISC_CLKSR_SIP)) - return 0; - - usleep_range(10, 250); - } - - return -ETIMEDOUT; -} - -static int isc_clk_prepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return ret; - - return isc_wait_clk_stable(hw); -} - -static void isc_clk_unprepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - isc_wait_clk_stable(hw); - - pm_runtime_put_sync(isc_clk->dev); -} - -static int isc_clk_enable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - struct regmap *regmap = isc_clk->regmap; - unsigned long flags; - unsigned int status; - - dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", - __func__, id, isc_clk->div, isc_clk->parent_id); - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_update_bits(regmap, ISC_CLKCFG, - ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), - (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | - (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); - - regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); - - regmap_read(regmap, ISC_CLKSR, &status); - if (status & ISC_CLK(id)) - return 0; - else - return -EINVAL; -} - -static void isc_clk_disable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - unsigned long flags; - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); -} - -static int isc_clk_is_enabled(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 status; - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return 0; - - regmap_read(isc_clk->regmap, ISC_CLKSR, &status); - - pm_runtime_put_sync(isc_clk->dev); - - return status & ISC_CLK(isc_clk->id) ? 1 : 0; -} - -static unsigned long -isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); -} - -static int isc_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - long best_rate = -EINVAL; - int best_diff = -1; - unsigned int i, div; - - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - struct clk_hw *parent; - unsigned long parent_rate; - - parent = clk_hw_get_parent_by_index(hw, i); - if (!parent) - continue; - - parent_rate = clk_hw_get_rate(parent); - if (!parent_rate) - continue; - - for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { - unsigned long rate; - int diff; - - rate = DIV_ROUND_CLOSEST(parent_rate, div); - diff = abs(req->rate - rate); - - if (best_diff < 0 || best_diff > diff) { - best_rate = rate; - best_diff = diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } - - if (!best_diff || rate < req->rate) - break; - } - - if (!best_diff) - break; - } - - dev_dbg(isc_clk->dev, - "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", - __func__, best_rate, - __clk_get_name((req->best_parent_hw)->clk), - req->best_parent_rate); - - if (best_rate < 0) - return best_rate; - - req->rate = best_rate; - - return 0; -} - -static int isc_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - if (index >= clk_hw_get_num_parents(hw)) - return -EINVAL; - - isc_clk->parent_id = index; - - return 0; -} - -static u8 isc_clk_get_parent(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return isc_clk->parent_id; -} - -static int isc_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 div; - - if (!rate) - return -EINVAL; - - div = DIV_ROUND_CLOSEST(parent_rate, rate); - if (div > (ISC_CLK_MAX_DIV + 1) || !div) - return -EINVAL; - - isc_clk->div = div - 1; - - return 0; -} - -static const struct clk_ops isc_clk_ops = { - .prepare = isc_clk_prepare, - .unprepare = isc_clk_unprepare, - .enable = isc_clk_enable, - .disable = isc_clk_disable, - .is_enabled = isc_clk_is_enabled, - .recalc_rate = isc_clk_recalc_rate, - .determine_rate = isc_clk_determine_rate, - .set_parent = isc_clk_set_parent, - .get_parent = isc_clk_get_parent, - .set_rate = isc_clk_set_rate, -}; - -static int isc_clk_register(struct isc_device *isc, unsigned int id) -{ - struct regmap *regmap = isc->regmap; - struct device_node *np = isc->dev->of_node; - struct isc_clk *isc_clk; - struct clk_init_data init; - const char *clk_name = np->name; - const char *parent_names[3]; - int num_parents; - - if (id == ISC_ISPCK && !isc->ispck_required) - return 0; - - num_parents = of_clk_get_parent_count(np); - if (num_parents < 1 || num_parents > 3) - return -EINVAL; - - if (num_parents > 2 && id == ISC_ISPCK) - num_parents = 2; - - of_clk_parent_fill(np, parent_names, num_parents); - - if (id == ISC_MCK) - of_property_read_string(np, "clock-output-names", &clk_name); - else - clk_name = "isc-ispck"; - - init.parent_names = parent_names; - init.num_parents = num_parents; - init.name = clk_name; - init.ops = &isc_clk_ops; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; - - isc_clk = &isc->isc_clks[id]; - isc_clk->hw.init = &init; - isc_clk->regmap = regmap; - isc_clk->id = id; - isc_clk->dev = isc->dev; - spin_lock_init(&isc_clk->lock); - - isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); - if (IS_ERR(isc_clk->clk)) { - dev_err(isc->dev, "%s: clock register fail\n", clk_name); - return PTR_ERR(isc_clk->clk); - } else if (id == ISC_MCK) - of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); - - return 0; -} - -int isc_clk_init(struct isc_device *isc) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) - isc->isc_clks[i].clk = ERR_PTR(-EINVAL); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - ret = isc_clk_register(isc, i); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(isc_clk_init); - -void isc_clk_cleanup(struct isc_device *isc) -{ - unsigned int i; - - of_clk_del_provider(isc->dev->of_node); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - struct isc_clk *isc_clk = &isc->isc_clks[i]; - - if (!IS_ERR(isc_clk->clk)) - clk_unregister(isc_clk->clk); - } -} -EXPORT_SYMBOL_GPL(isc_clk_cleanup); static int isc_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -912,6 +617,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 8; + isc->try_config.bpp_v4l2 = 8; break; case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: @@ -921,6 +627,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SGBRG12: @@ -930,24 +637,28 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_RGB565: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_ARGB444: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_ARGB555: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_ABGR32: case V4L2_PIX_FMT_XBGR32: @@ -955,42 +666,49 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 32; + isc->try_config.bpp_v4l2 = 32; break; case V4L2_PIX_FMT_YUV420: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; isc->try_config.bpp = 12; + isc->try_config.bpp_v4l2 = 8; /* only first plane */ break; case V4L2_PIX_FMT_YUV422P: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 8; /* only first plane */ break; case V4L2_PIX_FMT_YUYV: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_UYVY: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_VYUY: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; case V4L2_PIX_FMT_GREY: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 8; + isc->try_config.bpp_v4l2 = 8; break; case V4L2_PIX_FMT_Y16: isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; @@ -1000,6 +718,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; break; default: return -EINVAL; @@ -1248,8 +967,9 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, pixfmt->height = isc->max_height; pixfmt->field = V4L2_FIELD_NONE; - pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp) >> 3; - pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; + pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * + pixfmt->height; if (code) *code = mbus_code; @@ -1369,14 +1089,12 @@ static int isc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev_frame_size_enum fse = { - .code = isc->config.sd_format->mbus_code, - .index = fsize->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; int ret = -EINVAL; int i; + if (fsize->index) + return -EINVAL; + for (i = 0; i < isc->num_user_formats; i++) if (isc->user_formats[i]->fourcc == fsize->pixel_format) ret = 0; @@ -1388,50 +1106,14 @@ static int isc_enum_framesizes(struct file *file, void *fh, if (ret) return ret; - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - NULL, &fse); - if (ret) - return ret; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int isc_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *fival) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev_frame_interval_enum fie = { - .code = isc->config.sd_format->mbus_code, - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret = -EINVAL; - unsigned int i; - - for (i = 0; i < isc->num_user_formats; i++) - if (isc->user_formats[i]->fourcc == fival->pixel_format) - ret = 0; - - for (i = 0; i < isc->controller_formats_size; i++) - if (isc->controller_formats[i].fourcc == fival->pixel_format) - ret = 0; - - if (ret) - return ret; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, - enum_frame_interval, NULL, &fie); - if (ret) - return ret; + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; + fsize->stepwise.min_width = 16; + fsize->stepwise.max_width = isc->max_width; + fsize->stepwise.min_height = 16; + fsize->stepwise.max_height = isc->max_height; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; return 0; } @@ -1460,7 +1142,6 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = { .vidioc_g_parm = isc_g_parm, .vidioc_s_parm = isc_s_parm, .vidioc_enum_framesizes = isc_enum_framesizes, - .vidioc_enum_frameintervals = isc_enum_frameintervals, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, @@ -1608,10 +1289,15 @@ static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) if (!*min) *min = 1; + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: hist_id %u, hist_count %u", + ctrls->hist_id, *hist_count); } static void isc_wb_update(struct isc_ctrls *ctrls) { + struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); u32 *hist_count = &ctrls->hist_count[0]; u32 c, offset[4]; u64 avg = 0; @@ -1628,6 +1314,9 @@ static void isc_wb_update(struct isc_ctrls *ctrls) (u64)hist_count[ISC_HIS_CFG_MODE_GB]; avg >>= 1; + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: green components average %llu\n", avg); + /* Green histogram is null, nothing to do */ if (!avg) return; @@ -1680,9 +1369,19 @@ static void isc_wb_update(struct isc_ctrls *ctrls) else gw_gain[c] = 1 << 9; + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: component %d, s_gain %u, gw_gain %u\n", + c, s_gain[c], gw_gain[c]); /* multiply both gains and adjust for decimals */ ctrls->gain[c] = s_gain[c] * gw_gain[c]; ctrls->gain[c] >>= 9; + + /* make sure we are not out of range */ + ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: component %d, final gain %u\n", + c, ctrls->gain[c]); } } @@ -1706,6 +1405,10 @@ static void isc_awb_work(struct work_struct *w) return; isc_hist_count(isc, &min, &max); + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); + ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; @@ -2181,7 +1884,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) } /* Register video device */ - strscpy(vdev->name, "microchip-isc", sizeof(vdev->name)); + strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); vdev->release = video_device_release_empty; vdev->fops = &isc_fops; vdev->ioctl_ops = &isc_ioctl_ops; diff --git a/drivers/media/platform/atmel/atmel-isc-clk.c b/drivers/media/platform/atmel/atmel-isc-clk.c new file mode 100644 index 000000000000..2059fe376b00 --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc-clk.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip Image Sensor Controller (ISC) common clock driver setup + * + * Copyright (C) 2016 Microchip Technology, Inc. + * + * Author: Songjun Wu + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include "atmel-isc-regs.h" +#include "atmel-isc.h" + +static int isc_wait_clk_stable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + struct regmap *regmap = isc_clk->regmap; + unsigned long timeout = jiffies + usecs_to_jiffies(1000); + unsigned int status; + + while (time_before(jiffies, timeout)) { + regmap_read(regmap, ISC_CLKSR, &status); + if (!(status & ISC_CLKSR_SIP)) + return 0; + + usleep_range(10, 250); + } + + return -ETIMEDOUT; +} + +static int isc_clk_prepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + int ret; + + ret = pm_runtime_resume_and_get(isc_clk->dev); + if (ret < 0) + return ret; + + return isc_wait_clk_stable(hw); +} + +static void isc_clk_unprepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + isc_wait_clk_stable(hw); + + pm_runtime_put_sync(isc_clk->dev); +} + +static int isc_clk_enable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + struct regmap *regmap = isc_clk->regmap; + unsigned long flags; + unsigned int status; + + dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", + __func__, id, isc_clk->div, isc_clk->parent_id); + + spin_lock_irqsave(&isc_clk->lock, flags); + regmap_update_bits(regmap, ISC_CLKCFG, + ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), + (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | + (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); + + regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); + spin_unlock_irqrestore(&isc_clk->lock, flags); + + regmap_read(regmap, ISC_CLKSR, &status); + if (status & ISC_CLK(id)) + return 0; + else + return -EINVAL; +} + +static void isc_clk_disable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + unsigned long flags; + + spin_lock_irqsave(&isc_clk->lock, flags); + regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); + spin_unlock_irqrestore(&isc_clk->lock, flags); +} + +static int isc_clk_is_enabled(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 status; + int ret; + + ret = pm_runtime_resume_and_get(isc_clk->dev); + if (ret < 0) + return 0; + + regmap_read(isc_clk->regmap, ISC_CLKSR, &status); + + pm_runtime_put_sync(isc_clk->dev); + + return status & ISC_CLK(isc_clk->id) ? 1 : 0; +} + +static unsigned long +isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); +} + +static int isc_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + long best_rate = -EINVAL; + int best_diff = -1; + unsigned int i, div; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + if (!parent_rate) + continue; + + for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { + unsigned long rate; + int diff; + + rate = DIV_ROUND_CLOSEST(parent_rate, div); + diff = abs(req->rate - rate); + + if (best_diff < 0 || best_diff > diff) { + best_rate = rate; + best_diff = diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + dev_dbg(isc_clk->dev, + "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + + return 0; +} + +static int isc_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + isc_clk->parent_id = index; + + return 0; +} + +static u8 isc_clk_get_parent(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return isc_clk->parent_id; +} + +static int isc_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > (ISC_CLK_MAX_DIV + 1) || !div) + return -EINVAL; + + isc_clk->div = div - 1; + + return 0; +} + +static const struct clk_ops isc_clk_ops = { + .prepare = isc_clk_prepare, + .unprepare = isc_clk_unprepare, + .enable = isc_clk_enable, + .disable = isc_clk_disable, + .is_enabled = isc_clk_is_enabled, + .recalc_rate = isc_clk_recalc_rate, + .determine_rate = isc_clk_determine_rate, + .set_parent = isc_clk_set_parent, + .get_parent = isc_clk_get_parent, + .set_rate = isc_clk_set_rate, +}; + +static int isc_clk_register(struct isc_device *isc, unsigned int id) +{ + struct regmap *regmap = isc->regmap; + struct device_node *np = isc->dev->of_node; + struct isc_clk *isc_clk; + struct clk_init_data init; + const char *clk_name = np->name; + const char *parent_names[3]; + int num_parents; + + if (id == ISC_ISPCK && !isc->ispck_required) + return 0; + + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1 || num_parents > 3) + return -EINVAL; + + if (num_parents > 2 && id == ISC_ISPCK) + num_parents = 2; + + of_clk_parent_fill(np, parent_names, num_parents); + + if (id == ISC_MCK) + of_property_read_string(np, "clock-output-names", &clk_name); + else + clk_name = "isc-ispck"; + + init.parent_names = parent_names; + init.num_parents = num_parents; + init.name = clk_name; + init.ops = &isc_clk_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + isc_clk = &isc->isc_clks[id]; + isc_clk->hw.init = &init; + isc_clk->regmap = regmap; + isc_clk->id = id; + isc_clk->dev = isc->dev; + spin_lock_init(&isc_clk->lock); + + isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); + if (IS_ERR(isc_clk->clk)) { + dev_err(isc->dev, "%s: clock register fail\n", clk_name); + return PTR_ERR(isc_clk->clk); + } else if (id == ISC_MCK) { + of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); + } + + return 0; +} + +int isc_clk_init(struct isc_device *isc) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) + isc->isc_clks[i].clk = ERR_PTR(-EINVAL); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + ret = isc_clk_register(isc, i); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(isc_clk_init); + +void isc_clk_cleanup(struct isc_device *isc) +{ + unsigned int i; + + of_clk_del_provider(isc->dev->of_node); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + struct isc_clk *isc_clk = &isc->isc_clks[i]; + + if (!IS_ERR(isc_clk->clk)) + clk_unregister(isc_clk->clk); + } +} +EXPORT_SYMBOL_GPL(isc_clk_cleanup); diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index 2bfcb135ef13..07fa6dbf8460 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -10,6 +10,13 @@ */ #ifndef _ATMEL_ISC_H_ +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-contig.h> + #define ISC_CLK_MAX_DIV 255 enum isc_clk_id { @@ -95,6 +102,9 @@ struct isc_format { configuration. * @fourcc: Fourcc code for this format. * @bpp: Bytes per pixel in the current format. + * @bpp_v4l2: Bytes per pixel in the current format, for v4l2. + This differs from 'bpp' in the sense that in planar + formats, it refers only to the first plane. * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging) * @dcfg_imode: Configuration of the input of the DMA module * @dctrl_dview: Configuration of the output of the DMA module @@ -105,6 +115,7 @@ struct fmt_config { u32 fourcc; u8 bpp; + u8 bpp_v4l2; u32 rlp_cfg_mode; u32 dcfg_imode; diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 1b2063cce0f7..c5b9563e36cb 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -88,6 +88,30 @@ static const struct isc_format sama5d2_controller_formats[] = { { .fourcc = V4L2_PIX_FMT_Y10, }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + }, }; /* This is a list of formats that the ISC can receive as *input* */ diff --git a/drivers/media/platform/atmel/atmel-sama7g5-isc.c b/drivers/media/platform/atmel/atmel-sama7g5-isc.c index 5d1c76f680f3..07a80b08bc54 100644 --- a/drivers/media/platform/atmel/atmel-sama7g5-isc.c +++ b/drivers/media/platform/atmel/atmel-sama7g5-isc.c @@ -100,6 +100,30 @@ static const struct isc_format sama7g5_controller_formats[] = { { .fourcc = V4L2_PIX_FMT_Y16, }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + }, }; /* This is a list of formats that the ISC can receive as *input* */ @@ -188,7 +212,7 @@ static struct isc_format sama7g5_formats_list[] = { }, { .fourcc = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, }, { @@ -556,7 +580,6 @@ static int microchip_xisc_remove(struct platform_device *pdev) v4l2_device_unregister(&isc->v4l2_dev); - clk_disable_unprepare(isc->ispck); clk_disable_unprepare(isc->hclock); isc_clk_cleanup(isc); @@ -568,7 +591,6 @@ static int __maybe_unused xisc_runtime_suspend(struct device *dev) { struct isc_device *isc = dev_get_drvdata(dev); - clk_disable_unprepare(isc->ispck); clk_disable_unprepare(isc->hclock); return 0; @@ -583,10 +605,6 @@ static int __maybe_unused xisc_runtime_resume(struct device *dev) if (ret) return ret; - ret = clk_prepare_enable(isc->ispck); - if (ret) - clk_disable_unprepare(isc->hclock); - return ret; } diff --git a/drivers/media/platform/atmel/microchip-csi2dc.c b/drivers/media/platform/atmel/microchip-csi2dc.c new file mode 100644 index 000000000000..2487978db1f1 --- /dev/null +++ b/drivers/media/platform/atmel/microchip-csi2dc.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip CSI2 Demux Controller (CSI2DC) driver + * + * Copyright (C) 2018 Microchip Technology, Inc. + * + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/videodev2.h> + +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* Global configuration register */ +#define CSI2DC_GCFG 0x0 + +/* MIPI sensor pixel clock is free running */ +#define CSI2DC_GCFG_MIPIFRN BIT(0) +/* GPIO parallel interface selection */ +#define CSI2DC_GCFG_GPIOSEL BIT(1) +/* Output waveform inter-line minimum delay */ +#define CSI2DC_GCFG_HLC(v) ((v) << 4) +#define CSI2DC_GCFG_HLC_MASK GENMASK(7, 4) +/* SAMA7G5 requires a HLC delay of 15 */ +#define SAMA7G5_HLC (15) + +/* Global control register */ +#define CSI2DC_GCTLR 0x04 +#define CSI2DC_GCTLR_SWRST BIT(0) + +/* Global status register */ +#define CSI2DC_GS 0x08 + +/* SSP interrupt status register */ +#define CSI2DC_SSPIS 0x28 +/* Pipe update register */ +#define CSI2DC_PU 0xc0 +/* Video pipe attributes update */ +#define CSI2DC_PU_VP BIT(0) + +/* Pipe update status register */ +#define CSI2DC_PUS 0xc4 + +/* Video pipeline Interrupt Status Register */ +#define CSI2DC_VPISR 0xf4 + +/* Video pipeline enable register */ +#define CSI2DC_VPE 0xf8 +#define CSI2DC_VPE_ENABLE BIT(0) + +/* Video pipeline configuration register */ +#define CSI2DC_VPCFG 0xfc +/* Data type */ +#define CSI2DC_VPCFG_DT(v) ((v) << 0) +#define CSI2DC_VPCFG_DT_MASK GENMASK(5, 0) +/* Virtual channel identifier */ +#define CSI2DC_VPCFG_VC(v) ((v) << 6) +#define CSI2DC_VPCFG_VC_MASK GENMASK(7, 6) +/* Decompression enable */ +#define CSI2DC_VPCFG_DE BIT(8) +/* Decoder mode */ +#define CSI2DC_VPCFG_DM(v) ((v) << 9) +#define CSI2DC_VPCFG_DM_DECODER8TO12 0 +/* Decoder predictor 2 selection */ +#define CSI2DC_VPCFG_DP2 BIT(12) +/* Recommended memory storage */ +#define CSI2DC_VPCFG_RMS BIT(13) +/* Post adjustment */ +#define CSI2DC_VPCFG_PA BIT(14) + +/* Video pipeline column register */ +#define CSI2DC_VPCOL 0x100 +/* Column number */ +#define CSI2DC_VPCOL_COL(v) ((v) << 0) +#define CSI2DC_VPCOL_COL_MASK GENMASK(15, 0) + +/* Video pipeline row register */ +#define CSI2DC_VPROW 0x104 +/* Row number */ +#define CSI2DC_VPROW_ROW(v) ((v) << 0) +#define CSI2DC_VPROW_ROW_MASK GENMASK(15, 0) + +/* Version register */ +#define CSI2DC_VERSION 0x1fc + +/* register read/write helpers */ +#define csi2dc_readl(st, reg) readl_relaxed((st)->base + (reg)) +#define csi2dc_writel(st, reg, val) writel_relaxed((val), \ + (st)->base + (reg)) + +/* supported RAW data types */ +#define CSI2DC_DT_RAW6 0x28 +#define CSI2DC_DT_RAW7 0x29 +#define CSI2DC_DT_RAW8 0x2a +#define CSI2DC_DT_RAW10 0x2b +#define CSI2DC_DT_RAW12 0x2c +#define CSI2DC_DT_RAW14 0x2d +/* YUV data types */ +#define CSI2DC_DT_YUV422_8B 0x1e + +/* + * struct csi2dc_format - CSI2DC format type struct + * @mbus_code: Media bus code for the format + * @dt: Data type constant for this format + */ +struct csi2dc_format { + u32 mbus_code; + u32 dt; +}; + +static const struct csi2dc_format csi2dc_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .dt = CSI2DC_DT_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .dt = CSI2DC_DT_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .dt = CSI2DC_DT_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .dt = CSI2DC_DT_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .dt = CSI2DC_DT_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .dt = CSI2DC_DT_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .dt = CSI2DC_DT_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .dt = CSI2DC_DT_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dt = CSI2DC_DT_YUV422_8B, + }, +}; + +enum mipi_csi_pads { + CSI2DC_PAD_SINK = 0, + CSI2DC_PAD_SOURCE = 1, + CSI2DC_PADS_NUM = 2, +}; + +/* + * struct csi2dc_device - CSI2DC device driver data/config struct + * @base: Register map base address + * @csi2dc_sd: v4l2 subdevice for the csi2dc device + * This is the subdevice that the csi2dc device itself + * registers in v4l2 subsystem + * @dev: struct device for this csi2dc device + * @pclk: Peripheral clock reference + * Input clock that clocks the hardware block internal + * logic + * @scck: Sensor Controller clock reference + * Input clock that is used to generate the pixel clock + * @format: Current saved format used in g/s fmt + * @cur_fmt: Current state format + * @try_fmt: Try format that is being tried + * @pads: Media entity pads for the csi2dc subdevice + * @clk_gated: Whether the clock is gated or free running + * @video_pipe: Whether video pipeline is configured + * @parallel_mode: The underlying subdevice is connected on a parallel bus + * @vc: Current set virtual channel + * @notifier: Async notifier that is used to bound the underlying + * subdevice to the csi2dc subdevice + * @input_sd: Reference to the underlying subdevice bound to the + * csi2dc subdevice + * @remote_pad: Pad number of the underlying subdevice that is linked + * to the csi2dc subdevice sink pad. + */ +struct csi2dc_device { + void __iomem *base; + struct v4l2_subdev csi2dc_sd; + struct device *dev; + struct clk *pclk; + struct clk *scck; + + struct v4l2_mbus_framefmt format; + + const struct csi2dc_format *cur_fmt; + const struct csi2dc_format *try_fmt; + + struct media_pad pads[CSI2DC_PADS_NUM]; + + bool clk_gated; + bool video_pipe; + bool parallel_mode; + u32 vc; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *input_sd; + + u32 remote_pad; +}; + +static inline struct csi2dc_device * +csi2dc_sd_to_csi2dc_device(struct v4l2_subdev *csi2dc_sd) +{ + return container_of(csi2dc_sd, struct csi2dc_device, csi2dc_sd); +} + +static int csi2dc_enum_mbus_code(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(csi2dc_formats)) + return -EINVAL; + + code->code = csi2dc_formats[code->index].mbus_code; + + return 0; +} + +static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); + struct v4l2_mbus_framefmt *v4l2_try_fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, + format->pad); + format->format = *v4l2_try_fmt; + + return 0; + } + + format->format = csi2dc->format; + + return 0; +} + +static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *req_fmt) +{ + struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); + const struct csi2dc_format *fmt, *try_fmt = NULL; + struct v4l2_mbus_framefmt *v4l2_try_fmt; + unsigned int i; + + /* + * Setting the source pad is disabled. + * The same format is being propagated from the sink to source. + */ + if (req_fmt->pad == CSI2DC_PAD_SOURCE) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(csi2dc_formats); i++) { + fmt = &csi2dc_formats[i]; + if (req_fmt->format.code == fmt->mbus_code) + try_fmt = fmt; + fmt++; + } + + /* in case we could not find the desired format, default to something */ + if (!try_fmt) { + try_fmt = &csi2dc_formats[0]; + + dev_dbg(csi2dc->dev, + "CSI2DC unsupported format 0x%x, defaulting to 0x%x\n", + req_fmt->format.code, csi2dc_formats[0].mbus_code); + } + + req_fmt->format.code = try_fmt->mbus_code; + req_fmt->format.colorspace = V4L2_COLORSPACE_SRGB; + req_fmt->format.field = V4L2_FIELD_NONE; + + if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, + req_fmt->pad); + *v4l2_try_fmt = req_fmt->format; + /* Trying on the sink pad makes the source pad change too */ + v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, + sd_state, + CSI2DC_PAD_SOURCE); + *v4l2_try_fmt = req_fmt->format; + + /* if we are just trying, we are done */ + return 0; + } + + /* save the format for later requests */ + csi2dc->format = req_fmt->format; + + /* update config */ + csi2dc->cur_fmt = try_fmt; + + dev_dbg(csi2dc->dev, "new format set: 0x%x @%dx%d\n", + csi2dc->format.code, csi2dc->format.width, + csi2dc->format.height); + + return 0; +} + +static int csi2dc_power(struct csi2dc_device *csi2dc, int on) +{ + int ret = 0; + + if (on) { + ret = clk_prepare_enable(csi2dc->pclk); + if (ret) { + dev_err(csi2dc->dev, "failed to enable pclk:%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(csi2dc->scck); + if (ret) { + dev_err(csi2dc->dev, "failed to enable scck:%d\n", ret); + clk_disable_unprepare(csi2dc->pclk); + return ret; + } + + /* if powering up, deassert reset line */ + csi2dc_writel(csi2dc, CSI2DC_GCTLR, CSI2DC_GCTLR_SWRST); + } else { + /* if powering down, assert reset line */ + csi2dc_writel(csi2dc, CSI2DC_GCTLR, 0); + + clk_disable_unprepare(csi2dc->scck); + clk_disable_unprepare(csi2dc->pclk); + } + + return ret; +} + +static int csi2dc_get_mbus_config(struct csi2dc_device *csi2dc) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + int ret; + + ret = v4l2_subdev_call(csi2dc->input_sd, pad, get_mbus_config, + csi2dc->remote_pad, &mbus_config); + if (ret == -ENOIOCTLCMD) { + dev_dbg(csi2dc->dev, + "no remote mbus configuration available\n"); + return 0; + } + + if (ret) { + dev_err(csi2dc->dev, + "failed to get remote mbus configuration\n"); + return 0; + } + + dev_dbg(csi2dc->dev, "subdev sending on channel %d\n", csi2dc->vc); + + csi2dc->clk_gated = mbus_config.bus.parallel.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + dev_dbg(csi2dc->dev, "mbus_config: %s clock\n", + csi2dc->clk_gated ? "gated" : "free running"); + + return 0; +} + +static void csi2dc_vp_update(struct csi2dc_device *csi2dc) +{ + u32 vp, gcfg; + + if (!csi2dc->video_pipe) { + dev_err(csi2dc->dev, "video pipeline unavailable\n"); + return; + } + + if (csi2dc->parallel_mode) { + /* In parallel mode, GPIO parallel interface must be selected */ + gcfg = csi2dc_readl(csi2dc, CSI2DC_GCFG); + gcfg |= CSI2DC_GCFG_GPIOSEL; + csi2dc_writel(csi2dc, CSI2DC_GCFG, gcfg); + return; + } + + /* serial video pipeline */ + + csi2dc_writel(csi2dc, CSI2DC_GCFG, + (SAMA7G5_HLC & CSI2DC_GCFG_HLC_MASK) | + (csi2dc->clk_gated ? 0 : CSI2DC_GCFG_MIPIFRN)); + + vp = CSI2DC_VPCFG_DT(csi2dc->cur_fmt->dt) & CSI2DC_VPCFG_DT_MASK; + vp |= CSI2DC_VPCFG_VC(csi2dc->vc) & CSI2DC_VPCFG_VC_MASK; + vp &= ~CSI2DC_VPCFG_DE; + vp |= CSI2DC_VPCFG_DM(CSI2DC_VPCFG_DM_DECODER8TO12); + vp &= ~CSI2DC_VPCFG_DP2; + vp &= ~CSI2DC_VPCFG_RMS; + vp |= CSI2DC_VPCFG_PA; + + csi2dc_writel(csi2dc, CSI2DC_VPCFG, vp); + csi2dc_writel(csi2dc, CSI2DC_VPE, CSI2DC_VPE_ENABLE); + csi2dc_writel(csi2dc, CSI2DC_PU, CSI2DC_PU_VP); +} + +static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) +{ + struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); + int ret; + + if (enable) { + ret = pm_runtime_resume_and_get(csi2dc->dev); + if (ret < 0) + return ret; + + csi2dc_get_mbus_config(csi2dc); + + csi2dc_vp_update(csi2dc); + + return v4l2_subdev_call(csi2dc->input_sd, video, s_stream, + true); + } + + dev_dbg(csi2dc->dev, + "Last frame received: VPCOLR = %u, VPROWR= %u, VPISR = %x\n", + csi2dc_readl(csi2dc, CSI2DC_VPCOL), + csi2dc_readl(csi2dc, CSI2DC_VPROW), + csi2dc_readl(csi2dc, CSI2DC_VPISR)); + + /* stop streaming scenario */ + ret = v4l2_subdev_call(csi2dc->input_sd, video, s_stream, false); + + pm_runtime_put_sync(csi2dc->dev); + + return ret; +} + +static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *v4l2_try_fmt = + v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); + + v4l2_try_fmt->height = 480; + v4l2_try_fmt->width = 640; + v4l2_try_fmt->code = csi2dc_formats[0].mbus_code; + v4l2_try_fmt->colorspace = V4L2_COLORSPACE_SRGB; + v4l2_try_fmt->field = V4L2_FIELD_NONE; + v4l2_try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + v4l2_try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + v4l2_try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { + .enum_mbus_code = csi2dc_enum_mbus_code, + .set_fmt = csi2dc_set_fmt, + .get_fmt = csi2dc_get_fmt, + .init_cfg = csi2dc_init_cfg, +}; + +static const struct v4l2_subdev_video_ops csi2dc_video_ops = { + .s_stream = csi2dc_s_stream, +}; + +static const struct v4l2_subdev_ops csi2dc_subdev_ops = { + .pad = &csi2dc_pad_ops, + .video = &csi2dc_video_ops, +}; + +static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct csi2dc_device *csi2dc = container_of(notifier, + struct csi2dc_device, notifier); + int pad; + int ret; + + csi2dc->input_sd = subdev; + + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(csi2dc->dev, "Failed to find pad for %s\n", + subdev->name); + return pad; + } + + csi2dc->remote_pad = pad; + + ret = media_create_pad_link(&csi2dc->input_sd->entity, + csi2dc->remote_pad, + &csi2dc->csi2dc_sd.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(csi2dc->dev, + "Failed to create pad link: %s to %s\n", + csi2dc->input_sd->entity.name, + csi2dc->csi2dc_sd.entity.name); + return ret; + } + + dev_dbg(csi2dc->dev, "link with %s pad: %d\n", + csi2dc->input_sd->name, csi2dc->remote_pad); + + return ret; +} + +static const struct v4l2_async_notifier_operations csi2dc_async_ops = { + .bound = csi2dc_async_bound, +}; + +static int csi2dc_prepare_notifier(struct csi2dc_device *csi2dc, + struct fwnode_handle *input_fwnode) +{ + struct v4l2_async_subdev *asd; + int ret = 0; + + v4l2_async_nf_init(&csi2dc->notifier); + + asd = v4l2_async_nf_add_fwnode_remote(&csi2dc->notifier, + input_fwnode, + struct v4l2_async_subdev); + + fwnode_handle_put(input_fwnode); + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + dev_err(csi2dc->dev, + "failed to add async notifier for node %pOF: %d\n", + to_of_node(input_fwnode), ret); + v4l2_async_nf_cleanup(&csi2dc->notifier); + return ret; + } + + csi2dc->notifier.ops = &csi2dc_async_ops; + + ret = v4l2_async_subdev_nf_register(&csi2dc->csi2dc_sd, + &csi2dc->notifier); + if (ret) { + dev_err(csi2dc->dev, "fail to register async notifier: %d\n", + ret); + v4l2_async_nf_cleanup(&csi2dc->notifier); + } + + return ret; +} + +static int csi2dc_of_parse(struct csi2dc_device *csi2dc, + struct device_node *of_node) +{ + struct fwnode_handle *input_fwnode, *output_fwnode; + struct v4l2_fwnode_endpoint input_endpoint = { 0 }, + output_endpoint = { 0 }; + int ret; + + input_fwnode = fwnode_graph_get_next_endpoint(of_fwnode_handle(of_node), + NULL); + if (!input_fwnode) { + dev_err(csi2dc->dev, + "missing port node at %pOF, input node is mandatory.\n", + of_node); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(input_fwnode, &input_endpoint); + if (ret) { + dev_err(csi2dc->dev, "endpoint not defined at %pOF\n", of_node); + goto csi2dc_of_parse_err; + } + + if (input_endpoint.bus_type == V4L2_MBUS_PARALLEL || + input_endpoint.bus_type == V4L2_MBUS_BT656) { + csi2dc->parallel_mode = true; + dev_dbg(csi2dc->dev, + "subdevice connected on parallel interface\n"); + } + + if (input_endpoint.bus_type == V4L2_MBUS_CSI2_DPHY) { + csi2dc->clk_gated = input_endpoint.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + dev_dbg(csi2dc->dev, + "subdevice connected on serial interface\n"); + dev_dbg(csi2dc->dev, "DT: %s clock\n", + csi2dc->clk_gated ? "gated" : "free running"); + } + + output_fwnode = fwnode_graph_get_next_endpoint + (of_fwnode_handle(of_node), input_fwnode); + + if (output_fwnode) + ret = v4l2_fwnode_endpoint_parse(output_fwnode, + &output_endpoint); + + fwnode_handle_put(output_fwnode); + + if (!output_fwnode || ret) { + dev_info(csi2dc->dev, + "missing output node at %pOF, data pipe available only.\n", + of_node); + } else { + if (output_endpoint.bus_type != V4L2_MBUS_PARALLEL && + output_endpoint.bus_type != V4L2_MBUS_BT656) { + dev_err(csi2dc->dev, + "output port must be parallel/bt656.\n"); + ret = -EINVAL; + goto csi2dc_of_parse_err; + } + + csi2dc->video_pipe = true; + + dev_dbg(csi2dc->dev, + "block %pOF [%d.%d]->[%d.%d] video pipeline\n", + of_node, input_endpoint.base.port, + input_endpoint.base.id, output_endpoint.base.port, + output_endpoint.base.id); + } + + /* prepare async notifier for subdevice completion */ + return csi2dc_prepare_notifier(csi2dc, input_fwnode); + +csi2dc_of_parse_err: + fwnode_handle_put(input_fwnode); + return ret; +} + +static void csi2dc_default_format(struct csi2dc_device *csi2dc) +{ + csi2dc->cur_fmt = &csi2dc_formats[0]; + + csi2dc->format.height = 480; + csi2dc->format.width = 640; + csi2dc->format.code = csi2dc_formats[0].mbus_code; + csi2dc->format.colorspace = V4L2_COLORSPACE_SRGB; + csi2dc->format.field = V4L2_FIELD_NONE; + csi2dc->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + csi2dc->format.quantization = V4L2_QUANTIZATION_DEFAULT; + csi2dc->format.xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int csi2dc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct csi2dc_device *csi2dc; + int ret = 0; + u32 ver; + + csi2dc = devm_kzalloc(dev, sizeof(*csi2dc), GFP_KERNEL); + if (!csi2dc) + return -ENOMEM; + + csi2dc->dev = dev; + + csi2dc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi2dc->base)) { + dev_err(dev, "base address not set\n"); + return PTR_ERR(csi2dc->base); + } + + csi2dc->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(csi2dc->pclk)) { + ret = PTR_ERR(csi2dc->pclk); + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + csi2dc->scck = devm_clk_get(dev, "scck"); + if (IS_ERR(csi2dc->scck)) { + ret = PTR_ERR(csi2dc->scck); + dev_err(dev, "failed to get scck: %d\n", ret); + return ret; + } + + v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); + + csi2dc->csi2dc_sd.owner = THIS_MODULE; + csi2dc->csi2dc_sd.dev = dev; + snprintf(csi2dc->csi2dc_sd.name, sizeof(csi2dc->csi2dc_sd.name), + "csi2dc"); + + csi2dc->csi2dc_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2dc->csi2dc_sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + platform_set_drvdata(pdev, csi2dc); + + ret = csi2dc_of_parse(csi2dc, dev->of_node); + if (ret) + goto csi2dc_probe_cleanup_entity; + + csi2dc->pads[CSI2DC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + if (csi2dc->video_pipe) + csi2dc->pads[CSI2DC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csi2dc->csi2dc_sd.entity, + csi2dc->video_pipe ? CSI2DC_PADS_NUM : 1, + csi2dc->pads); + if (ret < 0) { + dev_err(dev, "media entity init failed\n"); + goto csi2dc_probe_cleanup_notifier; + } + + csi2dc_default_format(csi2dc); + + /* turn power on to validate capabilities */ + ret = csi2dc_power(csi2dc, true); + if (ret < 0) + goto csi2dc_probe_cleanup_notifier; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + ver = csi2dc_readl(csi2dc, CSI2DC_VERSION); + + /* + * we must register the subdev after PM runtime has been requested, + * otherwise we might bound immediately and request pm_runtime_resume + * before runtime_enable. + */ + ret = v4l2_async_register_subdev(&csi2dc->csi2dc_sd); + if (ret) { + dev_err(csi2dc->dev, "failed to register the subdevice\n"); + goto csi2dc_probe_cleanup_notifier; + } + + dev_info(dev, "Microchip CSI2DC version %x\n", ver); + + return 0; + +csi2dc_probe_cleanup_notifier: + v4l2_async_nf_cleanup(&csi2dc->notifier); +csi2dc_probe_cleanup_entity: + media_entity_cleanup(&csi2dc->csi2dc_sd.entity); + + return ret; +} + +static int csi2dc_remove(struct platform_device *pdev) +{ + struct csi2dc_device *csi2dc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_unregister_subdev(&csi2dc->csi2dc_sd); + v4l2_async_nf_unregister(&csi2dc->notifier); + v4l2_async_nf_cleanup(&csi2dc->notifier); + media_entity_cleanup(&csi2dc->csi2dc_sd.entity); + + return 0; +} + +static int __maybe_unused csi2dc_runtime_suspend(struct device *dev) +{ + struct csi2dc_device *csi2dc = dev_get_drvdata(dev); + + return csi2dc_power(csi2dc, false); +} + +static int __maybe_unused csi2dc_runtime_resume(struct device *dev) +{ + struct csi2dc_device *csi2dc = dev_get_drvdata(dev); + + return csi2dc_power(csi2dc, true); +} + +static const struct dev_pm_ops csi2dc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(csi2dc_runtime_suspend, csi2dc_runtime_resume, NULL) +}; + +static const struct of_device_id csi2dc_of_match[] = { + { .compatible = "microchip,sama7g5-csi2dc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, csi2dc_of_match); + +static struct platform_driver csi2dc_driver = { + .probe = csi2dc_probe, + .remove = csi2dc_remove, + .driver = { + .name = "microchip-csi2dc", + .pm = &csi2dc_dev_pm_ops, + .of_match_table = of_match_ptr(csi2dc_of_match), + }, +}; + +module_platform_driver(csi2dc_driver); + +MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); +MODULE_DESCRIPTION("Microchip CSI2 Demux Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 80cf601323ce..480325d053de 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -1,18 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -config VIDEO_CADENCE - bool "Cadence Video Devices" - help - If you have a media device designed by Cadence, say Y. - - Note that this option doesn't include new drivers in the kernel: - saying N will just cause Kconfig to skip all the questions about - Cadence media devices. -if VIDEO_CADENCE +comment "Cadence media platform drivers" config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -24,7 +16,7 @@ config VIDEO_CADENCE_CSI2RX config VIDEO_CADENCE_CSI2TX tristate "Cadence MIPI-CSI2 TX Controller" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -33,5 +25,3 @@ config VIDEO_CADENCE_CSI2TX To compile this driver as a module, choose M here: the module will be called cdns-csi2tx. - -endif diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig new file mode 100644 index 000000000000..57f8f8a22df8 --- /dev/null +++ b/drivers/media/platform/chips-media/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Chips&Media media platform drivers" + +config VIDEO_CODA + tristate "Chips&Media Coda multi-standard codec IP" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST) + select SRAM + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_JPEG_HELPER + select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR + help + Coda is a range of video codec IPs that supports + H.264, MPEG-4, and other video formats. + +config VIDEO_IMX_VDOA + def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/chips-media/Makefile index bbb16425a875..bbb16425a875 100644 --- a/drivers/media/platform/coda/Makefile +++ b/drivers/media/platform/chips-media/Makefile diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/chips-media/coda-bit.c index c484c008ab02..c484c008ab02 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/chips-media/coda-bit.c diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/chips-media/coda-common.c index 3cd47ba26357..a57822b05070 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/chips-media/coda-common.c @@ -409,6 +409,7 @@ static struct vdoa_data *coda_get_vdoa_data(void) if (!vdoa_data) vdoa_data = ERR_PTR(-EPROBE_DEFER); + put_device(&vdoa_pdev->dev); out: of_node_put(vdoa_node); diff --git a/drivers/media/platform/coda/coda-gdi.c b/drivers/media/platform/chips-media/coda-gdi.c index 59d65daca153..59d65daca153 100644 --- a/drivers/media/platform/coda/coda-gdi.c +++ b/drivers/media/platform/chips-media/coda-gdi.c diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/chips-media/coda-h264.c index 8bd0aa8af114..8bd0aa8af114 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/chips-media/coda-h264.c diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda-jpeg.c index a72f4655e5ad..a72f4655e5ad 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/chips-media/coda-jpeg.c diff --git a/drivers/media/platform/coda/coda-mpeg2.c b/drivers/media/platform/chips-media/coda-mpeg2.c index 6f3f6721d286..6f3f6721d286 100644 --- a/drivers/media/platform/coda/coda-mpeg2.c +++ b/drivers/media/platform/chips-media/coda-mpeg2.c diff --git a/drivers/media/platform/coda/coda-mpeg4.c b/drivers/media/platform/chips-media/coda-mpeg4.c index 483a4fba1b4f..483a4fba1b4f 100644 --- a/drivers/media/platform/coda/coda-mpeg4.c +++ b/drivers/media/platform/chips-media/coda-mpeg4.c diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/chips-media/coda.h index dcf35641c603..dcf35641c603 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/chips-media/coda.h diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/chips-media/coda_regs.h index da5bb3212528..db81a904cf3f 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/chips-media/coda_regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/coda/coda_regs.h + * linux/drivers/media/platform/chips-media/coda_regs.h * * Copyright (C) 2012 Vista Silicon SL * Javier Martin <javier.martin@vista-silicon.com> diff --git a/drivers/media/platform/coda/imx-vdoa.c b/drivers/media/platform/chips-media/imx-vdoa.c index 00643f37b3e6..c70871bae193 100644 --- a/drivers/media/platform/coda/imx-vdoa.c +++ b/drivers/media/platform/chips-media/imx-vdoa.c @@ -284,7 +284,6 @@ EXPORT_SYMBOL(vdoa_context_configure); static int vdoa_probe(struct platform_device *pdev) { struct vdoa_data *vdoa; - struct resource *res; int ret; ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); @@ -309,10 +308,10 @@ static int vdoa_probe(struct platform_device *pdev) if (IS_ERR(vdoa->regs)) return PTR_ERR(vdoa->regs); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) - return -EINVAL; - ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL, + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, vdoa_irq_handler, IRQF_ONESHOT, "vdoa", vdoa); if (ret < 0) { diff --git a/drivers/media/platform/coda/imx-vdoa.h b/drivers/media/platform/chips-media/imx-vdoa.h index a62eab476d58..a62eab476d58 100644 --- a/drivers/media/platform/coda/imx-vdoa.h +++ b/drivers/media/platform/chips-media/imx-vdoa.h diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/chips-media/trace.h index c0791c847f7c..19f98e6dafb9 100644 --- a/drivers/media/platform/coda/trace.h +++ b/drivers/media/platform/chips-media/trace.h @@ -167,7 +167,7 @@ DEFINE_EVENT(coda_buf_class, coda_jpeg_done, #endif /* __CODA_TRACE_H__ */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../drivers/media/platform/coda +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig new file mode 100644 index 000000000000..724e80a9086d --- /dev/null +++ b/drivers/media/platform/intel/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Intel media platform drivers" + +config VIDEO_PXA27x + tristate "PXA27x Quick Capture Interface driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on PXA27x || COMPILE_TEST + select VIDEOBUF2_DMA_SG + select SG_SPLIT + select V4L2_FWNODE + help + This is a v4l2 driver for the PXA27x Quick Capture Interface diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile new file mode 100644 index 000000000000..7e8889cbd2df --- /dev/null +++ b/drivers/media/platform/intel/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index 3ba00b0f9320..35145e3348f0 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -1573,39 +1573,40 @@ static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev) mbus_config |= V4L2_MBUS_PCLK_SAMPLE_FALLING; mbus_config |= V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg.flags = mbus_config; - ret = sensor_call(pcdev, pad, set_mbus_config, 0, &cfg); + ret = sensor_call(pcdev, pad, get_mbus_config, 0, &cfg); if (ret < 0 && ret != -ENOIOCTLCMD) { dev_err(pcdev_to_dev(pcdev), - "Failed to call set_mbus_config: %d\n", ret); + "Failed to call get_mbus_config: %d\n", ret); return ret; } /* - * If the requested media bus configuration has not been fully applied - * make sure it is supported by the platform. + * If the media bus configuration of the sensor differs, make sure it + * is supported by the platform. * * PXA does not support V4L2_MBUS_DATA_ACTIVE_LOW and the bus mastering * roles should match. */ - if (cfg.flags != mbus_config) { + if (cfg.bus.parallel.flags != mbus_config) { unsigned int pxa_mbus_role = mbus_config & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - if (pxa_mbus_role != (cfg.flags & (V4L2_MBUS_MASTER | - V4L2_MBUS_SLAVE))) { + unsigned int flags = cfg.bus.parallel.flags; + + if (pxa_mbus_role != (flags & (V4L2_MBUS_MASTER | + V4L2_MBUS_SLAVE))) { dev_err(pcdev_to_dev(pcdev), "Unsupported mbus configuration: bus mastering\n"); return -EINVAL; } - if (cfg.flags & V4L2_MBUS_DATA_ACTIVE_LOW) { + if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) { dev_err(pcdev_to_dev(pcdev), "Unsupported mbus configuration: DATA_ACTIVE_LOW\n"); return -EINVAL; } } - pxa_camera_setup_cicr(pcdev, cfg.flags, pixfmt); + pxa_camera_setup_cicr(pcdev, cfg.bus.parallel.flags, pixfmt); return 0; } diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell/Kconfig index 3e3f86264762..ec1a16734a28 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell/Kconfig @@ -1,7 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only + +comment "Marvell media platform drivers" + config VIDEO_CAFE_CCIC tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" - depends on PCI && I2C && VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on PCI && I2C && VIDEO_DEV depends on COMMON_CLK select VIDEO_OV7670 select VIDEOBUF2_VMALLOC @@ -14,7 +18,8 @@ config VIDEO_CAFE_CCIC config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" - depends on I2C && VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on I2C && VIDEO_DEV depends on ARCH_MMP || COMPILE_TEST depends on COMMON_CLK select VIDEO_OV7670 diff --git a/drivers/media/platform/marvell-ccic/Makefile b/drivers/media/platform/marvell/Makefile index 90c3c2bc6dde..90c3c2bc6dde 100644 --- a/drivers/media/platform/marvell-ccic/Makefile +++ b/drivers/media/platform/marvell/Makefile diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index 03dcf8bf705e..03dcf8bf705e 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index ad4a7922d0d7..ad4a7922d0d7 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h index f324d808d737..f324d808d737 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell/mcam-core.h diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index 343ab4f7d807..df16899ab1cb 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -270,12 +270,10 @@ static int mmpcam_probe(struct platform_device *pdev) * Finally, set up our IRQ now that the core is ready to * deal with it. */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - ret = -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto out; - } - cam->irq = res->start; + cam->irq = ret; ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED, "mmp-camera", mcam); if (ret) diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig new file mode 100644 index 000000000000..af47d9888552 --- /dev/null +++ b/drivers/media/platform/mediatek/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Mediatek media platform drivers" + +source "drivers/media/platform/mediatek/jpeg/Kconfig" +source "drivers/media/platform/mediatek/mdp/Kconfig" +source "drivers/media/platform/mediatek/vcodec/Kconfig" +source "drivers/media/platform/mediatek/vpu/Kconfig" diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile new file mode 100644 index 000000000000..d3850a13f128 --- /dev/null +++ b/drivers/media/platform/mediatek/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += jpeg/ +obj-y += mdp/ +obj-y += vcodec/ +obj-y += vpu/ diff --git a/drivers/media/platform/mediatek/jpeg/Kconfig b/drivers/media/platform/mediatek/jpeg/Kconfig new file mode 100644 index 000000000000..39c4d1bc66ce --- /dev/null +++ b/drivers/media/platform/mediatek/jpeg/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEDIATEK_JPEG + tristate "Mediatek JPEG Codec driver" + depends on V4L_MEM2MEM_DRIVERS + depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Mediatek jpeg codec driver provides HW capability to decode + JPEG format + + To compile this driver as a module, choose M here: the + module will be called mtk-jpeg diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mediatek/jpeg/Makefile index 76c33aad0f3f..76c33aad0f3f 100644 --- a/drivers/media/platform/mtk-jpeg/Makefile +++ b/drivers/media/platform/mediatek/jpeg/Makefile diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index f332beb06d51..ab5485dfc20c 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -22,7 +22,6 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include <soc/mediatek/smi.h> #include "mtk_jpeg_enc_hw.h" #include "mtk_jpeg_dec_hw.h" @@ -681,7 +680,7 @@ static int mtk_jpeg_buf_prepare(struct vb2_buffer *vb) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mtk_jpeg_q_data *q_data = NULL; - struct v4l2_plane_pix_format plane_fmt = {}; + struct v4l2_plane_pix_format plane_fmt; int i; q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type); @@ -1055,10 +1054,6 @@ static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg) { int ret; - ret = mtk_smi_larb_get(jpeg->larb); - if (ret) - dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret); - ret = clk_bulk_prepare_enable(jpeg->variant->num_clks, jpeg->variant->clks); if (ret) @@ -1069,7 +1064,6 @@ static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg) { clk_bulk_disable_unprepare(jpeg->variant->num_clks, jpeg->variant->clks); - mtk_smi_larb_put(jpeg->larb); } static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg) @@ -1284,35 +1278,6 @@ static struct clk_bulk_data mtk_jpeg_clocks[] = { { .id = "jpgenc" }, }; -static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg) -{ - struct device_node *node; - struct platform_device *pdev; - int ret; - - node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0); - if (!node) - return -EINVAL; - pdev = of_find_device_by_node(node); - if (WARN_ON(!pdev)) { - of_node_put(node); - return -EINVAL; - } - of_node_put(node); - - jpeg->larb = &pdev->dev; - - ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks, - jpeg->variant->clks); - if (ret) { - dev_err(&pdev->dev, "failed to get jpeg clock:%d\n", ret); - put_device(&pdev->dev); - return ret; - } - - return 0; -} - static void mtk_jpeg_job_timeout_work(struct work_struct *work) { struct mtk_jpeg_dev *jpeg = container_of(work, struct mtk_jpeg_dev, @@ -1333,11 +1298,6 @@ static void mtk_jpeg_job_timeout_work(struct work_struct *work) v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } -static inline void mtk_jpeg_clk_release(struct mtk_jpeg_dev *jpeg) -{ - put_device(jpeg->larb); -} - static int mtk_jpeg_probe(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg; @@ -1372,7 +1332,8 @@ static int mtk_jpeg_probe(struct platform_device *pdev) goto err_req_irq; } - ret = mtk_jpeg_clk_init(jpeg); + ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks, + jpeg->variant->clks); if (ret) { dev_err(&pdev->dev, "Failed to init clk, err %d\n", ret); goto err_clk_init; @@ -1438,7 +1399,6 @@ err_m2m_init: v4l2_device_unregister(&jpeg->v4l2_dev); err_dev_register: - mtk_jpeg_clk_release(jpeg); err_clk_init: @@ -1456,7 +1416,6 @@ static int mtk_jpeg_remove(struct platform_device *pdev) video_device_release(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); - mtk_jpeg_clk_release(jpeg); return 0; } diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h index 595f7f10c9fd..3e4811a41ba2 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h @@ -85,7 +85,6 @@ struct mtk_jpeg_variant { * @alloc_ctx: videobuf2 memory allocator's context * @vdev: video device node for jpeg mem2mem mode * @reg_base: JPEG registers mapping - * @larb: SMI device * @job_timeout_work: IRQ timeout structure * @variant: driver variant to be used */ @@ -99,7 +98,6 @@ struct mtk_jpeg_dev { void *alloc_ctx; struct video_device *vdev; void __iomem *reg_base; - struct device *larb; struct delayed_work job_timeout_work; const struct mtk_jpeg_variant *variant; }; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c index afbbfd5d02bc..afbbfd5d02bc 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h index fa0d45fd7c34..fa0d45fd7c34 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c index b95c45791c29..b95c45791c29 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h index 2918f15811f8..2918f15811f8 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h index 21ec8f96797f..21ec8f96797f 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c index 1cf037bf72dd..1cf037bf72dd 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h index 61c60e4e58ea..61c60e4e58ea 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h diff --git a/drivers/media/platform/mediatek/mdp/Kconfig b/drivers/media/platform/mediatek/mdp/Kconfig new file mode 100644 index 000000000000..9f13a42899bd --- /dev/null +++ b/drivers/media/platform/mediatek/mdp/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEDIATEK_MDP + tristate "Mediatek MDP driver" + depends on V4L_MEM2MEM_DRIVERS + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_MEDIATEK_VPU + help + It is a v4l2 driver and present in Mediatek MT8173 SoCs. + The driver supports for scaling and color space conversion. + + To compile this driver as a module, choose M here: the + module will be called mtk-mdp. diff --git a/drivers/media/platform/mtk-mdp/Makefile b/drivers/media/platform/mediatek/mdp/Makefile index 5982d65c9971..b7c16ebecc80 100644 --- a/drivers/media/platform/mtk-mdp/Makefile +++ b/drivers/media/platform/mediatek/mdp/Makefile @@ -7,4 +7,4 @@ mtk-mdp-y += mtk_mdp_vpu.o obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp.o -ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu +ccflags-y += -I$(srctree)/drivers/media/platform/mediatek/vpu diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c index b3426a551bea..1e3833f1c9ae 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c @@ -9,7 +9,6 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> -#include <soc/mediatek/smi.h> #include "mtk_mdp_comp.h" @@ -18,14 +17,6 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) { int i, err; - if (comp->larb_dev) { - err = mtk_smi_larb_get(comp->larb_dev); - if (err) - dev_err(dev, - "failed to get larb, err %d. type:%d\n", - err, comp->type); - } - for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { if (IS_ERR(comp->clk[i])) continue; @@ -46,17 +37,12 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp) continue; clk_disable_unprepare(comp->clk[i]); } - - if (comp->larb_dev) - mtk_smi_larb_put(comp->larb_dev); } int mtk_mdp_comp_init(struct device *dev, struct device_node *node, struct mtk_mdp_comp *comp, enum mtk_mdp_comp_type comp_type) { - struct device_node *larb_node; - struct platform_device *larb_pdev; int ret; int i; @@ -77,32 +63,6 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, break; } - /* Only DMA capable components need the LARB property */ - comp->larb_dev = NULL; - if (comp->type != MTK_MDP_RDMA && - comp->type != MTK_MDP_WDMA && - comp->type != MTK_MDP_WROT) - return 0; - - larb_node = of_parse_phandle(node, "mediatek,larb", 0); - if (!larb_node) { - dev_err(dev, - "Missing mediadek,larb phandle in %pOF node\n", node); - ret = -EINVAL; - goto put_dev; - } - - larb_pdev = of_find_device_by_node(larb_node); - if (!larb_pdev) { - dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); - of_node_put(larb_node); - ret = -EPROBE_DEFER; - goto put_dev; - } - of_node_put(larb_node); - - comp->larb_dev = &larb_pdev->dev; - return 0; put_dev: diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h index 7897766c96bb..ae41dd3cd72a 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h @@ -26,14 +26,12 @@ enum mtk_mdp_comp_type { * @node: list node to track sibing MDP components * @dev_node: component device node * @clk: clocks required for component - * @larb_dev: SMI device required for component * @type: component type */ struct mtk_mdp_comp { struct list_head node; struct device_node *dev_node; struct clk *clk[2]; - struct device *larb_dev; enum mtk_mdp_comp_type type; }; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c index 3d38793aaa25..d83c4964eaf9 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c @@ -17,7 +17,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/workqueue.h> -#include <soc/mediatek/smi.h> #include "mtk_mdp_core.h" #include "mtk_mdp_m2m.h" @@ -246,10 +245,8 @@ static int mtk_mdp_remove(struct platform_device *pdev) mtk_mdp_unregister_m2m_device(mdp); v4l2_device_unregister(&mdp->v4l2_dev); - flush_workqueue(mdp->wdt_wq); destroy_workqueue(mdp->wdt_wq); - flush_workqueue(mdp->job_wq); destroy_workqueue(mdp->job_wq); list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.h index a6e6dc36307b..a6e6dc36307b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.h diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_ipi.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h index 2cb8cecb3077..2cb8cecb3077 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_ipi.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c index f14779e7596e..f14779e7596e 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h index 485dbdbbf51d..485dbdbbf51d 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c index ba476d50ae43..ba476d50ae43 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h index 32cf202f2399..32cf202f2399 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c index b065ccd06914..b065ccd06914 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h index 5a1020508446..5a1020508446 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h diff --git a/drivers/media/platform/mediatek/vcodec/Kconfig b/drivers/media/platform/mediatek/vcodec/Kconfig new file mode 100644 index 000000000000..c5c76753c626 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEDIATEK_VCODEC_SCP + bool + +config VIDEO_MEDIATEK_VCODEC_VPU + bool + +config VIDEO_MEDIATEK_VCODEC + tristate "Mediatek Video Codec driver" + depends on V4L_MEM2MEM_DRIVERS + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on VIDEO_MEDIATEK_VPU || MTK_SCP + # The two following lines ensure we have the same state ("m" or "y") as + # our dependencies, to avoid missing symbols during link. + depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU + depends on MTK_SCP || !MTK_SCP + depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU + select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP + select V4L2_H264 + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API + help + Mediatek video codec driver provides HW capability to + encode and decode in a range of video formats on MT8173 + and MT8183. + + Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to + also be selected. Support for MT8183 depends on MTK_SCP. + + To compile this driver as modules, choose M here: the + modules will be called mtk-vcodec-dec and mtk-vcodec-enc. diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mediatek/vcodec/Makefile index ca8e9e7a9c4e..359619653a0e 100644 --- a/drivers/media/platform/mtk-vcodec/Makefile +++ b/drivers/media/platform/mediatek/vcodec/Makefile @@ -2,7 +2,8 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \ mtk-vcodec-enc.o \ - mtk-vcodec-common.o + mtk-vcodec-common.o \ + mtk-vcodec-dec-hw.o mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ vdec/vdec_vp8_if.o \ @@ -11,11 +12,14 @@ mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ mtk_vcodec_dec_drv.o \ vdec_drv_if.o \ vdec_vpu_if.o \ + vdec_msg_queue.o \ mtk_vcodec_dec.o \ mtk_vcodec_dec_stateful.o \ mtk_vcodec_dec_stateless.o \ mtk_vcodec_dec_pm.o \ +mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o + mtk-vcodec-enc-y := venc/venc_vp8_if.o \ venc/venc_h264_if.o \ mtk_vcodec_enc.o \ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c index 2b334a8a81c6..130ecef2e766 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c @@ -105,12 +105,12 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx) { - mutex_unlock(&ctx->dev->dec_mutex); + mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); } void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx) { - mutex_lock(&ctx->dev->dec_mutex); + mutex_lock(&ctx->dev->dec_mutex[ctx->hw_id]); } void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h index e08886a600a3..66cd6d2242c3 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h @@ -68,6 +68,7 @@ extern const struct v4l2_m2m_ops mtk_vdec_m2m_ops; extern const struct media_device_ops mtk_vcodec_media_ops; extern const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata; extern const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata; +extern const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata; /* diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c index 40c39e1e596b..df7b25e9cbc8 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <media/v4l2-event.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-dma-contig.h> @@ -18,21 +19,23 @@ #include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" +#include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_dec_pm.h" #include "mtk_vcodec_intr.h" #include "mtk_vcodec_util.h" #include "mtk_vcodec_fw.h" -#define VDEC_HW_ACTIVE 0x10 -#define VDEC_IRQ_CFG 0x11 -#define VDEC_IRQ_CLR 0x10 -#define VDEC_IRQ_CFG_REG 0xa4 - -/* Wake up context wait_queue */ -static void wake_up_ctx(struct mtk_vcodec_ctx *ctx) +static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev) { - ctx->int_cond = 1; - wake_up_interruptible(&ctx->queue); + switch (dev->vdec_pdata->hw_arch) { + case MTK_VDEC_PURE_SINGLE_CORE: + return MTK_VDEC_ONE_CORE; + case MTK_VDEC_LAT_SINGLE_CORE: + return MTK_VDEC_ONE_LAT_ONE_CORE; + default: + mtk_v4l2_err("hw arch %d not supported", dev->vdec_pdata->hw_arch); + return MTK_VDEC_NO_HW; + } } static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) @@ -44,7 +47,7 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG; - ctx = mtk_vcodec_get_curr_ctx(dev); + ctx = mtk_vcodec_get_curr_ctx(dev, MTK_VDEC_CORE); /* check if HW active or not */ cg_status = readl(dev->reg_base[0]); @@ -66,7 +69,7 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR), dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); - wake_up_ctx(ctx); + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); mtk_v4l2_debug(3, "mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x", @@ -75,11 +78,70 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) return IRQ_HANDLED; } +static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int reg_num, i; + + /* Sizeof(u32) * 4 bytes for each register base. */ + reg_num = of_property_count_elems_of_size(pdev->dev.of_node, "reg", + sizeof(u32) * 4); + if (reg_num <= 0 || reg_num > NUM_MAX_VDEC_REG_BASE) { + dev_err(&pdev->dev, "Invalid register property size: %d\n", reg_num); + return -EINVAL; + } + + for (i = 0; i < reg_num; i++) { + dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(dev->reg_base[i])) + return PTR_ERR(dev->reg_base[i]); + + mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); + } + + return 0; +} + +static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int ret; + + ret = mtk_vcodec_get_reg_bases(dev); + if (ret) + return ret; + + if (dev->vdec_pdata->is_subdev_supported) + return 0; + + dev->dec_irq = platform_get_irq(pdev, 0); + if (dev->dec_irq < 0) + return dev->dec_irq; + + irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, dev->dec_irq, + mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "failed to install dev->dec_irq %d (%d)", + dev->dec_irq, ret); + return ret; + } + + ret = mtk_vcodec_init_dec_clk(pdev, &dev->pm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get mt vcodec clock source"); + return ret; + } + + pm_runtime_enable(&pdev->dev); + return 0; +} + static int fops_vcodec_open(struct file *file) { struct mtk_vcodec_dev *dev = video_drvdata(file); struct mtk_vcodec_ctx *ctx = NULL; - int ret = 0; + int ret = 0, i, hw_count; struct vb2_queue *src_vq; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -93,7 +155,22 @@ static int fops_vcodec_open(struct file *file) v4l2_fh_add(&ctx->fh); INIT_LIST_HEAD(&ctx->list); ctx->dev = dev; - init_waitqueue_head(&ctx->queue); + if (ctx->dev->vdec_pdata->is_subdev_supported) { + hw_count = mtk_vcodec_get_hw_count(dev); + if (!hw_count || !dev->subdev_prob_done) { + ret = -EINVAL; + goto err_ctrls_setup; + } + + ret = dev->subdev_prob_done(dev); + if (ret) + goto err_ctrls_setup; + + for (i = 0; i < hw_count; i++) + init_waitqueue_head(&ctx->queue[i]); + } else { + init_waitqueue_head(&ctx->queue[0]); + } mutex_init(&ctx->lock); ctx->type = MTK_INST_DECODER; @@ -116,7 +193,7 @@ static int fops_vcodec_open(struct file *file) mtk_vcodec_dec_set_default_params(ctx); if (v4l2_fh_is_singular(&ctx->fh)) { - ret = mtk_vcodec_dec_pw_on(&dev->pm); + ret = mtk_vcodec_dec_pw_on(dev, MTK_VDEC_LAT0); if (ret < 0) goto err_load_fw; /* @@ -176,7 +253,7 @@ static int fops_vcodec_release(struct file *file) mtk_vcodec_dec_release(ctx); if (v4l2_fh_is_singular(&ctx->fh)) - mtk_vcodec_dec_pw_off(&dev->pm); + mtk_vcodec_dec_pw_off(dev, MTK_VDEC_LAT0); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); @@ -200,7 +277,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) { struct mtk_vcodec_dev *dev; struct video_device *vfd_dec; - struct resource *res; phandle rproc_phandle; enum mtk_vcodec_fw_type fw_type; int i, ret; @@ -229,40 +305,34 @@ static int mtk_vcodec_probe(struct platform_device *pdev) if (IS_ERR(dev->fw_handler)) return PTR_ERR(dev->fw_handler); - ret = mtk_vcodec_init_dec_pm(dev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get mt vcodec clock source"); + ret = mtk_vcodec_init_dec_resources(dev); + if (ret) { + dev_err(&pdev->dev, "Failed to init dec resources"); goto err_dec_pm; } - for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) { - dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); - if (IS_ERR((__force void *)dev->reg_base[i])) { - ret = PTR_ERR((__force void *)dev->reg_base[i]); + if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) { + vdec_msg_queue_init_ctx(&dev->msg_queue_core_ctx, MTK_VDEC_CORE); + dev->core_workqueue = + alloc_ordered_workqueue("core-decoder", + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->core_workqueue) { + mtk_v4l2_err("Failed to create core workqueue"); + ret = -EINVAL; goto err_res; } - mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get irq resource"); - ret = -ENOENT; - goto err_res; } - dev->dec_irq = platform_get_irq(pdev, 0); - irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, dev->dec_irq, - mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", - dev->dec_irq, - ret); - goto err_res; + if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + if (ret) { + mtk_v4l2_err("Failed to set mask"); + goto err_core_workq; + } } - mutex_init(&dev->dec_mutex); + for (i = 0; i < MTK_VDEC_HW_MAX; i++) + mutex_init(&dev->dec_mutex[i]); mutex_init(&dev->dev_mutex); spin_lock_init(&dev->irqlock); @@ -272,7 +342,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { mtk_v4l2_err("v4l2_device_register err=%d", ret); - goto err_res; + goto err_core_workq; } init_waitqueue_head(&dev->queue); @@ -302,7 +372,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) if (IS_ERR((__force void *)dev->m2m_dev_dec)) { mtk_v4l2_err("Failed to init mem2mem dec device"); ret = PTR_ERR((__force void *)dev->m2m_dev_dec); - goto err_dec_mem_init; + goto err_dec_alloc; } dev->decode_workqueue = @@ -314,6 +384,21 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } + if (dev->vdec_pdata->is_subdev_supported) { + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, + &pdev->dev); + if (ret) { + mtk_v4l2_err("Main device of_platform_populate failed."); + goto err_reg_cont; + } + } + + ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); + if (ret) { + mtk_v4l2_err("Failed to register video device"); + goto err_reg_cont; + } + if (dev->vdec_pdata->uses_stateless_api) { dev->mdev_dec.dev = &pdev->dev; strscpy(dev->mdev_dec.model, MTK_VCODEC_DEC_NAME, @@ -327,7 +412,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) MEDIA_ENT_F_PROC_VIDEO_DECODER); if (ret) { mtk_v4l2_err("Failed to register media controller"); - goto err_reg_cont; + goto err_dec_mem_init; } ret = media_device_register(&dev->mdev_dec); @@ -338,34 +423,28 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor); } - ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0); - if (ret) { - mtk_v4l2_err("Failed to register video device"); - goto err_dec_reg; - } mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor); return 0; -err_dec_reg: - if (dev->vdec_pdata->uses_stateless_api) - media_device_unregister(&dev->mdev_dec); err_media_reg: - if (dev->vdec_pdata->uses_stateless_api) - v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); + v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); +err_dec_mem_init: + video_unregister_device(vfd_dec); err_reg_cont: if (dev->vdec_pdata->uses_stateless_api) media_device_cleanup(&dev->mdev_dec); destroy_workqueue(dev->decode_workqueue); err_event_workq: v4l2_m2m_release(dev->m2m_dev_dec); -err_dec_mem_init: - video_unregister_device(vfd_dec); err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); +err_core_workq: + if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) + destroy_workqueue(dev->core_workqueue); err_res: - mtk_vcodec_release_dec_pm(dev); + pm_runtime_disable(dev->pm.dev); err_dec_pm: mtk_vcodec_fw_release(dev->fw_handler); return ret; @@ -380,6 +459,10 @@ static const struct of_device_id mtk_vcodec_match[] = { .compatible = "mediatek,mt8183-vcodec-dec", .data = &mtk_vdec_8183_pdata, }, + { + .compatible = "mediatek,mt8192-vcodec-dec", + .data = &mtk_lat_sig_core_pdata, + }, {}, }; @@ -404,7 +487,7 @@ static int mtk_vcodec_dec_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); - mtk_vcodec_release_dec_pm(dev); + pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); return 0; } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c new file mode 100644 index 000000000000..14bed2bd4283 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +static const struct of_device_id mtk_vdec_hw_match[] = { + { + .compatible = "mediatek,mtk-vcodec-lat", + .data = (void *)MTK_VDEC_LAT0, + }, + { + .compatible = "mediatek,mtk-vcodec-core", + .data = (void *)MTK_VDEC_CORE, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); + +static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev) +{ + struct platform_device *pdev = vdec_dev->plat_dev; + struct device_node *subdev_node; + enum mtk_vdec_hw_id hw_idx; + const struct of_device_id *of_id; + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_vdec_hw_match); i++) { + of_id = &mtk_vdec_hw_match[i]; + subdev_node = of_find_compatible_node(NULL, NULL, + of_id->compatible); + if (!subdev_node) + continue; + + of_node_put(subdev_node); + + hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; + if (!test_bit(hw_idx, vdec_dev->subdev_bitmap)) { + dev_err(&pdev->dev, "vdec %d is not ready", hw_idx); + return -EAGAIN; + } + } + + return 0; +} + +static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) +{ + struct mtk_vdec_hw_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + u32 cg_status; + unsigned int dec_done_status; + void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] + + VDEC_IRQ_CFG_REG; + + ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx); + + /* check if HW active or not */ + cg_status = readl(dev->reg_base[VDEC_HW_SYS]); + if (cg_status & VDEC_HW_ACTIVE) { + mtk_v4l2_err("vdec active is not 0x0 (0x%08x)", + cg_status); + return IRQ_HANDLED; + } + + dec_done_status = readl(vdec_misc_addr); + if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != + MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) + return IRQ_HANDLED; + + /* clear interrupt */ + writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr); + writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); + + mtk_v4l2_debug(3, "wake up ctx %d, dec_done_status=%x", + ctx->id, dec_done_status); + + return IRQ_HANDLED; +} + +static int mtk_vdec_hw_init_irq(struct mtk_vdec_hw_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int ret; + + dev->dec_irq = platform_get_irq(pdev, 0); + if (dev->dec_irq < 0) + return dev->dec_irq; + + irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, dev->dec_irq, + mtk_vdec_hw_irq_handler, 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", + dev->dec_irq, ret); + return ret; + } + + return 0; +} + +static int mtk_vdec_hw_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_dev *main_dev; + const struct of_device_id *of_id; + int hw_idx; + int ret; + + if (!dev->parent) { + dev_err(dev, "no parent for hardware devices.\n"); + return -ENODEV; + } + + main_dev = dev_get_drvdata(dev->parent); + if (!main_dev) { + dev_err(dev, "failed to get parent driver data"); + return -EINVAL; + } + + subdev_dev = devm_kzalloc(dev, sizeof(*subdev_dev), GFP_KERNEL); + if (!subdev_dev) + return -ENOMEM; + + subdev_dev->plat_dev = pdev; + ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm); + if (ret) + return ret; + pm_runtime_enable(&pdev->dev); + + of_id = of_match_device(mtk_vdec_hw_match, dev); + if (!of_id) { + dev_err(dev, "Can't get vdec subdev id.\n"); + ret = -EINVAL; + goto err; + } + + hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; + if (hw_idx >= MTK_VDEC_HW_MAX) { + dev_err(dev, "Hardware index %d not correct.\n", hw_idx); + ret = -EINVAL; + goto err; + } + + main_dev->subdev_dev[hw_idx] = subdev_dev; + subdev_dev->hw_idx = hw_idx; + subdev_dev->main_dev = main_dev; + subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS]; + set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap); + + ret = mtk_vdec_hw_init_irq(subdev_dev); + if (ret) + goto err; + + subdev_dev->reg_base[VDEC_HW_MISC] = + devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC])) { + ret = PTR_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC]); + goto err; + } + + if (!main_dev->subdev_prob_done) + main_dev->subdev_prob_done = mtk_vdec_hw_prob_done; + + platform_set_drvdata(pdev, subdev_dev); + return 0; +err: + pm_runtime_disable(subdev_dev->pm.dev); + return ret; +} + +static struct platform_driver mtk_vdec_driver = { + .probe = mtk_vdec_hw_probe, + .driver = { + .name = "mtk-vdec-comp", + .of_match_table = mtk_vdec_hw_match, + }, +}; +module_platform_driver(mtk_vdec_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video decoder hardware driver"); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h new file mode 100644 index 000000000000..a63e4b1b81c3 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#ifndef _MTK_VCODEC_DEC_HW_H_ +#define _MTK_VCODEC_DEC_HW_H_ + +#include <linux/io.h> +#include <linux/platform_device.h> + +#include "mtk_vcodec_drv.h" + +#define VDEC_HW_ACTIVE 0x10 +#define VDEC_IRQ_CFG 0x11 +#define VDEC_IRQ_CLR 0x10 +#define VDEC_IRQ_CFG_REG 0xa4 + +/** + * enum mtk_vdec_hw_reg_idx - subdev hardware register base index + * @VDEC_HW_SYS : vdec soc register index + * @VDEC_HW_MISC: vdec misc register index + * @VDEC_HW_MAX : vdec supported max register index + */ +enum mtk_vdec_hw_reg_idx { + VDEC_HW_SYS, + VDEC_HW_MISC, + VDEC_HW_MAX +}; + +/** + * struct mtk_vdec_hw_dev - vdec hardware driver data + * @plat_dev: platform device + * @main_dev: main device + * @reg_base: mapped address of MTK Vcodec registers. + * + * @curr_ctx: the context that is waiting for codec hardware + * + * @dec_irq : decoder irq resource + * @pm : power management control + * @hw_idx : each hardware index + */ +struct mtk_vdec_hw_dev { + struct platform_device *plat_dev; + struct mtk_vcodec_dev *main_dev; + void __iomem *reg_base[VDEC_HW_MAX]; + + struct mtk_vcodec_ctx *curr_ctx; + + int dec_irq; + struct mtk_vcodec_pm pm; + int hw_idx; +}; + +#endif /* _MTK_VCODEC_DEC_HW_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c new file mode 100644 index 000000000000..7e0c2644bf7b --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Tiffany Lin <tiffany.lin@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> + +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm) +{ + struct mtk_vcodec_clk *dec_clk; + struct mtk_vcodec_clk_info *clk_info; + int i = 0, ret; + + dec_clk = &pm->vdec_clk; + pm->dev = &pdev->dev; + + dec_clk->clk_num = + of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (dec_clk->clk_num > 0) { + dec_clk->clk_info = devm_kcalloc(&pdev->dev, + dec_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!dec_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get vdec clock count"); + return -EINVAL; + } + + for (i = 0; i < dec_clk->clk_num; i++) { + clk_info = &dec_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("Failed to get clock name id = %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_init_dec_clk); + +int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + int ret; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return -EINVAL; + } + pm = &subdev_dev->pm; + } else { + pm = &vdec_dev->pm; + } + + ret = pm_runtime_resume_and_get(pm->dev); + if (ret) + mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_on); + +void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + int ret; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + } else { + pm = &vdec_dev->pm; + } + + ret = pm_runtime_put_sync(pm->dev); + if (ret) + mtk_v4l2_err("pm_runtime_put_sync fail %d", ret); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_off); + +void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + struct mtk_vcodec_clk *dec_clk; + int ret, i; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + enable_irq(subdev_dev->dec_irq); + } else { + pm = &vdec_dev->pm; + enable_irq(vdec_dev->dec_irq); + } + + dec_clk = &pm->vdec_clk; + for (i = 0; i < dec_clk->clk_num; i++) { + ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, + dec_clk->clk_info[i].clk_name, ret); + goto error; + } + } + + return; +error: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_on); + +void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + struct mtk_vcodec_clk *dec_clk; + int i; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + disable_irq(subdev_dev->dec_irq); + } else { + pm = &vdec_dev->pm; + disable_irq(vdec_dev->dec_irq); + } + + dec_clk = &pm->vdec_clk; + for (i = dec_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_off); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h new file mode 100644 index 000000000000..3cc721bbfaf6 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Tiffany Lin <tiffany.lin@mediatek.com> + */ + +#ifndef _MTK_VCODEC_DEC_PM_H_ +#define _MTK_VCODEC_DEC_PM_H_ + +#include "mtk_vcodec_drv.h" + +int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm); + +int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); + +#endif /* _MTK_VCODEC_DEC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c index bef49244e61b..04ca43c77e5f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c @@ -625,4 +625,6 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata = { .num_framesizes = NUM_SUPPORTED_FRAMESIZE, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = false, + .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, }; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c index 3d9f47555884..23d997ac114d 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c @@ -356,4 +356,25 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata = { .uses_stateless_api = true, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = false, + .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, +}; + +/* This platform data is used for one lat and one core architecture. */ +const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata = { + .chip = MTK_MT8192, + .init_vdec_params = mtk_init_vdec_params, + .ctrls_setup = mtk_vcodec_dec_ctrls_setup, + .vdec_vb2_ops = &mtk_vdec_request_vb2_ops, + .vdec_formats = mtk_video_formats, + .num_formats = NUM_FORMATS, + .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], + .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], + .vdec_framesizes = mtk_vdec_framesizes, + .num_framesizes = NUM_SUPPORTED_FRAMESIZE, + .uses_stateless_api = true, + .worker = mtk_vdec_worker, + .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = true, + .hw_arch = MTK_VDEC_LAT_SINGLE_CORE, }; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h index 581522177308..813901c4be5e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h @@ -15,7 +15,9 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> + #include "mtk_vcodec_util.h" +#include "vdec_msg_queue.h" #define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" #define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" @@ -25,6 +27,7 @@ #define MTK_VCODEC_MAX_PLANES 3 #define MTK_V4L2_BENCHMARK 0 #define WAIT_INTR_TIMEOUT_MS 1000 +#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) /* * enum mtk_hw_reg_idx - MTK hw register base index @@ -94,6 +97,27 @@ enum mtk_fmt_type { }; /* + * enum mtk_vdec_hw_id - Hardware index used to separate + * different hardware + */ +enum mtk_vdec_hw_id { + MTK_VDEC_CORE, + MTK_VDEC_LAT0, + MTK_VDEC_LAT1, + MTK_VDEC_HW_MAX, +}; + +/* + * enum mtk_vdec_hw_count - Supported hardware count + */ +enum mtk_vdec_hw_count { + MTK_VDEC_NO_HW = 0, + MTK_VDEC_ONE_CORE, + MTK_VDEC_ONE_LAT_ONE_CORE, + MTK_VDEC_MAX_HW_COUNT, +}; + +/* * struct mtk_video_fmt - Structure used to store information about pixelformats */ struct mtk_video_fmt { @@ -190,12 +214,8 @@ struct mtk_vcodec_clk { */ struct mtk_vcodec_pm { struct mtk_vcodec_clk vdec_clk; - struct device *larbvdec; - struct mtk_vcodec_clk venc_clk; - struct device *larbvenc; struct device *dev; - struct mtk_vcodec_dev *mtkdev; }; /** @@ -262,6 +282,9 @@ struct vdec_pic_info { * @decoded_frame_cnt: number of decoded frames * @lock: protect variables accessed by V4L2 threads and worker thread such as * mtk_video_dec_buf. + * @hw_id: hardware index used to identify different hardware. + * + * @msg_queue: msg queue used to store lat buffer information. */ struct mtk_vcodec_ctx { enum mtk_instance_type type; @@ -283,9 +306,9 @@ struct mtk_vcodec_ctx { struct vdec_pic_info picinfo; int dpb_size; - int int_cond; - int int_type; - wait_queue_head_t queue; + int int_cond[MTK_VDEC_HW_MAX]; + int int_type[MTK_VDEC_HW_MAX]; + wait_queue_head_t queue[MTK_VDEC_HW_MAX]; unsigned int irq_status; struct v4l2_ctrl_handler ctrl_hdl; @@ -304,7 +327,9 @@ struct mtk_vcodec_ctx { int decoded_frame_cnt; struct mutex lock; + int hw_id; + struct vdec_msg_queue msg_queue; }; enum mtk_chip { @@ -314,6 +339,14 @@ enum mtk_chip { MTK_MT8195, }; +/* + * enum mtk_vdec_hw_arch - Used to separate different hardware architecture + */ +enum mtk_vdec_hw_arch { + MTK_VDEC_PURE_SINGLE_CORE, + MTK_VDEC_LAT_SINGLE_CORE, +}; + /** * struct mtk_vcodec_dec_pdata - compatible data for each IC * @init_vdec_params: init vdec params @@ -332,7 +365,9 @@ enum mtk_chip { * @num_framesizes: count of video decoder frame sizes * * @chip: chip this decoder is compatible with + * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core * + * @is_subdev_supported: whether support parent-node architecture(subdev) * @uses_stateless_api: whether the decoder uses the stateless API with requests */ @@ -353,7 +388,9 @@ struct mtk_vcodec_dec_pdata { const int num_framesizes; enum mtk_chip chip; + enum mtk_vdec_hw_arch hw_arch; + bool is_subdev_supported; bool uses_stateless_api; }; @@ -424,6 +461,13 @@ struct mtk_vcodec_enc_pdata { * @pm: power management control * @dec_capability: used to identify decode capability, ex: 4k * @enc_capability: used to identify encode capability + * + * @core_workqueue: queue used for core hardware decode + * @msg_queue_core_ctx: msg queue context used for core workqueue + * + * @subdev_dev: subdev hardware device + * @subdev_prob_done: check whether all used hw device is prob done + * @subdev_bitmap: used to record hardware is ready or not */ struct mtk_vcodec_dev { struct v4l2_device v4l2_dev; @@ -455,12 +499,20 @@ struct mtk_vcodec_dev { int dec_irq; int enc_irq; - struct mutex dec_mutex; + /* decoder hardware mutex lock */ + struct mutex dec_mutex[MTK_VDEC_HW_MAX]; struct mutex enc_mutex; struct mtk_vcodec_pm pm; unsigned int dec_capability; unsigned int enc_capability; + + struct workqueue_struct *core_workqueue; + struct vdec_msg_queue_ctx msg_queue_core_ctx; + + void *subdev_dev[MTK_VDEC_HW_MAX]; + int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev); + DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); }; static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) @@ -473,4 +525,13 @@ static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); } +/* Wake up context wait_queue */ +static inline void +wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason, unsigned int hw_id) +{ + ctx->int_cond[hw_id] = 1; + ctx->int_type[hw_id] = reason; + wake_up_interruptible(&ctx->queue[hw_id]); +} + #endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index ffb046eec610..c21367038c34 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -8,7 +8,6 @@ #include <media/v4l2-event.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-dma-contig.h> -#include <soc/mediatek/smi.h> #include <linux/pm_runtime.h> #include "mtk_vcodec_drv.h" diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h index 513ee7993e34..513ee7993e34 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index aeaecb8d416e..5172cfe0db4a 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -11,10 +11,10 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <media/v4l2-event.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-dma-contig.h> -#include <linux/pm_runtime.h> #include "mtk_vcodec_drv.h" #include "mtk_vcodec_enc.h" @@ -62,14 +62,6 @@ static const struct mtk_video_fmt mtk_video_formats_capture_vp8[] = { }, }; -/* Wake up context wait_queue */ -static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason) -{ - ctx->int_cond = 1; - ctx->int_type = reason; - wake_up_interruptible(&ctx->queue); -} - static void clean_irq_status(unsigned int irq_status, void __iomem *addr) { if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) @@ -111,7 +103,7 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) clean_irq_status(ctx->irq_status, addr); - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED); + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); return IRQ_HANDLED; } @@ -137,7 +129,7 @@ static int fops_vcodec_open(struct file *file) v4l2_fh_add(&ctx->fh); INIT_LIST_HEAD(&ctx->list); ctx->dev = dev; - init_waitqueue_head(&ctx->queue); + init_waitqueue_head(&ctx->queue[0]); ctx->type = MTK_INST_ENCODER; ret = mtk_vcodec_enc_ctrls_setup(ctx); @@ -265,7 +257,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) return PTR_ERR(dev->fw_handler); dev->venc_pdata = of_device_get_match_data(&pdev->dev); - ret = mtk_vcodec_init_enc_pm(dev); + ret = mtk_vcodec_init_enc_clk(dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!"); goto err_enc_pm; @@ -357,7 +349,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1); + ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1); if (ret) { mtk_v4l2_err("Failed to register video device"); goto err_enc_reg; @@ -377,7 +369,7 @@ err_enc_mem_init: err_enc_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_res: - mtk_vcodec_release_enc_pm(dev); + pm_runtime_disable(dev->pm.dev); err_enc_pm: mtk_vcodec_fw_release(dev->fw_handler); return ret; @@ -466,7 +458,7 @@ static int mtk_vcodec_enc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); v4l2_device_unregister(&dev->v4l2_dev); - mtk_vcodec_release_enc_pm(dev); + pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c index 1b2e4930ed27..7055954eb2af 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c @@ -8,58 +8,35 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> -#include <soc/mediatek/smi.h> #include "mtk_vcodec_enc_pm.h" #include "mtk_vcodec_util.h" -int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) { - struct device_node *node; struct platform_device *pdev; struct mtk_vcodec_pm *pm; struct mtk_vcodec_clk *enc_clk; struct mtk_vcodec_clk_info *clk_info; - int ret = 0, i = 0; - struct device *dev; + int ret, i; pdev = mtkdev->plat_dev; pm = &mtkdev->pm; memset(pm, 0, sizeof(struct mtk_vcodec_pm)); - pm->mtkdev = mtkdev; pm->dev = &pdev->dev; - dev = &pdev->dev; enc_clk = &pm->venc_clk; - node = of_parse_phandle(dev->of_node, "mediatek,larb", 0); - if (!node) { - mtk_v4l2_err("no mediatek,larb found"); - return -ENODEV; - } - pdev = of_find_device_by_node(node); - of_node_put(node); - if (!pdev) { - mtk_v4l2_err("no mediatek,larb device found"); - return -ENODEV; - } - pm->larbvenc = &pdev->dev; - pdev = mtkdev->plat_dev; - pm->dev = &pdev->dev; - enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node, "clock-names"); if (enc_clk->clk_num > 0) { enc_clk->clk_info = devm_kcalloc(&pdev->dev, enc_clk->clk_num, sizeof(*clk_info), GFP_KERNEL); - if (!enc_clk->clk_info) { - ret = -ENOMEM; - goto put_larbvenc; - } + if (!enc_clk->clk_info) + return -ENOMEM; } else { mtk_v4l2_err("Failed to get venc clock count"); - ret = -EINVAL; - goto put_larbvenc; + return -EINVAL; } for (i = 0; i < enc_clk->clk_num; i++) { @@ -68,32 +45,20 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) "clock-names", i, &clk_info->clk_name); if (ret) { mtk_v4l2_err("venc failed to get clk name %d", i); - goto put_larbvenc; + return ret; } clk_info->vcodec_clk = devm_clk_get(&pdev->dev, clk_info->clk_name); if (IS_ERR(clk_info->vcodec_clk)) { mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, clk_info->clk_name); - ret = PTR_ERR(clk_info->vcodec_clk); - goto put_larbvenc; + return PTR_ERR(clk_info->vcodec_clk); } } return 0; - -put_larbvenc: - put_device(pm->larbvenc); - return ret; -} - -void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) -{ - pm_runtime_disable(mtkdev->pm.dev); - put_device(mtkdev->pm.larbvenc); } - void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) { struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; @@ -108,11 +73,6 @@ void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) } } - ret = mtk_smi_larb_get(pm->larbvenc); - if (ret) { - mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret); - goto clkerr; - } return; clkerr: @@ -125,7 +85,6 @@ void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; int i = 0; - mtk_smi_larb_put(pm->larbvenc); for (i = enc_clk->clk_num - 1; i >= 0; i--) clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h index b7ecdfd74823..bc455cefc0cd 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h @@ -9,8 +9,7 @@ #include "mtk_vcodec_drv.h" -int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *dev); -void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *dev); +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *dev); void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c index 94b39ae5c2e1..94b39ae5c2e1 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h index 539bb626772c..15ab6b8e3ae2 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h @@ -6,7 +6,7 @@ #include <linux/remoteproc.h> #include <linux/remoteproc/mtk_scp.h> -#include "../mtk-vpu/mtk_vpu.h" +#include "../vpu/mtk_vpu.h" struct mtk_vcodec_dev; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h index b41e66185cec..b41e66185cec 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c index d8e66b645bd8..d8e66b645bd8 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c index cd27f637dbe7..cfc7ebed8fb7 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c @@ -102,6 +102,8 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id); fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return ERR_PTR(-ENOMEM); fw->type = VPU; fw->ops = &mtk_vcodec_vpu_msg; fw->pdev = fw_pdev; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c new file mode 100644 index 000000000000..552b4c93d972 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin <tiffany.lin@mediatek.com> +*/ + +#include <linux/errno.h> +#include <linux/wait.h> + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, + int command, unsigned int timeout_ms, + unsigned int hw_id) +{ + long timeout_jiff, ret; + int status = 0; + + timeout_jiff = msecs_to_jiffies(timeout_ms); + ret = wait_event_interruptible_timeout(ctx->queue[hw_id], + ctx->int_cond[hw_id], + timeout_jiff); + + if (!ret) { + status = -1; /* timeout */ + mtk_v4l2_err("[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)", + ctx->id, command, ctx->type, timeout_ms, + ctx->int_cond[hw_id], ctx->int_type[hw_id]); + } else if (-ERESTARTSYS == ret) { + status = -1; + mtk_v4l2_err("[%d] cmd=%d, type=%d, dec inter fail (%d %d)", + ctx->id, command, ctx->type, + ctx->int_cond[hw_id], ctx->int_type[hw_id]); + } + + ctx->int_cond[hw_id] = 0; + ctx->int_type[hw_id] = 0; + + return status; +} +EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h index 638cd1f3526a..9681f492813b 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h @@ -12,7 +12,8 @@ struct mtk_vcodec_ctx; /* timeout is ms */ -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *data, int command, - unsigned int timeout_ms); +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, + int command, unsigned int timeout_ms, + unsigned int hw_id); #endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c index 5bac820a47fc..ace78c4b5b9e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c @@ -6,7 +6,10 @@ */ #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_drv.h" #include "mtk_vcodec_util.h" @@ -71,25 +74,59 @@ void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, } EXPORT_SYMBOL(mtk_vcodec_mem_free); -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *dev, - struct mtk_vcodec_ctx *ctx) +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx) { - unsigned long flags; + if (hw_idx >= MTK_VDEC_HW_MAX || hw_idx < 0 || !dev->subdev_dev[hw_idx]) { + mtk_v4l2_err("hw idx is out of range:%d", hw_idx); + return NULL; + } + + return dev->subdev_dev[hw_idx]; +} +EXPORT_SYMBOL(mtk_vcodec_get_hw_dev); - spin_lock_irqsave(&dev->irqlock, flags); - dev->curr_ctx = ctx; - spin_unlock_irqrestore(&dev->irqlock, flags); +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + struct mtk_vcodec_ctx *ctx, int hw_idx) +{ + unsigned long flags; + struct mtk_vdec_hw_dev *subdev_dev; + + spin_lock_irqsave(&vdec_dev->irqlock, flags); + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev"); + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); + return; + } + subdev_dev->curr_ctx = ctx; + } else { + vdec_dev->curr_ctx = ctx; + } + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); } EXPORT_SYMBOL(mtk_vcodec_set_curr_ctx); -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev) +struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + unsigned int hw_idx) { unsigned long flags; struct mtk_vcodec_ctx *ctx; - - spin_lock_irqsave(&dev->irqlock, flags); - ctx = dev->curr_ctx; - spin_unlock_irqrestore(&dev->irqlock, flags); + struct mtk_vdec_hw_dev *subdev_dev; + + spin_lock_irqsave(&vdec_dev->irqlock, flags); + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev"); + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); + return NULL; + } + ctx = subdev_dev->curr_ctx; + } else { + ctx = vdec_dev->curr_ctx; + } + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); return ctx; } EXPORT_SYMBOL(mtk_vcodec_get_curr_ctx); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h index 87c3d6d4bfa7..71956627a0e2 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h @@ -54,8 +54,10 @@ int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, struct mtk_vcodec_mem *mem); void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, struct mtk_vcodec_mem *mem); -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *dev, - struct mtk_vcodec_ctx *ctx); -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev); +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + struct mtk_vcodec_ctx *ctx, int hw_idx); +struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + unsigned int hw_idx); +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx); #endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c index 40d6e6c5ac7a..481655bb6016 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c @@ -413,7 +413,7 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* wait decoder done interrupt */ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS); + WAIT_INTR_TIMEOUT_MS, 0); if (err) goto err_free_fb_out; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c index fada4d146703..43542de11e9c 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c @@ -727,7 +727,7 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* wait decoder done interrupt */ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS); + WAIT_INTR_TIMEOUT_MS, 0); if (err) goto err_free_fb_out; vpu_dec_end(vpu); diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c index e5393f841080..88c046731754 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c @@ -488,7 +488,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* wait decoder done interrupt */ mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS); + WAIT_INTR_TIMEOUT_MS, 0); if (inst->vsi->load_data) load_dec_table(inst); diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c index 71cdc3ddafcb..70b8383f7c8e 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c @@ -539,7 +539,7 @@ static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS); + WAIT_INTR_TIMEOUT_MS, 0); if (ctx->irq_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) return true; diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h index e913f963b7db..e913f963b7db 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c index 42008243ceac..05a5b240e906 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c @@ -24,21 +24,24 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) break; case V4L2_PIX_FMT_H264: ctx->dec_if = &vdec_h264_if; + ctx->hw_id = MTK_VDEC_CORE; break; case V4L2_PIX_FMT_VP8: ctx->dec_if = &vdec_vp8_if; + ctx->hw_id = MTK_VDEC_CORE; break; case V4L2_PIX_FMT_VP9: ctx->dec_if = &vdec_vp9_if; + ctx->hw_id = MTK_VDEC_CORE; break; default: return -EINVAL; } mtk_vdec_lock(ctx); - mtk_vcodec_dec_clock_on(&ctx->dev->pm); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); ret = ctx->dec_if->init(ctx); - mtk_vcodec_dec_clock_off(&ctx->dev->pm); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); mtk_vdec_unlock(ctx); return ret; @@ -69,13 +72,11 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, mtk_vdec_lock(ctx); - mtk_vcodec_set_curr_ctx(ctx->dev, ctx); - mtk_vcodec_dec_clock_on(&ctx->dev->pm); - enable_irq(ctx->dev->dec_irq); + mtk_vcodec_set_curr_ctx(ctx->dev, ctx, ctx->hw_id); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); ret = ctx->dec_if->decode(ctx->drv_handle, bs, fb, res_chg); - disable_irq(ctx->dev->dec_irq); - mtk_vcodec_dec_clock_off(&ctx->dev->pm); - mtk_vcodec_set_curr_ctx(ctx->dev, NULL); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); + mtk_vcodec_set_curr_ctx(ctx->dev, NULL, ctx->hw_id); mtk_vdec_unlock(ctx); @@ -103,9 +104,9 @@ void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) return; mtk_vdec_lock(ctx); - mtk_vcodec_dec_clock_on(&ctx->dev->pm); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); ctx->dec_if->deinit(ctx->drv_handle); - mtk_vcodec_dec_clock_off(&ctx->dev->pm); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); mtk_vdec_unlock(ctx); ctx->drv_handle = NULL; diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h index d467e8af4a84..d467e8af4a84 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h diff --git a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h index 5f45a537beb4..bf54d6d9a857 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h @@ -18,12 +18,16 @@ enum vdec_ipi_msgid { AP_IPIMSG_DEC_END = 0xA002, AP_IPIMSG_DEC_DEINIT = 0xA003, AP_IPIMSG_DEC_RESET = 0xA004, + AP_IPIMSG_DEC_CORE = 0xA005, + AP_IPIMSG_DEC_CORE_END = 0xA006, VPU_IPIMSG_DEC_INIT_ACK = 0xB000, VPU_IPIMSG_DEC_START_ACK = 0xB001, VPU_IPIMSG_DEC_END_ACK = 0xB002, VPU_IPIMSG_DEC_DEINIT_ACK = 0xB003, VPU_IPIMSG_DEC_RESET_ACK = 0xB004, + VPU_IPIMSG_DEC_CORE_ACK = 0xB005, + VPU_IPIMSG_DEC_CORE_END_ACK = 0xB006, }; /** @@ -31,6 +35,8 @@ enum vdec_ipi_msgid { * @msg_id : vdec_ipi_msgid * @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2. * @inst_id : instance ID. Used if the ABI version >= 2. + * @codec_type : codec fourcc + * @reserved : reserved param */ struct vdec_ap_ipi_cmd { uint32_t msg_id; @@ -38,6 +44,8 @@ struct vdec_ap_ipi_cmd { uint32_t vpu_inst_addr; uint32_t inst_id; }; + u32 codec_type; + u32 reserved; }; /** @@ -55,12 +63,12 @@ struct vdec_vpu_ipi_ack { /** * struct vdec_ap_ipi_init - for AP_IPIMSG_DEC_INIT * @msg_id : AP_IPIMSG_DEC_INIT - * @reserved : Reserved field + * @codec_type : codec fourcc * @ap_inst_addr : AP video decoder instance address */ struct vdec_ap_ipi_init { uint32_t msg_id; - uint32_t reserved; + u32 codec_type; uint64_t ap_inst_addr; }; @@ -73,7 +81,7 @@ struct vdec_ap_ipi_init { * H264 decoder [0]:buf_sz [1]:nal_start * VP8 decoder [0]:width/height * VP9 decoder [0]:profile, [1][2] width/height - * @reserved : Reserved field + * @codec_type : codec fourcc */ struct vdec_ap_ipi_dec_start { uint32_t msg_id; @@ -82,7 +90,7 @@ struct vdec_ap_ipi_dec_start { uint32_t inst_id; }; uint32_t data[3]; - uint32_t reserved; + u32 codec_type; }; /** diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c new file mode 100644 index 000000000000..4b062a8128b4 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#include <linux/freezer.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> + +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_drv.h" +#include "vdec_msg_queue.h" + +#define VDEC_MSG_QUEUE_TIMEOUT_MS 1500 + +/* the size used to store lat slice header information */ +#define VDEC_LAT_SLICE_HEADER_SZ (640 * SZ_1K) + +/* the size used to store avc error information */ +#define VDEC_ERR_MAP_SZ_AVC (17 * SZ_1K) + +/* core will read the trans buffer which decoded by lat to decode again. + * The trans buffer size of FHD and 4K bitstreams are different. + */ +static int vde_msg_queue_get_trans_size(int width, int height) +{ + if (width > 1920 || height > 1088) + return 30 * SZ_1M; + else + return 6 * SZ_1M; +} + +void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index) +{ + init_waitqueue_head(&ctx->ready_to_use); + INIT_LIST_HEAD(&ctx->ready_queue); + spin_lock_init(&ctx->ready_lock); + ctx->ready_num = 0; + ctx->hardware_index = hardware_index; +} + +static struct list_head *vdec_get_buf_list(int hardware_index, struct vdec_lat_buf *buf) +{ + switch (hardware_index) { + case MTK_VDEC_CORE: + return &buf->core_list; + case MTK_VDEC_LAT0: + return &buf->lat_list; + default: + return NULL; + } +} + +int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) +{ + struct list_head *head; + + head = vdec_get_buf_list(msg_ctx->hardware_index, buf); + if (!head) { + mtk_v4l2_err("fail to qbuf: %d", msg_ctx->hardware_index); + return -EINVAL; + } + + spin_lock(&msg_ctx->ready_lock); + list_add_tail(head, &msg_ctx->ready_queue); + msg_ctx->ready_num++; + + if (msg_ctx->hardware_index != MTK_VDEC_CORE) + wake_up_all(&msg_ctx->ready_to_use); + else + queue_work(buf->ctx->dev->core_workqueue, + &buf->ctx->msg_queue.core_work); + + mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + return 0; +} + +static bool vdec_msg_queue_wait_event(struct vdec_msg_queue_ctx *msg_ctx) +{ + int ret; + + ret = wait_event_timeout(msg_ctx->ready_to_use, + !list_empty(&msg_ctx->ready_queue), + msecs_to_jiffies(VDEC_MSG_QUEUE_TIMEOUT_MS)); + if (!ret) + return false; + + return true; +} + +struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) +{ + struct vdec_lat_buf *buf; + struct list_head *head; + int ret; + + spin_lock(&msg_ctx->ready_lock); + if (list_empty(&msg_ctx->ready_queue)) { + mtk_v4l2_debug(3, "queue is NULL, type:%d num: %d", + msg_ctx->hardware_index, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + if (msg_ctx->hardware_index == MTK_VDEC_CORE) + return NULL; + + ret = vdec_msg_queue_wait_event(msg_ctx); + if (!ret) + return NULL; + spin_lock(&msg_ctx->ready_lock); + } + + if (msg_ctx->hardware_index == MTK_VDEC_CORE) + buf = list_first_entry(&msg_ctx->ready_queue, + struct vdec_lat_buf, core_list); + else + buf = list_first_entry(&msg_ctx->ready_queue, + struct vdec_lat_buf, lat_list); + + head = vdec_get_buf_list(msg_ctx->hardware_index, buf); + if (!head) { + spin_unlock(&msg_ctx->ready_lock); + mtk_v4l2_err("fail to dqbuf: %d", msg_ctx->hardware_index); + return NULL; + } + list_del(head); + + msg_ctx->ready_num--; + mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + return buf; +} + +void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr) +{ + spin_lock(&msg_queue->lat_ctx.ready_lock); + msg_queue->wdma_rptr_addr = ube_rptr; + mtk_v4l2_debug(3, "update ube rprt (0x%llx)", ube_rptr); + spin_unlock(&msg_queue->lat_ctx.ready_lock); +} + +void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr) +{ + spin_lock(&msg_queue->lat_ctx.ready_lock); + msg_queue->wdma_wptr_addr = ube_wptr; + mtk_v4l2_debug(3, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx", + msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr, + ube_wptr); + spin_unlock(&msg_queue->lat_ctx.ready_lock); +} + +bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) +{ + long timeout_jiff; + int ret; + + timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2)); + ret = wait_event_timeout(msg_queue->lat_ctx.ready_to_use, + msg_queue->lat_ctx.ready_num == NUM_BUFFER_COUNT, + timeout_jiff); + if (ret) { + mtk_v4l2_debug(3, "success to get lat buf: %d", + msg_queue->lat_ctx.ready_num); + return true; + } + mtk_v4l2_err("failed with lat buf isn't full: %d", + msg_queue->lat_ctx.ready_num); + return false; +} + +void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx) +{ + struct vdec_lat_buf *lat_buf; + struct mtk_vcodec_mem *mem; + int i; + + mem = &msg_queue->wdma_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + for (i = 0; i < NUM_BUFFER_COUNT; i++) { + lat_buf = &msg_queue->lat_buf[i]; + + mem = &lat_buf->wdma_err_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + + mem = &lat_buf->slice_bc_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + + kfree(lat_buf->private_data); + } +} + +static void vdec_msg_queue_core_work(struct work_struct *work) +{ + struct vdec_msg_queue *msg_queue = + container_of(work, struct vdec_msg_queue, core_work); + struct mtk_vcodec_ctx *ctx = + container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); + struct mtk_vcodec_dev *dev = ctx->dev; + struct vdec_lat_buf *lat_buf; + + lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); + if (!lat_buf) + return; + + ctx = lat_buf->ctx; + mtk_vcodec_set_curr_ctx(dev, ctx, MTK_VDEC_CORE); + + lat_buf->core_decode(lat_buf); + + mtk_vcodec_set_curr_ctx(dev, NULL, MTK_VDEC_CORE); + vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); + + if (!list_empty(&ctx->msg_queue.lat_ctx.ready_queue)) { + mtk_v4l2_debug(3, "re-schedule to decode for core: %d", + dev->msg_queue_core_ctx.ready_num); + queue_work(dev->core_workqueue, &msg_queue->core_work); + } +} + +int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + int private_size) +{ + struct vdec_lat_buf *lat_buf; + int i, err; + + /* already init msg queue */ + if (msg_queue->wdma_addr.size) + return 0; + + vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); + INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); + msg_queue->wdma_addr.size = + vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, + ctx->picinfo.buf_h); + + err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_addr buf"); + return -ENOMEM; + } + msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr; + msg_queue->wdma_wptr_addr = msg_queue->wdma_addr.dma_addr; + + for (i = 0; i < NUM_BUFFER_COUNT; i++) { + lat_buf = &msg_queue->lat_buf[i]; + + lat_buf->wdma_err_addr.size = VDEC_ERR_MAP_SZ_AVC; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->wdma_err_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_err_addr buf[%d]", i); + goto mem_alloc_err; + } + + lat_buf->slice_bc_addr.size = VDEC_LAT_SLICE_HEADER_SZ; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->slice_bc_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_addr buf[%d]", i); + goto mem_alloc_err; + } + + lat_buf->private_data = kzalloc(private_size, GFP_KERNEL); + if (!lat_buf->private_data) { + err = -ENOMEM; + goto mem_alloc_err; + } + + lat_buf->ctx = ctx; + lat_buf->core_decode = core_decode; + err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf); + if (err) { + mtk_v4l2_err("failed to qbuf buf[%d]", i); + goto mem_alloc_err; + } + } + return 0; + +mem_alloc_err: + vdec_msg_queue_deinit(msg_queue, ctx); + return err; +} diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h new file mode 100644 index 000000000000..b6ba66d3e026 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#ifndef _VDEC_MSG_QUEUE_H_ +#define _VDEC_MSG_QUEUE_H_ + +#include <linux/sched.h> +#include <linux/semaphore.h> +#include <linux/slab.h> +#include <media/videobuf2-v4l2.h> + +#include "mtk_vcodec_util.h" + +#define NUM_BUFFER_COUNT 3 + +struct vdec_lat_buf; +struct mtk_vcodec_ctx; +struct mtk_vcodec_dev; +typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); + +/** + * struct vdec_msg_queue_ctx - represents a queue for buffers ready to be processed + * @ready_to_use: ready used queue used to signalize when get a job queue + * @ready_queue: list of ready lat buffer queues + * @ready_lock: spin lock to protect the lat buffer usage + * @ready_num: number of buffers ready to be processed + * @hardware_index: hardware id that this queue is used for + */ +struct vdec_msg_queue_ctx { + wait_queue_head_t ready_to_use; + struct list_head ready_queue; + /* protect lat buffer */ + spinlock_t ready_lock; + int ready_num; + int hardware_index; +}; + +/** + * struct vdec_lat_buf - lat buffer message used to store lat info for core decode + * @wdma_err_addr: wdma error address used for lat hardware + * @slice_bc_addr: slice bc address used for lat hardware + * @ts_info: need to set timestamp from output to capture + * + * @private_data: shared information used to lat and core hardware + * @ctx: mtk vcodec context information + * @core_decode: different codec use different decode callback function + * @lat_list: add lat buffer to lat head list + * @core_list: add lat buffer to core head list + */ +struct vdec_lat_buf { + struct mtk_vcodec_mem wdma_err_addr; + struct mtk_vcodec_mem slice_bc_addr; + struct vb2_v4l2_buffer ts_info; + + void *private_data; + struct mtk_vcodec_ctx *ctx; + core_decode_cb_t core_decode; + struct list_head lat_list; + struct list_head core_list; +}; + +/** + * struct vdec_msg_queue - used to store lat buffer message + * @lat_buf: lat buffer used to store lat buffer information + * @wdma_addr: wdma address used for ube + * @wdma_rptr_addr: ube read point + * @wdma_wptr_addr: ube write point + * @core_work: core hardware work + * @lat_ctx: used to store lat buffer list + */ +struct vdec_msg_queue { + struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; + + struct mtk_vcodec_mem wdma_addr; + u64 wdma_rptr_addr; + u64 wdma_wptr_addr; + + struct work_struct core_work; + struct vdec_msg_queue_ctx lat_ctx; +}; + +/** + * vdec_msg_queue_init - init lat buffer information. + * @msg_queue: used to store the lat buffer information + * @ctx: v4l2 ctx + * @core_decode: core decode callback for each codec + * @private_size: the private data size used to share with core + * + * Return: returns 0 if init successfully, or fail. + */ +int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + int private_size); + +/** + * vdec_msg_queue_init_ctx - used to init msg queue context information. + * @ctx: message queue context + * @hardware_index: hardware index + */ +void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index); + +/** + * vdec_msg_queue_qbuf - enqueue lat buffer to queue list. + * @ctx: message queue context + * @buf: current lat buffer + * + * Return: returns 0 if qbuf successfully, or fail. + */ +int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf); + +/** + * vdec_msg_queue_dqbuf - dequeue lat buffer from queue list. + * @ctx: message queue context + * + * Return: returns not null if dq successfully, or fail. + */ +struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx); + +/** + * vdec_msg_queue_update_ube_rptr - used to updata the ube read point. + * @msg_queue: used to store the lat buffer information + * @ube_rptr: current ube read point + */ +void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr); + +/** + * vdec_msg_queue_update_ube_wptr - used to updata the ube write point. + * @msg_queue: used to store the lat buffer information + * @ube_wptr: current ube write point + */ +void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr); + +/** + * vdec_msg_queue_wait_lat_buf_full - used to check whether all lat buffer + * in lat list. + * @msg_queue: used to store the lat buffer information + * + * Return: returns true if successfully, or fail. + */ +bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue); + +/** + * vdec_msg_queue_deinit - deinit lat buffer information. + * @msg_queue: used to store the lat buffer information + * @ctx: v4l2 ctx + */ +void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx); + +#endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c index 5dffc459a33d..dd35d2c5f920 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c @@ -85,6 +85,8 @@ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) case VPU_IPIMSG_DEC_END_ACK: case VPU_IPIMSG_DEC_DEINIT_ACK: case VPU_IPIMSG_DEC_RESET_ACK: + case VPU_IPIMSG_DEC_CORE_ACK: + case VPU_IPIMSG_DEC_CORE_END_ACK: break; default: @@ -98,18 +100,29 @@ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) { - int err; + int err, id, msgid; - mtk_vcodec_debug(vpu, "id=%X", *(uint32_t *)msg); + msgid = *(uint32_t *)msg; + mtk_vcodec_debug(vpu, "id=%X", msgid); vpu->failure = 0; vpu->signaled = 0; - err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, + if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { + if (msgid == AP_IPIMSG_DEC_CORE || + msgid == AP_IPIMSG_DEC_CORE_END) + id = vpu->core_id; + else + id = vpu->id; + } else { + id = vpu->id; + } + + err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg, len, 2000); if (err) { mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d", - vpu->id, *(uint32_t *)msg, err); + id, msgid, err); return err; } @@ -129,6 +142,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) msg.vpu_inst_addr = vpu->inst_addr; else msg.inst_id = vpu->inst_id; + msg.codec_type = vpu->codec_type; err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg)); mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err); @@ -147,14 +161,25 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, vpu->handler, "vdec", NULL); - if (err != 0) { + if (err) { mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err); return err; } + if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { + err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, + vpu->core_id, vpu->handler, + "vdec", NULL); + if (err) { + mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err); + return err; + } + } + memset(&msg, 0, sizeof(msg)); msg.msg_id = AP_IPIMSG_DEC_INIT; msg.ap_inst_addr = (unsigned long)vpu; + msg.codec_type = vpu->codec_type; mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu); @@ -185,17 +210,28 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) for (i = 0; i < len; i++) msg.data[i] = data[i]; + msg.codec_type = vpu->codec_type; err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); mtk_vcodec_debug(vpu, "- ret=%d", err); return err; } +int vpu_dec_core(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE); +} + int vpu_dec_end(struct vdec_vpu_inst *vpu) { return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_END); } +int vpu_dec_core_end(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE_END); +} + int vpu_dec_deinit(struct vdec_vpu_inst *vpu) { return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_DEINIT); diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h index c2ed5b6cab8b..4cb3c7f5a3ad 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h @@ -14,6 +14,7 @@ struct mtk_vcodec_ctx; /** * struct vdec_vpu_inst - VPU instance for video codec * @id : ipi msg id for each decoder + * @core_id : core id used to separate different hardware * @vsi : driver structure allocated by VPU side and shared to AP side * for control and info share * @failure : VPU execution result status, 0: success, others: fail @@ -26,9 +27,11 @@ struct mtk_vcodec_ctx; * @dev : platform device of VPU * @wq : wait queue to wait VPU message ack * @handler : ipi handler for each decoder + * @codec_type : use codec type to separate different codecs */ struct vdec_vpu_inst { int id; + int core_id; void *vsi; int32_t failure; uint32_t inst_addr; @@ -38,6 +41,7 @@ struct vdec_vpu_inst { struct mtk_vcodec_ctx *ctx; wait_queue_head_t wq; mtk_vcodec_ipi_handler handler; + unsigned int codec_type; }; /** @@ -82,4 +86,22 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu); */ int vpu_dec_reset(struct vdec_vpu_inst *vpu); +/** + * vpu_dec_core - core start decoding, basically the function will be invoked once + * every frame. + * + * @vpu : instance for vdec_vpu_inst + */ +int vpu_dec_core(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_core_end - core end decoding, basically the function will be invoked once + * when core HW decoding done and receive interrupt successfully. The + * decoder in VPU will updata hardware information and deinit hardware + * and check if there is a new decoded frame available to display. + * + * @vpu : instance for vdec_vpu_inst + */ +int vpu_dec_core_end(struct vdec_vpu_inst *vpu); + #endif diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c index bf03888a824f..4d9b8798dffe 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c @@ -335,7 +335,7 @@ static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS)) { + WAIT_INTR_TIMEOUT_MS, 0)) { irq_status = ctx->irq_status; mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); } diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c index 6b66957d5192..56ce58f761f1 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c @@ -222,7 +222,7 @@ static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst) struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS)) { + WAIT_INTR_TIMEOUT_MS, 0)) { irq_status = ctx->irq_status; mtk_vcodec_debug(inst, "isr return %x", irq_status); } diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mediatek/vcodec/venc_drv_base.h index 3d718411dc73..3d718411dc73 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_base.h +++ b/drivers/media/platform/mediatek/vcodec/venc_drv_base.h diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/venc_drv_if.c index ce0bce811615..ce0bce811615 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc_drv_if.c diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/venc_drv_if.h index 0b04a1020873..0b04a1020873 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/venc_drv_if.h diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h index 587a2cf15b76..587a2cf15b76 100644 --- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h +++ b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c index e7899d8a3e4e..e7899d8a3e4e 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h index f83bc1b3f2bf..f83bc1b3f2bf 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h +++ b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h diff --git a/drivers/media/platform/mediatek/vpu/Kconfig b/drivers/media/platform/mediatek/vpu/Kconfig new file mode 100644 index 000000000000..2a8443a93ce0 --- /dev/null +++ b/drivers/media/platform/mediatek/vpu/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_MEDIATEK_VPU + tristate "Mediatek Video Processor Unit" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This driver provides downloading VPU firmware and + communicating with VPU. This driver for hw video + codec embedded in Mediatek's MT8173 SOCs. It is able + to handle video decoding/encoding in a range of formats. + + To compile this driver as a module, choose M here: the + module will be called mtk-vpu. diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mediatek/vpu/Makefile index ecd2d392b818..ecd2d392b818 100644 --- a/drivers/media/platform/mtk-vpu/Makefile +++ b/drivers/media/platform/mediatek/vpu/Makefile diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c index 7bd715fc844d..47b684b92f81 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c @@ -810,7 +810,6 @@ static int mtk_vpu_probe(struct platform_device *pdev) { struct mtk_vpu *vpu; struct device *dev; - struct resource *res; int ret = 0; dev_dbg(&pdev->dev, "initialization\n"); @@ -908,13 +907,10 @@ static int mtk_vpu_probe(struct platform_device *pdev) init_waitqueue_head(&vpu->run.wq); init_waitqueue_head(&vpu->ack_wq); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "get IRQ resource failed.\n"); - ret = -ENXIO; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto free_p_mem; - } - vpu->reg.irq = platform_get_irq(pdev, 0); + vpu->reg.irq = ret; ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0, pdev->name, vpu); if (ret) { diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mediatek/vpu/mtk_vpu.h index a56053ff135a..a56053ff135a 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.h +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.h diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c deleted file mode 100644 index 6038db96f71c..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Tiffany Lin <tiffany.lin@mediatek.com> - */ - -#include <linux/clk.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> -#include <linux/pm_runtime.h> -#include <soc/mediatek/smi.h> - -#include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) -{ - struct device_node *node; - struct platform_device *pdev; - struct mtk_vcodec_pm *pm; - struct mtk_vcodec_clk *dec_clk; - struct mtk_vcodec_clk_info *clk_info; - int i = 0, ret = 0; - - pdev = mtkdev->plat_dev; - pm = &mtkdev->pm; - pm->mtkdev = mtkdev; - dec_clk = &pm->vdec_clk; - node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0); - if (!node) { - mtk_v4l2_err("of_parse_phandle mediatek,larb fail!"); - return -1; - } - - pdev = of_find_device_by_node(node); - of_node_put(node); - if (WARN_ON(!pdev)) { - return -1; - } - pm->larbvdec = &pdev->dev; - pdev = mtkdev->plat_dev; - pm->dev = &pdev->dev; - - dec_clk->clk_num = - of_property_count_strings(pdev->dev.of_node, "clock-names"); - if (dec_clk->clk_num > 0) { - dec_clk->clk_info = devm_kcalloc(&pdev->dev, - dec_clk->clk_num, sizeof(*clk_info), - GFP_KERNEL); - if (!dec_clk->clk_info) { - ret = -ENOMEM; - goto put_device; - } - } else { - mtk_v4l2_err("Failed to get vdec clock count"); - ret = -EINVAL; - goto put_device; - } - - for (i = 0; i < dec_clk->clk_num; i++) { - clk_info = &dec_clk->clk_info[i]; - ret = of_property_read_string_index(pdev->dev.of_node, - "clock-names", i, &clk_info->clk_name); - if (ret) { - mtk_v4l2_err("Failed to get clock name id = %d", i); - goto put_device; - } - clk_info->vcodec_clk = devm_clk_get(&pdev->dev, - clk_info->clk_name); - if (IS_ERR(clk_info->vcodec_clk)) { - mtk_v4l2_err("devm_clk_get (%d)%s fail", i, - clk_info->clk_name); - ret = PTR_ERR(clk_info->vcodec_clk); - goto put_device; - } - } - - pm_runtime_enable(&pdev->dev); - return 0; -put_device: - put_device(pm->larbvdec); - return ret; -} - -void mtk_vcodec_release_dec_pm(struct mtk_vcodec_dev *dev) -{ - pm_runtime_disable(dev->pm.dev); - put_device(dev->pm.larbvdec); -} - -int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm) -{ - int ret; - - ret = pm_runtime_resume_and_get(pm->dev); - if (ret) - mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); - - return ret; -} - -void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm) -{ - int ret; - - ret = pm_runtime_put_sync(pm->dev); - if (ret) - mtk_v4l2_err("pm_runtime_put_sync fail %d", ret); -} - -void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) -{ - struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; - int ret, i = 0; - - for (i = 0; i < dec_clk->clk_num; i++) { - ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); - if (ret) { - mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, - dec_clk->clk_info[i].clk_name, ret); - goto error; - } - } - - ret = mtk_smi_larb_get(pm->larbvdec); - if (ret) { - mtk_v4l2_err("mtk_smi_larb_get larbvdec fail %d", ret); - goto error; - } - return; - -error: - for (i -= 1; i >= 0; i--) - clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); -} - -void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm) -{ - struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; - int i = 0; - - mtk_smi_larb_put(pm->larbvdec); - for (i = dec_clk->clk_num - 1; i >= 0; i--) - clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h deleted file mode 100644 index 280aeaefdb65..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Tiffany Lin <tiffany.lin@mediatek.com> - */ - -#ifndef _MTK_VCODEC_DEC_PM_H_ -#define _MTK_VCODEC_DEC_PM_H_ - -#include "mtk_vcodec_drv.h" - -int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *dev); -void mtk_vcodec_release_dec_pm(struct mtk_vcodec_dev *dev); - -int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm); -void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm); -void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm); -void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm); - -#endif /* _MTK_VCODEC_DEC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c deleted file mode 100644 index 70580c2525ba..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin <tiffany.lin@mediatek.com> -*/ - -#include <linux/errno.h> -#include <linux/wait.h> - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command, - unsigned int timeout_ms) -{ - wait_queue_head_t *waitqueue; - long timeout_jiff, ret; - int status = 0; - - waitqueue = (wait_queue_head_t *)&ctx->queue; - timeout_jiff = msecs_to_jiffies(timeout_ms); - - ret = wait_event_interruptible_timeout(*waitqueue, - ctx->int_cond, - timeout_jiff); - - if (!ret) { - status = -1; /* timeout */ - mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout time=%ums out %d %d!", - ctx->id, ctx->type, command, timeout_ms, - ctx->int_cond, ctx->int_type); - } else if (-ERESTARTSYS == ret) { - mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", - ctx->id, ctx->type, command, ctx->int_cond, - ctx->int_type); - status = -1; - } - - ctx->int_cond = 0; - ctx->int_type = 0; - - return status; -} -EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/nvidia/Kconfig b/drivers/media/platform/nvidia/Kconfig new file mode 100644 index 000000000000..b211b46877f6 --- /dev/null +++ b/drivers/media/platform/nvidia/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "NVidia media platform drivers" + +source "drivers/media/platform/nvidia/tegra-vde/Kconfig" diff --git a/drivers/media/platform/nvidia/Makefile b/drivers/media/platform/nvidia/Makefile new file mode 100644 index 000000000000..428415ff83de --- /dev/null +++ b/drivers/media/platform/nvidia/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += tegra-vde/ diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig new file mode 100644 index 000000000000..f7454823bbbb --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -0,0 +1,17 @@ +config VIDEO_TEGRA_VDE + tristate "NVIDIA Tegra Video Decoder Engine driver" + depends on V4L_MEM2MEM_DRIVERS + depends on ARCH_TEGRA || COMPILE_TEST + depends on VIDEO_DEV + select DMA_SHARED_BUFFER + select IOMMU_IOVA + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API + select SRAM + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG + select V4L2_H264 + select V4L2_MEM2MEM_DEV + help + Support for the NVIDIA Tegra video decoder unit. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/nvidia/tegra-vde/Makefile b/drivers/media/platform/nvidia/tegra-vde/Makefile new file mode 100644 index 000000000000..4e96f3305567 --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +tegra-vde-y := vde.o iommu.o dmabuf-cache.o h264.o v4l2.o +obj-$(CONFIG_VIDEO_TEGRA_VDE) += tegra-vde.o diff --git a/drivers/staging/media/tegra-vde/dmabuf-cache.c b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c index a98d03419b8f..69c346148070 100644 --- a/drivers/staging/media/tegra-vde/dmabuf-cache.c +++ b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c @@ -66,9 +66,9 @@ int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde, struct dma_buf_attachment **ap, dma_addr_t *addrp) { - struct device *dev = vde->miscdev.parent; struct dma_buf_attachment *attachment; struct tegra_vde_cache_entry *entry; + struct device *dev = vde->dev; struct sg_table *sgt; struct iova *iova; int err; diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c new file mode 100644 index 000000000000..d8e5534e80c8 --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NVIDIA Tegra Video decoder driver + * + * Copyright (C) 2016-2022 Dmitry Osipenko <digetx@gmail.com> + * + */ + +#include <linux/iopoll.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <media/v4l2-h264.h> + +#include "trace.h" +#include "vde.h" + +#define FLAG_B_FRAME 0x1 +#define FLAG_REFERENCE 0x2 + +struct tegra_vde_h264_frame { + unsigned int frame_num; + unsigned int flags; +}; + +struct tegra_vde_h264_decoder_ctx { + unsigned int dpb_frames_nb; + unsigned int dpb_ref_frames_with_earlier_poc_nb; + unsigned int baseline_profile; + unsigned int level_idc; + unsigned int log2_max_pic_order_cnt_lsb; + unsigned int log2_max_frame_num; + unsigned int pic_order_cnt_type; + unsigned int direct_8x8_inference_flag; + unsigned int pic_width_in_mbs; + unsigned int pic_height_in_mbs; + unsigned int pic_init_qp; + unsigned int deblocking_filter_control_present_flag; + unsigned int constrained_intra_pred_flag; + unsigned int chroma_qp_index_offset; + unsigned int pic_order_present_flag; + unsigned int num_ref_idx_l0_active_minus1; + unsigned int num_ref_idx_l1_active_minus1; +}; + +struct h264_reflists { + u8 p[V4L2_H264_NUM_DPB_ENTRIES]; + u8 b0[V4L2_H264_NUM_DPB_ENTRIES]; + u8 b1[V4L2_H264_NUM_DPB_ENTRIES]; +}; + +static int tegra_vde_wait_mbe(struct tegra_vde *vde) +{ + u32 tmp; + + return readl_relaxed_poll_timeout(vde->mbe + 0x8C, tmp, + tmp >= 0x10, 1, 100); +} + +static int tegra_vde_setup_mbe_frame_idx(struct tegra_vde *vde, + unsigned int refs_nb, + bool setup_refs) +{ + u32 value, frame_idx_enb_mask = 0; + unsigned int frame_idx; + unsigned int idx; + int err; + + tegra_vde_writel(vde, 0xD0000000 | (0 << 23), vde->mbe, 0x80); + tegra_vde_writel(vde, 0xD0200000 | (0 << 23), vde->mbe, 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) + return err; + + if (!setup_refs) + return 0; + + for (idx = 0, frame_idx = 1; idx < refs_nb; idx++, frame_idx++) { + tegra_vde_writel(vde, 0xD0000000 | (frame_idx << 23), + vde->mbe, 0x80); + tegra_vde_writel(vde, 0xD0200000 | (frame_idx << 23), + vde->mbe, 0x80); + + frame_idx_enb_mask |= frame_idx << (6 * (idx % 4)); + + if (idx % 4 == 3 || idx == refs_nb - 1) { + value = 0xC0000000; + value |= (idx >> 2) << 24; + value |= frame_idx_enb_mask; + + tegra_vde_writel(vde, value, vde->mbe, 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) + return err; + + frame_idx_enb_mask = 0; + } + } + + return 0; +} + +static void tegra_vde_mbe_set_0xa_reg(struct tegra_vde *vde, int reg, u32 val) +{ + tegra_vde_writel(vde, 0xA0000000 | (reg << 24) | (val & 0xFFFF), + vde->mbe, 0x80); + tegra_vde_writel(vde, 0xA0000000 | ((reg + 1) << 24) | (val >> 16), + vde->mbe, 0x80); +} + +static int tegra_vde_wait_bsev(struct tegra_vde *vde, bool wait_dma) +{ + struct device *dev = vde->dev; + u32 value; + int err; + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + !(value & BIT(2)), 1, 100); + if (err) { + dev_err(dev, "BSEV unknown bit timeout\n"); + return err; + } + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + (value & BSE_ICMDQUE_EMPTY), 1, 100); + if (err) { + dev_err(dev, "BSEV ICMDQUE flush timeout\n"); + return err; + } + + if (!wait_dma) + return 0; + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + !(value & BSE_DMA_BUSY), 1, 1000); + if (err) { + dev_err(dev, "BSEV DMA timeout\n"); + return err; + } + + return 0; +} + +static int tegra_vde_push_to_bsev_icmdqueue(struct tegra_vde *vde, + u32 value, bool wait_dma) +{ + tegra_vde_writel(vde, value, vde->bsev, ICMDQUE_WR); + + return tegra_vde_wait_bsev(vde, wait_dma); +} + +static void tegra_vde_setup_frameid(struct tegra_vde *vde, + struct tegra_video_frame *frame, + unsigned int frameid, + u32 mbs_width, u32 mbs_height) +{ + u32 y_addr = frame ? frame->y_addr : 0x6CDEAD00; + u32 cb_addr = frame ? frame->cb_addr : 0x6CDEAD00; + u32 cr_addr = frame ? frame->cr_addr : 0x6CDEAD00; + u32 value1 = frame ? ((frame->luma_atoms_pitch << 16) | mbs_height) : 0; + u32 value2 = frame ? ((frame->chroma_atoms_pitch << 6) | 1) : 0; + + tegra_vde_writel(vde, y_addr >> 8, vde->frameid, 0x000 + frameid * 4); + tegra_vde_writel(vde, cb_addr >> 8, vde->frameid, 0x100 + frameid * 4); + tegra_vde_writel(vde, cr_addr >> 8, vde->frameid, 0x180 + frameid * 4); + tegra_vde_writel(vde, value1, vde->frameid, 0x080 + frameid * 4); + tegra_vde_writel(vde, value2, vde->frameid, 0x280 + frameid * 4); +} + +static void tegra_setup_frameidx(struct tegra_vde *vde, + struct tegra_video_frame *frames, + unsigned int frames_nb, + u32 mbs_width, u32 mbs_height) +{ + unsigned int idx; + + for (idx = 0; idx < frames_nb; idx++) + tegra_vde_setup_frameid(vde, &frames[idx], idx, + mbs_width, mbs_height); + + for (; idx < 17; idx++) + tegra_vde_setup_frameid(vde, NULL, idx, 0, 0); +} + +static void tegra_vde_setup_iram_entry(struct tegra_vde *vde, + unsigned int table, + unsigned int row, + u32 value1, u32 value2) +{ + u32 *iram_tables = vde->iram; + + trace_vde_setup_iram_entry(table, row, value1, value2); + + iram_tables[0x20 * table + row * 2 + 0] = value1; + iram_tables[0x20 * table + row * 2 + 1] = value2; +} + +static void tegra_vde_setup_iram_tables(struct tegra_vde *vde, + struct tegra_video_frame *dpb_frames, + unsigned int ref_frames_nb, + unsigned int with_earlier_poc_nb) +{ + struct tegra_video_frame *frame; + int with_later_poc_nb; + u32 value, aux_addr; + unsigned int i, k; + + trace_vde_ref_l0(dpb_frames[0].frame_num); + + for (i = 0; i < 16; i++) { + if (i < ref_frames_nb) { + frame = &dpb_frames[i + 1]; + + aux_addr = frame->aux_addr; + + value = (i + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + } else { + aux_addr = 0x6ADEAD00; + value = 0x3f; + } + + tegra_vde_setup_iram_entry(vde, 0, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 1, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 3, i, value, aux_addr); + } + + if (!(dpb_frames[0].flags & FLAG_B_FRAME)) + return; + + if (with_earlier_poc_nb >= ref_frames_nb) + return; + + with_later_poc_nb = ref_frames_nb - with_earlier_poc_nb; + + trace_vde_ref_l1(with_later_poc_nb, with_earlier_poc_nb); + + for (i = 0, k = with_earlier_poc_nb; i < with_later_poc_nb; i++, k++) { + frame = &dpb_frames[k + 1]; + + aux_addr = frame->aux_addr; + + value = (k + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + } + + for (k = 0; i < ref_frames_nb; i++, k++) { + frame = &dpb_frames[k + 1]; + + aux_addr = frame->aux_addr; + + value = (k + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + } +} + +static int tegra_vde_setup_hw_context(struct tegra_vde *vde, + struct tegra_vde_h264_decoder_ctx *ctx, + struct tegra_video_frame *dpb_frames, + dma_addr_t bitstream_data_addr, + size_t bitstream_data_size, + unsigned int macroblocks_nb) +{ + struct device *dev = vde->dev; + u32 value; + int err; + + tegra_vde_set_bits(vde, 0x000A, vde->sxe, 0xF0); + tegra_vde_set_bits(vde, 0x000B, vde->bsev, CMDQUE_CONTROL); + tegra_vde_set_bits(vde, 0x8002, vde->mbe, 0x50); + tegra_vde_set_bits(vde, 0x000A, vde->mbe, 0xA0); + tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x14); + tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x28); + tegra_vde_set_bits(vde, 0x0A00, vde->mce, 0x08); + tegra_vde_set_bits(vde, 0x000A, vde->tfe, 0x00); + tegra_vde_set_bits(vde, 0x0005, vde->vdma, 0x04); + + tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x1C); + tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x00); + tegra_vde_writel(vde, 0x00000007, vde->vdma, 0x04); + tegra_vde_writel(vde, 0x00000007, vde->frameid, 0x200); + tegra_vde_writel(vde, 0x00000005, vde->tfe, 0x04); + tegra_vde_writel(vde, 0x00000000, vde->mbe, 0x84); + tegra_vde_writel(vde, 0x00000010, vde->sxe, 0x08); + tegra_vde_writel(vde, 0x00000150, vde->sxe, 0x54); + tegra_vde_writel(vde, 0x0000054C, vde->sxe, 0x58); + tegra_vde_writel(vde, 0x00000E34, vde->sxe, 0x5C); + tegra_vde_writel(vde, 0x063C063C, vde->mce, 0x10); + tegra_vde_writel(vde, 0x0003FC00, vde->bsev, INTR_STATUS); + tegra_vde_writel(vde, 0x0000150D, vde->bsev, BSE_CONFIG); + tegra_vde_writel(vde, 0x00000100, vde->bsev, BSE_INT_ENB); + tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x98); + tegra_vde_writel(vde, 0x00000060, vde->bsev, 0x9C); + + memset(vde->iram + 128, 0, macroblocks_nb / 2); + + tegra_setup_frameidx(vde, dpb_frames, ctx->dpb_frames_nb, + ctx->pic_width_in_mbs, ctx->pic_height_in_mbs); + + tegra_vde_setup_iram_tables(vde, dpb_frames, + ctx->dpb_frames_nb - 1, + ctx->dpb_ref_frames_with_earlier_poc_nb); + + /* + * The IRAM mapping is write-combine, ensure that CPU buffers have + * been flushed at this point. + */ + wmb(); + + tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x8C); + tegra_vde_writel(vde, bitstream_data_addr + bitstream_data_size, + vde->bsev, 0x54); + + vde->bitstream_data_addr = bitstream_data_addr; + + value = ctx->pic_width_in_mbs << 11 | ctx->pic_height_in_mbs << 3; + + tegra_vde_writel(vde, value, vde->bsev, 0x88); + + err = tegra_vde_wait_bsev(vde, false); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x800003FC, false); + if (err) + return err; + + value = 0x01500000; + value |= ((vde->iram_lists_addr + 512) >> 2) & 0xFFFF; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x840F054C, false); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x80000080, false); + if (err) + return err; + + value = 0x0E340000 | ((vde->iram_lists_addr >> 2) & 0xFFFF); + + err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); + if (err) + return err; + + value = 0x00800005; + value |= ctx->pic_width_in_mbs << 11; + value |= ctx->pic_height_in_mbs << 3; + + tegra_vde_writel(vde, value, vde->sxe, 0x10); + + value = !ctx->baseline_profile << 17; + value |= ctx->level_idc << 13; + value |= ctx->log2_max_pic_order_cnt_lsb << 7; + value |= ctx->pic_order_cnt_type << 5; + value |= ctx->log2_max_frame_num; + + tegra_vde_writel(vde, value, vde->sxe, 0x40); + + value = ctx->pic_init_qp << 25; + value |= !!(ctx->deblocking_filter_control_present_flag) << 2; + value |= !!ctx->pic_order_present_flag; + + tegra_vde_writel(vde, value, vde->sxe, 0x44); + + value = ctx->chroma_qp_index_offset; + value |= ctx->num_ref_idx_l0_active_minus1 << 5; + value |= ctx->num_ref_idx_l1_active_minus1 << 10; + value |= !!ctx->constrained_intra_pred_flag << 15; + + tegra_vde_writel(vde, value, vde->sxe, 0x48); + + value = 0x0C000000; + value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 24; + + tegra_vde_writel(vde, value, vde->sxe, 0x4C); + + value = 0x03800000; + value |= bitstream_data_size & GENMASK(19, 15); + + tegra_vde_writel(vde, value, vde->sxe, 0x68); + + tegra_vde_writel(vde, bitstream_data_addr, vde->sxe, 0x6C); + + if (vde->soc->supports_ref_pic_marking) + tegra_vde_writel(vde, vde->secure_bo->dma_addr, vde->sxe, 0x7c); + + value = 0x10000005; + value |= ctx->pic_width_in_mbs << 11; + value |= ctx->pic_height_in_mbs << 3; + + tegra_vde_writel(vde, value, vde->mbe, 0x80); + + value = 0x26800000; + value |= ctx->level_idc << 4; + value |= !ctx->baseline_profile << 1; + value |= !!ctx->direct_8x8_inference_flag; + + tegra_vde_writel(vde, value, vde->mbe, 0x80); + + tegra_vde_writel(vde, 0xF4000001, vde->mbe, 0x80); + tegra_vde_writel(vde, 0x20000000, vde->mbe, 0x80); + tegra_vde_writel(vde, 0xF4000101, vde->mbe, 0x80); + + value = 0x20000000; + value |= ctx->chroma_qp_index_offset << 8; + + tegra_vde_writel(vde, value, vde->mbe, 0x80); + + err = tegra_vde_setup_mbe_frame_idx(vde, + ctx->dpb_frames_nb - 1, + ctx->pic_order_cnt_type == 0); + if (err) { + dev_err(dev, "MBE frames setup failed %d\n", err); + return err; + } + + tegra_vde_mbe_set_0xa_reg(vde, 0, 0x000009FC); + tegra_vde_mbe_set_0xa_reg(vde, 2, 0x61DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 4, 0x62DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 6, 0x63DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 8, dpb_frames[0].aux_addr); + + value = 0xFC000000; + value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 2; + + if (!ctx->baseline_profile) + value |= !!(dpb_frames[0].flags & FLAG_REFERENCE) << 1; + + tegra_vde_writel(vde, value, vde->mbe, 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) { + dev_err(dev, "MBE programming failed %d\n", err); + return err; + } + + return 0; +} + +static void tegra_vde_decode_frame(struct tegra_vde *vde, + unsigned int macroblocks_nb) +{ + reinit_completion(&vde->decode_completion); + + tegra_vde_writel(vde, 0x00000001, vde->bsev, 0x8C); + tegra_vde_writel(vde, 0x20000000 | (macroblocks_nb - 1), + vde->sxe, 0x00); +} + +static int tegra_vde_validate_h264_ctx(struct device *dev, + struct tegra_vde_h264_decoder_ctx *ctx) +{ + if (ctx->dpb_frames_nb == 0 || ctx->dpb_frames_nb > 17) { + dev_err(dev, "Bad DPB size %u\n", ctx->dpb_frames_nb); + return -EINVAL; + } + + if (ctx->level_idc > 15) { + dev_err(dev, "Bad level value %u\n", ctx->level_idc); + return -EINVAL; + } + + if (ctx->pic_init_qp > 52) { + dev_err(dev, "Bad pic_init_qp value %u\n", ctx->pic_init_qp); + return -EINVAL; + } + + if (ctx->log2_max_pic_order_cnt_lsb > 16) { + dev_err(dev, "Bad log2_max_pic_order_cnt_lsb value %u\n", + ctx->log2_max_pic_order_cnt_lsb); + return -EINVAL; + } + + if (ctx->log2_max_frame_num > 16) { + dev_err(dev, "Bad log2_max_frame_num value %u\n", + ctx->log2_max_frame_num); + return -EINVAL; + } + + if (ctx->chroma_qp_index_offset > 31) { + dev_err(dev, "Bad chroma_qp_index_offset value %u\n", + ctx->chroma_qp_index_offset); + return -EINVAL; + } + + if (ctx->pic_order_cnt_type > 2) { + dev_err(dev, "Bad pic_order_cnt_type value %u\n", + ctx->pic_order_cnt_type); + return -EINVAL; + } + + if (ctx->num_ref_idx_l0_active_minus1 > 15) { + dev_err(dev, "Bad num_ref_idx_l0_active_minus1 value %u\n", + ctx->num_ref_idx_l0_active_minus1); + return -EINVAL; + } + + if (ctx->num_ref_idx_l1_active_minus1 > 15) { + dev_err(dev, "Bad num_ref_idx_l1_active_minus1 value %u\n", + ctx->num_ref_idx_l1_active_minus1); + return -EINVAL; + } + + if (!ctx->pic_width_in_mbs || ctx->pic_width_in_mbs > 127) { + dev_err(dev, "Bad pic_width_in_mbs value %u\n", + ctx->pic_width_in_mbs); + return -EINVAL; + } + + if (!ctx->pic_height_in_mbs || ctx->pic_height_in_mbs > 127) { + dev_err(dev, "Bad pic_height_in_mbs value %u\n", + ctx->pic_height_in_mbs); + return -EINVAL; + } + + return 0; +} + +static int tegra_vde_decode_begin(struct tegra_vde *vde, + struct tegra_vde_h264_decoder_ctx *ctx, + struct tegra_video_frame *dpb_frames, + dma_addr_t bitstream_data_addr, + size_t bitstream_data_size) +{ + struct device *dev = vde->dev; + unsigned int macroblocks_nb; + int err; + + err = mutex_lock_interruptible(&vde->lock); + if (err) + return err; + + err = pm_runtime_resume_and_get(dev); + if (err < 0) + goto unlock; + + /* + * We rely on the VDE registers reset value, otherwise VDE + * causes bus lockup. + */ + err = reset_control_assert(vde->rst_mc); + if (err) { + dev_err(dev, "DEC start: Failed to assert MC reset: %d\n", + err); + goto put_runtime_pm; + } + + err = reset_control_reset(vde->rst); + if (err) { + dev_err(dev, "DEC start: Failed to reset HW: %d\n", err); + goto put_runtime_pm; + } + + err = reset_control_deassert(vde->rst_mc); + if (err) { + dev_err(dev, "DEC start: Failed to deassert MC reset: %d\n", + err); + goto put_runtime_pm; + } + + macroblocks_nb = ctx->pic_width_in_mbs * ctx->pic_height_in_mbs; + + err = tegra_vde_setup_hw_context(vde, ctx, dpb_frames, + bitstream_data_addr, + bitstream_data_size, + macroblocks_nb); + if (err) + goto put_runtime_pm; + + tegra_vde_decode_frame(vde, macroblocks_nb); + + return 0; + +put_runtime_pm: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + +unlock: + mutex_unlock(&vde->lock); + + return err; +} + +static void tegra_vde_decode_abort(struct tegra_vde *vde) +{ + struct device *dev = vde->dev; + int err; + + /* + * At first reset memory client to avoid resetting VDE HW in the + * middle of DMA which could result into memory corruption or hang + * the whole system. + */ + err = reset_control_assert(vde->rst_mc); + if (err) + dev_err(dev, "DEC end: Failed to assert MC reset: %d\n", err); + + err = reset_control_assert(vde->rst); + if (err) + dev_err(dev, "DEC end: Failed to assert HW reset: %d\n", err); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + mutex_unlock(&vde->lock); +} + +static int tegra_vde_decode_end(struct tegra_vde *vde) +{ + unsigned int read_bytes, macroblocks_nb; + struct device *dev = vde->dev; + dma_addr_t bsev_ptr; + long timeout; + int ret; + + timeout = wait_for_completion_interruptible_timeout( + &vde->decode_completion, msecs_to_jiffies(1000)); + if (timeout == 0) { + bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10); + macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF; + read_bytes = bsev_ptr ? bsev_ptr - vde->bitstream_data_addr : 0; + + dev_err(dev, "Decoding failed: read 0x%X bytes, %u macroblocks parsed\n", + read_bytes, macroblocks_nb); + + ret = -EIO; + } else if (timeout < 0) { + ret = timeout; + } else { + ret = 0; + } + + tegra_vde_decode_abort(vde); + + return ret; +} + +static struct vb2_buffer *get_ref_buf(struct tegra_ctx *ctx, + struct vb2_v4l2_buffer *dst, + unsigned int dpb_idx) +{ + const struct v4l2_h264_dpb_entry *dpb = ctx->h264.decode_params->dpb; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + int buf_idx = -1; + + if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) + buf_idx = vb2_find_timestamp(cap_q, + dpb[dpb_idx].reference_ts, 0); + + /* + * If a DPB entry is unused or invalid, address of current destination + * buffer is returned. + */ + if (buf_idx < 0) + return &dst->vb2_buf; + + return vb2_get_buffer(cap_q, buf_idx); +} + +static int tegra_vde_validate_vb_size(struct tegra_ctx *ctx, + struct vb2_buffer *vb, + unsigned int plane_id, + size_t min_size) +{ + u64 offset = vb->planes[plane_id].data_offset; + struct device *dev = ctx->vde->dev; + + if (offset + min_size > vb2_plane_size(vb, plane_id)) { + dev_err(dev, "Too small plane[%u] size %lu @0x%llX, should be at least %zu\n", + plane_id, vb2_plane_size(vb, plane_id), offset, min_size); + return -EINVAL; + } + + return 0; +} + +static int tegra_vde_h264_setup_frame(struct tegra_ctx *ctx, + struct tegra_vde_h264_decoder_ctx *h264, + struct v4l2_h264_reflist_builder *b, + struct vb2_buffer *vb, + unsigned int ref_id, + unsigned int id) +{ + struct v4l2_pix_format_mplane *pixfmt = &ctx->decoded_fmt.fmt.pix_mp; + struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); + struct tegra_ctx_h264 *h = &ctx->h264; + struct tegra_vde *vde = ctx->vde; + struct device *dev = vde->dev; + unsigned int cstride, lstride; + unsigned int flags = 0; + size_t lsize, csize; + int err, frame_num; + + lsize = h264->pic_width_in_mbs * 16 * h264->pic_height_in_mbs * 16; + csize = h264->pic_width_in_mbs * 8 * h264->pic_height_in_mbs * 8; + lstride = pixfmt->plane_fmt[0].bytesperline; + cstride = pixfmt->plane_fmt[1].bytesperline; + + err = tegra_vde_validate_vb_size(ctx, vb, 0, lsize); + if (err) + return err; + + err = tegra_vde_validate_vb_size(ctx, vb, 1, csize); + if (err) + return err; + + err = tegra_vde_validate_vb_size(ctx, vb, 2, csize); + if (err) + return err; + + if (!tb->aux || tb->aux->size < csize) { + dev_err(dev, "Too small aux size %zd, should be at least %zu\n", + tb->aux ? tb->aux->size : -1, csize); + return -EINVAL; + } + + if (id == 0) { + frame_num = h->decode_params->frame_num; + + if (h->decode_params->nal_ref_idc) + flags |= FLAG_REFERENCE; + } else { + frame_num = b->refs[ref_id].frame_num; + } + + if (tb->b_frame) + flags |= FLAG_B_FRAME; + + vde->frames[id].flags = flags; + vde->frames[id].y_addr = tb->dma_addr[0]; + vde->frames[id].cb_addr = tb->dma_addr[1]; + vde->frames[id].cr_addr = tb->dma_addr[2]; + vde->frames[id].aux_addr = tb->aux->dma_addr; + vde->frames[id].frame_num = frame_num & 0x7fffff; + vde->frames[id].luma_atoms_pitch = lstride / VDE_ATOM; + vde->frames[id].chroma_atoms_pitch = cstride / VDE_ATOM; + + return 0; +} + +static int tegra_vde_h264_setup_frames(struct tegra_ctx *ctx, + struct tegra_vde_h264_decoder_ctx *h264) +{ + struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + const struct v4l2_h264_dpb_entry *dpb = ctx->h264.decode_params->dpb; + struct tegra_m2m_buffer *tb = vb_to_tegra_buf(&dst->vb2_buf); + struct tegra_ctx_h264 *h = &ctx->h264; + struct v4l2_h264_reflist_builder b; + struct h264_reflists reflists; + struct vb2_buffer *ref; + unsigned int i; + u8 *dpb_id; + int err; + + /* + * Tegra hardware requires information about frame's type, assuming + * that frame consists of the same type slices. Userspace must tag + * frame's type appropriately. + * + * Decoding of a non-uniform frames isn't supported by hardware and + * require software preprocessing that we don't implement. Decoding + * is expected to fail in this case. Such video streams are rare in + * practice, so not a big deal. + * + * If userspace doesn't tell us frame's type, then we will try decode + * as-is. + */ + v4l2_m2m_buf_copy_metadata(src, dst, true); + + if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) + tb->b_frame = true; + else + tb->b_frame = false; + + err = tegra_vde_h264_setup_frame(ctx, h264, NULL, &dst->vb2_buf, 0, + h264->dpb_frames_nb++); + if (err) + return err; + + if (!(h->decode_params->flags & (V4L2_H264_DECODE_PARAM_FLAG_PFRAME | + V4L2_H264_DECODE_PARAM_FLAG_BFRAME))) + return 0; + + v4l2_h264_init_reflist_builder(&b, h->decode_params, h->sps, dpb); + + if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) { + v4l2_h264_build_b_ref_lists(&b, reflists.b0, reflists.b1); + dpb_id = reflists.b0; + } else { + v4l2_h264_build_p_ref_list(&b, reflists.p); + dpb_id = reflists.p; + } + + for (i = 0; i < b.num_valid; i++) { + ref = get_ref_buf(ctx, dst, dpb_id[i]); + + err = tegra_vde_h264_setup_frame(ctx, h264, &b, ref, dpb_id[i], + h264->dpb_frames_nb++); + if (err) + return err; + + if (b.refs[dpb_id[i]].pic_order_count < b.cur_pic_order_count) + h264->dpb_ref_frames_with_earlier_poc_nb++; + } + + return 0; +} + +static unsigned int to_tegra_vde_h264_level_idc(unsigned int level_idc) +{ + switch (level_idc) { + case 11: + return 2; + case 12: + return 3; + case 13: + return 4; + case 20: + return 5; + case 21: + return 6; + case 22: + return 7; + case 30: + return 8; + case 31: + return 9; + case 32: + return 10; + case 40: + return 11; + case 41: + return 12; + case 42: + return 13; + case 50: + return 14; + default: + break; + } + + return 15; +} + +static int tegra_vde_h264_setup_context(struct tegra_ctx *ctx, + struct tegra_vde_h264_decoder_ctx *h264) +{ + struct tegra_ctx_h264 *h = &ctx->h264; + struct tegra_vde *vde = ctx->vde; + struct device *dev = vde->dev; + int err; + + memset(h264, 0, sizeof(*h264)); + memset(vde->frames, 0, sizeof(vde->frames)); + + tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); + tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_SPS); + tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_PPS); + + /* CABAC unsupported by hardware, requires software preprocessing */ + if (h->pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) + return -EOPNOTSUPP; + + if (h->sps->profile_idc == 66) + h264->baseline_profile = 1; + + if (h->sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) + h264->direct_8x8_inference_flag = 1; + + if (h->pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) + h264->constrained_intra_pred_flag = 1; + + if (h->pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) + h264->deblocking_filter_control_present_flag = 1; + + if (h->pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT) + h264->pic_order_present_flag = 1; + + h264->level_idc = to_tegra_vde_h264_level_idc(h->sps->level_idc); + h264->log2_max_pic_order_cnt_lsb = h->sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + h264->log2_max_frame_num = h->sps->log2_max_frame_num_minus4 + 4; + h264->pic_order_cnt_type = h->sps->pic_order_cnt_type; + h264->pic_width_in_mbs = h->sps->pic_width_in_mbs_minus1 + 1; + h264->pic_height_in_mbs = h->sps->pic_height_in_map_units_minus1 + 1; + + h264->num_ref_idx_l0_active_minus1 = h->pps->num_ref_idx_l0_default_active_minus1; + h264->num_ref_idx_l1_active_minus1 = h->pps->num_ref_idx_l1_default_active_minus1; + h264->chroma_qp_index_offset = h->pps->chroma_qp_index_offset & 0x1f; + h264->pic_init_qp = h->pps->pic_init_qp_minus26 + 26; + + err = tegra_vde_h264_setup_frames(ctx, h264); + if (err) + return err; + + err = tegra_vde_validate_h264_ctx(dev, h264); + if (err) + return err; + + return 0; +} + +int tegra_vde_h264_decode_run(struct tegra_ctx *ctx) +{ + struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct tegra_m2m_buffer *bitstream = vb_to_tegra_buf(&src->vb2_buf); + size_t bitstream_size = vb2_get_plane_payload(&src->vb2_buf, 0); + struct tegra_vde_h264_decoder_ctx h264; + struct tegra_vde *vde = ctx->vde; + int err; + + err = tegra_vde_h264_setup_context(ctx, &h264); + if (err) + return err; + + err = tegra_vde_decode_begin(vde, &h264, vde->frames, + bitstream->dma_addr[0], + bitstream_size); + if (err) + return err; + + return 0; +} + +int tegra_vde_h264_decode_wait(struct tegra_ctx *ctx) +{ + return tegra_vde_decode_end(ctx->vde); +} diff --git a/drivers/staging/media/tegra-vde/iommu.c b/drivers/media/platform/nvidia/tegra-vde/iommu.c index adf8dc7ee25c..5521ed3e465f 100644 --- a/drivers/staging/media/tegra-vde/iommu.c +++ b/drivers/media/platform/nvidia/tegra-vde/iommu.c @@ -60,7 +60,7 @@ void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova) int tegra_vde_iommu_init(struct tegra_vde *vde) { - struct device *dev = vde->miscdev.parent; + struct device *dev = vde->dev; struct iova *iova; unsigned long order; unsigned long shift; diff --git a/drivers/staging/media/tegra-vde/trace.h b/drivers/media/platform/nvidia/tegra-vde/trace.h index e5714107db58..7853ab095ca4 100644 --- a/drivers/staging/media/tegra-vde/trace.h +++ b/drivers/media/platform/nvidia/tegra-vde/trace.h @@ -90,6 +90,6 @@ TRACE_EVENT(vde_ref_l1, /* This part must be outside protection */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../drivers/staging/media/tegra-vde +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/nvidia/tegra-vde #define TRACE_INCLUDE_FILE trace #include <trace/define_trace.h> diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c new file mode 100644 index 000000000000..bd8c207d5b54 --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c @@ -0,0 +1,1018 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NVIDIA Tegra Video decoder driver + * + * Copyright (C) 2019-2022 Dmitry Osipenko <digetx@gmail.com> + * + * Based on Cedrus driver by Bootlin. + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * + * Based on Rockchip driver by Collabora. + * Copyright (C) 2019 Boris Brezillon <boris.brezillon@collabora.com> + */ + +#include <linux/err.h> +#include <linux/slab.h> + +#include "vde.h" + +static const struct v4l2_ctrl_config ctrl_cfgs[] = { + { .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, + { .id = V4L2_CID_STATELESS_H264_SPS, }, + { .id = V4L2_CID_STATELESS_H264_PPS, }, + { + .id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + }, + { + .id = V4L2_CID_STATELESS_H264_START_CODE, + .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + }, +}; + +static inline struct tegra_ctx *fh_to_tegra_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct tegra_ctx, fh); +} + +static void tegra_set_control_data(struct tegra_ctx *ctx, void *data, u32 id) +{ + switch (id) { + case V4L2_CID_STATELESS_H264_DECODE_PARAMS: + ctx->h264.decode_params = data; + break; + case V4L2_CID_STATELESS_H264_SPS: + ctx->h264.sps = data; + break; + case V4L2_CID_STATELESS_H264_PPS: + ctx->h264.pps = data; + break; + } +} + +void tegra_vde_prepare_control_data(struct tegra_ctx *ctx, u32 id) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) { + if (ctx->ctrls[i]->id == id) { + tegra_set_control_data(ctx, ctx->ctrls[i]->p_cur.p, id); + return; + } + } + + tegra_set_control_data(ctx, NULL, id); +} + +static int tegra_queue_setup(struct vb2_queue *vq, + unsigned int *nbufs, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct tegra_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_format *f; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + f = &ctx->coded_fmt; + else + f = &ctx->decoded_fmt; + + if (*num_planes) { + if (*num_planes != f->fmt.pix_mp.num_planes) + return -EINVAL; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = f->fmt.pix_mp.num_planes; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int tegra_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static void __tegra_buf_cleanup(struct vb2_buffer *vb, unsigned int i) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tegra_ctx *ctx = vb2_get_drv_priv(vq); + struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); + + while (i--) { + if (tb->a[i]) { + tegra_vde_dmabuf_cache_unmap(ctx->vde, tb->a[i], true); + tb->a[i] = NULL; + } + + if (tb->iova[i]) { + tegra_vde_iommu_unmap(ctx->vde, tb->iova[i]); + tb->iova[i] = NULL; + } + } + + if (tb->aux) { + tegra_vde_free_bo(tb->aux); + tb->aux = NULL; + } +} + +static int tegra_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tegra_ctx *ctx = vb2_get_drv_priv(vq); + struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); + struct tegra_vde *vde = ctx->vde; + enum dma_data_direction dma_dir; + struct sg_table *sgt; + unsigned int i; + int err; + + if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb->num_planes > 1) { + /* + * Tegra decoder writes auxiliary data for I/P frames. + * This data is needed for decoding of B frames. + */ + err = tegra_vde_alloc_bo(vde, &tb->aux, DMA_FROM_DEVICE, + vb2_plane_size(vb, 1)); + if (err) + return err; + } + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + dma_dir = DMA_TO_DEVICE; + else + dma_dir = DMA_FROM_DEVICE; + + for (i = 0; i < vb->num_planes; i++) { + if (vq->memory == VB2_MEMORY_DMABUF) { + get_dma_buf(vb->planes[i].dbuf); + + err = tegra_vde_dmabuf_cache_map(vde, vb->planes[i].dbuf, + dma_dir, &tb->a[i], + &tb->dma_base[i]); + if (err) { + dma_buf_put(vb->planes[i].dbuf); + goto cleanup; + } + + continue; + } + + if (vde->domain) { + sgt = vb2_dma_sg_plane_desc(vb, i); + + err = tegra_vde_iommu_map(vde, sgt, &tb->iova[i], + vb2_plane_size(vb, i)); + if (err) + goto cleanup; + + tb->dma_base[i] = iova_dma_addr(&vde->iova, tb->iova[i]); + } else { + tb->dma_base[i] = vb2_dma_contig_plane_dma_addr(vb, i); + } + } + + return 0; + +cleanup: + __tegra_buf_cleanup(vb, i); + + return err; +} + +static void tegra_buf_cleanup(struct vb2_buffer *vb) +{ + __tegra_buf_cleanup(vb, vb->num_planes); +} + +static int tegra_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tegra_ctx *ctx = vb2_get_drv_priv(vq); + struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb); + size_t hw_align, hw_size, hw_payload, size, offset; + struct v4l2_pix_format_mplane *pixfmt; + unsigned int i; + void *vb_data; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + hw_align = BSEV_ALIGN; + pixfmt = &ctx->coded_fmt.fmt.pix_mp; + } else { + hw_align = FRAMEID_ALIGN; + pixfmt = &ctx->decoded_fmt.fmt.pix_mp; + } + + for (i = 0; i < vb->num_planes; i++) { + offset = vb->planes[i].data_offset; + + if (offset & (hw_align - 1)) + return -EINVAL; + + if (V4L2_TYPE_IS_CAPTURE(vq->type)) { + size = pixfmt->plane_fmt[i].sizeimage; + hw_payload = ALIGN(size, VDE_ATOM); + } else { + size = vb2_get_plane_payload(vb, i) - offset; + hw_payload = ALIGN(size + VDE_ATOM, SXE_BUFFER); + } + + hw_size = offset + hw_payload; + + if (vb2_plane_size(vb, i) < hw_size) + return -EINVAL; + + vb2_set_plane_payload(vb, i, hw_payload); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + vb_data = vb2_plane_vaddr(vb, i); + + /* + * Hardware requires zero-padding of coded data. + * Otherwise it will fail to parse the trailing + * data and abort the decoding. + */ + if (vb_data) + memset(vb_data + offset + size, 0, + hw_size - offset - size); + } + + tb->dma_addr[i] = tb->dma_base[i] + offset; + } + + switch (pixfmt->pixelformat) { + case V4L2_PIX_FMT_YVU420M: + swap(tb->dma_addr[1], tb->dma_addr[2]); + break; + } + + return 0; +} + +static void tegra_buf_queue(struct vb2_buffer *vb) +{ + struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void tegra_buf_request_complete(struct vb2_buffer *vb) +{ + struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); +} + +static int tegra_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + return 0; +} + +static void tegra_stop_streaming(struct vb2_queue *vq) +{ + struct tegra_ctx *ctx = vb2_get_drv_priv(vq); + + while (true) { + struct vb2_v4l2_buffer *vbuf; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!vbuf) + break; + + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops tegra_qops = { + .queue_setup = tegra_queue_setup, + .buf_init = tegra_buf_init, + .buf_cleanup = tegra_buf_cleanup, + .buf_prepare = tegra_buf_prepare, + .buf_queue = tegra_buf_queue, + .buf_out_validate = tegra_buf_out_validate, + .buf_request_complete = tegra_buf_request_complete, + .start_streaming = tegra_start_streaming, + .stop_streaming = tegra_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int tegra_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct tegra_ctx *ctx = priv; + struct tegra_vde *vde = ctx->vde; + const struct vb2_mem_ops *mem_ops; + unsigned long dma_attrs; + int err; + + /* + * TODO: Switch to use of vb2_dma_contig_memops uniformly once we + * will add IOMMU_DOMAIN support for video decoder to tegra-smmu + * driver. For now we need to stick with SG ops in order to be able + * to get SGT table easily. This is suboptimal since SG mappings are + * wasting CPU cache and we don't need that caching. + */ + if (vde->domain) + mem_ops = &vb2_dma_sg_memops; + else + mem_ops = &vb2_dma_contig_memops; + + dma_attrs = DMA_ATTR_WRITE_COMBINE; + + src_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->supports_requests = true; + src_vq->requires_requests = true; + src_vq->lock = &vde->v4l2_lock; + src_vq->dma_attrs = dma_attrs; + src_vq->mem_ops = mem_ops; + src_vq->ops = &tegra_qops; + src_vq->drv_priv = ctx; + src_vq->dev = vde->dev; + + err = vb2_queue_init(src_vq); + if (err) { + v4l2_err(&vde->v4l2_dev, + "failed to initialize src queue: %d\n", err); + return err; + } + + /* + * We may need to zero the end of bitstream in kernel if userspace + * doesn't do that, hence kmap is needed for the coded data. It's not + * needed for framebuffers. + */ + dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; + + dst_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->lock = &vde->v4l2_lock; + dst_vq->dma_attrs = dma_attrs; + dst_vq->mem_ops = mem_ops; + dst_vq->ops = &tegra_qops; + dst_vq->drv_priv = ctx; + dst_vq->dev = vde->dev; + + err = vb2_queue_init(dst_vq); + if (err) { + v4l2_err(&vde->v4l2_dev, + "failed to initialize dst queue: %d\n", err); + return err; + } + + return 0; +} + +static void tegra_reset_fmt(struct tegra_ctx *ctx, struct v4l2_format *f, + u32 fourcc) +{ + memset(f, 0, sizeof(*f)); + f->fmt.pix_mp.pixelformat = fourcc; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; +} + +static void tegra_reset_coded_fmt(struct tegra_ctx *ctx) +{ + const struct tegra_vde_soc *soc = ctx->vde->soc; + struct v4l2_format *f = &ctx->coded_fmt; + + ctx->coded_fmt_desc = &soc->coded_fmts[0]; + tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); + + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; + f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; +} + +static void tegra_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height) +{ + const struct v4l2_format_info *info = v4l2_format_info(pixelformat); + struct v4l2_plane_pix_format *plane; + unsigned int i; + + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + pixfmt->width = width; + pixfmt->height = height; + pixfmt->pixelformat = pixelformat; + pixfmt->num_planes = info->mem_planes; + + for (i = 0; i < pixfmt->num_planes; i++) { + unsigned int hdiv = (i == 0) ? 1 : 2; + unsigned int vdiv = (i == 0) ? 1 : 2; + + /* + * VDE is connected to Graphics Memory using 128bit port, + * all memory accesses are made using 16B atoms. + * + * V4L requires Cb/Cr strides to be exactly half of the + * Y stride, hence we're aligning Y to 16B x 2. + */ + plane = &pixfmt->plane_fmt[i]; + plane->bytesperline = ALIGN(width, VDE_ATOM * 2) / hdiv; + plane->sizeimage = plane->bytesperline * height / vdiv; + } + + break; + } +} + +static void tegra_reset_decoded_fmt(struct tegra_ctx *ctx) +{ + struct v4l2_format *f = &ctx->decoded_fmt; + + tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + tegra_fill_pixfmt_mp(&f->fmt.pix_mp, + ctx->coded_fmt_desc->decoded_fmts[0], + ctx->coded_fmt.fmt.pix_mp.width, + ctx->coded_fmt.fmt.pix_mp.height); +} + +static void tegra_job_finish(struct tegra_ctx *ctx, + enum vb2_buffer_state result) +{ + v4l2_m2m_buf_done_and_job_finish(ctx->vde->m2m, ctx->fh.m2m_ctx, + result); +} + +static void tegra_decode_complete(struct work_struct *work) +{ + struct tegra_ctx *ctx = container_of(work, struct tegra_ctx, work); + int err; + + err = ctx->coded_fmt_desc->decode_wait(ctx); + if (err) + tegra_job_finish(ctx, VB2_BUF_STATE_ERROR); + else + tegra_job_finish(ctx, VB2_BUF_STATE_DONE); +} + +static int tegra_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->bus_info, "platform:tegra-vde", sizeof(cap->bus_info)); + strscpy(cap->driver, "tegra-vde", sizeof(cap->driver)); + strscpy(cap->card, "tegra-vde", sizeof(cap->card)); + + return 0; +} + +static int tegra_enum_decoded_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + + if (WARN_ON(!ctx->coded_fmt_desc)) + return -EINVAL; + + if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts) + return -EINVAL; + + f->pixelformat = ctx->coded_fmt_desc->decoded_fmts[f->index]; + + return 0; +} + +static int tegra_g_decoded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + + *f = ctx->decoded_fmt; + return 0; +} + +static int tegra_try_decoded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + const struct tegra_coded_fmt_desc *coded_desc; + unsigned int i; + + /* + * The codec context should point to a coded format desc, if the format + * on the coded end has not been set yet, it should point to the + * default value. + */ + coded_desc = ctx->coded_fmt_desc; + if (WARN_ON(!coded_desc)) + return -EINVAL; + + if (!coded_desc->num_decoded_fmts) + return -EINVAL; + + for (i = 0; i < coded_desc->num_decoded_fmts; i++) { + if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat) + break; + } + + if (i == coded_desc->num_decoded_fmts) + pix_mp->pixelformat = coded_desc->decoded_fmts[0]; + + /* always apply the frmsize constraint of the coded end */ + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &coded_desc->frmsize); + + tegra_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + pix_mp->field = V4L2_FIELD_NONE; + + return 0; +} + +static int tegra_s_decoded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + struct vb2_queue *vq; + int err; + + /* change not allowed if queue is busy */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + err = tegra_try_decoded_fmt(file, priv, f); + if (err) + return err; + + ctx->decoded_fmt = *f; + + return 0; +} + +static int tegra_enum_coded_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + const struct tegra_vde_soc *soc = ctx->vde->soc; + + if (f->index >= soc->num_coded_fmts) + return -EINVAL; + + f->pixelformat = soc->coded_fmts[f->index].fourcc; + + return 0; +} + +static int tegra_g_coded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + + *f = ctx->coded_fmt; + return 0; +} + +static const struct tegra_coded_fmt_desc * +tegra_find_coded_fmt_desc(struct tegra_ctx *ctx, u32 fourcc) +{ + const struct tegra_vde_soc *soc = ctx->vde->soc; + unsigned int i; + + for (i = 0; i < soc->num_coded_fmts; i++) { + if (soc->coded_fmts[i].fourcc == fourcc) + return &soc->coded_fmts[i]; + } + + return NULL; +} + +static int tegra_try_coded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + const struct tegra_vde_soc *soc = ctx->vde->soc; + int size = pix_mp->plane_fmt[0].sizeimage; + const struct tegra_coded_fmt_desc *desc; + + desc = tegra_find_coded_fmt_desc(ctx, pix_mp->pixelformat); + if (!desc) { + pix_mp->pixelformat = soc->coded_fmts[0].fourcc; + desc = &soc->coded_fmts[0]; + } + + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &desc->frmsize); + + pix_mp->plane_fmt[0].sizeimage = max(ALIGN(size, SXE_BUFFER), SZ_2M); + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->num_planes = 1; + + return 0; +} + +static int tegra_s_coded_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct tegra_coded_fmt_desc *desc; + struct vb2_queue *peer_vq, *vq; + struct v4l2_format *cap_fmt; + int err; + + /* + * In order to support dynamic resolution change, the decoder admits + * a resolution change, as long as the pixelformat remains. Can't be + * done if streaming. + */ + vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_streaming(vq) || + (vb2_is_busy(vq) && + f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat)) + return -EBUSY; + + /* + * Since format change on the OUTPUT queue will reset the CAPTURE + * queue, we can't allow doing so when the CAPTURE queue has buffers + * allocated. + */ + peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(peer_vq)) + return -EBUSY; + + err = tegra_try_coded_fmt(file, priv, f); + if (err) + return err; + + desc = tegra_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat); + if (!desc) + return -EINVAL; + + ctx->coded_fmt_desc = desc; + ctx->coded_fmt = *f; + + /* + * Current decoded format might have become invalid with newly + * selected codec, so reset it to default just to be safe and + * keep internal driver state sane. User is mandated to set + * the decoded format again after we return, so we don't need + * anything smarter. + * + * Note that this will propagates any size changes to the decoded format. + */ + tegra_reset_decoded_fmt(ctx); + + /* propagate colorspace information to capture */ + cap_fmt = &ctx->decoded_fmt; + cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; + cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; + cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + + return 0; +} + +static int tegra_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct tegra_ctx *ctx = fh_to_tegra_ctx(priv); + const struct tegra_coded_fmt_desc *fmt; + + if (fsize->index) + return -EINVAL; + + fmt = tegra_find_coded_fmt_desc(ctx, fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->frmsize; + + return 0; +} + +static const struct v4l2_ioctl_ops tegra_v4l2_ioctl_ops = { + .vidioc_querycap = tegra_querycap, + .vidioc_enum_framesizes = tegra_enum_framesizes, + + .vidioc_try_fmt_vid_out_mplane = tegra_try_coded_fmt, + .vidioc_g_fmt_vid_out_mplane = tegra_g_coded_fmt, + .vidioc_s_fmt_vid_out_mplane = tegra_s_coded_fmt, + .vidioc_enum_fmt_vid_out = tegra_enum_coded_fmt, + + .vidioc_try_fmt_vid_cap_mplane = tegra_try_decoded_fmt, + .vidioc_g_fmt_vid_cap_mplane = tegra_g_decoded_fmt, + .vidioc_s_fmt_vid_cap_mplane = tegra_s_decoded_fmt, + .vidioc_enum_fmt_vid_cap = tegra_enum_decoded_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int tegra_init_ctrls(struct tegra_ctx *ctx) +{ + unsigned int i; + int err; + + err = v4l2_ctrl_handler_init(&ctx->hdl, ARRAY_SIZE(ctrl_cfgs)); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) { + ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->hdl, &ctrl_cfgs[i], + NULL); + if (ctx->hdl.error) { + err = ctx->hdl.error; + goto free_ctrls; + } + } + + err = v4l2_ctrl_handler_setup(&ctx->hdl); + if (err) + goto free_ctrls; + + ctx->fh.ctrl_handler = &ctx->hdl; + + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(&ctx->hdl); + + return err; +} + +static int tegra_init_m2m(struct tegra_ctx *ctx) +{ + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ctx->vde->m2m, + ctx, tegra_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) + return PTR_ERR(ctx->fh.m2m_ctx); + + return 0; +} + +static int tegra_open(struct file *file) +{ + struct tegra_vde *vde = video_drvdata(file); + struct tegra_ctx *ctx; + int err; + + ctx = kzalloc(offsetof(struct tegra_ctx, ctrls[ARRAY_SIZE(ctrl_cfgs)]), + GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->vde = vde; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + INIT_WORK(&ctx->work, tegra_decode_complete); + + err = tegra_init_ctrls(ctx); + if (err) { + v4l2_err(&vde->v4l2_dev, "failed to add controls: %d\n", err); + goto free_ctx; + } + + err = tegra_init_m2m(ctx); + if (err) { + v4l2_err(&vde->v4l2_dev, "failed to initialize m2m: %d\n", err); + goto free_ctrls; + } + + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + tegra_reset_coded_fmt(ctx); + tegra_try_coded_fmt(file, file->private_data, &ctx->coded_fmt); + + tegra_reset_decoded_fmt(ctx); + tegra_try_decoded_fmt(file, file->private_data, &ctx->decoded_fmt); + + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(&ctx->hdl); +free_ctx: + kfree(ctx); + + return err; +} + +static int tegra_release(struct file *file) +{ + struct v4l2_fh *fh = file->private_data; + struct tegra_ctx *ctx = fh_to_tegra_ctx(fh); + struct tegra_vde *vde = ctx->vde; + + v4l2_fh_del(fh); + v4l2_m2m_ctx_release(fh->m2m_ctx); + v4l2_ctrl_handler_free(&ctx->hdl); + v4l2_fh_exit(fh); + kfree(ctx); + + tegra_vde_dmabuf_cache_unmap_sync(vde); + + return 0; +} + +static const struct v4l2_file_operations tegra_v4l2_fops = { + .owner = THIS_MODULE, + .open = tegra_open, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, + .release = tegra_release, + .unlocked_ioctl = video_ioctl2, +}; + +static void tegra_device_run(void *priv) +{ + struct tegra_ctx *ctx = priv; + struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct media_request *src_req = src->vb2_buf.req_obj.req; + int err; + + v4l2_ctrl_request_setup(src_req, &ctx->hdl); + + err = ctx->coded_fmt_desc->decode_run(ctx); + + v4l2_ctrl_request_complete(src_req, &ctx->hdl); + + if (err) + tegra_job_finish(ctx, VB2_BUF_STATE_ERROR); + else + queue_work(ctx->vde->wq, &ctx->work); +} + +static const struct v4l2_m2m_ops tegra_v4l2_m2m_ops = { + .device_run = tegra_device_run, +}; + +static int tegra_request_validate(struct media_request *req) +{ + unsigned int count; + + count = vb2_request_buffer_cnt(req); + if (!count) + return -ENOENT; + else if (count > 1) + return -EINVAL; + + return vb2_request_validate(req); +} + +static const struct media_device_ops tegra_media_device_ops = { + .req_validate = tegra_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +int tegra_vde_v4l2_init(struct tegra_vde *vde) +{ + struct device *dev = vde->dev; + int err; + + mutex_init(&vde->v4l2_lock); + media_device_init(&vde->mdev); + video_set_drvdata(&vde->vdev, vde); + + vde->vdev.lock = &vde->v4l2_lock, + vde->vdev.fops = &tegra_v4l2_fops, + vde->vdev.vfl_dir = VFL_DIR_M2M, + vde->vdev.release = video_device_release_empty, + vde->vdev.v4l2_dev = &vde->v4l2_dev; + vde->vdev.ioctl_ops = &tegra_v4l2_ioctl_ops, + vde->vdev.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, + + vde->v4l2_dev.mdev = &vde->mdev; + vde->mdev.ops = &tegra_media_device_ops; + vde->mdev.dev = dev; + + strscpy(vde->mdev.model, "tegra-vde", sizeof(vde->mdev.model)); + strscpy(vde->vdev.name, "tegra-vde", sizeof(vde->vdev.name)); + strscpy(vde->mdev.bus_info, "platform:tegra-vde", + sizeof(vde->mdev.bus_info)); + + vde->wq = create_workqueue("tegra-vde"); + if (!vde->wq) + return -ENOMEM; + + err = media_device_register(&vde->mdev); + if (err) { + dev_err(dev, "failed to register media device: %d\n", err); + goto clean_up_media_device; + } + + err = v4l2_device_register(dev, &vde->v4l2_dev); + if (err) { + dev_err(dev, "failed to register v4l2 device: %d\n", err); + goto unreg_media_device; + } + + err = video_register_device(&vde->vdev, VFL_TYPE_VIDEO, -1); + if (err) { + dev_err(dev, "failed to register video device: %d\n", err); + goto unreg_v4l2; + } + + vde->m2m = v4l2_m2m_init(&tegra_v4l2_m2m_ops); + err = PTR_ERR_OR_ZERO(vde->m2m); + if (err) { + dev_err(dev, "failed to initialize m2m device: %d\n", err); + goto unreg_video_device; + } + + err = v4l2_m2m_register_media_controller(vde->m2m, &vde->vdev, + MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (err) { + dev_err(dev, "failed to register media controller: %d\n", err); + goto release_m2m; + } + + v4l2_info(&vde->v4l2_dev, "v4l2 device registered as /dev/video%d\n", + vde->vdev.num); + + return 0; + +release_m2m: + v4l2_m2m_release(vde->m2m); +unreg_video_device: + video_unregister_device(&vde->vdev); +unreg_v4l2: + v4l2_device_unregister(&vde->v4l2_dev); +unreg_media_device: + media_device_unregister(&vde->mdev); +clean_up_media_device: + media_device_cleanup(&vde->mdev); + + destroy_workqueue(vde->wq); + + return err; +} + +void tegra_vde_v4l2_deinit(struct tegra_vde *vde) +{ + v4l2_m2m_unregister_media_controller(vde->m2m); + v4l2_m2m_release(vde->m2m); + + video_unregister_device(&vde->vdev); + v4l2_device_unregister(&vde->v4l2_dev); + + media_device_unregister(&vde->mdev); + media_device_cleanup(&vde->mdev); + + destroy_workqueue(vde->wq); +} diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c new file mode 100644 index 000000000000..f3e863a94c5a --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/vde.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NVIDIA Tegra Video decoder driver + * + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> + * + */ + +#include <linux/clk.h> +#include <linux/dma-buf.h> +#include <linux/genalloc.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <soc/tegra/common.h> +#include <soc/tegra/pmc.h> + +#include "vde.h" + +#define CREATE_TRACE_POINTS +#include "trace.h" + +void tegra_vde_writel(struct tegra_vde *vde, u32 value, + void __iomem *base, u32 offset) +{ + trace_vde_writel(vde, base, offset, value); + + writel_relaxed(value, base + offset); +} + +u32 tegra_vde_readl(struct tegra_vde *vde, void __iomem *base, u32 offset) +{ + u32 value = readl_relaxed(base + offset); + + trace_vde_readl(vde, base, offset, value); + + return value; +} + +void tegra_vde_set_bits(struct tegra_vde *vde, u32 mask, + void __iomem *base, u32 offset) +{ + u32 value = tegra_vde_readl(vde, base, offset); + + tegra_vde_writel(vde, value | mask, base, offset); +} + +int tegra_vde_alloc_bo(struct tegra_vde *vde, + struct tegra_vde_bo **ret_bo, + enum dma_data_direction dma_dir, + size_t size) +{ + struct device *dev = vde->dev; + struct tegra_vde_bo *bo; + int err; + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return -ENOMEM; + + bo->vde = vde; + bo->size = size; + bo->dma_dir = dma_dir; + bo->dma_attrs = DMA_ATTR_WRITE_COMBINE | + DMA_ATTR_NO_KERNEL_MAPPING; + + if (!vde->domain) + bo->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + + bo->dma_cookie = dma_alloc_attrs(dev, bo->size, &bo->dma_handle, + GFP_KERNEL, bo->dma_attrs); + if (!bo->dma_cookie) { + dev_err(dev, "Failed to allocate DMA buffer of size: %zu\n", + bo->size); + err = -ENOMEM; + goto free_bo; + } + + err = dma_get_sgtable_attrs(dev, &bo->sgt, bo->dma_cookie, + bo->dma_handle, bo->size, bo->dma_attrs); + if (err) { + dev_err(dev, "Failed to get DMA buffer SG table: %d\n", err); + goto free_attrs; + } + + err = dma_map_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); + if (err) { + dev_err(dev, "Failed to map DMA buffer SG table: %d\n", err); + goto free_table; + } + + if (vde->domain) { + err = tegra_vde_iommu_map(vde, &bo->sgt, &bo->iova, bo->size); + if (err) { + dev_err(dev, "Failed to map DMA buffer IOVA: %d\n", err); + goto unmap_sgtable; + } + + bo->dma_addr = iova_dma_addr(&vde->iova, bo->iova); + } else { + bo->dma_addr = sg_dma_address(bo->sgt.sgl); + } + + *ret_bo = bo; + + return 0; + +unmap_sgtable: + dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); +free_table: + sg_free_table(&bo->sgt); +free_attrs: + dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle, + bo->dma_attrs); +free_bo: + kfree(bo); + + return err; +} + +void tegra_vde_free_bo(struct tegra_vde_bo *bo) +{ + struct tegra_vde *vde = bo->vde; + struct device *dev = vde->dev; + + if (vde->domain) + tegra_vde_iommu_unmap(vde, bo->iova); + + dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); + + sg_free_table(&bo->sgt); + + dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle, + bo->dma_attrs); + kfree(bo); +} + +static irqreturn_t tegra_vde_isr(int irq, void *data) +{ + struct tegra_vde *vde = data; + + if (completion_done(&vde->decode_completion)) + return IRQ_NONE; + + tegra_vde_set_bits(vde, 0, vde->frameid, 0x208); + complete(&vde->decode_completion); + + return IRQ_HANDLED; +} + +static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + if (!dev->pm_domain) { + err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC); + if (err) { + dev_err(dev, "Failed to power down HW: %d\n", err); + return err; + } + } + + clk_disable_unprepare(vde->clk); + reset_control_release(vde->rst); + reset_control_release(vde->rst_mc); + + return 0; +} + +static __maybe_unused int tegra_vde_runtime_resume(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + err = reset_control_acquire(vde->rst_mc); + if (err) { + dev_err(dev, "Failed to acquire mc reset: %d\n", err); + return err; + } + + err = reset_control_acquire(vde->rst); + if (err) { + dev_err(dev, "Failed to acquire reset: %d\n", err); + goto release_mc_reset; + } + + if (!dev->pm_domain) { + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC, + vde->clk, vde->rst); + if (err) { + dev_err(dev, "Failed to power up HW : %d\n", err); + goto release_reset; + } + } else { + /* + * tegra_powergate_sequence_power_up() leaves clocks enabled, + * while GENPD not. + */ + err = clk_prepare_enable(vde->clk); + if (err) { + dev_err(dev, "Failed to enable clock: %d\n", err); + goto release_reset; + } + } + + return 0; + +release_reset: + reset_control_release(vde->rst); +release_mc_reset: + reset_control_release(vde->rst_mc); + + return err; +} + +static int tegra_vde_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra_vde *vde; + int irq, err; + + vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL); + if (!vde) + return -ENOMEM; + + platform_set_drvdata(pdev, vde); + + vde->soc = of_device_get_match_data(&pdev->dev); + vde->dev = dev; + + vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe"); + if (IS_ERR(vde->sxe)) + return PTR_ERR(vde->sxe); + + vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev"); + if (IS_ERR(vde->bsev)) + return PTR_ERR(vde->bsev); + + vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe"); + if (IS_ERR(vde->mbe)) + return PTR_ERR(vde->mbe); + + vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe"); + if (IS_ERR(vde->ppe)) + return PTR_ERR(vde->ppe); + + vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce"); + if (IS_ERR(vde->mce)) + return PTR_ERR(vde->mce); + + vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe"); + if (IS_ERR(vde->tfe)) + return PTR_ERR(vde->tfe); + + vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb"); + if (IS_ERR(vde->ppb)) + return PTR_ERR(vde->ppb); + + vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma"); + if (IS_ERR(vde->vdma)) + return PTR_ERR(vde->vdma); + + vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid"); + if (IS_ERR(vde->frameid)) + return PTR_ERR(vde->frameid); + + vde->clk = devm_clk_get(dev, NULL); + if (IS_ERR(vde->clk)) { + err = PTR_ERR(vde->clk); + dev_err(dev, "Could not get VDE clk %d\n", err); + return err; + } + + vde->rst = devm_reset_control_get_exclusive_released(dev, NULL); + if (IS_ERR(vde->rst)) { + err = PTR_ERR(vde->rst); + dev_err(dev, "Could not get VDE reset %d\n", err); + return err; + } + + vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc"); + if (IS_ERR(vde->rst_mc)) { + err = PTR_ERR(vde->rst_mc); + dev_err(dev, "Could not get MC reset %d\n", err); + return err; + } + + irq = platform_get_irq_byname(pdev, "sync-token"); + if (irq < 0) + return irq; + + err = devm_request_irq(dev, irq, tegra_vde_isr, 0, + dev_name(dev), vde); + if (err) { + dev_err(dev, "Could not request IRQ %d\n", err); + return err; + } + + err = devm_tegra_core_dev_init_opp_table_common(dev); + if (err) { + dev_err(dev, "Could initialize OPP table %d\n", err); + return err; + } + + vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0); + if (!vde->iram_pool) { + dev_err(dev, "Could not get IRAM pool\n"); + return -EPROBE_DEFER; + } + + vde->iram = gen_pool_dma_alloc(vde->iram_pool, + gen_pool_size(vde->iram_pool), + &vde->iram_lists_addr); + if (!vde->iram) { + dev_err(dev, "Could not reserve IRAM\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&vde->map_list); + mutex_init(&vde->map_lock); + mutex_init(&vde->lock); + init_completion(&vde->decode_completion); + + err = tegra_vde_iommu_init(vde); + if (err) { + dev_err(dev, "Failed to initialize IOMMU: %d\n", err); + goto err_gen_free; + } + + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 300); + + /* + * VDE partition may be left ON after bootloader, hence let's + * power-cycle it in order to put hardware into a predictable lower + * power state. + */ + err = pm_runtime_resume_and_get(dev); + if (err) + goto err_pm_runtime; + + pm_runtime_put(dev); + + err = tegra_vde_alloc_bo(vde, &vde->secure_bo, DMA_FROM_DEVICE, 4096); + if (err) { + dev_err(dev, "Failed to allocate secure BO: %d\n", err); + goto err_pm_runtime; + } + + err = tegra_vde_v4l2_init(vde); + if (err) { + dev_err(dev, "Failed to initialize V4L2: %d\n", err); + goto err_free_secure_bo; + } + + return 0; + +err_free_secure_bo: + tegra_vde_free_bo(vde->secure_bo); +err_pm_runtime: + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + + tegra_vde_iommu_deinit(vde); + +err_gen_free: + gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, + gen_pool_size(vde->iram_pool)); + + return err; +} + +static int tegra_vde_remove(struct platform_device *pdev) +{ + struct tegra_vde *vde = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + tegra_vde_v4l2_deinit(vde); + tegra_vde_free_bo(vde->secure_bo); + + /* + * As it increments RPM usage_count even on errors, we don't need to + * check the returned code here. + */ + pm_runtime_get_sync(dev); + + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + + /* + * Balance RPM state, the VDE power domain is left ON and hardware + * is clock-gated. It's safe to reboot machine now. + */ + pm_runtime_put_noidle(dev); + clk_disable_unprepare(vde->clk); + + tegra_vde_dmabuf_cache_unmap_all(vde); + tegra_vde_iommu_deinit(vde); + + gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, + gen_pool_size(vde->iram_pool)); + + return 0; +} + +static void tegra_vde_shutdown(struct platform_device *pdev) +{ + /* + * On some devices bootloader isn't ready to a power-gated VDE on + * a warm-reboot, machine will hang in that case. + */ + pm_runtime_get_sync(&pdev->dev); +} + +static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + mutex_lock(&vde->lock); + + err = pm_runtime_force_suspend(dev); + if (err < 0) + return err; + + return 0; +} + +static __maybe_unused int tegra_vde_pm_resume(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + err = pm_runtime_force_resume(dev); + if (err < 0) + return err; + + mutex_unlock(&vde->lock); + + return 0; +} + +static const struct dev_pm_ops tegra_vde_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend, + tegra_vde_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend, + tegra_vde_pm_resume) +}; + +static const u32 tegra124_decoded_fmts[] = { + /* TBD: T124 supports only a non-standard Tegra tiled format */ +}; + +static const struct tegra_coded_fmt_desc tegra124_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 16, + .max_width = 1920, + .step_width = 16, + .min_height = 16, + .max_height = 2032, + .step_height = 16, + }, + .num_decoded_fmts = ARRAY_SIZE(tegra124_decoded_fmts), + .decoded_fmts = tegra124_decoded_fmts, + .decode_run = tegra_vde_h264_decode_run, + .decode_wait = tegra_vde_h264_decode_wait, + }, +}; + +static const u32 tegra20_decoded_fmts[] = { + V4L2_PIX_FMT_YUV420M, + V4L2_PIX_FMT_YVU420M, +}; + +static const struct tegra_coded_fmt_desc tegra20_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 16, + .max_width = 1920, + .step_width = 16, + .min_height = 16, + .max_height = 2032, + .step_height = 16, + }, + .num_decoded_fmts = ARRAY_SIZE(tegra20_decoded_fmts), + .decoded_fmts = tegra20_decoded_fmts, + .decode_run = tegra_vde_h264_decode_run, + .decode_wait = tegra_vde_h264_decode_wait, + }, +}; + +static const struct tegra_vde_soc tegra124_vde_soc = { + .supports_ref_pic_marking = true, + .coded_fmts = tegra124_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(tegra124_coded_fmts), +}; + +static const struct tegra_vde_soc tegra114_vde_soc = { + .supports_ref_pic_marking = true, + .coded_fmts = tegra20_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts), +}; + +static const struct tegra_vde_soc tegra30_vde_soc = { + .supports_ref_pic_marking = false, + .coded_fmts = tegra20_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts), +}; + +static const struct tegra_vde_soc tegra20_vde_soc = { + .supports_ref_pic_marking = false, + .coded_fmts = tegra20_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts), +}; + +static const struct of_device_id tegra_vde_of_match[] = { + { .compatible = "nvidia,tegra124-vde", .data = &tegra124_vde_soc }, + { .compatible = "nvidia,tegra114-vde", .data = &tegra114_vde_soc }, + { .compatible = "nvidia,tegra30-vde", .data = &tegra30_vde_soc }, + { .compatible = "nvidia,tegra20-vde", .data = &tegra20_vde_soc }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_vde_of_match); + +static struct platform_driver tegra_vde_driver = { + .probe = tegra_vde_probe, + .remove = tegra_vde_remove, + .shutdown = tegra_vde_shutdown, + .driver = { + .name = "tegra-vde", + .of_match_table = tegra_vde_of_match, + .pm = &tegra_vde_pm_ops, + }, +}; +module_platform_driver(tegra_vde_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra Video Decoder driver"); +MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h new file mode 100644 index 000000000000..0fbb1f3d2c88 --- /dev/null +++ b/drivers/media/platform/nvidia/tegra-vde/vde.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * NVIDIA Tegra Video decoder driver + * + * Copyright (C) 2016-2019 GRATE-DRIVER project + */ + +#ifndef TEGRA_VDE_H +#define TEGRA_VDE_H + +#include <linux/completion.h> +#include <linux/dma-direction.h> +#include <linux/iova.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <media/media-device.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-dma-sg.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> + +#define ICMDQUE_WR 0x00 +#define CMDQUE_CONTROL 0x08 +#define INTR_STATUS 0x18 +#define BSE_INT_ENB 0x40 +#define BSE_CONFIG 0x44 + +#define BSE_ICMDQUE_EMPTY BIT(3) +#define BSE_DMA_BUSY BIT(23) + +#define BSEV_ALIGN SZ_1 +#define FRAMEID_ALIGN SZ_256 +#define SXE_BUFFER SZ_32K +#define VDE_ATOM SZ_16 + +struct clk; +struct dma_buf; +struct gen_pool; +struct tegra_ctx; +struct iommu_group; +struct iommu_domain; +struct reset_control; +struct dma_buf_attachment; +struct tegra_vde_h264_frame; +struct tegra_vde_h264_decoder_ctx; + +struct tegra_video_frame { + struct dma_buf_attachment *y_dmabuf_attachment; + struct dma_buf_attachment *cb_dmabuf_attachment; + struct dma_buf_attachment *cr_dmabuf_attachment; + struct dma_buf_attachment *aux_dmabuf_attachment; + dma_addr_t y_addr; + dma_addr_t cb_addr; + dma_addr_t cr_addr; + dma_addr_t aux_addr; + u32 frame_num; + u32 flags; + u32 luma_atoms_pitch; + u32 chroma_atoms_pitch; +}; + +struct tegra_coded_fmt_desc { + u32 fourcc; + struct v4l2_frmsize_stepwise frmsize; + unsigned int num_decoded_fmts; + const u32 *decoded_fmts; + int (*decode_run)(struct tegra_ctx *ctx); + int (*decode_wait)(struct tegra_ctx *ctx); +}; + +struct tegra_vde_soc { + bool supports_ref_pic_marking; + const struct tegra_coded_fmt_desc *coded_fmts; + u32 num_coded_fmts; +}; + +struct tegra_vde_bo { + struct iova *iova; + struct sg_table sgt; + struct tegra_vde *vde; + enum dma_data_direction dma_dir; + unsigned long dma_attrs; + dma_addr_t dma_handle; + dma_addr_t dma_addr; + void *dma_cookie; + size_t size; +}; + +struct tegra_vde { + void __iomem *sxe; + void __iomem *bsev; + void __iomem *mbe; + void __iomem *ppe; + void __iomem *mce; + void __iomem *tfe; + void __iomem *ppb; + void __iomem *vdma; + void __iomem *frameid; + struct device *dev; + struct mutex lock; + struct mutex map_lock; + struct list_head map_list; + struct reset_control *rst; + struct reset_control *rst_mc; + struct gen_pool *iram_pool; + struct completion decode_completion; + struct clk *clk; + struct iommu_domain *domain; + struct iommu_group *group; + struct iova_domain iova; + struct iova *iova_resv_static_addresses; + struct iova *iova_resv_last_page; + const struct tegra_vde_soc *soc; + struct tegra_vde_bo *secure_bo; + dma_addr_t bitstream_data_addr; + dma_addr_t iram_lists_addr; + u32 *iram; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m; + struct media_device mdev; + struct video_device vdev; + struct mutex v4l2_lock; + struct workqueue_struct *wq; + struct tegra_video_frame frames[V4L2_H264_NUM_DPB_ENTRIES + 1]; +}; + +int tegra_vde_alloc_bo(struct tegra_vde *vde, + struct tegra_vde_bo **ret_bo, + enum dma_data_direction dma_dir, + size_t size); +void tegra_vde_free_bo(struct tegra_vde_bo *bo); + +struct tegra_ctx_h264 { + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; +}; + +struct tegra_ctx { + struct tegra_vde *vde; + struct tegra_ctx_h264 h264; + struct work_struct work; + struct v4l2_fh fh; + struct v4l2_ctrl_handler hdl; + struct v4l2_format coded_fmt; + struct v4l2_format decoded_fmt; + const struct tegra_coded_fmt_desc *coded_fmt_desc; + struct v4l2_ctrl *ctrls[]; +}; + +struct tegra_m2m_buffer { + struct v4l2_m2m_buffer m2m; + struct dma_buf_attachment *a[VB2_MAX_PLANES]; + dma_addr_t dma_base[VB2_MAX_PLANES]; + dma_addr_t dma_addr[VB2_MAX_PLANES]; + struct iova *iova[VB2_MAX_PLANES]; + struct tegra_vde_bo *aux; + bool b_frame; +}; + +static inline struct tegra_m2m_buffer * +vb_to_tegra_buf(struct vb2_buffer *vb) +{ + struct v4l2_m2m_buffer *m2m = container_of(vb, struct v4l2_m2m_buffer, + vb.vb2_buf); + + return container_of(m2m, struct tegra_m2m_buffer, m2m); +} + +void tegra_vde_prepare_control_data(struct tegra_ctx *ctx, u32 id); + +void tegra_vde_writel(struct tegra_vde *vde, u32 value, void __iomem *base, + u32 offset); +u32 tegra_vde_readl(struct tegra_vde *vde, void __iomem *base, u32 offset); +void tegra_vde_set_bits(struct tegra_vde *vde, u32 mask, void __iomem *base, + u32 offset); + +int tegra_vde_h264_decode_run(struct tegra_ctx *ctx); +int tegra_vde_h264_decode_wait(struct tegra_ctx *ctx); + +int tegra_vde_iommu_init(struct tegra_vde *vde); +void tegra_vde_iommu_deinit(struct tegra_vde *vde); +int tegra_vde_iommu_map(struct tegra_vde *vde, + struct sg_table *sgt, + struct iova **iovap, + size_t size); +void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova); + +int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde, + struct dma_buf *dmabuf, + enum dma_data_direction dma_dir, + struct dma_buf_attachment **ap, + dma_addr_t *addrp); +void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde, + struct dma_buf_attachment *a, + bool release); +void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde); +void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde); + +static __maybe_unused char const * +tegra_vde_reg_base_name(struct tegra_vde *vde, void __iomem *base) +{ + if (vde->sxe == base) + return "SXE"; + + if (vde->bsev == base) + return "BSEV"; + + if (vde->mbe == base) + return "MBE"; + + if (vde->ppe == base) + return "PPE"; + + if (vde->mce == base) + return "MCE"; + + if (vde->tfe == base) + return "TFE"; + + if (vde->ppb == base) + return "PPB"; + + if (vde->vdma == base) + return "VDMA"; + + if (vde->frameid == base) + return "FRAMEID"; + + return "???"; +} + +int tegra_vde_v4l2_init(struct tegra_vde *vde); +void tegra_vde_v4l2_deinit(struct tegra_vde *vde); + +#endif /* TEGRA_VDE_H */ diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig new file mode 100644 index 000000000000..28f2bafc14d2 --- /dev/null +++ b/drivers/media/platform/nxp/Kconfig @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# V4L drivers + +comment "NXP media platform drivers" + +config VIDEO_IMX_MIPI_CSIS + tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models" + depends on ARCH_MXC || COMPILE_TEST + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + default n + help + Video4Linux2 sub-device driver for the MIPI CSI-2 CSIS receiver + v3.3/v3.6.3 found on some i.MX7 and i.MX8 SoCs. + +config VIDEO_VIU + tristate "NXP VIU Video Driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && (PPC_MPC512x || COMPILE_TEST) && I2C + select VIDEOBUF_DMA_CONTIG + default y + help + Support for Freescale VIU video driver. This device captures + video data, or overlays video on DIU frame buffer. + + Say Y here if you want to enable VIU device on MPC5121e Rev2+. + In doubt, say N. + +# mem2mem drivers + +config VIDEO_IMX_PXP + tristate "NXP i.MX Pixel Pipeline (PXP)" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && (ARCH_MXC || COMPILE_TEST) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + The i.MX Pixel Pipeline is a memory-to-memory engine for scaling, + color space conversion, and rotation. + +config VIDEO_MX2_EMMAPRP + tristate "NXP MX2 eMMa-PrP support" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on SOC_IMX27 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + MX2X chips have a PrP that can be used to process buffers from + memory to memory. Operations include resizing and format + conversion. + +source "drivers/media/platform/nxp/imx-jpeg/Kconfig" diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile new file mode 100644 index 000000000000..efc38c6578ce --- /dev/null +++ b/drivers/media/platform/nxp/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += imx-jpeg/ + +obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o +obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o +obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o +obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/nxp/fsl-viu.c index a4bfa70b49b2..afc96f6db2a1 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/nxp/fsl-viu.c @@ -1407,7 +1407,7 @@ static int viu_of_probe(struct platform_device *op) } /* Prepare our private structure */ - viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC); + viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_KERNEL); if (!viu_dev) { dev_err(&op->dev, "Can't allocate private structure\n"); ret = -ENOMEM; diff --git a/drivers/media/platform/imx-jpeg/Kconfig b/drivers/media/platform/nxp/imx-jpeg/Kconfig index 2fdd648cda80..5214dcd7fab5 100644 --- a/drivers/media/platform/imx-jpeg/Kconfig +++ b/drivers/media/platform/nxp/imx-jpeg/Kconfig @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_IMX8_JPEG tristate "IMX8 JPEG Encoder/Decoder" + depends on V4L_MEM2MEM_DRIVERS depends on ARCH_MXC || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select V4L2_JPEG_HELPER diff --git a/drivers/media/platform/imx-jpeg/Makefile b/drivers/media/platform/nxp/imx-jpeg/Makefile index bf19c82e61b4..bf19c82e61b4 100644 --- a/drivers/media/platform/imx-jpeg/Makefile +++ b/drivers/media/platform/nxp/imx-jpeg/Makefile diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c index 29c604b1b179..29c604b1b179 100644 --- a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h index ae70d3a0dc24..ae70d3a0dc24 100644 --- a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 4ca96cf9def7..d1ec1f4b506b 100644 --- a/drivers/media/platform/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -35,7 +35,7 @@ * it, enable dynamic debug for this module and: * echo 1 > /sys/module/mxc_jpeg_encdec/parameters/jpeg_tracing * - * This is inspired by the drivers/media/platform/s5p-jpeg driver + * This is inspired by the drivers/media/platform/samsung/s5p-jpeg driver * * Copyright 2018-2019 NXP */ @@ -96,7 +96,7 @@ static const struct mxc_jpeg_fmt mxc_formats[] = { }, { .name = "YUV420", /* 1st plane = Y, 2nd plane = UV */ - .fourcc = V4L2_PIX_FMT_NV12, + .fourcc = V4L2_PIX_FMT_NV12M, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, .nc = 3, .depth = 12, /* 6 bytes (4Y + UV) for 4 pixels */ @@ -404,7 +404,7 @@ static enum mxc_jpeg_image_format mxc_jpeg_fourcc_to_imgfmt(u32 fourcc) return MXC_JPEG_GRAY; case V4L2_PIX_FMT_YUYV: return MXC_JPEG_YUV422; - case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: return MXC_JPEG_YUV420; case V4L2_PIX_FMT_YUV24: return MXC_JPEG_YUV444; @@ -673,7 +673,7 @@ static int mxc_jpeg_fixup_sof(struct mxc_jpeg_sof *sof, _bswap16(&sof->width); switch (fourcc) { - case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: sof->components_no = 3; sof->comp[0].v = 0x2; sof->comp[0].h = 0x2; @@ -709,7 +709,7 @@ static int mxc_jpeg_fixup_sos(struct mxc_jpeg_sos *sos, u8 *sof_u8 = (u8 *)sos; switch (fourcc) { - case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12M: sos->components_no = 3; break; case V4L2_PIX_FMT_YUYV: @@ -947,8 +947,13 @@ static void mxc_jpeg_device_run(void *priv) v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf); + if (q_data_cap->fmt->colplanes != dst_buf->vb2_buf.num_planes) { + dev_err(dev, "Capture format %s has %d planes, but capture buffer has %d planes\n", + q_data_cap->fmt->name, q_data_cap->fmt->colplanes, + dst_buf->vb2_buf.num_planes); + jpeg_src_buf->jpeg_parse_error = true; + } if (jpeg_src_buf->jpeg_parse_error) { - jpeg->slot_data[ctx->slot].used = false; v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); @@ -992,6 +997,20 @@ end: spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags); } +static void mxc_jpeg_set_last_buffer_dequeued(struct mxc_jpeg_ctx *ctx) +{ + struct vb2_queue *q; + + ctx->stopped = 1; + q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); + if (!list_empty(&q->done_list)) + return; + + q->last_buffer_dequeued = true; + wake_up(&q->done_wq); + ctx->stopped = 0; +} + static int mxc_jpeg_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { @@ -1009,6 +1028,7 @@ static int mxc_jpeg_decoder_cmd(struct file *file, void *priv, if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) { /* No more src bufs, notify app EOS */ notify_eos(ctx); + mxc_jpeg_set_last_buffer_dequeued(ctx); } else { /* will send EOS later*/ ctx->stopping = 1; @@ -1035,6 +1055,7 @@ static int mxc_jpeg_encoder_cmd(struct file *file, void *priv, if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) { /* No more src bufs, notify app EOS */ notify_eos(ctx); + mxc_jpeg_set_last_buffer_dequeued(ctx); } else { /* will send EOS later*/ ctx->stopping = 1; @@ -1111,6 +1132,10 @@ static void mxc_jpeg_stop_streaming(struct vb2_queue *q) v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); } pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev); + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + ctx->stopping = 0; + ctx->stopped = 0; + } } static int mxc_jpeg_valid_comp_id(struct device *dev, @@ -1183,7 +1208,7 @@ static void mxc_jpeg_bytesperline(struct mxc_jpeg_q_data *q, /* bytesperline unused for compressed formats */ q->bytesperline[0] = 0; q->bytesperline[1] = 0; - } else if (q->fmt->fourcc == V4L2_PIX_FMT_NV12) { + } else if (q->fmt->fourcc == V4L2_PIX_FMT_NV12M) { /* When the image format is planar the bytesperline value * applies to the first plane and is divided by the same factor * as the width field for the other planes @@ -1215,7 +1240,7 @@ static void mxc_jpeg_sizeimage(struct mxc_jpeg_q_data *q) } else { q->sizeimage[0] = q->bytesperline[0] * q->h; q->sizeimage[1] = 0; - if (q->fmt->fourcc == V4L2_PIX_FMT_NV12) + if (q->fmt->fourcc == V4L2_PIX_FMT_NV12M) q->sizeimage[1] = q->sizeimage[0] / 2; } } @@ -1402,12 +1427,29 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb) return 0; } +static void mxc_jpeg_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *q = vb->vb2_queue; + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + return; + if (!ctx->stopped) + return; + if (list_empty(&q->done_list)) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + ctx->stopped = 0; + } +} + static const struct vb2_ops mxc_jpeg_qops = { .queue_setup = mxc_jpeg_queue_setup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .buf_out_validate = mxc_jpeg_buf_out_validate, .buf_prepare = mxc_jpeg_buf_prepare, + .buf_finish = mxc_jpeg_buf_finish, .start_streaming = mxc_jpeg_start_streaming, .stop_streaming = mxc_jpeg_stop_streaming, .buf_queue = mxc_jpeg_buf_queue, @@ -1843,14 +1885,14 @@ static int mxc_jpeg_dqbuf(struct file *file, void *priv, int ret; dev_dbg(dev, "DQBUF type=%d, index=%d", buf->type, buf->index); - if (ctx->stopping == 1 && num_src_ready == 0) { + if (ctx->stopping == 1 && num_src_ready == 0) { /* No more src bufs, notify app EOS */ notify_eos(ctx); ctx->stopping = 0; + mxc_jpeg_set_last_buffer_dequeued(ctx); } ret = v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf); - return ret; } @@ -2016,7 +2058,6 @@ static int mxc_jpeg_probe(struct platform_device *pdev) for (slot = 0; slot < MXC_MAX_SLOTS; slot++) { dec_irq = platform_get_irq(pdev, slot); if (dec_irq < 0) { - dev_err(&pdev->dev, "Failed to get irq %d\n", dec_irq); ret = dec_irq; goto err_irq; } diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index 9fb2a5aaa941..f53f004ba851 100644 --- a/drivers/media/platform/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -91,6 +91,7 @@ struct mxc_jpeg_ctx { struct v4l2_fh fh; enum mxc_jpeg_enc_state enc_state; unsigned int stopping; + unsigned int stopped; unsigned int slot; }; diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 2b73fa55c938..0a72734db55e 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver + * Samsung CSIS MIPI CSI-2 receiver driver. + * + * The Samsung CSIS IP is a MIPI CSI-2 receiver found in various NXP i.MX7 and + * i.MX8 SoCs. The i.MX7 features version 3.3 of the IP, while i.MX8 features + * version 3.6.3. * * Copyright (C) 2019 Linaro Ltd * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved. @@ -31,8 +35,7 @@ #include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> -#define CSIS_DRIVER_NAME "imx7-mipi-csis" -#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME +#define CSIS_DRIVER_NAME "imx-mipi-csis" #define CSIS_PAD_SINK 0 #define CSIS_PAD_SOURCE 1 @@ -170,6 +173,7 @@ #define MIPI_CSIS_ISPCFG_PIXEL_MODE_SINGLE (0 << 12) #define MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL (1 << 12) #define MIPI_CSIS_ISPCFG_PIXEL_MODE_QUAD (2 << 12) /* i.MX8M[MNP] only */ +#define MIPI_CSIS_ISPCFG_PIXEL_MASK (3 << 12) #define MIPI_CSIS_ISPCFG_ALIGN_32BIT BIT(11) #define MIPI_CSIS_ISPCFG_FMT(fmt) ((fmt) << 2) #define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2) @@ -211,6 +215,8 @@ #define MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_FALL BIT(4) #define MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_RISE BIT(0) +#define MIPI_CSIS_FRAME_COUNTER_CH(n) (0x0100 + (n) * 4) + /* Non-image packet data buffers */ #define MIPI_CSIS_PKTDATA_ODD 0x2000 #define MIPI_CSIS_PKTDATA_EVEN 0x3000 @@ -311,27 +317,30 @@ struct csi_state { struct reset_control *mrst; struct regulator *mipi_phy_regulator; const struct mipi_csis_info *info; - u8 index; struct v4l2_subdev sd; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_async_notifier notifier; struct v4l2_subdev *src_sd; - struct v4l2_fwnode_bus_mipi_csi2 bus; + struct v4l2_mbus_config_mipi_csi2 bus; u32 clk_frequency; u32 hs_settle; u32 clk_settle; struct mutex lock; /* Protect csis_fmt, format_mbus and state */ const struct csis_pix_format *csis_fmt; - struct v4l2_mbus_framefmt format_mbus; + struct v4l2_mbus_framefmt format_mbus[CSIS_PADS_NUM]; u32 state; spinlock_t slock; /* Protect events */ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS]; struct dentry *debugfs_root; - bool debug; + struct { + bool enable; + u32 hs_settle; + u32 clk_settle; + } debug; }; /* ----------------------------------------------------------------------------- @@ -340,6 +349,7 @@ struct csi_state { struct csis_pix_format { u32 code; + u32 output; u32 data_type; u8 width; }; @@ -348,84 +358,116 @@ static const struct csis_pix_format mipi_csis_formats[] = { /* YUV formats. */ { .code = MEDIA_BUS_FMT_UYVY8_1X16, + .output = MEDIA_BUS_FMT_UYVY8_1X16, .data_type = MIPI_CSI2_DATA_TYPE_YUV422_8, .width = 16, }, + /* RGB formats. */ + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .output = MEDIA_BUS_FMT_RGB565_1X16, + .data_type = MIPI_CSI2_DATA_TYPE_RGB565, + .width = 16, + }, { + .code = MEDIA_BUS_FMT_BGR888_1X24, + .output = MEDIA_BUS_FMT_RGB888_1X24, + .data_type = MIPI_CSI2_DATA_TYPE_RGB888, + .width = 24, + }, /* RAW (Bayer and greyscale) formats. */ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .output = MEDIA_BUS_FMT_SBGGR8_1X8, .data_type = MIPI_CSI2_DATA_TYPE_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .output = MEDIA_BUS_FMT_SGBRG8_1X8, .data_type = MIPI_CSI2_DATA_TYPE_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .output = MEDIA_BUS_FMT_SGRBG8_1X8, .data_type = MIPI_CSI2_DATA_TYPE_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .output = MEDIA_BUS_FMT_SRGGB8_1X8, .data_type = MIPI_CSI2_DATA_TYPE_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_Y8_1X8, + .output = MEDIA_BUS_FMT_Y8_1X8, .data_type = MIPI_CSI2_DATA_TYPE_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .output = MEDIA_BUS_FMT_SBGGR10_1X10, .data_type = MIPI_CSI2_DATA_TYPE_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .output = MEDIA_BUS_FMT_SGBRG10_1X10, .data_type = MIPI_CSI2_DATA_TYPE_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .output = MEDIA_BUS_FMT_SGRBG10_1X10, .data_type = MIPI_CSI2_DATA_TYPE_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .output = MEDIA_BUS_FMT_SRGGB10_1X10, .data_type = MIPI_CSI2_DATA_TYPE_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_Y10_1X10, + .output = MEDIA_BUS_FMT_Y10_1X10, .data_type = MIPI_CSI2_DATA_TYPE_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .output = MEDIA_BUS_FMT_SBGGR12_1X12, .data_type = MIPI_CSI2_DATA_TYPE_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .output = MEDIA_BUS_FMT_SGBRG12_1X12, .data_type = MIPI_CSI2_DATA_TYPE_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .output = MEDIA_BUS_FMT_SGRBG12_1X12, .data_type = MIPI_CSI2_DATA_TYPE_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .output = MEDIA_BUS_FMT_SRGGB12_1X12, .data_type = MIPI_CSI2_DATA_TYPE_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_Y12_1X12, + .output = MEDIA_BUS_FMT_Y12_1X12, .data_type = MIPI_CSI2_DATA_TYPE_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .output = MEDIA_BUS_FMT_SBGGR14_1X14, .data_type = MIPI_CSI2_DATA_TYPE_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .output = MEDIA_BUS_FMT_SGBRG14_1X14, .data_type = MIPI_CSI2_DATA_TYPE_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .output = MEDIA_BUS_FMT_SGRBG14_1X14, .data_type = MIPI_CSI2_DATA_TYPE_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .output = MEDIA_BUS_FMT_SRGGB14_1X14, .data_type = MIPI_CSI2_DATA_TYPE_RAW14, .width = 14, } @@ -493,12 +535,30 @@ static void mipi_csis_system_enable(struct csi_state *state, int on) /* Called with the state.lock mutex held */ static void __mipi_csis_set_format(struct csi_state *state) { - struct v4l2_mbus_framefmt *mf = &state->format_mbus; + struct v4l2_mbus_framefmt *mf = &state->format_mbus[CSIS_PAD_SINK]; u32 val; /* Color format */ val = mipi_csis_read(state, MIPI_CSIS_ISP_CONFIG_CH(0)); - val &= ~(MIPI_CSIS_ISPCFG_ALIGN_32BIT | MIPI_CSIS_ISPCFG_FMT_MASK); + val &= ~(MIPI_CSIS_ISPCFG_ALIGN_32BIT | MIPI_CSIS_ISPCFG_FMT_MASK + | MIPI_CSIS_ISPCFG_PIXEL_MASK); + + /* + * YUV 4:2:2 can be transferred with 8 or 16 bits per clock sample + * (referred to in the documentation as single and dual pixel modes + * respectively, although the 8-bit mode transfers half a pixel per + * clock sample and the 16-bit mode one pixel). While both mode work + * when the CSIS is connected to a receiver that supports either option, + * single pixel mode requires clock rates twice as high. As all SoCs + * that integrate the CSIS can operate in 16-bit bit mode, and some do + * not support 8-bit mode (this is the case of the i.MX8MP), use dual + * pixel mode unconditionally. + * + * TODO: Verify which other formats require DUAL (or QUAD) modes. + */ + if (state->csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8) + val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL; + val |= MIPI_CSIS_ISPCFG_FMT(state->csis_fmt->data_type); mipi_csis_write(state, MIPI_CSIS_ISP_CONFIG_CH(0), val); @@ -541,6 +601,18 @@ static int mipi_csis_calculate_params(struct csi_state *state) dev_dbg(state->dev, "lane rate %u, Tclk_settle %u, Ths_settle %u\n", lane_rate, state->clk_settle, state->hs_settle); + if (state->debug.hs_settle < 0xff) { + dev_dbg(state->dev, "overriding Ths_settle with %u\n", + state->debug.hs_settle); + state->hs_settle = state->debug.hs_settle; + } + + if (state->debug.clk_settle < 4) { + dev_dbg(state->dev, "overriding Tclk_settle with %u\n", + state->debug.clk_settle); + state->clk_settle = state->debug.clk_settle; + } + return 0; } @@ -657,7 +729,7 @@ static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id) spin_lock_irqsave(&state->slock, flags); /* Update the event/error counters */ - if ((status & MIPI_CSIS_INT_SRC_ERRORS) || state->debug) { + if ((status & MIPI_CSIS_INT_SRC_ERRORS) || state->debug.enable) { for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) { struct mipi_csis_event *event = &state->events[i]; @@ -747,7 +819,7 @@ static void mipi_csis_log_counters(struct csi_state *state, bool non_errors) spin_lock_irqsave(&state->slock, flags); for (i = 0; i < num_events; ++i) { - if (state->events[i].counter > 0 || state->debug) + if (state->events[i].counter > 0 || state->debug.enable) dev_info(state->dev, "%s events: %d\n", state->events[i].name, state->events[i].counter); @@ -773,6 +845,7 @@ static int mipi_csis_dump_regs(struct csi_state *state) { MIPI_CSIS_SDW_CONFIG_CH(0), "SDW_CONFIG_CH0" }, { MIPI_CSIS_SDW_RESOL_CH(0), "SDW_RESOL_CH0" }, { MIPI_CSIS_DBG_CTRL, "DBG_CTRL" }, + { MIPI_CSIS_FRAME_COUNTER_CH(0), "FRAME_COUNTER_CH0" }, }; unsigned int i; @@ -798,12 +871,19 @@ DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs); static void mipi_csis_debugfs_init(struct csi_state *state) { + state->debug.hs_settle = UINT_MAX; + state->debug.clk_settle = UINT_MAX; + state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL); debugfs_create_bool("debug_enable", 0600, state->debugfs_root, - &state->debug); + &state->debug.enable); debugfs_create_file("dump_regs", 0600, state->debugfs_root, state, &mipi_csis_dump_regs_fops); + debugfs_create_u32("tclk_settle", 0600, state->debugfs_root, + &state->debug.clk_settle); + debugfs_create_u32("ths_settle", 0600, state->debugfs_root, + &state->debug.hs_settle); } static void mipi_csis_debugfs_exit(struct csi_state *state) @@ -864,7 +944,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) ret = 0; mipi_csis_stop_stream(state); state->state &= ~ST_STREAMING; - if (state->debug) + if (state->debug.enable) mipi_csis_log_counters(state, true); } @@ -887,7 +967,7 @@ mipi_csis_get_format(struct csi_state *state, if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_format(&state->sd, sd_state, pad); - return &state->format_mbus; + return &state->format_mbus[pad]; } static int mipi_csis_init_cfg(struct v4l2_subdev *sd, @@ -1038,6 +1118,10 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, fmt->code = csis_fmt->code; fmt->width = sdformat->format.width; fmt->height = sdformat->format.height; + fmt->colorspace = sdformat->format.colorspace; + fmt->quantization = sdformat->format.quantization; + fmt->xfer_func = sdformat->format.xfer_func; + fmt->ycbcr_enc = sdformat->format.ycbcr_enc; sdformat->format = *fmt; @@ -1046,6 +1130,9 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, CSIS_PAD_SOURCE); *fmt = sdformat->format; + /* The format on the source pad might change due to unpacking. */ + fmt->code = csis_fmt->output; + /* Store the CSIS format descriptor for active formats. */ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) state->csis_fmt = csis_fmt; @@ -1061,7 +1148,7 @@ static int mipi_csis_log_status(struct v4l2_subdev *sd) mutex_lock(&state->lock); mipi_csis_log_counters(state, true); - if (state->debug && (state->state & ST_POWERED)) + if (state->debug.enable && (state->state & ST_POWERED)) mipi_csis_dump_regs(state); mutex_unlock(&state->lock); @@ -1303,8 +1390,8 @@ static int mipi_csis_subdev_init(struct csi_state *state) v4l2_subdev_init(sd, &mipi_csis_subdev_ops); sd->owner = THIS_MODULE; - snprintf(sd->name, sizeof(sd->name), "%s.%d", - CSIS_SUBDEV_NAME, state->index); + snprintf(sd->name, sizeof(sd->name), "csis-%s", + dev_name(state->dev)); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->ctrl_handler = NULL; @@ -1491,4 +1578,4 @@ module_platform_driver(mipi_csis_driver); MODULE_DESCRIPTION("i.MX7 & i.MX8 MIPI CSI-2 receiver driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx7-mipi-csi2"); +MODULE_ALIAS("platform:imx-mipi-csi2"); diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index 689ae5e6ac62..689ae5e6ac62 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c diff --git a/drivers/media/platform/imx-pxp.h b/drivers/media/platform/nxp/imx-pxp.h index 44f95c749d2e..44f95c749d2e 100644 --- a/drivers/media/platform/imx-pxp.h +++ b/drivers/media/platform/nxp/imx-pxp.h diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c index 3ce84d0f078c..3ce84d0f078c 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/nxp/mx2_emmaprp.c diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig new file mode 100644 index 000000000000..cc5799b9ea00 --- /dev/null +++ b/drivers/media/platform/qcom/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Qualcomm media platform drivers" + +source "drivers/media/platform/qcom/camss/Kconfig" +source "drivers/media/platform/qcom/venus/Kconfig" diff --git a/drivers/media/platform/qcom/Makefile b/drivers/media/platform/qcom/Makefile new file mode 100644 index 000000000000..4f055c396e04 --- /dev/null +++ b/drivers/media/platform/qcom/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += camss/ +obj-y += venus/ diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/platform/qcom/camss/Kconfig new file mode 100644 index 000000000000..4eda48cb1adf --- /dev/null +++ b/drivers/media/platform/qcom/camss/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_QCOM_CAMSS + tristate "Qualcomm V4L2 Camera Subsystem driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_SG + select V4L2_FWNODE diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 0752c46ea37b..4e2222358973 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -6,7 +6,7 @@ qcom-camss-objs += \ camss-csid.o \ camss-csid-4-1.o \ camss-csid-4-7.o \ - camss-csid-170.o \ + camss-csid-gen2.o \ camss-csiphy-2ph-1-0.o \ camss-csiphy-3ph-1-0.o \ camss-csiphy.o \ @@ -15,6 +15,7 @@ qcom-camss-objs += \ camss-vfe-4-7.o \ camss-vfe-4-8.o \ camss-vfe-170.o \ + camss-vfe-480.o \ camss-vfe-gen1.o \ camss-vfe.o \ camss-video.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-170.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index ac22ff29d2a9..2031bde13a93 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-170.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -21,7 +21,7 @@ * interface support. As a result of that it has an * alternate register layout. */ -#define IS_LITE (csid->id == 2 ? 1 : 0) +#define IS_LITE (csid->id >= 2 ? 1 : 0) #define CSID_HW_VERSION 0x0 #define HW_VERSION_STEPPING 0 @@ -105,7 +105,8 @@ #define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\ + 0x100 * (rdi)) #define RDI_CTRL_HALT_CMD 0 -#define ALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 +#define HALT_CMD_HALT_AT_FRAME_BOUNDARY 0 +#define HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 #define RDI_CTRL_HALT_MODE 2 #define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\ @@ -262,6 +263,13 @@ static const struct csid_format csid_formats[] = { 1, }, { + MEDIA_BUS_FMT_Y8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { MEDIA_BUS_FMT_Y10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, @@ -366,7 +374,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) val |= input_format->width & 0x1fff << TPG_DT_n_CFG_0_FRAME_WIDTH; writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - val = DATA_TYPE_RAW_10BIT << TPG_DT_n_CFG_1_DATA_TYPE; + val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); val = tg->mode << TPG_DT_n_CFG_2_PAYLOAD_MODE; @@ -382,8 +390,9 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) val = 1 << RDI_CFG0_BYTE_CNTR_EN; val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; val |= 1 << RDI_CFG0_TIMESTAMP_EN; + /* note: for non-RDI path, this should be format->decode_format */ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; - val |= DATA_TYPE_RAW_10BIT << RDI_CFG0_DATA_TYPE; + val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); @@ -396,7 +405,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0)); val = 0; - writel_relaxed(0, csid->base + CSID_RDI_FRM_DROP_PATTERN(0)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(0)); val = 1; writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0)); @@ -441,15 +450,12 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; val |= 1 << CSI2_RX_CFG1_MISR_EN; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); // csi2_vc_mode_shift_val ? - - /* error irqs start at BIT(11) */ - writel_relaxed(~0u, csid->base + CSID_CSI2_RX_IRQ_MASK); - - /* RDI irq */ - writel_relaxed(~0u, csid->base + CSID_TOP_IRQ_MASK); + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); - val = 1 << RDI_CTRL_HALT_CMD; + if (enable) + val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + else + val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); } @@ -588,7 +594,7 @@ static void csid_subdev_init(struct csid_device *csid) csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2; } -const struct csid_hw_ops csid_ops_170 = { +const struct csid_hw_ops csid_ops_gen2 = { .configure_stream = csid_configure_stream, .configure_testgen_pattern = csid_configure_testgen_pattern, .hw_version = csid_hw_version, diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index a1637b78568b..f993f349b66b 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -25,6 +25,10 @@ #include "camss-csid-gen1.h" #include "camss.h" +/* offset of CSID registers in VFE region for VFE 480 */ +#define VFE_480_CSID_OFFSET 0x1200 +#define VFE_480_LITE_CSID_OFFSET 0x200 + #define MSM_CSID_NAME "msm_csid" const char * const csid_testgen_modes[] = { @@ -152,15 +156,25 @@ static int csid_set_clock_rates(struct csid_device *csid) static int csid_set_power(struct v4l2_subdev *sd, int on) { struct csid_device *csid = v4l2_get_subdevdata(sd); - struct device *dev = csid->camss->dev; - int ret; + struct camss *camss = csid->camss; + struct device *dev = camss->dev; + struct vfe_device *vfe = &camss->vfe[csid->id]; + u32 version = camss->version; + int ret = 0; if (on) { + if (version == CAMSS_8250 || version == CAMSS_845) { + ret = vfe_get(vfe); + if (ret < 0) + return ret; + } + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; - ret = regulator_enable(csid->vdda); + ret = regulator_bulk_enable(csid->num_supplies, + csid->supplies); if (ret < 0) { pm_runtime_put_sync(dev); return ret; @@ -168,14 +182,16 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) ret = csid_set_clock_rates(csid); if (ret < 0) { - regulator_disable(csid->vdda); + regulator_bulk_disable(csid->num_supplies, + csid->supplies); pm_runtime_put_sync(dev); return ret; } ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); if (ret < 0) { - regulator_disable(csid->vdda); + regulator_bulk_disable(csid->num_supplies, + csid->supplies); pm_runtime_put_sync(dev); return ret; } @@ -186,7 +202,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) if (ret < 0) { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); - regulator_disable(csid->vdda); + regulator_bulk_disable(csid->num_supplies, + csid->supplies); pm_runtime_put_sync(dev); return ret; } @@ -195,8 +212,11 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) } else { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); - ret = regulator_disable(csid->vdda); + regulator_bulk_disable(csid->num_supplies, + csid->supplies); pm_runtime_put_sync(dev); + if (version == CAMSS_8250 || version == CAMSS_845) + vfe_put(vfe); } return ret; @@ -544,7 +564,6 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); - struct resource *r; int i, j; int ret; @@ -556,8 +575,9 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, } else if (camss->version == CAMSS_8x96 || camss->version == CAMSS_660) { csid->ops = &csid_ops_4_7; - } else if (camss->version == CAMSS_845) { - csid->ops = &csid_ops_170; + } else if (camss->version == CAMSS_845 || + camss->version == CAMSS_8250) { + csid->ops = &csid_ops_gen2; } else { return -EINVAL; } @@ -565,20 +585,28 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, /* Memory */ - csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); - if (IS_ERR(csid->base)) - return PTR_ERR(csid->base); + if (camss->version == CAMSS_8250) { + /* for titan 480, CSID registers are inside the VFE region, + * between the VFE "top" and "bus" registers. this requires + * VFE to be initialized before CSID + */ + if (id >= 2) /* VFE/CSID lite */ + csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET; + else + csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET; + } else { + csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); + if (IS_ERR(csid->base)) + return PTR_ERR(csid->base); + } /* Interrupt */ - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } + ret = platform_get_irq_byname(pdev, res->interrupt[0]); + if (ret < 0) + return ret; - csid->irq = r->start; + csid->irq = ret; snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", dev_name(dev), MSM_CSID_NAME, csid->id); ret = devm_request_irq(dev, csid->irq, csid->ops->isr, @@ -630,13 +658,28 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, } /* Regulator */ + for (i = 0; i < ARRAY_SIZE(res->regulators); i++) { + if (res->regulators[i]) + csid->num_supplies++; + } - csid->vdda = devm_regulator_get(dev, res->regulator[0]); - if (IS_ERR(csid->vdda)) { - dev_err(dev, "could not get regulator\n"); - return PTR_ERR(csid->vdda); + if (csid->num_supplies) { + csid->supplies = devm_kmalloc_array(camss->dev, + csid->num_supplies, + sizeof(csid->supplies), + GFP_KERNEL); + if (!csid->supplies) + return -ENOMEM; } + for (i = 0; i < csid->num_supplies; i++) + csid->supplies[i].supply = res->regulators[i]; + + ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies, + csid->supplies); + if (ret) + return ret; + init_completion(&csid->reset_complete); return 0; diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 814ebc7c29d6..f06040e44c51 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -152,7 +152,8 @@ struct csid_device { char irq_name[30]; struct camss_clock *clock; int nclocks; - struct regulator *vdda; + struct regulator_bulk_data *supplies; + int num_supplies; struct completion reset_complete; struct csid_testgen_config testgen; struct csid_phy_config phy; @@ -205,7 +206,7 @@ extern const char * const csid_testgen_modes[]; extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; -extern const struct csid_hw_ops csid_ops_170; +extern const struct csid_hw_ops csid_ops_gen2; #endif /* QC_MSM_CAMSS_CSID_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c index 30b454c369ab..cd4a8c369234 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c @@ -16,6 +16,7 @@ #define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) #define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) +#define CAMSS_CSI_PHY_LN_CLK 1 #define CAMSS_CSI_PHY_GLBL_RESET 0x140 #define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 #define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 @@ -26,6 +27,19 @@ #define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec #define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 +static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) +{ + u8 lane_mask; + int i; + + lane_mask = 1 << CAMSS_CSI_PHY_LN_CLK; + + for (i = 0; i < lane_cfg->num_data; i++) + lane_mask |= 1 << lane_cfg->data[i].pos; + + return lane_mask; +} + static void csiphy_hw_version_read(struct csiphy_device *csiphy, struct device *dev) { @@ -105,7 +119,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, for (i = 0; i <= c->num_data; i++) { if (i == c->num_data) - l = c->clk.pos; + l = CAMSS_CSI_PHY_LN_CLK; else l = c->data[i].pos; @@ -129,7 +143,7 @@ static void csiphy_lanes_disable(struct csiphy_device *csiphy, for (i = 0; i <= c->num_data; i++) { if (i == c->num_data) - l = c->clk.pos; + l = CAMSS_CSI_PHY_LN_CLK; else l = c->data[i].pos; @@ -167,6 +181,7 @@ static irqreturn_t csiphy_isr(int irq, void *dev) } const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = { + .get_lane_mask = csiphy_get_lane_mask, .hw_version_read = csiphy_hw_version_read, .reset = csiphy_reset, .lanes_enable = csiphy_lanes_enable, diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index e318c822ab04..451a4c9b3d30 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -43,6 +43,7 @@ #define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8 #define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n)) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7) #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0) #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) #define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n)) @@ -62,6 +63,7 @@ struct csiphy_reg_t { u32 csiphy_param_type; }; +/* GEN2 1.0 2PH */ static const struct csiphy_reg_t lane_regs_sdm845[5][14] = { { @@ -146,6 +148,121 @@ csiphy_reg_t lane_regs_sdm845[5][14] = { }, }; +/* GEN2 1.2.1 2PH */ +static const struct +csiphy_reg_t lane_regs_sm8250[5][20] = { + { + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0900, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0908, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0010, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0000, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0024, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C80, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C88, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x070c, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0724, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0A00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0A08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0A04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0A04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0210, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0200, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x020c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0224, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0B00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0B08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0B04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0B04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0410, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0400, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x040c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0424, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0610, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0600, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x060c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0624, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + static void csiphy_hw_version_read(struct csiphy_device *csiphy, struct device *dev) { @@ -163,7 +280,7 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy, hw_version |= readl_relaxed(csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24; - dev_err(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); + dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); } /* @@ -298,13 +415,25 @@ static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy, static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, u8 settle_cnt) { - int i, l; + const struct csiphy_reg_t *r; + int i, l, array_size; u32 val; - for (l = 0; l < 5; l++) { - for (i = 0; i < 14; i++) { - const struct csiphy_reg_t *r = &lane_regs_sdm845[l][i]; + switch (csiphy->camss->version) { + case CAMSS_845: + r = &lane_regs_sdm845[0][0]; + array_size = ARRAY_SIZE(lane_regs_sdm845[0]); + break; + case CAMSS_8250: + r = &lane_regs_sm8250[0][0]; + array_size = ARRAY_SIZE(lane_regs_sm8250[0]); + break; + default: + unreachable(); + } + for (l = 0; l < 5; l++) { + for (i = 0; i < array_size; i++, r++) { switch (r->csiphy_param_type) { case CSIPHY_SETTLE_CNT_LOWER_BYTE: val = settle_cnt & 0xff; @@ -320,18 +449,33 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, } } +static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) +{ + u8 lane_mask; + int i; + + lane_mask = CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE; + + for (i = 0; i < lane_cfg->num_data; i++) + lane_mask |= 1 << lane_cfg->data[i].pos; + + return lane_mask; +} + static void csiphy_lanes_enable(struct csiphy_device *csiphy, struct csiphy_config *cfg, s64 link_freq, u8 lane_mask) { struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + bool is_gen2 = (csiphy->camss->version == CAMSS_845 || + csiphy->camss->version == CAMSS_8250); u8 settle_cnt; u8 val; int i; settle_cnt = csiphy_settle_cnt_calc(link_freq, csiphy->timer_clk_rate); - val = BIT(c->clk.pos); + val = is_gen2 ? BIT(7) : CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE; for (i = 0; i < c->num_data; i++) val |= BIT(c->data[i].pos * 2); @@ -346,44 +490,14 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, val = 0x00; writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); - if (csiphy->camss->version == CAMSS_8x16 || - csiphy->camss->version == CAMSS_8x96) - csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt); - else if (csiphy->camss->version == CAMSS_845) + if (is_gen2) csiphy_gen2_config_lanes(csiphy, settle_cnt); + else + csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt); - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(12)); - - val = 0xfb; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(13)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(14)); - - val = 0x7f; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(15)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(16)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(17)); - - val = 0xef; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(18)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(19)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(20)); - - val = 0xff; - writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(21)); + /* IRQ_MASK registers - disable all interrupts */ + for (i = 11; i < 22; i++) + writel_relaxed(0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i)); } static void csiphy_lanes_disable(struct csiphy_device *csiphy, @@ -397,6 +511,7 @@ static void csiphy_lanes_disable(struct csiphy_device *csiphy, } const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = { + .get_lane_mask = csiphy_get_lane_mask, .hw_version_read = csiphy_hw_version_read, .reset = csiphy_reset, .lanes_enable = csiphy_lanes_enable, diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 24eec16197e7..75fcfc627400 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -94,6 +94,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y8_1X8, 8 }, { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; @@ -230,25 +231,6 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) } /* - * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter - * @lane_cfg - CSI2 lane configuration - * - * Return lane mask - */ -static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) -{ - u8 lane_mask; - int i; - - lane_mask = 1 << lane_cfg->clk.pos; - - for (i = 0; i < lane_cfg->num_data; i++) - lane_mask |= 1 << lane_cfg->data[i].pos; - - return lane_mask; -} - -/* * csiphy_stream_on - Enable streaming on CSIPHY module * @csiphy: CSIPHY device * @@ -261,7 +243,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; - u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); + u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg); u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -568,7 +550,6 @@ int msm_csiphy_subdev_init(struct camss *camss, { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); - struct resource *r; int i, j; int ret; @@ -585,7 +566,8 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->ops = &csiphy_ops_3ph_1_0; csiphy->formats = csiphy_formats_8x96; csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); - } else if (camss->version == CAMSS_845) { + } else if (camss->version == CAMSS_845 || + camss->version == CAMSS_8250) { csiphy->ops = &csiphy_ops_3ph_1_0; csiphy->formats = csiphy_formats_sdm845; csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); @@ -611,14 +593,11 @@ int msm_csiphy_subdev_init(struct camss *camss, /* Interrupt */ - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } + ret = platform_get_irq_byname(pdev, res->interrupt[0]); + if (ret < 0) + return ret; - csiphy->irq = r->start; + csiphy->irq = ret; snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); @@ -679,7 +658,10 @@ int msm_csiphy_subdev_init(struct camss *camss, if (!strcmp(clock->name, "csiphy0_timer") || !strcmp(clock->name, "csiphy1_timer") || - !strcmp(clock->name, "csiphy2_timer")) + !strcmp(clock->name, "csiphy2_timer") || + !strcmp(clock->name, "csiphy3_timer") || + !strcmp(clock->name, "csiphy4_timer") || + !strcmp(clock->name, "csiphy5_timer")) csiphy->rate_set[i] = true; if (camss->version == CAMSS_660 && diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index d71b8bc6ec00..1c14947f92d3 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -45,6 +45,13 @@ struct csiphy_config { struct csiphy_device; struct csiphy_hw_ops { + /* + * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter + * @lane_cfg - CSI2 lane configuration + * + * Return lane mask + */ + u8 (*get_lane_mask)(struct csiphy_lanes_cfg *lane_cfg); void (*hw_version_read)(struct csiphy_device *csiphy, struct device *dev); void (*reset)(struct csiphy_device *csiphy); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index ba5d65f6ef34..4ee11bb979cd 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1100,7 +1100,6 @@ int msm_ispif_subdev_init(struct camss *camss, struct device *dev = camss->dev; struct ispif_device *ispif = camss->ispif; struct platform_device *pdev = to_platform_device(dev); - struct resource *r; int i; int ret; @@ -1153,14 +1152,11 @@ int msm_ispif_subdev_init(struct camss *camss, /* Interrupt */ - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt); - - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } + ret = platform_get_irq_byname(pdev, res->interrupt); + if (ret < 0) + return ret; - ispif->irq = r->start; + ispif->irq = ret; snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", dev_name(dev), MSM_ISPIF_NAME); if (camss->version == CAMSS_8x16) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c index f524af712a84..600150cfc4f7 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c @@ -395,17 +395,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) */ static int vfe_halt(struct vfe_device *vfe) { - unsigned long time; - - reinit_completion(&vfe->halt_complete); - - time = wait_for_completion_timeout(&vfe->halt_complete, - msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); - if (!time) { - dev_err(vfe->camss->dev, "VFE halt timeout\n"); - return -EIO; - } - + /* rely on vfe_disable_output() to stop the VFE */ return 0; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c new file mode 100644 index 000000000000..129585110393 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-480.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v480 (SM8250) + * + * Copyright (C) 2020-2021 Linaro Ltd. + * Copyright (C) 2021 Jonathan Marek + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> + +#include "camss.h" +#include "camss-vfe.h" + +/* VFE 2/3 are lite and have a different register layout */ +#define IS_LITE (vfe->id >= 2 ? 1 : 0) + +#define VFE_HW_VERSION (0x00) + +#define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c) +#define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0)) + +#define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34) +static inline int reg_update_rdi(struct vfe_device *vfe, int n) +{ + return IS_LITE ? BIT(n) : BIT(1 + (n)); +} + +#define REG_UPDATE_RDI reg_update_rdi +#define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38) +#define IRQ_CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4) +#define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0)) +#define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7)) +#define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4) +#define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4) + +#define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00) + +#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08) +#define WM_CGC_OVERRIDE_ALL (0x3FFFFFF) + +#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xdc) + +#define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4) +static inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n) +{ + return IS_LITE ? BIT(n) : BIT(3 + (n)); +} + +#define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup +static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) +{ + return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n)); +} + +#define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done +#define VFE_BUS_IRQ_CLEAR(n) (BUS_REG_BASE + 0x20 + (n) * 4) +#define VFE_BUS_IRQ_STATUS(n) (BUS_REG_BASE + 0x28 + (n) * 4) +#define VFE_BUS_IRQ_CLEAR_GLOBAL (BUS_REG_BASE + 0x30) + +#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100) +#define WM_CFG_EN (0) +#define WM_CFG_MODE (16) +#define MODE_QCOM_PLAIN (0) +#define MODE_MIPI_RAW (1) +#define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100) +#define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100) +#define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100) +#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF) +#define VFE_BUS_WM_IMAGE_CFG_1(n) (BUS_REG_BASE + 0x210 + (n) * 0x100) +#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100) +#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100) +#define VFE_BUS_WM_HEADER_ADDR(n) (BUS_REG_BASE + 0x220 + (n) * 0x100) +#define VFE_BUS_WM_HEADER_INCR(n) (BUS_REG_BASE + 0x224 + (n) * 0x100) +#define VFE_BUS_WM_HEADER_CFG(n) (BUS_REG_BASE + 0x228 + (n) * 0x100) + +#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100) +#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100) +#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100) +#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100) + +#define VFE_BUS_WM_SYSTEM_CACHE_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100) +#define VFE_BUS_WM_BURST_LIMIT(n) (BUS_REG_BASE + 0x264 + (n) * 0x100) + +/* for titan 480, each bus client is hardcoded to a specific path + * and each bus client is part of a hardcoded "comp group" + */ +#define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n)) +#define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n)) + +static u32 vfe_hw_version(struct vfe_device *vfe) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION); + + u32 gen = (hw_version >> 28) & 0xF; + u32 rev = (hw_version >> 16) & 0xFFF; + u32 step = hw_version & 0xFFFF; + + dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step); + + return hw_version; +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0)); + writel_relaxed(GLOBAL_RESET_HW_AND_REG, vfe->base + VFE_GLOBAL_RESET_CMD); +} + +static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) +{ + struct v4l2_pix_format_mplane *pix = + &line->video_out.active_fmt.fmt.pix_mp; + + wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ + + /* no clock gating at bus input */ + writel_relaxed(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE); + + writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL); + + writel_relaxed(pix->plane_fmt[0].bytesperline * pix->height, + vfe->base + VFE_BUS_WM_FRAME_INCR(wm)); + writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm)); + writel_relaxed(WM_IMAGE_CFG_0_DEFAULT_WIDTH, + vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm)); + writel_relaxed(pix->plane_fmt[0].bytesperline, + vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm)); + writel_relaxed(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); + + /* no dropped frames, one irq per frame */ + writel_relaxed(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm)); + writel_relaxed(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm)); + writel_relaxed(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm)); + writel_relaxed(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm)); + + writel_relaxed(1 << WM_CFG_EN | MODE_MIPI_RAW << WM_CFG_MODE, + vfe->base + VFE_BUS_WM_CFG(wm)); +} + +static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) +{ + wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ + writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm)); +} + +static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, + struct vfe_line *line) +{ + wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ + writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= REG_UPDATE_RDI(vfe, line_id); + writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id); +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + /* enable only the IRQs used: rup and comp_done irqs for RDI0 */ + writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ, + vfe->base + VFE_IRQ_MASK(0)); + writel_relaxed(BUS_IRQ_MASK_0_RDI_RUP(vfe, 0) | + BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0)), + vfe->base + VFE_BUS_IRQ_MASK(0)); +} + +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id); +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm); + +/* + * vfe_isr - VFE module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 status; + + status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0)); + writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0)); + writel_relaxed(IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD); + + if (status & IRQ_MASK_0_RESET_ACK) + vfe_isr_reset_ack(vfe); + + if (status & IRQ_MASK_0_BUS_TOP_IRQ) { + u32 status = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(0)); + + writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0)); + writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL); + + if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, 0)) + vfe_isr_reg_update(vfe, 0); + + if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0))) + vfe_isr_wm_done(vfe, 0); + } + + return IRQ_HANDLED; +} + +/* + * vfe_halt - Trigger halt on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_halt(struct vfe_device *vfe) +{ + /* rely on vfe_disable_output() to stop the VFE */ + return 0; +} + +static int vfe_get_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output; + unsigned long flags; + int wm_idx; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output = &line->output; + if (output->state != VFE_OUTPUT_OFF) { + dev_err(vfe->camss->dev, "Output is running\n"); + goto error; + } + + output->wm_num = 1; + + wm_idx = vfe_reserve_wm(vfe, line->id); + if (wm_idx < 0) { + dev_err(vfe->camss->dev, "Can not reserve wm\n"); + goto error_get_wm; + } + output->wm_idx[0] = wm_idx; + + output->drop_update_idx = 0; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; + +error_get_wm: + vfe_release_wm(vfe, output->wm_idx[0]); + output->state = VFE_OUTPUT_OFF; +error: + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return -EINVAL; +} + +static int vfe_enable_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&vfe->output_lock, flags); + + vfe_reg_update_clear(vfe, line->id); + + if (output->state != VFE_OUTPUT_OFF) { + dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", + output->state); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return -EINVAL; + } + + WARN_ON(output->gen2.active_num); + + output->state = VFE_OUTPUT_ON; + + output->sequence = 0; + output->wait_reg_update = 0; + reinit_completion(&output->reg_update); + + vfe_wm_start(vfe, output->wm_idx[0], line); + + for (i = 0; i < 2; i++) { + output->buf[i] = vfe_buf_get_pending(output); + if (!output->buf[i]) + break; + output->gen2.active_num++; + vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line); + } + + vfe_reg_update(vfe, line->id); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +static int vfe_disable_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned int i; + bool done; + int timeout = 0; + + do { + spin_lock_irqsave(&vfe->output_lock, flags); + done = !output->gen2.active_num; + spin_unlock_irqrestore(&vfe->output_lock, flags); + usleep_range(10000, 20000); + + if (timeout++ == 100) { + dev_err(vfe->camss->dev, "VFE idle timeout - resetting\n"); + vfe_reset(vfe); + output->gen2.active_num = 0; + return 0; + } + } while (!done); + + spin_lock_irqsave(&vfe->output_lock, flags); + for (i = 0; i < output->wm_num; i++) + vfe_wm_stop(vfe, output->wm_idx[i]); + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +/* + * vfe_enable - Enable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_enable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + int ret; + + mutex_lock(&vfe->stream_lock); + + if (!vfe->stream_count) + vfe_enable_irq_common(vfe); + + vfe->stream_count++; + + mutex_unlock(&vfe->stream_lock); + + ret = vfe_get_output(line); + if (ret < 0) + goto error_get_output; + + ret = vfe_enable_output(line); + if (ret < 0) + goto error_enable_output; + + vfe->was_streaming = 1; + + return 0; + +error_enable_output: + vfe_put_output(line); + +error_get_output: + mutex_lock(&vfe->stream_lock); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return ret; +} + +/* + * vfe_disable - Disable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_disable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + + vfe_disable_output(line); + + vfe_put_output(line); + + mutex_lock(&vfe->stream_lock); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return 0; +} + +/* + * vfe_isr_reg_update - Process reg update interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + vfe_reg_update_clear(vfe, line_id); + + output = &vfe->line[line_id].output; + + if (output->wait_reg_update) { + output->wait_reg_update = 0; + complete(&output->reg_update); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_wm_done - Process write master done interrupt + * @vfe: VFE Device + * @wm: Write master id + */ +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) +{ + struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]]; + struct camss_buffer *ready_buf; + struct vfe_output *output; + unsigned long flags; + u32 index; + u64 ts = ktime_get_ns(); + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { + dev_err_ratelimited(vfe->camss->dev, + "Received wm done for unmapped index\n"); + goto out_unlock; + } + output = &vfe->line[vfe->wm_output_map[wm]].output; + + ready_buf = output->buf[0]; + if (!ready_buf) { + dev_err_ratelimited(vfe->camss->dev, + "Missing ready buf %d!\n", output->state); + goto out_unlock; + } + + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + index = 0; + output->buf[0] = output->buf[1]; + if (output->buf[0]) + index = 1; + + output->buf[index] = vfe_buf_get_pending(output); + + if (output->buf[index]) + vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line); + else + output->gen2.active_num--; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + return; + +out_unlock: + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +static void vfe_pm_domain_off(struct vfe_device *vfe) +{ + /* nop */ +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +static int vfe_pm_domain_on(struct vfe_device *vfe) +{ + return 0; +} + +/* + * vfe_queue_buffer - Add empty buffer + * @vid: Video device structure + * @buf: Buffer to be enqueued + * + * Add an empty buffer - depending on the current number of buffers it will be + * put in pending buffer queue or directly given to the hardware to be filled. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_queue_buffer(struct camss_video *vid, + struct camss_buffer *buf) +{ + struct vfe_line *line = container_of(vid, struct vfe_line, video_out); + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output; + unsigned long flags; + + output = &line->output; + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) { + output->buf[output->gen2.active_num++] = buf; + vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line); + } else { + vfe_buf_add_pending(output, buf); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +static const struct camss_video_ops vfe_video_ops_480 = { + .queue_buffer = vfe_queue_buffer, + .flush_buffers = vfe_flush_buffers, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->video_ops = vfe_video_ops_480; + vfe->line_num = 1; +} + +const struct vfe_hw_ops vfe_ops_480 = { + .global_reset = vfe_global_reset, + .hw_version = vfe_hw_version, + .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_disable, + .vfe_enable = vfe_enable, + .vfe_halt = vfe_halt, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 71f78b40e7f5..5b148e9f8134 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -118,6 +118,7 @@ static const struct vfe_format formats_rdi_845[] = { { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y8_1X8, 8 }, { MEDIA_BUS_FMT_Y10_1X10, 10 }, { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, }; @@ -219,7 +220,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, } else if (vfe->camss->version == CAMSS_8x96 || vfe->camss->version == CAMSS_660 || - vfe->camss->version == CAMSS_845) + vfe->camss->version == CAMSS_845 || + vfe->camss->version == CAMSS_8250) switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_2X8: { @@ -573,7 +575,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) * * Return 0 on success or a negative error code otherwise */ -static int vfe_get(struct vfe_device *vfe) +int vfe_get(struct vfe_device *vfe) { int ret; @@ -635,7 +637,7 @@ error_pm_domain: * vfe_put - Power down VFE module * @vfe: VFE Device */ -static void vfe_put(struct vfe_device *vfe) +void vfe_put(struct vfe_device *vfe) { mutex_lock(&vfe->power_lock); @@ -1279,7 +1281,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); - struct resource *r; int i, j; int ret; @@ -1293,10 +1294,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, case CAMSS_660: vfe->ops = &vfe_ops_4_8; break; - case CAMSS_845: vfe->ops = &vfe_ops_170; break; + case CAMSS_8250: + vfe->ops = &vfe_ops_480; + break; default: return -EINVAL; } @@ -1312,16 +1315,13 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, /* Interrupt */ - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, - res->interrupt[0]); - if (!r) { - dev_err(dev, "missing IRQ\n"); - return -EINVAL; - } + ret = platform_get_irq_byname(pdev, res->interrupt[0]); + if (ret < 0) + return ret; - vfe->irq = r->start; + vfe->irq = ret; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", - dev_name(dev), MSM_VFE_NAME, vfe->id); + dev_name(dev), MSM_VFE_NAME, id); ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { @@ -1407,7 +1407,8 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, l->formats = formats_rdi_8x96; l->nformats = ARRAY_SIZE(formats_rdi_8x96); } - } else if (camss->version == CAMSS_845) { + } else if (camss->version == CAMSS_845 || + camss->version == CAMSS_8250) { l->formats = formats_rdi_845; l->nformats = ARRAY_SIZE(formats_rdi_845); } else { diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index f166d176cb77..0eba04eb9b77 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -201,5 +201,9 @@ extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; extern const struct vfe_hw_ops vfe_ops_170; +extern const struct vfe_hw_ops vfe_ops_480; + +int vfe_get(struct vfe_device *vfe); +void vfe_put(struct vfe_device *vfe); #endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index f282275af626..5dc1ddbe6d65 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -176,6 +176,8 @@ static const struct camss_format_info formats_rdi_845[] = { { { 1, 1 } }, { { 1, 1 } }, { 14 } }, { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, { { 1, 1 } }, { { 1, 1 } }, { 10 } }, { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, @@ -1009,7 +1011,8 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, video->formats = formats_rdi_8x96; video->nformats = ARRAY_SIZE(formats_rdi_8x96); } - } else if (video->camss->version == CAMSS_845) { + } else if (video->camss->version == CAMSS_845 || + video->camss->version == CAMSS_8250) { video->formats = formats_rdi_845; video->nformats = ARRAY_SIZE(formats_rdi_845); } else { diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index be091c50a3c0..79ad82e233cb 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -8,6 +8,7 @@ * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/clk.h> +#include <linux/interconnect.h> #include <linux/media-bus-format.h> #include <linux/media.h> #include <linux/module.h> @@ -33,7 +34,7 @@ static const struct resources csiphy_res_8x16[] = { /* CSIPHY0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -45,7 +46,7 @@ static const struct resources csiphy_res_8x16[] = { /* CSIPHY1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -59,7 +60,7 @@ static const struct resources csiphy_res_8x16[] = { static const struct resources csid_res_8x16[] = { /* CSID0 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, @@ -76,7 +77,7 @@ static const struct resources csid_res_8x16[] = { /* CSID1 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, @@ -106,7 +107,7 @@ static const struct resources_ispif ispif_res_8x16 = { static const struct resources vfe_res_8x16[] = { /* VFE0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "vfe0", "csi_vfe0", "vfe_ahb", "vfe_axi", "ahb" }, .clock_rate = { { 0 }, @@ -128,7 +129,7 @@ static const struct resources vfe_res_8x16[] = { static const struct resources csiphy_res_8x96[] = { /* CSIPHY0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -140,7 +141,7 @@ static const struct resources csiphy_res_8x96[] = { /* CSIPHY1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -152,7 +153,7 @@ static const struct resources csiphy_res_8x96[] = { /* CSIPHY2 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -166,7 +167,7 @@ static const struct resources csiphy_res_8x96[] = { static const struct resources csid_res_8x96[] = { /* CSID0 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, @@ -183,7 +184,7 @@ static const struct resources csid_res_8x96[] = { /* CSID1 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, @@ -200,7 +201,7 @@ static const struct resources csid_res_8x96[] = { /* CSID2 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, .clock_rate = { { 0 }, @@ -217,7 +218,7 @@ static const struct resources csid_res_8x96[] = { /* CSID3 */ { - .regulator = { "vdda" }, + .regulators = { "vdda" }, .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" }, .clock_rate = { { 0 }, @@ -248,7 +249,7 @@ static const struct resources_ispif ispif_res_8x96 = { static const struct resources vfe_res_8x96[] = { /* VFE0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb", "vfe0_ahb", "vfe_axi", "vfe0_stream"}, .clock_rate = { { 0 }, @@ -266,7 +267,7 @@ static const struct resources vfe_res_8x96[] = { /* VFE1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb", "vfe1_ahb", "vfe_axi", "vfe1_stream"}, .clock_rate = { { 0 }, @@ -286,7 +287,7 @@ static const struct resources vfe_res_8x96[] = { static const struct resources csiphy_res_660[] = { /* CSIPHY0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer", "csi0_phy", "csiphy_ahb2crif" }, .clock_rate = { { 0 }, @@ -300,7 +301,7 @@ static const struct resources csiphy_res_660[] = { /* CSIPHY1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer", "csi1_phy", "csiphy_ahb2crif" }, .clock_rate = { { 0 }, @@ -314,7 +315,7 @@ static const struct resources csiphy_res_660[] = { /* CSIPHY2 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer", "csi2_phy", "csiphy_ahb2crif" }, .clock_rate = { { 0 }, @@ -330,7 +331,7 @@ static const struct resources csiphy_res_660[] = { static const struct resources csid_res_660[] = { /* CSID0 */ { - .regulator = { "vdda", "vdd_sec" }, + .regulators = { "vdda", "vdd_sec" }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi", "cphy_csid0" }, @@ -350,7 +351,7 @@ static const struct resources csid_res_660[] = { /* CSID1 */ { - .regulator = { "vdda", "vdd_sec" }, + .regulators = { "vdda", "vdd_sec" }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi", "cphy_csid1" }, @@ -370,7 +371,7 @@ static const struct resources csid_res_660[] = { /* CSID2 */ { - .regulator = { "vdda", "vdd_sec" }, + .regulators = { "vdda", "vdd_sec" }, .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", "csi2", "csi2_phy", "csi2_pix", "csi2_rdi", "cphy_csid2" }, @@ -390,7 +391,7 @@ static const struct resources csid_res_660[] = { /* CSID3 */ { - .regulator = { "vdda", "vdd_sec" }, + .regulators = { "vdda", "vdd_sec" }, .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", "csi3", "csi3_phy", "csi3_pix", "csi3_rdi", "cphy_csid3" }, @@ -424,7 +425,7 @@ static const struct resources_ispif ispif_res_660 = { static const struct resources vfe_res_660[] = { /* VFE0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "throttle_axi", "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb", "vfe0_ahb", "vfe_axi", "vfe0_stream"}, @@ -445,7 +446,7 @@ static const struct resources vfe_res_660[] = { /* VFE1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "throttle_axi", "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb", "vfe1_ahb", "vfe_axi", "vfe1_stream"}, @@ -468,7 +469,7 @@ static const struct resources vfe_res_660[] = { static const struct resources csiphy_res_845[] = { /* CSIPHY0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", "cpas_ahb", "cphy_rx_src", "csiphy0", "csiphy0_timer_src", "csiphy0_timer" }, @@ -486,7 +487,7 @@ static const struct resources csiphy_res_845[] = { /* CSIPHY1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", "cpas_ahb", "cphy_rx_src", "csiphy1", "csiphy1_timer_src", "csiphy1_timer" }, @@ -504,7 +505,7 @@ static const struct resources csiphy_res_845[] = { /* CSIPHY2 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", "cpas_ahb", "cphy_rx_src", "csiphy2", "csiphy2_timer_src", "csiphy2_timer" }, @@ -522,7 +523,7 @@ static const struct resources csiphy_res_845[] = { /* CSIPHY3 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", "cpas_ahb", "cphy_rx_src", "csiphy3", "csiphy3_timer_src", "csiphy3_timer" }, @@ -542,7 +543,7 @@ static const struct resources csiphy_res_845[] = { static const struct resources csid_res_845[] = { /* CSID0 */ { - .regulator = { "vdda-csi0" }, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe0", "vfe0_src", "vfe0_cphy_rx", "csi0", @@ -562,7 +563,7 @@ static const struct resources csid_res_845[] = { /* CSID1 */ { - .regulator = { "vdda-csi1" }, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe1", "vfe1_src", "vfe1_cphy_rx", "csi1", @@ -582,7 +583,7 @@ static const struct resources csid_res_845[] = { /* CSID2 */ { - .regulator = { "vdda-csi2" }, + .regulators = { "vdda-phy", "vdda-pll" }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe_lite", "vfe_lite_src", "vfe_lite_cphy_rx", "csi2", @@ -604,7 +605,7 @@ static const struct resources csid_res_845[] = { static const struct resources vfe_res_845[] = { /* VFE0 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", "soc_ahb", "vfe0", "vfe0_axi", "vfe0_src", "csi0", @@ -624,7 +625,7 @@ static const struct resources vfe_res_845[] = { /* VFE1 */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", "soc_ahb", "vfe1", "vfe1_axi", "vfe1_src", "csi1", @@ -644,7 +645,7 @@ static const struct resources vfe_res_845[] = { /* VFE-lite */ { - .regulator = { NULL }, + .regulators = {}, .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", "soc_ahb", "vfe_lite", "vfe_lite_src", "csi2", @@ -662,6 +663,208 @@ static const struct resources vfe_res_845[] = { } }; +static const struct resources csiphy_res_8250[] = { + /* CSIPHY0 */ + { + .regulators = {}, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" } + }, + /* CSIPHY1 */ + { + .regulators = {}, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" } + }, + /* CSIPHY2 */ + { + .regulators = {}, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" } + }, + /* CSIPHY3 */ + { + .regulators = {}, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" } + }, + /* CSIPHY4 */ + { + .regulators = {}, + .clock = { "csiphy4", "csiphy4_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" } + }, + /* CSIPHY5 */ + { + .regulators = {}, + .clock = { "csiphy5", "csiphy5_timer" }, + .clock_rate = { { 400000000 }, + { 300000000 } }, + .reg = { "csiphy5" }, + .interrupt = { "csiphy5" } + } +}; + +static const struct resources csid_res_8250[] = { + /* CSID0 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_areg", "vfe0_ahb" }, + .clock_rate = { { 400000000 }, + { 400000000 }, + { 350000000, 475000000, 576000000, 720000000 }, + { 100000000, 200000000, 300000000, 400000000 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + /* CSID1 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_areg", "vfe1_ahb" }, + .clock_rate = { { 400000000 }, + { 400000000 }, + { 350000000, 475000000, 576000000, 720000000 }, + { 100000000, 200000000, 300000000, 400000000 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, + /* CSID2 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" }, + .clock_rate = { { 400000000 }, + { 400000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" } + }, + /* CSID3 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" }, + .clock_rate = { { 400000000 }, + { 400000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid3" }, + .interrupt = { "csid3" } + } +}; + +static const struct resources vfe_res_8250[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb", + "camnoc_axi", "vfe0_ahb", "vfe0_areg", "vfe0", + "vfe0_axi", "cam_hf_axi" }, + .clock_rate = { { 19200000, 300000000, 400000000, 480000000 }, + { 19200000, 80000000 }, + { 19200000 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 300000000, 400000000 }, + { 350000000, 475000000, 576000000, 720000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb", + "camnoc_axi", "vfe1_ahb", "vfe1_areg", "vfe1", + "vfe1_axi", "cam_hf_axi" }, + .clock_rate = { { 19200000, 300000000, 400000000, 480000000 }, + { 19200000, 80000000 }, + { 19200000 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 300000000, 400000000 }, + { 350000000, 475000000, 576000000, 720000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" } + }, + /* VFE2 (lite) */ + { + .regulators = {}, + .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb", + "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi", + "vfe_lite", "cam_hf_axi" }, + .clock_rate = { { 19200000, 300000000, 400000000, 480000000 }, + { 19200000, 80000000 }, + { 19200000 }, + { 0 }, + { 0 }, + { 0 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" } + }, + /* VFE3 (lite) */ + { + .regulators = {}, + .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb", + "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi", + "vfe_lite", "cam_hf_axi" }, + .clock_rate = { { 19200000, 300000000, 400000000, 480000000 }, + { 19200000, 80000000 }, + { 19200000 }, + { 0 }, + { 0 }, + { 0 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" } + }, +}; + +static const struct resources_icc icc_res_sm8250[] = { + { + .name = "cam_ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "cam_hf_0_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "cam_sf_0_mnoc", + .icc_bw_tbl.avg = 0, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "cam_sf_icp_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + /* * camss_add_clock_margin - Add margin to clock frequency rate * @rate: Clock frequency rate @@ -832,7 +1035,7 @@ static int camss_of_parse_endpoint_node(struct device *dev, struct camss_async_subdev *csd) { struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; + struct v4l2_mbus_config_mipi_csi2 *mipi_csi2; struct v4l2_fwnode_endpoint vep = { { 0 } }; unsigned int i; @@ -945,6 +1148,12 @@ static int camss_init_subdevices(struct camss *camss) /* Titan VFEs don't have an ISPIF */ ispif_res = NULL; vfe_res = vfe_res_845; + } else if (camss->version == CAMSS_8250) { + csiphy_res = csiphy_res_8250; + csid_res = csid_res_8250; + /* Titan VFEs don't have an ISPIF */ + ispif_res = NULL; + vfe_res = vfe_res_8250; } else { return -EINVAL; } @@ -960,6 +1169,17 @@ static int camss_init_subdevices(struct camss *camss) } } + /* note: SM8250 requires VFE to be initialized before CSID */ + for (i = 0; i < camss->vfe_num; i++) { + ret = msm_vfe_subdev_init(camss, &camss->vfe[i], + &vfe_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Fail to init vfe%d sub-device: %d\n", i, ret); + return ret; + } + } + for (i = 0; i < camss->csid_num; i++) { ret = msm_csid_subdev_init(camss, &camss->csid[i], &csid_res[i], i); @@ -978,16 +1198,6 @@ static int camss_init_subdevices(struct camss *camss) return ret; } - for (i = 0; i < camss->vfe_num; i++) { - ret = msm_vfe_subdev_init(camss, &camss->vfe[i], - &vfe_res[i], i); - if (ret < 0) { - dev_err(camss->dev, - "Fail to init vfe%d sub-device: %d\n", i, ret); - return ret; - } - } - return 0; } @@ -1250,7 +1460,8 @@ static int camss_configure_pd(struct camss *camss) if (camss->version == CAMSS_8x96 || camss->version == CAMSS_660) nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; - else if (camss->version == CAMSS_845) + else if (camss->version == CAMSS_845 || + camss->version == CAMSS_8250) nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; for (i = 0; i < nbr_pm_domains; i++) { @@ -1283,6 +1494,29 @@ fail_pm: return ret; } +static int camss_icc_get(struct camss *camss) +{ + const struct resources_icc *icc_res; + int nbr_icc_paths = 0; + int i; + + if (camss->version == CAMSS_8250) { + icc_res = &icc_res_sm8250[0]; + nbr_icc_paths = ICC_SM8250_COUNT; + } + + for (i = 0; i < nbr_icc_paths; i++) { + camss->icc_path[i] = devm_of_icc_get(camss->dev, + icc_res[i].name); + if (IS_ERR(camss->icc_path[i])) + return PTR_ERR(camss->icc_path[i]); + + camss->icc_bw_tbl[i] = icc_res[i].icc_bw_tbl; + } + + return 0; +} + /* * camss_probe - Probe CAMSS platform device * @pdev: Pointer to CAMSS platform device @@ -1326,6 +1560,12 @@ static int camss_probe(struct platform_device *pdev) camss->csiphy_num = 4; camss->csid_num = 3; camss->vfe_num = 3; + } else if (of_device_is_compatible(dev->of_node, + "qcom,sm8250-camss")) { + camss->version = CAMSS_8250; + camss->csiphy_num = 6; + camss->csid_num = 4; + camss->vfe_num = 4; } else { ret = -EINVAL; goto err_free; @@ -1369,6 +1609,10 @@ static int camss_probe(struct platform_device *pdev) goto err_cleanup; } + ret = camss_icc_get(camss); + if (ret < 0) + goto err_cleanup; + ret = camss_init_subdevices(camss); if (ret < 0) goto err_cleanup; @@ -1457,7 +1701,8 @@ void camss_delete(struct camss *camss) if (camss->version == CAMSS_8x96 || camss->version == CAMSS_660) nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; - else if (camss->version == CAMSS_845) + else if (camss->version == CAMSS_845 || + camss->version == CAMSS_8250) nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; for (i = 0; i < nbr_pm_domains; i++) { @@ -1493,6 +1738,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8996-camss" }, { .compatible = "qcom,sdm660-camss" }, { .compatible = "qcom,sdm845-camss" }, + { .compatible = "qcom,sm8250-camss" }, { } }; @@ -1500,11 +1746,41 @@ MODULE_DEVICE_TABLE(of, camss_dt_match); static int __maybe_unused camss_runtime_suspend(struct device *dev) { + struct camss *camss = dev_get_drvdata(dev); + int nbr_icc_paths = 0; + int i; + int ret; + + if (camss->version == CAMSS_8250) + nbr_icc_paths = ICC_SM8250_COUNT; + + for (i = 0; i < nbr_icc_paths; i++) { + ret = icc_set_bw(camss->icc_path[i], 0, 0); + if (ret) + return ret; + } + return 0; } static int __maybe_unused camss_runtime_resume(struct device *dev) { + struct camss *camss = dev_get_drvdata(dev); + int nbr_icc_paths = 0; + int i; + int ret; + + if (camss->version == CAMSS_8250) + nbr_icc_paths = ICC_SM8250_COUNT; + + for (i = 0; i < nbr_icc_paths; i++) { + ret = icc_set_bw(camss->icc_path[i], + camss->icc_bw_tbl[i].avg, + camss->icc_bw_tbl[i].peak); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index dc8b4154f92b..c9b3e0df5be8 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -42,7 +42,7 @@ #define CAMSS_RES_MAX 17 struct resources { - char *regulator[CAMSS_RES_MAX]; + char *regulators[CAMSS_RES_MAX]; char *clock[CAMSS_RES_MAX]; u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; char *reg[CAMSS_RES_MAX]; @@ -56,6 +56,16 @@ struct resources_ispif { char *interrupt; }; +struct icc_bw_tbl { + u32 avg; + u32 peak; +}; + +struct resources_icc { + char *name; + struct icc_bw_tbl icc_bw_tbl; +}; + enum pm_domain { PM_DOMAIN_VFE0 = 0, PM_DOMAIN_VFE1 = 1, @@ -69,6 +79,12 @@ enum camss_version { CAMSS_8x96, CAMSS_660, CAMSS_845, + CAMSS_8250, +}; + +enum icc_count { + ICC_DEFAULT_COUNT = 0, + ICC_SM8250_COUNT = 4, }; struct camss { @@ -87,6 +103,8 @@ struct camss { atomic_t ref_count; struct device *genpd[PM_DOMAIN_GEN2_COUNT]; struct device_link *genpd_link[PM_DOMAIN_GEN2_COUNT]; + struct icc_path *icc_path[ICC_SM8250_COUNT]; + struct icc_bw_tbl icc_bw_tbl[ICC_SM8250_COUNT]; }; struct camss_camera_interface { diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig new file mode 100644 index 000000000000..bfd50e8f3421 --- /dev/null +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_QCOM_VENUS + tristate "Qualcomm Venus V4L2 encoder/decoder driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && QCOM_SMEM + depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select QCOM_MDT_LOADER if ARCH_QCOM + select QCOM_SCM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a V4L2 driver for Qualcomm Venus video accelerator + hardware. It accelerates encoding and decoding operations + on various Qualcomm SoCs. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7c3bac01cd49..c3023340d95c 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -127,6 +127,7 @@ struct venus_format { * @done: a completion for sync HFI operations * @error: an error returned during last HFI sync operations * @sys_error: an error flag that signal system error event + * @sys_err_done: a waitqueue to wait for system error recovery end * @core_ops: the core operations * @pm_ops: a pointer to pm operations * @pm_lock: a lock for PM operations @@ -346,6 +347,7 @@ enum venus_inst_modes { * @width: current capture width * @height: current capture height * @crop: current crop rectangle + * @fw_min_cnt: firmware minimum buffer count * @out_width: current output width * @out_height: current output height * @colorspace: current color space @@ -390,6 +392,8 @@ enum venus_inst_modes { * @pic_struct: bitstream progressive vs interlaced * @next_buf_last: a flag to mark next queued capture buffer as last * @drain_active: Drain sequence is in progress + * @flags: bitmask flags describing current instance mode + * @dpb_ids: DPB buffer ID's */ struct venus_inst { struct list_head list; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 84c3a511ec31..0bca95d01650 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -189,7 +189,6 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL, buf->attrs); if (!buf->va) { - kfree(buf); ret = -ENOMEM; goto fail; } @@ -209,6 +208,7 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) return 0; fail: + kfree(buf); venus_helper_free_dpb_bufs(inst); return ret; } diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 5aea07307e02..4ecd444050bb 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1054,6 +1054,8 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); break; } + case HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI: + return -ENOTSUPP; /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 84bafc3118cc..adea4c3b8c20 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -662,8 +662,8 @@ static int venc_set_properties(struct venus_inst *inst) ptype = HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8; h264_transform.enable_type = 0; - if (ctr->profile.h264 == HFI_H264_PROFILE_HIGH || - ctr->profile.h264 == HFI_H264_PROFILE_CONSTRAINED_HIGH) + if (ctr->profile.h264 == V4L2_MPEG_VIDEO_H264_PROFILE_HIGH || + ctr->profile.h264 == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) h264_transform.enable_type = ctr->h264_8x8_transform; ret = hfi_session_set_property(inst, ptype, &h264_transform); diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 1ada42df314d..ea5805e71c14 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -320,8 +320,8 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ctr->intra_refresh_period = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: - if (ctr->profile.h264 != HFI_H264_PROFILE_HIGH && - ctr->profile.h264 != HFI_H264_PROFILE_CONSTRAINED_HIGH) + if (ctr->profile.h264 != V4L2_MPEG_VIDEO_H264_PROFILE_HIGH && + ctr->profile.h264 != V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) return -EINVAL; /* @@ -457,7 +457,7 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 0); + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1); diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig new file mode 100644 index 000000000000..9fd90672ea2d --- /dev/null +++ b/drivers/media/platform/renesas/Kconfig @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Renesas media platform drivers" + +# V4L drivers + +config VIDEO_RENESAS_CEU + tristate "Renesas Capture Engine Unit (CEU) driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + This is a v4l2 driver for the Renesas CEU Interface + +config VIDEO_RCAR_ISP + tristate "R-Car Image Signal Processor (ISP)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_RENESAS || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select RESET_CONTROLLER + select V4L2_FWNODE + help + Support for Renesas R-Car Image Signal Processor (ISP). + Enable this to support the Renesas R-Car Image Signal + Processor (ISP). + + To compile this driver as a module, choose M here: the + module will be called rcar-isp. + +config VIDEO_SH_VOU + tristate "SuperH VOU video output driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && I2C + depends on ARCH_SHMOBILE || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + help + Support for the Video Output Unit (VOU) on SuperH SoCs. + +source "drivers/media/platform/renesas/rcar-vin/Kconfig" + +# Mem2mem drivers + +config VIDEO_RENESAS_FCP + tristate "Renesas Frame Compression Processor" + depends on V4L_MEM2MEM_DRIVERS + 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 and RZ/G2 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_FDP1 + tristate "Renesas Fine Display Processor" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_RENESAS || COMPILE_TEST + depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a V4L2 driver for the Renesas Fine Display Processor + providing colour space conversion, and de-interlacing features. + + To compile this driver as a module, choose M here: the module + will be called rcar_fdp1. + +config VIDEO_RENESAS_JPU + tristate "Renesas JPEG Processing Unit" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a V4L2 driver for the Renesas JPEG Processing Unit. + + To compile this driver as a module, choose M here: the module + will be called rcar_jpu. + +config VIDEO_RENESAS_VSP1 + tristate "Renesas VSP1 Video Processing Engine" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_RENESAS || COMPILE_TEST + depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + help + This is a V4L2 driver for the Renesas VSP1 video processing engine. + + To compile this driver as a module, choose M here: the module + will be called vsp1. + +# SDR drivers + +config VIDEO_RCAR_DRIF + tristate "Renesas Digital Radio Interface (DRIF)" + depends on SDR_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_RENESAS || COMPILE_TEST + select VIDEOBUF2_VMALLOC + select V4L2_ASYNC + help + Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital + Radio Interface that interfaces with an RF front end chip. It is a + receiver of digital data which uses DMA to transfer received data to + a configured location for an application to use. + + To compile this driver as a module, choose M here; the module + will be called rcar_drif. diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile new file mode 100644 index 000000000000..3ec226ef5fd2 --- /dev/null +++ b/drivers/media/platform/renesas/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Renesas capture/playback device drivers. +# + +obj-y += rcar-vin/ +obj-y += vsp1/ + +obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o +obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o +obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o +obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o +obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o +obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o +obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c index eb59a3ba6d0f..eb59a3ba6d0f 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/renesas/rcar-fcp.c diff --git a/drivers/media/platform/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 2ffab30bc011..10b3474f93a4 100644 --- a/drivers/media/platform/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -17,6 +17,7 @@ #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <media/mipi-csi2.h> #include <media/v4l2-subdev.h> #define ISPINPUTSEL0_REG 0x0008 @@ -51,12 +52,31 @@ struct rcar_isp_format { }; static const struct rcar_isp_format rcar_isp_formats[] = { - { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .procmode = 0x15 }, - { .code = MEDIA_BUS_FMT_Y10_1X10, .datatype = 0x2b, .procmode = 0x10 }, - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .procmode = 0x0c }, - { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .procmode = 0x0c }, - { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .procmode = 0x0c }, - { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .procmode = 0x0c }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .datatype = MIPI_CSI2_DT_RGB888, + .procmode = 0x15 + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x10, + }, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_YUYV10_2X10, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, }; static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code) diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/renesas/rcar-vin/Kconfig index 030312d862e7..de55fe63d84c 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/renesas/rcar-vin/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_RCAR_CSI2 tristate "R-Car MIPI CSI-2 Receiver" - depends on VIDEO_V4L2 && OF + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF depends on ARCH_RENESAS || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API @@ -16,7 +17,8 @@ config VIDEO_RCAR_CSI2 config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" - depends on VIDEO_V4L2 && OF + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF depends on ARCH_RENESAS || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/renesas/rcar-vin/Makefile index 00d809f5d2c1..00d809f5d2c1 100644 --- a/drivers/media/platform/rcar-vin/Makefile +++ b/drivers/media/platform/renesas/rcar-vin/Makefile diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 0186ae235113..64cb05b3907c 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -743,27 +743,6 @@ static int rvin_parallel_init(struct rvin_dev *vin) * CSI-2 */ -static unsigned int rvin_csi2_get_mask(struct rvin_dev *vin, - enum rvin_csi_id csi_id, - unsigned char channel) -{ - const struct rvin_group_route *route; - unsigned int mask = 0; - - for (route = vin->info->routes; route->mask; route++) { - if (route->vin == vin->id && - route->csi == csi_id && - route->channel == channel) { - vin_dbg(vin, - "Adding route: vin: %d csi: %d channel: %d\n", - route->vin, route->csi, route->channel); - mask |= route->mask; - } - } - - return mask; -} - /* * Link setup for the links between a VIN and a CSI-2 receiver is a bit * complex. The reason for this is that the register controlling routing @@ -793,12 +772,10 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags, { struct rvin_group *group = container_of(link->graph_obj.mdev, struct rvin_group, mdev); - unsigned int master_id, channel, mask_new, i; - unsigned int mask = ~0; struct media_entity *entity; struct video_device *vdev; - struct media_pad *csi_pad; - struct rvin_dev *vin = NULL; + struct rvin_dev *vin; + unsigned int i; int csi_id, ret; ret = v4l2_pipeline_link_notify(link, flags, notification); @@ -816,41 +793,16 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags, * running streams. */ media_device_for_each_entity(entity, &group->mdev) - if (entity->stream_count) + if (media_entity_is_streaming(entity)) return -EBUSY; - mutex_lock(&group->lock); - /* Find the master VIN that controls the routes. */ vdev = media_entity_to_video_device(link->sink->entity); vin = container_of(vdev, struct rvin_dev, vdev); - master_id = rvin_group_id_to_master(vin->id); - if (WARN_ON(!group->vin[master_id])) { - ret = -ENODEV; - goto out; - } - - /* Build a mask for already enabled links. */ - for (i = master_id; i < master_id + 4; i++) { - if (!group->vin[i]) - continue; - - /* Get remote CSI-2, if any. */ - csi_pad = media_entity_remote_pad( - &group->vin[i]->vdev.entity.pads[0]); - if (!csi_pad) - continue; - - csi_id = rvin_group_entity_to_remote_id(group, csi_pad->entity); - channel = rvin_group_csi_pad_to_channel(csi_pad->index); - - mask &= rvin_csi2_get_mask(group->vin[i], csi_id, channel); - } + mutex_lock(&group->lock); - /* Add the new link to the existing mask and check if it works. */ csi_id = rvin_group_entity_to_remote_id(group, link->source->entity); - if (csi_id == -ENODEV) { struct v4l2_subdev *sd; @@ -875,25 +827,58 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags, vin_err(vin, "Subdevice %s not registered to any VIN\n", link->source->entity->name); ret = -ENODEV; - goto out; - } + } else { + const struct rvin_group_route *route; + unsigned int chsel = UINT_MAX; + unsigned int master_id; - channel = rvin_group_csi_pad_to_channel(link->source->index); - mask_new = mask & rvin_csi2_get_mask(vin, csi_id, channel); - vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); + master_id = rvin_group_id_to_master(vin->id); - if (!mask_new) { - ret = -EMLINK; - goto out; - } + if (WARN_ON(!group->vin[master_id])) { + ret = -ENODEV; + goto out; + } - /* New valid CHSEL found, set the new value. */ - ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); - if (ret) - goto out; + /* Make sure group is connected to same CSI-2 */ + for (i = master_id; i < master_id + 4; i++) { + struct media_pad *csi_pad; + + if (!group->vin[i]) + continue; + + /* Get remote CSI-2, if any. */ + csi_pad = media_entity_remote_pad( + &group->vin[i]->vdev.entity.pads[0]); + if (!csi_pad) + continue; + + if (csi_pad->entity != link->source->entity) { + vin_dbg(vin, "Already attached to %s\n", + csi_pad->entity->name); + ret = -EBUSY; + goto out; + } + } + + for (route = vin->info->routes; route->chsel; route++) { + if (route->master == master_id && route->csi == csi_id) { + chsel = route->chsel; + break; + } + } - vin->is_csi = true; + if (chsel == UINT_MAX) { + vin_err(vin, "No CHSEL value found\n"); + ret = -EINVAL; + goto out; + } + + ret = rvin_set_channel_routing(group->vin[master_id], chsel); + if (ret) + goto out; + vin->is_csi = true; + } out: mutex_unlock(&group->lock); @@ -904,48 +889,60 @@ static const struct media_device_ops rvin_csi2_media_ops = { .link_notify = rvin_csi2_link_notify, }; +static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id, + const struct rvin_group_route *route) + +{ + struct media_entity *source = &group->remotes[route->csi].subdev->entity; + struct media_entity *sink = &group->vin[id]->vdev.entity; + struct media_pad *sink_pad = &sink->pads[0]; + unsigned int channel; + int ret; + + for (channel = 0; channel < 4; channel++) { + unsigned int source_idx = rvin_group_csi_channel_to_pad(channel); + struct media_pad *source_pad = &source->pads[source_idx]; + + /* Skip if link already exists. */ + if (media_entity_find_link(source_pad, sink_pad)) + continue; + + ret = media_create_pad_link(source, source_idx, sink, 0, 0); + if (ret) + return ret; + } + + return 0; +} + static int rvin_csi2_setup_links(struct rvin_dev *vin) { const struct rvin_group_route *route; + unsigned int id; int ret = -EINVAL; /* Create all media device links between VINs and CSI-2's. */ mutex_lock(&vin->group->lock); - for (route = vin->info->routes; route->mask; route++) { - struct media_pad *source_pad, *sink_pad; - struct media_entity *source, *sink; - unsigned int source_idx; - - /* Check that VIN is part of the group. */ - if (!vin->group->vin[route->vin]) - continue; - + for (route = vin->info->routes; route->chsel; route++) { /* Check that VIN' master is part of the group. */ - if (!vin->group->vin[rvin_group_id_to_master(route->vin)]) + if (!vin->group->vin[route->master]) continue; /* Check that CSI-2 is part of the group. */ if (!vin->group->remotes[route->csi].subdev) continue; - source = &vin->group->remotes[route->csi].subdev->entity; - source_idx = rvin_group_csi_channel_to_pad(route->channel); - source_pad = &source->pads[source_idx]; - - sink = &vin->group->vin[route->vin]->vdev.entity; - sink_pad = &sink->pads[0]; - - /* Skip if link already exists. */ - if (media_entity_find_link(source_pad, sink_pad)) - continue; + for (id = route->master; id < route->master + 4; id++) { + /* Check that VIN is part of the group. */ + if (!vin->group->vin[id]) + continue; - ret = media_create_pad_link(source, source_idx, sink, 0, 0); - if (ret) { - vin_err(vin, "Error adding link from %s to %s\n", - source->name, sink->name); - break; + ret = rvin_csi2_create_link(vin->group, id, route); + if (ret) + goto out; } } +out: mutex_unlock(&vin->group->lock); return ret; @@ -1155,30 +1152,9 @@ static const struct rvin_info rcar_info_gen2 = { }; static const struct rvin_group_route rcar_info_r8a774e1_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, { /* Sentinel */ } }; @@ -1191,38 +1167,10 @@ static const struct rvin_info rcar_info_r8a774e1 = { }; static const struct rvin_group_route rcar_info_r8a7795_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1236,48 +1184,12 @@ static const struct rvin_info rcar_info_r8a7795 = { }; static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) }, - { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) }, - { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, - { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, - { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) }, + { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 0, .csi = RVIN_CSI21, .chsel = 0x05 }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 4, .csi = RVIN_CSI21, .chsel = 0x05 }, + { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1290,34 +1202,10 @@ static const struct rvin_info rcar_info_r8a7795es1 = { }; static const struct rvin_group_route rcar_info_r8a7796_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1331,38 +1219,10 @@ static const struct rvin_info rcar_info_r8a7796 = { }; static const struct rvin_group_route rcar_info_r8a77965_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, - { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, - { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, - { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 }, + { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1376,13 +1236,7 @@ static const struct rvin_info rcar_info_r8a77965 = { }; static const struct rvin_group_route rcar_info_r8a77970_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1395,22 +1249,8 @@ static const struct rvin_info rcar_info_r8a77970 = { }; static const struct rvin_group_route rcar_info_r8a77980_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, - { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, - { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, - { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, - { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, - { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, - { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 }, { /* Sentinel */ } }; @@ -1424,10 +1264,7 @@ static const struct rvin_info rcar_info_r8a77980 = { }; static const struct rvin_group_route rcar_info_r8a77990_routes[] = { - { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, - { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, - { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, + { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, { /* Sentinel */ } }; diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 8c939cb3073d..fea8f00a9152 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -17,6 +17,7 @@ #include <linux/reset.h> #include <linux/sys_soc.h> +#include <media/mipi-csi2.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -412,17 +413,51 @@ struct rcar_csi2_format { }; static const struct rcar_csi2_format rcar_csi2_formats[] = { - { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .bpp = 24 }, - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, - { .code = MEDIA_BUS_FMT_Y10_1X10, .datatype = 0x2b, .bpp = 10 }, - { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 }, - { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 }, - { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 }, - { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, - { .code = MEDIA_BUS_FMT_Y8_1X8, .datatype = 0x2a, .bpp = 8 }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .datatype = MIPI_CSI2_DT_RGB888, + .bpp = 24, + }, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .bpp = 16, + }, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .bpp = 16, + }, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .bpp = 16, + }, { + .code = MEDIA_BUS_FMT_YUYV10_2X10, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .bpp = 20, + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, }; static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) @@ -468,6 +503,8 @@ struct rcar_csi2 { struct v4l2_subdev *remote; unsigned int remote_pad; + int channel_vc[4]; + struct mutex lock; /* Protects mf and stream_count. */ struct v4l2_mbus_framefmt mf; int stream_count; @@ -603,7 +640,6 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, unsigned int *lanes) { struct v4l2_mbus_config mbus_config = { 0 }; - unsigned int num_lanes = UINT_MAX; int ret; *lanes = priv->lanes; @@ -626,23 +662,14 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return -EINVAL; } - if (mbus_config.flags & V4L2_MBUS_CSI2_1_LANE) - num_lanes = 1; - else if (mbus_config.flags & V4L2_MBUS_CSI2_2_LANE) - num_lanes = 2; - else if (mbus_config.flags & V4L2_MBUS_CSI2_3_LANE) - num_lanes = 3; - else if (mbus_config.flags & V4L2_MBUS_CSI2_4_LANE) - num_lanes = 4; - - if (num_lanes > priv->lanes) { + if (mbus_config.bus.mipi_csi2.num_data_lanes > priv->lanes) { dev_err(priv->dev, "Unsupported mbus config: too many data lanes %u\n", - num_lanes); + mbus_config.bus.mipi_csi2.num_data_lanes); return -EINVAL; } - *lanes = num_lanes; + *lanes = mbus_config.bus.mipi_csi2.num_data_lanes; return 0; } @@ -675,8 +702,11 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) for (i = 0; i < priv->info->num_channels; i++) { u32 vcdt_part; - vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | - VCDT_SEL_DT(format->datatype); + if (priv->channel_vc[i] < 0) + continue; + + vcdt_part = VCDT_SEL_VC(priv->channel_vc[i]) | VCDT_VCDTN_EN | + VCDT_SEL_DTN_ON | VCDT_SEL_DT(format->datatype); /* Store in correct reg and offset. */ if (i < 2) @@ -1258,7 +1288,52 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, * Platform Device Driver. */ +static int rcsi2_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct rcar_csi2 *priv = sd_to_csi2(sd); + struct video_device *vdev; + int channel, vc; + u32 id; + + if (!is_media_entity_v4l2_video_device(remote->entity)) { + dev_err(priv->dev, "Remote is not a video device\n"); + return -EINVAL; + } + + vdev = media_entity_to_video_device(remote->entity); + + if (of_property_read_u32(vdev->dev_parent->of_node, "renesas,id", &id)) { + dev_err(priv->dev, "No renesas,id, can't configure routing\n"); + return -EINVAL; + } + + channel = id % 4; + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (media_entity_remote_pad(local)) { + dev_dbg(priv->dev, + "Each VC can only be routed to one output channel\n"); + return -EINVAL; + } + + vc = local->index - 1; + + dev_dbg(priv->dev, "Route VC%d to VIN%u on output channel %d\n", + vc, id, channel); + } else { + vc = -1; + } + + priv->channel_vc[channel] = vc; + + return 0; +} + static const struct media_entity_operations rcar_csi2_entity_ops = { + .link_setup = rcsi2_link_setup, .link_validate = v4l2_subdev_link_validate, }; @@ -1414,7 +1489,7 @@ static const struct soc_device_attribute r8a7795[] = { .soc_id = "r8a7795", .revision = "ES2.*", .data = &rcar_csi2_info_r8a7795es2, }, - { /* sentinel */ }, + { /* sentinel */ } }; static int rcsi2_probe(struct platform_device *pdev) @@ -1477,6 +1552,9 @@ static int rcsi2_probe(struct platform_device *pdev) if (ret) goto error_async; + for (i = 0; i < ARRAY_SIZE(priv->channel_vc); i++) + priv->channel_vc[i] = -1; + pm_runtime_enable(&pdev->dev); ret = v4l2_async_register_subdev(&priv->subdev); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 8136bc75e7c4..2272f1c96aaf 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1507,7 +1507,7 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) * register. IFMD_DES1 controls data expansion mode for CSI20/21, * IFMD_DES0 controls data expansion mode for CSI40/41. */ - for (route = vin->info->routes; route->mask; route++) { + for (route = vin->info->routes; route->chsel; route++) { if (route->csi == RVIN_CSI20 || route->csi == RVIN_CSI21) ifmd |= VNCSI_IFMD_DES1; else diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 2e60b9fce03b..2e60b9fce03b 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 6c06320174a2..1f94589d9ef1 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -118,7 +118,7 @@ struct rvin_parallel_entity { struct v4l2_subdev *subdev; enum v4l2_mbus_type mbus_type; - struct v4l2_fwnode_bus_parallel bus; + struct v4l2_mbus_config_parallel bus; unsigned int source_pad; unsigned int sink_pad; @@ -128,11 +128,9 @@ struct rvin_parallel_entity { * struct rvin_group_route - describes a route from a channel of a * CSI-2 receiver to a VIN * + * @master: VIN group master ID. * @csi: CSI-2 receiver ID. - * @channel: Output channel of the CSI-2 receiver. - * @vin: VIN ID. - * @mask: Bitmask of the different CHSEL register values that - * allow for a route from @csi + @chan to @vin. + * @chsel: CHSEL register values that connects VIN group to CSI-2. * * .. note:: * Each R-Car CSI-2 receiver has four output channels facing the VIN @@ -140,19 +138,11 @@ struct rvin_parallel_entity { * There is no correlation between channel number and CSI-2 VC. It's * up to the CSI-2 receiver driver to configure which VC is output * on which channel, the VIN devices only care about output channels. - * - * There are in some cases multiple CHSEL register settings which would - * allow for the same route from @csi + @channel to @vin. For example - * on R-Car H3 both the CHSEL values 0 and 3 allow for a route from - * CSI40/VC0 to VIN0. All possible CHSEL values for a route need to be - * recorded as a bitmask in @mask, in this example bit 0 and 3 should - * be set. */ struct rvin_group_route { + unsigned int master; enum rvin_csi_id csi; - unsigned int channel; - unsigned int vin; - unsigned int mask; + unsigned int chsel; }; /** diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 9a0982fa5c6b..9a0982fa5c6b 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c index 37ecf489d112..37ecf489d112 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index 56bb464629ed..293beba131e2 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -4,7 +4,7 @@ * Copyright (C) 2014-2015 Cogent Embedded, Inc. <source@cogentembedded.com> * Copyright (C) 2014-2015 Renesas Electronics Corporation * - * This is based on the drivers/media/platform/s5p-jpeg driver by + * This is based on the drivers/media/platform/samsung/s5p-jpeg driver by * Andrzej Pietrasiewicz and Jacek Anaszewski. * Some portions of code inspired by VSP1 driver by Laurent Pinchart. * diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 2e8dbacc414e..2e8dbacc414e 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index ca4310e26c49..ca4310e26c49 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile index 4bb4dcbef7b5..4bb4dcbef7b5 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/renesas/vsp1/Makefile diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index 37cf33c7e6ca..37cf33c7e6ca 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h diff --git a/drivers/media/platform/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 89385b4cabe5..89385b4cabe5 100644 --- a/drivers/media/platform/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/renesas/vsp1/vsp1_brx.h index 6abbb8c3343c..6abbb8c3343c 100644 --- a/drivers/media/platform/vsp1/vsp1_brx.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.h diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index c5217fee24f1..c5217fee24f1 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/renesas/vsp1/vsp1_clu.h index cef2f44481ba..cef2f44481ba 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.h diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index ad3fa1c9cc73..ad3fa1c9cc73 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/renesas/vsp1/vsp1_dl.h index bebe16483ca5..bebe16483ca5 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.h diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 0c2507dc03d6..0c2507dc03d6 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..ab8b7e3161a2 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index c9044785b903..502c7d9d6890 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -550,6 +550,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1) return 0; } +static void vsp1_mask_all_interrupts(struct vsp1_device *vsp1) +{ + unsigned int i; + + for (i = 0; i < vsp1->info->lif_count; ++i) + vsp1_write(vsp1, VI6_DISP_IRQ_ENB(i), 0); + for (i = 0; i < vsp1->info->wpf_count; ++i) + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(i), 0); +} + /* * vsp1_device_get - Acquire the VSP1 device * @@ -794,9 +804,9 @@ static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; struct device_node *fcp_node; - struct resource *irq; unsigned int i; int ret; + int irq; vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); if (vsp1 == NULL) @@ -813,18 +823,9 @@ static int vsp1_probe(struct platform_device *pdev) if (IS_ERR(vsp1->mmio)) return PTR_ERR(vsp1->mmio); - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "missing IRQ\n"); - return -EINVAL; - } - - ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler, - IRQF_SHARED, dev_name(&pdev->dev), vsp1); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); - return ret; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; /* FCP (optional). */ fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); @@ -855,7 +856,6 @@ static int vsp1_probe(struct platform_device *pdev) goto done; vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); - vsp1_device_put(vsp1); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == @@ -868,12 +868,31 @@ static int vsp1_probe(struct platform_device *pdev) if (!vsp1->info) { dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", vsp1->version); + vsp1_device_put(vsp1); ret = -ENXIO; goto done; } dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version); + /* + * Previous use of the hardware (e.g. by the bootloader) could leave + * some interrupts enabled and pending. + * + * TODO: Investigate if this shouldn't be better handled by using the + * device reset provided by the CPG. + */ + vsp1_mask_all_interrupts(vsp1); + + vsp1_device_put(vsp1); + + ret = devm_request_irq(&pdev->dev, irq, vsp1_irq_handler, + IRQF_SHARED, dev_name(&pdev->dev), vsp1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto done; + } + /* Instantiate entities. */ ret = vsp1_create_entities(vsp1); if (ret < 0) { diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 823c15facd1b..823c15facd1b 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index f22724439cdc..f22724439cdc 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index bf3f981f93a1..bf3f981f93a1 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/renesas/vsp1/vsp1_hgo.h index 6b0c8580e1bf..6b0c8580e1bf 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.h diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index aa1c718e0453..aa1c718e0453 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/renesas/vsp1/vsp1_hgt.h index 38ec237bdd2d..38ec237bdd2d 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.h diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 5e5013d2cd2a..5e5013d2cd2a 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/renesas/vsp1/vsp1_histo.h index 06f029846244..06f029846244 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.h diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 361a870380c2..361a870380c2 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/renesas/vsp1/vsp1_hsit.h index a658b1aa49e7..a658b1aa49e7 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.h diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 6a6857ac9327..6a6857ac9327 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/renesas/vsp1/vsp1_lif.h index 71a4eda9c2b2..71a4eda9c2b2 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.h diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index ac6802a325f5..ac6802a325f5 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/renesas/vsp1/vsp1_lut.h index 8cb0df1b7e27..8cb0df1b7e27 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.h diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index f72ac01c21ea..f72ac01c21ea 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index ae646c9ef337..ae646c9ef337 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index fae7286eb01e..fae7286eb01e 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 85587c1b6a37..85587c1b6a37 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 22a82d218152..22a82d218152 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index eac5c04c2239..eac5c04c2239 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index b614a2aea461..b614a2aea461 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/renesas/vsp1/vsp1_sru.h index ddb00eadd1ea..ddb00eadd1ea 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.h diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 1c290cda005a..1c290cda005a 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/renesas/vsp1/vsp1_uds.h index c34f95a666d2..c34f95a666d2 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.h diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 83d7f17df80e..83d7f17df80e 100644 --- a/drivers/media/platform/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/renesas/vsp1/vsp1_uif.h index c71ab5f6a6f8..c71ab5f6a6f8 100644 --- a/drivers/media/platform/vsp1/vsp1_uif.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.h diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 044eb5778820..044eb5778820 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/renesas/vsp1/vsp1_video.h index f3cf5e2fdf5a..f3cf5e2fdf5a 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.h diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 94e91d7bb56c..94e91d7bb56c 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig new file mode 100644 index 000000000000..b41d3960c1b4 --- /dev/null +++ b/drivers/media/platform/rockchip/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Rockchip media platform drivers" + +source "drivers/media/platform/rockchip/rga/Kconfig" +source "drivers/media/platform/rockchip/rkisp1/Kconfig" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile new file mode 100644 index 000000000000..4f782b876ac9 --- /dev/null +++ b/drivers/media/platform/rockchip/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += rga/ +obj-y += rkisp1/ diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig new file mode 100644 index 000000000000..727a0f6ea466 --- /dev/null +++ b/drivers/media/platform/rockchip/rga/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_ROCKCHIP_RGA + tristate "Rockchip Raster 2d Graphic Acceleration Unit" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_DMA_SG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator. + Rockchip RGA is a separate 2D raster graphic acceleration unit. + It accelerates 2D graphics operations, such as point/line drawing, + image scaling, rotation, BitBLT, alpha blending and image blur/sharpness. + + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig new file mode 100644 index 000000000000..dabd7e42c193 --- /dev/null +++ b/drivers/media/platform/rockchip/rkisp1/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_ROCKCHIP_ISP1 + tristate "Rockchip Image Signal Processing v1 Unit driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_ROCKCHIP || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_FWNODE + select GENERIC_PHY_MIPI_DPHY + default n + help + Enable this to support the Image Signal Processing (ISP) module + present in RK3399 SoCs. + + To compile this driver as a module, choose M here: the module + will be called rockchip-isp1. diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 768987d5f2dd..fee2aaacb26b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -249,7 +249,7 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = { .fourcc = V4L2_PIX_FMT_GREY, .uv_swap = 0, .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, - .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV400, + .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422, .mbus = MEDIA_BUS_FMT_YUYV8_2X8, }, /* rgb */ @@ -631,12 +631,26 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) rkisp1_write(cap->rkisp1, buff_addr[RKISP1_PLANE_Y], cap->config->mi.y_base_ad_init); - rkisp1_write(cap->rkisp1, - buff_addr[RKISP1_PLANE_CB], - cap->config->mi.cb_base_ad_init); - rkisp1_write(cap->rkisp1, - buff_addr[RKISP1_PLANE_CR], - cap->config->mi.cr_base_ad_init); + /* + * In order to support grey format we capture + * YUV422 planar format from the camera and + * set the U and V planes to the dummy buffer + */ + if (cap->pix.cfg->fourcc == V4L2_PIX_FMT_GREY) { + rkisp1_write(cap->rkisp1, + cap->buf.dummy.dma_addr, + cap->config->mi.cb_base_ad_init); + rkisp1_write(cap->rkisp1, + cap->buf.dummy.dma_addr, + cap->config->mi.cr_base_ad_init); + } else { + rkisp1_write(cap->rkisp1, + buff_addr[RKISP1_PLANE_CB], + cap->config->mi.cb_base_ad_init); + rkisp1_write(cap->rkisp1, + buff_addr[RKISP1_PLANE_CR], + cap->config->mi.cr_base_ad_init); + } } else { /* * Use the dummy space allocated by dma_alloc_coherent to diff --git a/drivers/media/platform/samsung/Kconfig b/drivers/media/platform/samsung/Kconfig new file mode 100644 index 000000000000..0e34c5fc1dfc --- /dev/null +++ b/drivers/media/platform/samsung/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Samsung media platform drivers" + +source "drivers/media/platform/samsung/exynos-gsc/Kconfig" +source "drivers/media/platform/samsung/exynos4-is/Kconfig" +source "drivers/media/platform/samsung/s3c-camif/Kconfig" +source "drivers/media/platform/samsung/s5p-g2d/Kconfig" +source "drivers/media/platform/samsung/s5p-jpeg/Kconfig" +source "drivers/media/platform/samsung/s5p-mfc/Kconfig" diff --git a/drivers/media/platform/samsung/Makefile b/drivers/media/platform/samsung/Makefile new file mode 100644 index 000000000000..21fea3330e4b --- /dev/null +++ b/drivers/media/platform/samsung/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += exynos-gsc/ +obj-y += exynos4-is/ +obj-y += s3c-camif/ +obj-y += s5p-g2d/ +obj-y += s5p-jpeg/ +obj-y += s5p-mfc/ diff --git a/drivers/media/platform/samsung/exynos-gsc/Kconfig b/drivers/media/platform/samsung/exynos-gsc/Kconfig new file mode 100644 index 000000000000..7244d63c9646 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SAMSUNG_EXYNOS_GSC + tristate "Samsung Exynos G-Scaler driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. diff --git a/drivers/media/platform/exynos-gsc/Makefile b/drivers/media/platform/samsung/exynos-gsc/Makefile index bcefbad17a73..bcefbad17a73 100644 --- a/drivers/media/platform/exynos-gsc/Makefile +++ b/drivers/media/platform/samsung/exynos-gsc/Makefile diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index cfd6ae70b8d8..e3559b047092 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -1106,9 +1106,9 @@ MODULE_DEVICE_TABLE(of, exynos_gsc_match); static int gsc_probe(struct platform_device *pdev) { struct gsc_dev *gsc; - struct resource *res; struct device *dev = &pdev->dev; const struct gsc_driverdata *drv_data = of_device_get_match_data(dev); + int irq; int ret; int i; @@ -1141,11 +1141,9 @@ static int gsc_probe(struct platform_device *pdev) if (IS_ERR(gsc->regs)) return PTR_ERR(gsc->regs); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "failed to get IRQ resource\n"); - return -ENXIO; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; for (i = 0; i < gsc->num_clocks; i++) { gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]); @@ -1167,8 +1165,8 @@ static int gsc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, res->start, gsc_irq_handler, - 0, pdev->name, gsc); + ret = devm_request_irq(dev, irq, gsc_irq_handler, + 0, pdev->name, gsc); if (ret) { dev_err(dev, "failed to install irq (%d)\n", ret); goto err_clk; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h index e894e85e84a4..e894e85e84a4 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c index f1cf847d1cc2..f1cf847d1cc2 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c index 995a1f0f875d..995a1f0f875d 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.h b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h index d4f7ead6b322..d4f7ead6b322 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.h +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/samsung/exynos4-is/Kconfig index 136d3b2a0fbb..da33faa7132e 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/samsung/exynos4-is/Kconfig @@ -2,7 +2,8 @@ config VIDEO_SAMSUNG_EXYNOS4_IS tristate "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" - depends on VIDEO_V4L2 && OF && COMMON_CLK + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF && COMMON_CLK depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/samsung/exynos4-is/Makefile index a5ab01c73b95..a5ab01c73b95 100644 --- a/drivers/media/platform/exynos4-is/Makefile +++ b/drivers/media/platform/samsung/exynos4-is/Makefile diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c index 944b224eb621..023f624d29d5 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/samsung/exynos4-is/common.c @@ -10,7 +10,10 @@ #include <media/drv-intf/exynos-fimc.h> #include "common.h" -/* Called with the media graph mutex held or entity->stream_count > 0. */ +/* + * Called with the media graph mutex held or media_entity_is_streaming(entity) + * true. + */ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) { struct media_pad *pad = &entity->pads[0]; diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/samsung/exynos4-is/common.h index 0389b66e5144..0389b66e5144 100644 --- a/drivers/media/platform/exynos4-is/common.h +++ b/drivers/media/platform/samsung/exynos4-is/common.h diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index 7ff4024003f4..7ff4024003f4 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index bfdee771cef9..91cc8d58a663 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -926,6 +926,7 @@ static int fimc_probe(struct platform_device *pdev) struct fimc_dev *fimc; struct resource *res; int ret = 0; + int irq; fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) @@ -965,11 +966,9 @@ static int fimc_probe(struct platform_device *pdev) if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "Failed to get IRQ resource\n"); - return -ENXIO; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ret = fimc_clk_get(fimc); if (ret) @@ -986,7 +985,7 @@ static int fimc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = devm_request_irq(dev, res->start, fimc_irq_handler, + ret = devm_request_irq(dev, irq, fimc_irq_handler, 0, dev_name(dev), fimc); if (ret < 0) { dev_err(dev, "failed to install irq (%d)\n", ret); diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/samsung/exynos4-is/fimc-core.h index 7a058f3e6298..7a058f3e6298 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-command.h index 87978609ad55..87978609ad55 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-command.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-command.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c index 5d9f4c1cdc5e..5d9f4c1cdc5e 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-errno.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h index da36b48b8f9f..da36b48b8f9f 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c index 83a28ef8e099..83a28ef8e099 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h index a23bd20be6c8..a23bd20be6c8 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c index 9c816ae3b3e5..9c816ae3b3e5 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h index 206904674927..206904674927 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c index 366e6393817d..366e6393817d 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h index 5d8b01bc84a2..5d8b01bc84a2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c index 0e5b9fede4ae..0e5b9fede4ae 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h index 9aefc63889de..9aefc63889de 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index e55e411038f4..e55e411038f4 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/samsung/exynos4-is/fimc-is.h index 06586e455b1d..06586e455b1d 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.h diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c index 83688a7982f7..83688a7982f7 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h index edcb3a5e3cb9..edcb3a5e3cb9 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c index 855235bea46d..b85986e50f46 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c @@ -226,7 +226,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, } } } else { - if (sd->entity.stream_count == 0) { + if (!media_entity_is_streaming(&sd->entity)) { if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { struct v4l2_subdev_format format = *fmt; diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/samsung/exynos4-is/fimc-isp.h index 12017cd924d9..12017cd924d9 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.h diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c index 57996b4104b4..57996b4104b4 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.h b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h index c5656e902750..c5656e902750 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index aaa3af0493ce..2e8f476efc5c 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -1073,7 +1073,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, mutex_lock(&fimc->lock); if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && - sd->entity.stream_count > 0) || + media_entity_is_streaming(&sd->entity)) || (atomic_read(&fimc->out_path) == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { mutex_unlock(&fimc->lock); @@ -1197,8 +1197,8 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) * Find sensor subdev linked to FIMC-LITE directly or through * MIPI-CSIS. This is required for configuration where FIMC-LITE * is used as a subdev only and feeds data internally to FIMC-IS. - * The pipeline links are protected through entity.stream_count - * so there is no need to take the media graph mutex here. + * The pipeline links are protected through entity.pipe so there is no + * need to take the media graph mutex here. */ fimc->sensor = fimc_find_remote_sensor(&sd->entity); @@ -1454,6 +1454,7 @@ static int fimc_lite_probe(struct platform_device *pdev) struct fimc_lite *fimc; struct resource *res; int ret; + int irq; if (!dev->of_node) return -ENODEV; @@ -1485,17 +1486,15 @@ static int fimc_lite_probe(struct platform_device *pdev) if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "Failed to get IRQ resource\n"); - return -ENXIO; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ret = fimc_lite_clk_get(fimc); if (ret) return ret; - ret = devm_request_irq(dev, res->start, flite_irq_handler, + ret = devm_request_irq(dev, irq, flite_irq_handler, 0, dev_name(dev), fimc); if (ret) { dev_err(dev, "Failed to install irq (%d)\n", ret); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/samsung/exynos4-is/fimc-lite.h index ddf29e0b5b1c..ddf29e0b5b1c 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.h diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c index df8e2aa454d8..df8e2aa454d8 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/samsung/exynos4-is/fimc-reg.c index 95165a2cc7d1..95165a2cc7d1 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-reg.c diff --git a/drivers/media/platform/exynos4-is/fimc-reg.h b/drivers/media/platform/samsung/exynos4-is/fimc-reg.h index b9b33aa1f12f..b9b33aa1f12f 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-reg.h diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index 544b54e428c9..544b54e428c9 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h index 62ad5d7e035a..62ad5d7e035a 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 27a214936cb0..27a214936cb0 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c diff --git a/drivers/media/platform/exynos4-is/mipi-csis.h b/drivers/media/platform/samsung/exynos4-is/mipi-csis.h index 193f253c7907..193f253c7907 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.h +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.h diff --git a/drivers/media/platform/samsung/s3c-camif/Kconfig b/drivers/media/platform/samsung/s3c-camif/Kconfig new file mode 100644 index 000000000000..8cb8d1ac3edc --- /dev/null +++ b/drivers/media/platform/samsung/s3c-camif/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_S3C_CAMIF + tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && I2C && PM + depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + help + This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera + host interface (CAMIF). + + To compile this driver as a module, choose M here: the module + will be called s3c-camif. diff --git a/drivers/media/platform/s3c-camif/Makefile b/drivers/media/platform/samsung/s3c-camif/Makefile index 70ee042a3dae..70ee042a3dae 100644 --- a/drivers/media/platform/s3c-camif/Makefile +++ b/drivers/media/platform/samsung/s3c-camif/Makefile diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 140854ab4dd8..140854ab4dd8 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index 6e8ef86566b7..6e8ef86566b7 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/samsung/s3c-camif/camif-core.h index f3442e251bc9..f3442e251bc9 100644 --- a/drivers/media/platform/s3c-camif/camif-core.h +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.h diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/samsung/s3c-camif/camif-regs.c index e80204f5720c..e80204f5720c 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-regs.c diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/samsung/s3c-camif/camif-regs.h index 052948a7b669..052948a7b669 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.h +++ b/drivers/media/platform/samsung/s3c-camif/camif-regs.h diff --git a/drivers/media/platform/samsung/s5p-g2d/Kconfig b/drivers/media/platform/samsung/s5p-g2d/Kconfig new file mode 100644 index 000000000000..28ab88fc2d7c --- /dev/null +++ b/drivers/media/platform/samsung/s5p-g2d/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_SAMSUNG_S5P_G2D + tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D + 2d graphics accelerator. + diff --git a/drivers/media/platform/s5p-g2d/Makefile b/drivers/media/platform/samsung/s5p-g2d/Makefile index ad2c5bf66a5f..ad2c5bf66a5f 100644 --- a/drivers/media/platform/s5p-g2d/Makefile +++ b/drivers/media/platform/samsung/s5p-g2d/Makefile diff --git a/drivers/media/platform/s5p-g2d/g2d-hw.c b/drivers/media/platform/samsung/s5p-g2d/g2d-hw.c index b69d3fb12502..b69d3fb12502 100644 --- a/drivers/media/platform/s5p-g2d/g2d-hw.c +++ b/drivers/media/platform/samsung/s5p-g2d/g2d-hw.c diff --git a/drivers/media/platform/s5p-g2d/g2d-regs.h b/drivers/media/platform/samsung/s5p-g2d/g2d-regs.h index b2630c6133f1..b2630c6133f1 100644 --- a/drivers/media/platform/s5p-g2d/g2d-regs.h +++ b/drivers/media/platform/samsung/s5p-g2d/g2d-regs.h diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c index fa0bb31bd2b9..dd8864779a7c 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c @@ -623,7 +623,6 @@ static int g2d_probe(struct platform_device *pdev) { struct g2d_dev *dev; struct video_device *vfd; - struct resource *res; const struct of_device_id *of_id; int ret = 0; @@ -664,14 +663,11 @@ static int g2d_probe(struct platform_device *pdev) goto put_clk_gate; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find IRQ\n"); - ret = -ENXIO; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto unprep_clk_gate; - } - dev->irq = res->start; + dev->irq = ret; ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, 0, pdev->name, dev); diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/samsung/s5p-g2d/g2d.h index c2309c1370da..c2309c1370da 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/samsung/s5p-g2d/g2d.h diff --git a/drivers/media/platform/samsung/s5p-jpeg/Kconfig b/drivers/media/platform/samsung/s5p-jpeg/Kconfig new file mode 100644 index 000000000000..11f6e99dec39 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_SAMSUNG_S5P_JPEG + tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung S5P, EXYNOS3250 + and EXYNOS4 JPEG codec diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/samsung/s5p-jpeg/Makefile index 8b0f92e27e70..8b0f92e27e70 100644 --- a/drivers/media/platform/s5p-jpeg/Makefile +++ b/drivers/media/platform/samsung/s5p-jpeg/Makefile diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c index a8d9159d5ed8..5479bc8d474d 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c * * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h index 4a5fb1b15455..5570c79f122f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.h +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c index 637a5104d948..637a5104d948 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h index 68160befce39..15af928fad76 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c index 0828cfa783fe..0828cfa783fe 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h index 3e2887526960..3e2887526960 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c index 491e9248286c..01b47b3df1e7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h index 98ddf7097562..f068d52c66b7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h index 86f376b50581..c2298b680022 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-regs.h +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h * * Register definition file for Samsung JPEG codec driver * diff --git a/drivers/media/platform/samsung/s5p-mfc/Kconfig b/drivers/media/platform/samsung/s5p-mfc/Kconfig new file mode 100644 index 000000000000..7ee3b0c8d98b --- /dev/null +++ b/drivers/media/platform/samsung/s5p-mfc/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SAMSUNG_S5P_MFC + tristate "Samsung S5P MFC Video Codec" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + help + MFC 5.1 and 6.x driver for V4L2 diff --git a/drivers/media/platform/s5p-mfc/Makefile b/drivers/media/platform/samsung/s5p-mfc/Makefile index 0b324af2ab00..0b324af2ab00 100644 --- a/drivers/media/platform/s5p-mfc/Makefile +++ b/drivers/media/platform/samsung/s5p-mfc/Makefile diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h index fadd9139b489..fadd9139b489 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h index fa49fe580e1a..fa49fe580e1a 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h index 4a7adfdaa359..4a7adfdaa359 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h index 162e3c7e920f..162e3c7e920f 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h diff --git a/drivers/media/platform/s5p-mfc/regs-mfc.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc.h index 9171e8181c18..9171e8181c18 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc.h diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index f6732f031e96..761341934925 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -1268,7 +1268,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) { struct s5p_mfc_dev *dev; struct video_device *vfd; - struct resource *res; int ret; pr_debug("%s++\n", __func__); @@ -1294,12 +1293,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get irq resource\n"); - return -ENOENT; - } - dev->irq = res->start; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + dev->irq = ret; ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, 0, pdev->name, dev); if (ret) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c index 0e88c28f4ad3..774c573dc075 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c * * Copyright (C) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h index ed4e32a12552..945d12fdceb7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h * * Copyright (C) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c index 1ea4eda9c8e0..327e54e70611 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h index 917854bffe9f..6eafa514aebc 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c index 1f42130cc865..f8588e52dfc8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h index c19884ea2bfc..9dc44460cc38 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h index c3ef4f6a42d2..5304f42c8c72 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h @@ -732,6 +732,7 @@ struct s5p_mfc_fmt { enum s5p_mfc_fmt_type type; u32 num_planes; u32 versions; + u32 flags; }; /* diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c index da138c314963..72d70984e99a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c * * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h index 7f32ef8a6b61..653ba5f3d048 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h * * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h index 752bbe4fe48e..bba5dad6dbff 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/media/platform/s5p-mfc/s5p_mfc_debug.h + * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h * * Header file for Samsung MFC (Multi Function Codec - FIMV) driver * This file contains debug macros diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c index c1d3bda8385b..4b89df8bfd18 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ @@ -62,6 +62,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_H264_MVC, @@ -69,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V6PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_H263, @@ -76,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .fourcc = V4L2_PIX_FMT_MPEG1, @@ -83,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_MPEG2, @@ -90,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_MPEG4, @@ -97,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_XVID, @@ -104,6 +115,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G, @@ -111,6 +123,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L, @@ -118,6 +131,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V5PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .fourcc = V4L2_PIX_FMT_VP8, @@ -125,6 +139,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V6PLUS_BITS, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .fourcc = V4L2_PIX_FMT_HEVC, @@ -132,6 +147,8 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V10_BIT, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION | + V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, }, { .fourcc = V4L2_PIX_FMT_VP9, @@ -139,6 +156,7 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_DEC, .num_planes = 1, .versions = MFC_V10_BIT, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h index 0e9a0e3bbbe7..0c52ab46cff7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c index 1fad99edb091..a8877d805b29 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c * * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h index cacd1ca43e19..3f1b1a037a4f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c index 0a38f6d70ee9..0a38f6d70ee9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h index d32860db17d2..d32860db17d2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h index 1a32266b7ddc..1a32266b7ddc 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c index bb65671eea91..673962301173 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * drivers/media/platform/s5p-mfc/s5p_mfc_opr.c + * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c * * Samsung MFC (Multi Function Codec - FIMV) driver * This file contains hw related functions. diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h index 1c5d2d4c0543..b9831275f3ab 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/media/platform/s5p-mfc/s5p_mfc_opr.h + * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h * * Header file for Samsung MFC (Multi Function Codec - FIMV) driver * Contains declarations of hw related functions. diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c index 28a06dc343fd..28a06dc343fd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h index b53d376ead60..b53d376ead60 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c index a1453053e31a..8227004f6746 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c + * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c * * Samsung MFC (Multi Function Codec - FIMV) driver * This file contains hw related functions. diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h index 8ca514bf5e37..e4dd03c5454c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h + * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h * * Header file for Samsung MFC (Multi Function Codec - FIMV) driver * Contains declarations of hw related functions. diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c index 88b7d33c9197..72a901e99450 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c * * Copyright (c) 2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h index 3d26443189a2..4159d2364e87 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h + * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ diff --git a/drivers/media/platform/st/Kconfig b/drivers/media/platform/st/Kconfig new file mode 100644 index 000000000000..b29c258ea5fc --- /dev/null +++ b/drivers/media/platform/st/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "STMicroelectronics media platform drivers" + +source "drivers/media/platform/st/sti/Kconfig" +source "drivers/media/platform/st/stm32/Kconfig" diff --git a/drivers/media/platform/st/Makefile b/drivers/media/platform/st/Makefile new file mode 100644 index 000000000000..a1f75b2a8225 --- /dev/null +++ b/drivers/media/platform/st/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += sti/bdisp/ +obj-y += sti/c8sectpfe/ +obj-y += sti/delta/ +obj-y += sti/hva/ +obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/Kconfig b/drivers/media/platform/st/sti/Kconfig new file mode 100644 index 000000000000..60068e8b47b8 --- /dev/null +++ b/drivers/media/platform/st/sti/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +source "drivers/media/platform/st/sti/bdisp/Kconfig" +source "drivers/media/platform/st/sti/c8sectpfe/Kconfig" +source "drivers/media/platform/st/sti/delta/Kconfig" +source "drivers/media/platform/st/sti/hva/Kconfig" diff --git a/drivers/media/platform/st/sti/Makefile b/drivers/media/platform/st/sti/Makefile new file mode 100644 index 000000000000..f9ce8169b040 --- /dev/null +++ b/drivers/media/platform/st/sti/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += bdisp/ +obj-y += c8sectpfe/ +obj-y += delta/ +obj-y += hva/ +obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/bdisp/Kconfig b/drivers/media/platform/st/sti/bdisp/Kconfig new file mode 100644 index 000000000000..496f8aedf0a4 --- /dev/null +++ b/drivers/media/platform/st/sti/bdisp/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_STI_BDISP + tristate "STMicroelectronics BDISP 2D blitter driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STI || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. diff --git a/drivers/media/platform/sti/bdisp/Makefile b/drivers/media/platform/st/sti/bdisp/Makefile index 39ade0a34723..39ade0a34723 100644 --- a/drivers/media/platform/sti/bdisp/Makefile +++ b/drivers/media/platform/st/sti/bdisp/Makefile diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c index a27f638df11c..a27f638df11c 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c diff --git a/drivers/media/platform/sti/bdisp/bdisp-filter.h b/drivers/media/platform/st/sti/bdisp/bdisp-filter.h index 9e1a95fd27ed..9e1a95fd27ed 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-filter.h +++ b/drivers/media/platform/st/sti/bdisp/bdisp-filter.h diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/st/sti/bdisp/bdisp-hw.c index a74e9fd65238..a74e9fd65238 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-hw.c diff --git a/drivers/media/platform/sti/bdisp/bdisp-reg.h b/drivers/media/platform/st/sti/bdisp/bdisp-reg.h index b07ecc903707..b07ecc903707 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-reg.h +++ b/drivers/media/platform/st/sti/bdisp/bdisp-reg.h diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index 01ce7b711774..5aa79d9277c8 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -1284,7 +1284,6 @@ static int bdisp_remove(struct platform_device *pdev) static int bdisp_probe(struct platform_device *pdev) { struct bdisp_dev *bdisp; - struct resource *res; struct device *dev = &pdev->dev; int ret; @@ -1335,14 +1334,11 @@ static int bdisp_probe(struct platform_device *pdev) goto err_wq; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "failed to get IRQ resource\n"); - ret = -EINVAL; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto err_clk; - } - ret = devm_request_threaded_irq(dev, res->start, bdisp_irq_handler, + ret = devm_request_threaded_irq(dev, ret, bdisp_irq_handler, bdisp_irq_thread, IRQF_ONESHOT, pdev->name, bdisp); if (ret) { diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/st/sti/bdisp/bdisp.h index 3fb009d24791..3fb009d24791 100644 --- a/drivers/media/platform/sti/bdisp/bdisp.h +++ b/drivers/media/platform/st/sti/bdisp/bdisp.h diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig index 369509e03071..702b910509c9 100644 --- a/drivers/media/platform/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/st/sti/c8sectpfe/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DVB_C8SECTPFE tristate "STMicroelectronics C8SECTPFE DVB support" + depends on DVB_PLATFORM_DRIVERS depends on PINCTRL && DVB_CORE && I2C depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST select FW_LOADER diff --git a/drivers/media/platform/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile index aedfc725cc19..aedfc725cc19 100644 --- a/drivers/media/platform/sti/c8sectpfe/Makefile +++ b/drivers/media/platform/st/sti/c8sectpfe/Makefile diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c index 5df67da25525..5df67da25525 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h index 5ab7ca448cf9..5ab7ca448cf9 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index 7bb1384e4bad..7bb1384e4bad 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h index c9d6021904cd..c9d6021904cd 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c index 301fa10f419b..301fa10f419b 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h index d2c35fb32d7e..d2c35fb32d7e 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c index feb48cb546d7..feb48cb546d7 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h index 3d87a9ae8702..3d87a9ae8702 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h diff --git a/drivers/media/platform/st/sti/delta/Kconfig b/drivers/media/platform/st/sti/delta/Kconfig new file mode 100644 index 000000000000..efa936b1cc8a --- /dev/null +++ b/drivers/media/platform/st/sti/delta/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_STI_DELTA + tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STI || COMPILE_TEST + help + This V4L2 driver enables DELTA multi-format video decoder + of STMicroelectronics STiH4xx SoC series allowing hardware + decoding of various compressed video bitstream format in + raw uncompressed format. + + Use this option to see the decoders available for such + hardware. + + Please notice that the driver will only be built if + at least one of the DELTA decoder below is selected. + +config VIDEO_STI_DELTA_MJPEG + bool "STMicroelectronics DELTA MJPEG support" + default y + depends on VIDEO_STI_DELTA + help + Enables DELTA MJPEG hardware support. + + To compile this driver as a module, choose M here: + the module will be called st-delta. + +config VIDEO_STI_DELTA_DRIVER + tristate + depends on VIDEO_STI_DELTA + depends on VIDEO_STI_DELTA_MJPEG + default VIDEO_STI_DELTA_MJPEG + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select RPMSG diff --git a/drivers/media/platform/sti/delta/Makefile b/drivers/media/platform/st/sti/delta/Makefile index 32412fa4c632..32412fa4c632 100644 --- a/drivers/media/platform/sti/delta/Makefile +++ b/drivers/media/platform/st/sti/delta/Makefile diff --git a/drivers/media/platform/sti/delta/delta-cfg.h b/drivers/media/platform/st/sti/delta/delta-cfg.h index f47c6e6ff083..f47c6e6ff083 100644 --- a/drivers/media/platform/sti/delta/delta-cfg.h +++ b/drivers/media/platform/st/sti/delta/delta-cfg.h diff --git a/drivers/media/platform/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c index 4b2eb6b63aa2..4b2eb6b63aa2 100644 --- a/drivers/media/platform/sti/delta/delta-debug.c +++ b/drivers/media/platform/st/sti/delta/delta-debug.c diff --git a/drivers/media/platform/sti/delta/delta-debug.h b/drivers/media/platform/st/sti/delta/delta-debug.h index fa90252623e1..fa90252623e1 100644 --- a/drivers/media/platform/sti/delta/delta-debug.h +++ b/drivers/media/platform/st/sti/delta/delta-debug.h diff --git a/drivers/media/platform/sti/delta/delta-ipc.c b/drivers/media/platform/st/sti/delta/delta-ipc.c index 21d3e08e259a..21d3e08e259a 100644 --- a/drivers/media/platform/sti/delta/delta-ipc.c +++ b/drivers/media/platform/st/sti/delta/delta-ipc.c diff --git a/drivers/media/platform/sti/delta/delta-ipc.h b/drivers/media/platform/st/sti/delta/delta-ipc.h index 9fba6b5d169a..9fba6b5d169a 100644 --- a/drivers/media/platform/sti/delta/delta-ipc.h +++ b/drivers/media/platform/st/sti/delta/delta-ipc.h diff --git a/drivers/media/platform/sti/delta/delta-mem.c b/drivers/media/platform/st/sti/delta/delta-mem.c index aeccd50583da..aeccd50583da 100644 --- a/drivers/media/platform/sti/delta/delta-mem.c +++ b/drivers/media/platform/st/sti/delta/delta-mem.c diff --git a/drivers/media/platform/sti/delta/delta-mem.h b/drivers/media/platform/st/sti/delta/delta-mem.h index ff7d02f00b28..ff7d02f00b28 100644 --- a/drivers/media/platform/sti/delta/delta-mem.h +++ b/drivers/media/platform/st/sti/delta/delta-mem.h diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-dec.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c index 0533d4a083d2..0533d4a083d2 100644 --- a/drivers/media/platform/sti/delta/delta-mjpeg-dec.c +++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-fw.h b/drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h index 5a9404f4d055..5a9404f4d055 100644 --- a/drivers/media/platform/sti/delta/delta-mjpeg-fw.h +++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-hdr.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c index 90e5b2f72c82..90e5b2f72c82 100644 --- a/drivers/media/platform/sti/delta/delta-mjpeg-hdr.c +++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c diff --git a/drivers/media/platform/sti/delta/delta-mjpeg.h b/drivers/media/platform/st/sti/delta/delta-mjpeg.h index 43f7a88b6e59..43f7a88b6e59 100644 --- a/drivers/media/platform/sti/delta/delta-mjpeg.h +++ b/drivers/media/platform/st/sti/delta/delta-mjpeg.h diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c index c887a31ebb54..c887a31ebb54 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c diff --git a/drivers/media/platform/sti/delta/delta.h b/drivers/media/platform/st/sti/delta/delta.h index 914556030e70..914556030e70 100644 --- a/drivers/media/platform/sti/delta/delta.h +++ b/drivers/media/platform/st/sti/delta/delta.h diff --git a/drivers/media/platform/st/sti/hva/Kconfig b/drivers/media/platform/st/sti/hva/Kconfig new file mode 100644 index 000000000000..46d6f82f648e --- /dev/null +++ b/drivers/media/platform/st/sti/hva/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_STI_HVA + tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STI || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format + video encoder of STMicroelectronics SoC, allowing hardware encoding of + raw uncompressed formats in various compressed video bitstreams format. + + To compile this driver as a module, choose M here: + the module will be called st-hva. + +config VIDEO_STI_HVA_DEBUGFS + bool "Export STMicroelectronics HVA internals in debugfs" + depends on VIDEO_STI_HVA + depends on DEBUG_FS + help + Select this to see information about the internal state and the last + operation of STMicroelectronics HVA multi-format video encoder in + debugfs. + + Choose N unless you know you need this. diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/st/sti/hva/Makefile index b5a5478bdd01..b5a5478bdd01 100644 --- a/drivers/media/platform/sti/hva/Makefile +++ b/drivers/media/platform/st/sti/hva/Makefile diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/st/sti/hva/hva-debugfs.c index a86a07b6fbc7..a86a07b6fbc7 100644 --- a/drivers/media/platform/sti/hva/hva-debugfs.c +++ b/drivers/media/platform/st/sti/hva/hva-debugfs.c diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/st/sti/hva/hva-h264.c index 98cb00d2d868..98cb00d2d868 100644 --- a/drivers/media/platform/sti/hva/hva-h264.c +++ b/drivers/media/platform/st/sti/hva/hva-h264.c diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/st/sti/hva/hva-hw.c index fe4ea2e7f37e..fe4ea2e7f37e 100644 --- a/drivers/media/platform/sti/hva/hva-hw.c +++ b/drivers/media/platform/st/sti/hva/hva-hw.c diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/st/sti/hva/hva-hw.h index b298990264d5..b298990264d5 100644 --- a/drivers/media/platform/sti/hva/hva-hw.h +++ b/drivers/media/platform/st/sti/hva/hva-hw.h diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/st/sti/hva/hva-mem.c index 68047b60b66c..68047b60b66c 100644 --- a/drivers/media/platform/sti/hva/hva-mem.c +++ b/drivers/media/platform/st/sti/hva/hva-mem.c diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/st/sti/hva/hva-mem.h index fec549dff2b3..fec549dff2b3 100644 --- a/drivers/media/platform/sti/hva/hva-mem.h +++ b/drivers/media/platform/st/sti/hva/hva-mem.h diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index bb34d6997d99..bb34d6997d99 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/st/sti/hva/hva.h index ba6b893416ec..ba6b893416ec 100644 --- a/drivers/media/platform/sti/hva/hva.h +++ b/drivers/media/platform/st/sti/hva/hva.h diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig new file mode 100644 index 000000000000..b22dd4753496 --- /dev/null +++ b/drivers/media/platform/st/stm32/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# V4L drivers +config VIDEO_STM32_DCMI + tristate "STM32 Digital Camera Memory Interface (DCMI) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_STM32 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This module makes the STM32 Digital Camera Memory Interface (DCMI) + available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-dcmi. + +# Mem2mem drivers +config VIDEO_STM32_DMA2D + tristate "STM32 Chrom-Art Accelerator (DMA2D)" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_STM32 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Enables DMA2D hardware support on stm32. + + The STM32 DMA2D is a memory-to-memory engine for pixel conversion + and specialized DMA dedicated to image manipulation. diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 896ef98a73ab..896ef98a73ab 100644 --- a/drivers/media/platform/stm32/Makefile +++ b/drivers/media/platform/st/stm32/Makefile diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/st/stm32/dma2d/dma2d-hw.c index ea4cc84d8a39..ea4cc84d8a39 100644 --- a/drivers/media/platform/stm32/dma2d/dma2d-hw.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d-hw.c diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/st/stm32/dma2d/dma2d-regs.h index 6444592d415b..6444592d415b 100644 --- a/drivers/media/platform/stm32/dma2d/dma2d-regs.h +++ b/drivers/media/platform/st/stm32/dma2d/dma2d-regs.h diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c index 17af90d86898..9706aa41b5d2 100644 --- a/drivers/media/platform/stm32/dma2d/dma2d.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c @@ -633,14 +633,11 @@ static int dma2d_probe(struct platform_device *pdev) goto put_clk_gate; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find IRQ\n"); - ret = -ENXIO; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto unprep_clk_gate; - } - dev->irq = res->start; + dev->irq = ret; ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr, 0, pdev->name, dev); diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/st/stm32/dma2d/dma2d.h index 3f03a7ca9ee3..3f03a7ca9ee3 100644 --- a/drivers/media/platform/stm32/dma2d/dma2d.h +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.h diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index e1b17c05229c..c4c65d852525 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -113,7 +113,7 @@ struct dcmi_framesize { struct dcmi_buf { struct vb2_v4l2_buffer vb; bool prepared; - dma_addr_t paddr; + struct sg_table sgt; size_t size; struct list_head list; }; @@ -150,13 +150,14 @@ struct stm32_dcmi { struct mutex lock; struct vb2_queue queue; - struct v4l2_fwnode_bus_parallel bus; + struct v4l2_mbus_config_parallel bus; enum v4l2_mbus_type bus_type; struct completion complete; struct clk *mclk; enum state state; struct dma_chan *dma_chan; dma_cookie_t dma_cookie; + u32 dma_max_burst; u32 misr; int errors_count; int overrun_count; @@ -326,13 +327,11 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, mutex_lock(&dcmi->dma_lock); /* Prepare a DMA transaction */ - desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, - buf->size, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); + desc = dmaengine_prep_slave_sg(dcmi->dma_chan, buf->sgt.sgl, buf->sgt.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); if (!desc) { - dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n", - __func__, &buf->paddr, buf->size); + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__); mutex_unlock(&dcmi->dma_lock); return -EINVAL; } @@ -524,6 +523,10 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); unsigned long size; + unsigned int num_sgs = 1; + dma_addr_t dma_buf; + struct scatterlist *sg; + int i, ret; size = dcmi->fmt.fmt.pix.sizeimage; @@ -537,15 +540,33 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb) if (!buf->prepared) { /* Get memory addresses */ - buf->paddr = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); - buf->prepared = true; + if (buf->size > dcmi->dma_max_burst) + num_sgs = DIV_ROUND_UP(buf->size, dcmi->dma_max_burst); - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + ret = sg_alloc_table(&buf->sgt, num_sgs, GFP_ATOMIC); + if (ret) { + dev_err(dcmi->dev, "sg table alloc failed\n"); + return ret; + } + + dma_buf = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n", - vb->index, &buf->paddr, buf->size); + vb->index, &dma_buf, buf->size); + + for_each_sg(buf->sgt.sgl, sg, num_sgs, i) { + size_t bytes = min_t(size_t, size, dcmi->dma_max_burst); + + sg_dma_address(sg) = dma_buf; + sg_dma_len(sg) = bytes; + dma_buf += bytes; + size -= bytes; + } + + buf->prepared = true; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); } return 0; @@ -1866,6 +1887,7 @@ static int dcmi_probe(struct platform_device *pdev) struct stm32_dcmi *dcmi; struct vb2_queue *q; struct dma_chan *chan; + struct dma_slave_caps caps; struct clk *mclk; int irq; int ret = 0; @@ -1953,6 +1975,11 @@ static int dcmi_probe(struct platform_device *pdev) return ret; } + dcmi->dma_max_burst = UINT_MAX; + ret = dma_get_slave_caps(chan, &caps); + if (!ret && caps.max_sg_burst) + dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES; + spin_lock_init(&dcmi->irqlock); mutex_init(&dcmi->lock); mutex_init(&dcmi->dma_lock); diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig index 7151cc249afa..46b7b9bf989c 100644 --- a/drivers/media/platform/sunxi/Kconfig +++ b/drivers/media/platform/sunxi/Kconfig @@ -1,4 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 +comment "Sunxi media platform drivers" + source "drivers/media/platform/sunxi/sun4i-csi/Kconfig" source "drivers/media/platform/sunxi/sun6i-csi/Kconfig" +source "drivers/media/platform/sunxi/sun8i-di/Kconfig" +source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig" diff --git a/drivers/media/platform/sunxi/sun4i-csi/Kconfig b/drivers/media/platform/sunxi/sun4i-csi/Kconfig index 903c6152f6e8..7960e6836f41 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/Kconfig +++ b/drivers/media/platform/sunxi/sun4i-csi/Kconfig @@ -2,7 +2,8 @@ config VIDEO_SUN4I_CSI tristate "Allwinner A10 CMOS Sensor Interface Support" - depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK && HAS_DMA depends on ARCH_SUNXI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index a5f61ee0ec4d..8eeed87bfb13 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -124,7 +124,7 @@ struct sun4i_csi { dma_addr_t paddr; } scratch; - struct v4l2_fwnode_bus_parallel bus; + struct v4l2_mbus_config_parallel bus; /* Main Device */ struct v4l2_device v4l; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 2c39cd7f2862..0912a1b6d525 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -226,7 +226,7 @@ static void return_all_buffers(struct sun4i_csi *csi, static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count) { struct sun4i_csi *csi = vb2_get_drv_priv(vq); - struct v4l2_fwnode_bus_parallel *bus = &csi->bus; + struct v4l2_mbus_config_parallel *bus = &csi->bus; const struct sun4i_csi_format *csi_fmt; unsigned long href_pol, pclk_pol, vref_pol; unsigned long flags; diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig index 586e3fb3a80d..0345901617d4 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/Kconfig +++ b/drivers/media/platform/sunxi/sun6i-csi/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_SUN6I_CSI tristate "Allwinner V3s Camera Sensor Interface driver" - depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK && HAS_DMA depends on ARCH_SUNXI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index 607a8d39fbe2..682c26536034 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -368,7 +368,11 @@ static int sun6i_video_try_fmt(struct sun6i_video *video, if (pixfmt->field == V4L2_FIELD_ANY) pixfmt->field = V4L2_FIELD_NONE; - pixfmt->colorspace = V4L2_COLORSPACE_RAW; + if (pixfmt->pixelformat == V4L2_PIX_FMT_JPEG) + pixfmt->colorspace = V4L2_COLORSPACE_JPEG; + else + pixfmt->colorspace = V4L2_COLORSPACE_SRGB; + pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT; pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; diff --git a/drivers/media/platform/sunxi/sun8i-di/Kconfig b/drivers/media/platform/sunxi/sun8i-di/Kconfig new file mode 100644 index 000000000000..ff71e06ee2df --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-di/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN8I_DEINTERLACE + tristate "Allwinner Deinterlace driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on COMMON_CLK && OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Allwinner deinterlace unit with scaling + capability found on some SoCs, like H3. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/sunxi/sun8i-rotate/Kconfig b/drivers/media/platform/sunxi/sun8i-rotate/Kconfig new file mode 100644 index 000000000000..cfba29072d75 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_SUN8I_ROTATE + tristate "Allwinner DE2 rotation driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on COMMON_CLK && OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Allwinner DE2 rotation unit. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig new file mode 100644 index 000000000000..e1ab56c3be1f --- /dev/null +++ b/drivers/media/platform/ti/Kconfig @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Texas Instruments drivers" + +# TI VIDEO PORT Helper Modules +# These will be selected by VPE and VIP +config VIDEO_TI_VPDMA + tristate + +config VIDEO_TI_SC + tristate + +config VIDEO_TI_CSC + tristate + +# V4L drivers + +config VIDEO_TI_CAL + tristate "TI CAL (Camera Adaptation Layer) driver" + depends on VIDEO_DEV + depends on V4L_PLATFORM_DRIVERS + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + depends on SOC_DRA7XX || ARCH_K3 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Support for the TI CAL (Camera Adaptation Layer) block + found on DRA72X SoC. + In TI Technical Reference Manual this module is referred as + Camera Interface Subsystem (CAMSS). + +config VIDEO_TI_CAL_MC + bool "Media Controller centric mode by default" + depends on VIDEO_TI_CAL + default n + help + Enables Media Controller centric mode by default. + + If set, CAL driver will start in Media Controller mode by + default. Note that this behavior can be overridden via + module parameter 'mc_api'. + +# Mem2mem drivers + +config VIDEO_TI_VPE + tristate "TI VPE (Video Processing Engine) driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on SOC_DRA7XX || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_TI_VPDMA + select VIDEO_TI_SC + select VIDEO_TI_CSC + help + Support for the TI VPE(Video Processing Engine) block + found on DRA7XX SoC. + +config VIDEO_TI_VPE_DEBUG + bool "VPE debug messages" + depends on VIDEO_TI_VPE + help + Enable debug messages on VPE driver. + +source "drivers/media/platform/ti/am437x/Kconfig" +source "drivers/media/platform/ti/davinci/Kconfig" +source "drivers/media/platform/ti/omap/Kconfig" +source "drivers/media/platform/ti/omap3isp/Kconfig" diff --git a/drivers/media/platform/ti/Makefile b/drivers/media/platform/ti/Makefile new file mode 100644 index 000000000000..98c5fe5c40d6 --- /dev/null +++ b/drivers/media/platform/ti/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += am437x/ +obj-y += cal/ +obj-y += vpe/ +obj-y += davinci/ +obj-y += omap/ +obj-y += omap3isp/ diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/ti/am437x/Kconfig index 9ef898f512de..2e24fff7e625 100644 --- a/drivers/media/platform/am437x/Kconfig +++ b/drivers/media/platform/ti/am437x/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_AM437X_VPFE tristate "TI AM437x VPFE video capture driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on SOC_AM43XX || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/ti/am437x/Makefile index 541043487268..541043487268 100644 --- a/drivers/media/platform/am437x/Makefile +++ b/drivers/media/platform/ti/am437x/Makefile diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 2dfae9bc0bba..2dfae9bc0bba 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/ti/am437x/am437x-vpfe.h index 05ee37db0273..05ee37db0273 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.h diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/ti/am437x/am437x-vpfe_regs.h index 63ecdca3b908..63ecdca3b908 100644 --- a/drivers/media/platform/am437x/am437x-vpfe_regs.h +++ b/drivers/media/platform/ti/am437x/am437x-vpfe_regs.h diff --git a/drivers/media/platform/ti/cal/Makefile b/drivers/media/platform/ti/cal/Makefile new file mode 100644 index 000000000000..45ac35585f0b --- /dev/null +++ b/drivers/media/platform/ti/cal/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o +ti-cal-y := cal.o cal-camerarx.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 4bf7a8c2e711..6b43a1525b45 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -47,7 +47,7 @@ static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) { - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; + struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; const struct cal_format_info *fmtinfo; u32 bpp; @@ -76,7 +76,7 @@ static void cal_camerarx_lane_config(struct cal_camerarx *phy) u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; int lane; @@ -518,7 +518,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, static int cal_camerarx_parse_dt(struct cal_camerarx *phy) { struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + char data_lanes[V4L2_MBUS_CSI2_MAX_DATA_LANES * 2]; struct device_node *ep_node; unsigned int i; int ret; diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 7799da1cc261..3e936a2ca36c 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -823,6 +823,9 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) /* Enumerate sub device formats and enable all matching local formats */ ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats, sizeof(*ctx->active_fmt), GFP_KERNEL); + if (!ctx->active_fmt) + return -ENOMEM; + ctx->num_active_fmt = 0; for (j = 0, i = 0; ; ++j) { diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti/cal/cal.c index 4a4a6c5983f7..4a4a6c5983f7 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti/cal/cal.c diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti/cal/cal.h index 527e22d022f3..527e22d022f3 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti/cal/cal.h diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti/cal/cal_regs.h index 40e4f972fcb7..40e4f972fcb7 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti/cal/cal_regs.h diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/ti/davinci/Kconfig index 9d2a9eeb3499..c61e697aeb12 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/ti/davinci/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_DAVINCI_VPIF_DISPLAY tristate "TI DaVinci VPIF V4L2-Display driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF2_DMA_CONTIG @@ -17,7 +18,8 @@ config VIDEO_DAVINCI_VPIF_DISPLAY config VIDEO_DAVINCI_VPIF_CAPTURE tristate "TI DaVinci VPIF video capture driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF2_DMA_CONTIG @@ -32,7 +34,8 @@ config VIDEO_DAVINCI_VPIF_CAPTURE config VIDEO_DM6446_CCDC tristate "TI DM6446 CCDC video capture driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF_DMA_CONTIG @@ -48,7 +51,8 @@ config VIDEO_DM6446_CCDC config VIDEO_DM355_CCDC tristate "TI DM355 CCDC video capture driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF_DMA_CONTIG @@ -64,7 +68,8 @@ config VIDEO_DM355_CCDC config VIDEO_DM365_ISIF tristate "TI DM365 ISIF video capture driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF_DMA_CONTIG @@ -78,7 +83,8 @@ config VIDEO_DM365_ISIF config VIDEO_DAVINCI_VPBE_DISPLAY tristate "TI DaVinci VPBE V4L2-Display driver" - depends on VIDEO_V4L2 + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/ti/davinci/Makefile index 05c45bf371aa..05c45bf371aa 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/ti/davinci/Makefile diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/ti/davinci/ccdc_hw_device.h index a545052a95a9..a545052a95a9 100644 --- a/drivers/media/platform/davinci/ccdc_hw_device.h +++ b/drivers/media/platform/ti/davinci/ccdc_hw_device.h diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/ti/davinci/dm355_ccdc.c index e06d113dfe96..e06d113dfe96 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/ti/davinci/dm355_ccdc.c diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h index eb381f075245..eb381f075245 100644 --- a/drivers/media/platform/davinci/dm355_ccdc_regs.h +++ b/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/ti/davinci/dm644x_ccdc.c index c6378c4e0074..c6378c4e0074 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/ti/davinci/dm644x_ccdc.c diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h index c4894f6a254e..c4894f6a254e 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/ti/davinci/isif.c index c53cecd072b1..c53cecd072b1 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/ti/davinci/isif.c diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/ti/davinci/isif_regs.h index d68d38841ae7..d68d38841ae7 100644 --- a/drivers/media/platform/davinci/isif_regs.h +++ b/drivers/media/platform/ti/davinci/isif_regs.h diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/ti/davinci/vpbe.c index 5f0aeb744e81..5f0aeb744e81 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/ti/davinci/vpbe.c diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/ti/davinci/vpbe_display.c index bf3c3e76b921..bf3c3e76b921 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/ti/davinci/vpbe_display.c diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/ti/davinci/vpbe_osd.c index 32f7ef547c82..32f7ef547c82 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/ti/davinci/vpbe_osd.c diff --git a/drivers/media/platform/davinci/vpbe_osd_regs.h b/drivers/media/platform/ti/davinci/vpbe_osd_regs.h index cecd5991d4c5..cecd5991d4c5 100644 --- a/drivers/media/platform/davinci/vpbe_osd_regs.h +++ b/drivers/media/platform/ti/davinci/vpbe_osd_regs.h diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/ti/davinci/vpbe_venc.c index 4c8e31de12b1..4c8e31de12b1 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/ti/davinci/vpbe_venc.c diff --git a/drivers/media/platform/davinci/vpbe_venc_regs.h b/drivers/media/platform/ti/davinci/vpbe_venc_regs.h index 29d8fc3af662..29d8fc3af662 100644 --- a/drivers/media/platform/davinci/vpbe_venc_regs.h +++ b/drivers/media/platform/ti/davinci/vpbe_venc_regs.h diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/ti/davinci/vpfe_capture.c index 0a2226b321d7..0a2226b321d7 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/ti/davinci/vpfe_capture.c diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c index 5a89d885d0e3..97ef770266af 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/ti/davinci/vpif.c @@ -20,8 +20,10 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spinlock.h> @@ -41,6 +43,11 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME); #define VPIF_CH2_MAX_MODES 15 #define VPIF_CH3_MAX_MODES 2 +struct vpif_data { + struct platform_device *capture; + struct platform_device *display; +}; + DEFINE_SPINLOCK(vpif_lock); EXPORT_SYMBOL_GPL(vpif_lock); @@ -423,21 +430,35 @@ int vpif_channel_getfid(u8 channel_id) } EXPORT_SYMBOL(vpif_channel_getfid); +static void vpif_pdev_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + kfree(pdev); +} + static int vpif_probe(struct platform_device *pdev) { - static struct resource *res_irq; + static struct resource res_irq; struct platform_device *pdev_capture, *pdev_display; struct device_node *endpoint = NULL; + struct vpif_data *data; + int ret; + int irq; vpif_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vpif_base)) return PTR_ERR(vpif_base); + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + pm_runtime_enable(&pdev->dev); pm_runtime_get(&pdev->dev); - dev_info(&pdev->dev, "vpif probe success\n"); - /* * If VPIF Node has endpoints, assume "new" DT support, * where capture and display drivers don't have DT nodes @@ -453,49 +474,83 @@ static int vpif_probe(struct platform_device *pdev) * For DT platforms, manually create platform_devices for * capture/display drivers. */ - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_irq) { - dev_warn(&pdev->dev, "Missing IRQ resource.\n"); - pm_runtime_put(&pdev->dev); - return -EINVAL; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_put_rpm; } + res_irq = (struct resource)DEFINE_RES_IRQ_NAMED(irq, of_node_full_name(pdev->dev.of_node)); + res_irq.flags |= irq_get_trigger_type(irq); - pdev_capture = devm_kzalloc(&pdev->dev, sizeof(*pdev_capture), - GFP_KERNEL); - if (pdev_capture) { - pdev_capture->name = "vpif_capture"; - pdev_capture->id = -1; - pdev_capture->resource = res_irq; - pdev_capture->num_resources = 1; - pdev_capture->dev.dma_mask = pdev->dev.dma_mask; - pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask; - pdev_capture->dev.parent = &pdev->dev; - platform_device_register(pdev_capture); - } else { - dev_warn(&pdev->dev, "Unable to allocate memory for pdev_capture.\n"); + pdev_capture = kzalloc(sizeof(*pdev_capture), GFP_KERNEL); + if (!pdev_capture) { + ret = -ENOMEM; + goto err_put_rpm; } - pdev_display = devm_kzalloc(&pdev->dev, sizeof(*pdev_display), - GFP_KERNEL); - if (pdev_display) { - pdev_display->name = "vpif_display"; - pdev_display->id = -1; - pdev_display->resource = res_irq; - pdev_display->num_resources = 1; - pdev_display->dev.dma_mask = pdev->dev.dma_mask; - pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask; - pdev_display->dev.parent = &pdev->dev; - platform_device_register(pdev_display); - } else { - dev_warn(&pdev->dev, "Unable to allocate memory for pdev_display.\n"); + pdev_capture->name = "vpif_capture"; + pdev_capture->id = -1; + pdev_capture->resource = &res_irq; + pdev_capture->num_resources = 1; + pdev_capture->dev.dma_mask = pdev->dev.dma_mask; + pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask; + pdev_capture->dev.parent = &pdev->dev; + pdev_capture->dev.release = vpif_pdev_release; + + ret = platform_device_register(pdev_capture); + if (ret) + goto err_put_pdev_capture; + + pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL); + if (!pdev_display) { + ret = -ENOMEM; + goto err_put_pdev_capture; } + pdev_display->name = "vpif_display"; + pdev_display->id = -1; + pdev_display->resource = &res_irq; + pdev_display->num_resources = 1; + pdev_display->dev.dma_mask = pdev->dev.dma_mask; + pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask; + pdev_display->dev.parent = &pdev->dev; + pdev_display->dev.release = vpif_pdev_release; + + ret = platform_device_register(pdev_display); + if (ret) + goto err_put_pdev_display; + + data->capture = pdev_capture; + data->display = pdev_display; + return 0; + +err_put_pdev_display: + platform_device_put(pdev_display); +err_put_pdev_capture: + platform_device_put(pdev_capture); +err_put_rpm: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + kfree(data); + + return ret; } static int vpif_remove(struct platform_device *pdev) { + struct vpif_data *data = platform_get_drvdata(pdev); + + if (data->capture) + platform_device_unregister(data->capture); + if (data->display) + platform_device_unregister(data->display); + + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); + + kfree(data); + return 0; } diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/ti/davinci/vpif.h index c6d1d890478a..c6d1d890478a 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/ti/davinci/vpif.h diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 8fe55374c5a3..bf76c5c83743 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1607,7 +1607,6 @@ static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; struct i2c_adapter *i2c_adap; - struct resource *res; int subdev_count; int res_idx = 0; int i, err; @@ -1632,17 +1631,23 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_free; } - while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { - err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, - IRQF_SHARED, VPIF_DRIVER_NAME, - (void *)(&vpif_obj.dev[res_idx]-> - channel_id)); - if (err) { - err = -EINVAL; + do { + int irq; + + err = platform_get_irq_optional(pdev, res_idx); + if (err < 0 && err != -ENXIO) goto vpif_unregister; - } - res_idx++; - } + if (err > 0) + irq = err; + else + break; + + err = devm_request_irq(&pdev->dev, irq, vpif_channel_isr, + IRQF_SHARED, VPIF_DRIVER_NAME, + (void *)(&vpif_obj.dev[res_idx]->channel_id)); + if (err) + goto vpif_unregister; + } while (++res_idx); vpif_obj.config = pdev->dev.platform_data; diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/ti/davinci/vpif_capture.h index d5951f61df47..d5951f61df47 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/ti/davinci/vpif_capture.h diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index 59f6b782e104..fca148b66471 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -1221,7 +1221,6 @@ static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; struct i2c_adapter *i2c_adap; - struct resource *res; int subdev_count; int res_idx = 0; int i, err; @@ -1245,18 +1244,25 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_free; } - while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { - err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, - IRQF_SHARED, VPIF_DRIVER_NAME, - (void *)(&vpif_obj.dev[res_idx]-> - channel_id)); + do { + int irq; + + err = platform_get_irq_optional(pdev, res_idx); + if (err < 0 && err != -ENXIO) + goto vpif_unregister; + if (err > 0) + irq = err; + else + break; + + err = devm_request_irq(&pdev->dev, irq, vpif_channel_isr, + IRQF_SHARED, VPIF_DRIVER_NAME, + (void *)(&vpif_obj.dev[res_idx]->channel_id)); if (err) { - err = -EINVAL; vpif_err("VPIF IRQ request failed\n"); goto vpif_unregister; } - res_idx++; - } + } while (++res_idx); vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/ti/davinci/vpif_display.h index f98062e79167..f98062e79167 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/ti/davinci/vpif_display.h diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/ti/davinci/vpss.c index d15b991ab17c..d15b991ab17c 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/ti/davinci/vpss.c diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/ti/omap/Kconfig index de16de46c0f4..a9dbe1097775 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/ti/omap/Kconfig @@ -6,10 +6,11 @@ config VIDEO_OMAP2_VOUT_VRFB config VIDEO_OMAP2_VOUT tristate "OMAP2/OMAP3 V4L2-Display driver" + depends on V4L_PLATFORM_DRIVERS depends on MMU depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n) depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 help diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/ti/omap/Makefile index b17a0ac10c00..b17a0ac10c00 100644 --- a/drivers/media/platform/omap/Makefile +++ b/drivers/media/platform/ti/omap/Makefile diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c index 3e0d9af7ffec..3e0d9af7ffec 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/ti/omap/omap_vout.c diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/ti/omap/omap_vout_vrfb.c index 0cfa0169875f..0cfa0169875f 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/ti/omap/omap_vout_vrfb.c diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/ti/omap/omap_vout_vrfb.h index 40bc9e54ecc6..40bc9e54ecc6 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.h +++ b/drivers/media/platform/ti/omap/omap_vout_vrfb.h diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/ti/omap/omap_voutdef.h index b586193341d2..b586193341d2 100644 --- a/drivers/media/platform/omap/omap_voutdef.h +++ b/drivers/media/platform/ti/omap/omap_voutdef.h diff --git a/drivers/media/platform/omap/omap_voutlib.c b/drivers/media/platform/ti/omap/omap_voutlib.c index 480a7e95533d..480a7e95533d 100644 --- a/drivers/media/platform/omap/omap_voutlib.c +++ b/drivers/media/platform/ti/omap/omap_voutlib.c diff --git a/drivers/media/platform/omap/omap_voutlib.h b/drivers/media/platform/ti/omap/omap_voutlib.h index f9d1c0779f33..f9d1c0779f33 100644 --- a/drivers/media/platform/omap/omap_voutlib.h +++ b/drivers/media/platform/ti/omap/omap_voutlib.h diff --git a/drivers/media/platform/ti/omap3isp/Kconfig b/drivers/media/platform/ti/omap3isp/Kconfig new file mode 100644 index 000000000000..f0a680938d5e --- /dev/null +++ b/drivers/media/platform/ti/omap3isp/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_OMAP3 + tristate "OMAP 3 Camera support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && I2C + depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST + depends on COMMON_CLK && OF + select ARM_DMA_USE_IOMMU if OMAP_IOMMU + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select MFD_SYSCON + select V4L2_FWNODE + help + Driver for an OMAP 3 camera controller. + +config VIDEO_OMAP3_DEBUG + bool "OMAP 3 Camera debug messages" + depends on VIDEO_OMAP3 + help + Enable debug messages on OMAP 3 camera controller driver. diff --git a/drivers/media/platform/omap3isp/Makefile b/drivers/media/platform/ti/omap3isp/Makefile index 56e99b4f7d23..56e99b4f7d23 100644 --- a/drivers/media/platform/omap3isp/Makefile +++ b/drivers/media/platform/ti/omap3isp/Makefile diff --git a/drivers/media/platform/omap3isp/cfa_coef_table.h b/drivers/media/platform/ti/omap3isp/cfa_coef_table.h index 786200c5e4fa..786200c5e4fa 100644 --- a/drivers/media/platform/omap3isp/cfa_coef_table.h +++ b/drivers/media/platform/ti/omap3isp/cfa_coef_table.h diff --git a/drivers/media/platform/omap3isp/gamma_table.h b/drivers/media/platform/ti/omap3isp/gamma_table.h index 442c82c2eb22..442c82c2eb22 100644 --- a/drivers/media/platform/omap3isp/gamma_table.h +++ b/drivers/media/platform/ti/omap3isp/gamma_table.h diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index 4c937f3f323e..4c937f3f323e 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/ti/omap3isp/isp.h index a9d760fbf349..a9d760fbf349 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/ti/omap3isp/isp.h diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 108b5e9f82cb..108b5e9f82cb 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/ti/omap3isp/ispccdc.h index 7883365d7203..7883365d7203 100644 --- a/drivers/media/platform/omap3isp/ispccdc.h +++ b/drivers/media/platform/ti/omap3isp/ispccdc.h diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index acb58b6ddba1..acb58b6ddba1 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c diff --git a/drivers/media/platform/omap3isp/ispccp2.h b/drivers/media/platform/ti/omap3isp/ispccp2.h index 03e6af3de1d9..03e6af3de1d9 100644 --- a/drivers/media/platform/omap3isp/ispccp2.h +++ b/drivers/media/platform/ti/omap3isp/ispccp2.h diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 6302e0c94034..6302e0c94034 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c diff --git a/drivers/media/platform/omap3isp/ispcsi2.h b/drivers/media/platform/ti/omap3isp/ispcsi2.h index 036b97f8470e..036b97f8470e 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.h +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.h diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/ti/omap3isp/ispcsiphy.c index 6dc7359c5131..6dc7359c5131 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/ti/omap3isp/ispcsiphy.c diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/ti/omap3isp/ispcsiphy.h index ed9b8d221e3f..ed9b8d221e3f 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/drivers/media/platform/ti/omap3isp/ispcsiphy.h diff --git a/drivers/media/platform/omap3isp/isph3a.h b/drivers/media/platform/ti/omap3isp/isph3a.h index 5144f7689dda..5144f7689dda 100644 --- a/drivers/media/platform/omap3isp/isph3a.h +++ b/drivers/media/platform/ti/omap3isp/isph3a.h diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/ti/omap3isp/isph3a_aewb.c index e6c54c4bbfca..e6c54c4bbfca 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/ti/omap3isp/isph3a_aewb.c diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/ti/omap3isp/isph3a_af.c index de7b116d0122..de7b116d0122 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/ti/omap3isp/isph3a_af.c diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/ti/omap3isp/isphist.c index 0ef78aace6da..0ef78aace6da 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/ti/omap3isp/isphist.c diff --git a/drivers/media/platform/omap3isp/isphist.h b/drivers/media/platform/ti/omap3isp/isphist.h index 93cd27a3b617..93cd27a3b617 100644 --- a/drivers/media/platform/omap3isp/isphist.h +++ b/drivers/media/platform/ti/omap3isp/isphist.h diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index 53aedec7990d..53aedec7990d 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c diff --git a/drivers/media/platform/omap3isp/isppreview.h b/drivers/media/platform/ti/omap3isp/isppreview.h index 5fff1ec3624f..5fff1ec3624f 100644 --- a/drivers/media/platform/omap3isp/isppreview.h +++ b/drivers/media/platform/ti/omap3isp/isppreview.h diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/ti/omap3isp/ispreg.h index 86b6ebb0438d..86b6ebb0438d 100644 --- a/drivers/media/platform/omap3isp/ispreg.h +++ b/drivers/media/platform/ti/omap3isp/ispreg.h diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index ed2fb0c7a57e..ed2fb0c7a57e 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/ti/omap3isp/ispresizer.h index 28cc89940ead..28cc89940ead 100644 --- a/drivers/media/platform/omap3isp/ispresizer.h +++ b/drivers/media/platform/ti/omap3isp/ispresizer.h diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c index 68cf68dbcace..68cf68dbcace 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/ti/omap3isp/ispstat.c diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/ti/omap3isp/ispstat.h index b548e617cf62..b548e617cf62 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/ti/omap3isp/ispstat.h diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index 8811d6dd4ee7..8811d6dd4ee7 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/ti/omap3isp/ispvideo.h index a0908670c0cf..a0908670c0cf 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/ti/omap3isp/ispvideo.h diff --git a/drivers/media/platform/omap3isp/luma_enhance_table.h b/drivers/media/platform/ti/omap3isp/luma_enhance_table.h index d5fbf9241f48..d5fbf9241f48 100644 --- a/drivers/media/platform/omap3isp/luma_enhance_table.h +++ b/drivers/media/platform/ti/omap3isp/luma_enhance_table.h diff --git a/drivers/media/platform/omap3isp/noise_filter_table.h b/drivers/media/platform/ti/omap3isp/noise_filter_table.h index da66bd0a3b9f..da66bd0a3b9f 100644 --- a/drivers/media/platform/omap3isp/noise_filter_table.h +++ b/drivers/media/platform/ti/omap3isp/noise_filter_table.h diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/ti/omap3isp/omap3isp.h index 4a003c8263ed..4a003c8263ed 100644 --- a/drivers/media/platform/omap3isp/omap3isp.h +++ b/drivers/media/platform/ti/omap3isp/omap3isp.h diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti/vpe/Makefile index ad624056e039..3fadfe084f87 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti/vpe/Makefile @@ -10,7 +10,3 @@ ti-sc-y := sc.o ti-csc-y := csc.o ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG - -obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o - -ti-cal-y := cal.o cal-camerarx.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti/vpe/csc.c index ff15bc589f1b..ff15bc589f1b 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti/vpe/csc.c diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti/vpe/csc.h index af2e86bccf57..af2e86bccf57 100644 --- a/drivers/media/platform/ti-vpe/csc.h +++ b/drivers/media/platform/ti/vpe/csc.h diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti/vpe/sc.c index 0202d278523f..0202d278523f 100644 --- a/drivers/media/platform/ti-vpe/sc.c +++ b/drivers/media/platform/ti/vpe/sc.c diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti/vpe/sc.h index d55de44d5257..d55de44d5257 100644 --- a/drivers/media/platform/ti-vpe/sc.h +++ b/drivers/media/platform/ti/vpe/sc.h diff --git a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti/vpe/sc_coeff.h index c525d1764099..c525d1764099 100644 --- a/drivers/media/platform/ti-vpe/sc_coeff.h +++ b/drivers/media/platform/ti/vpe/sc_coeff.h diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index f8998a8ad371..f8998a8ad371 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h index 393fcbb3cb40..393fcbb3cb40 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti/vpe/vpdma.h diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti/vpe/vpdma_priv.h index 0bbee45338bd..0bbee45338bd 100644 --- a/drivers/media/platform/ti-vpe/vpdma_priv.h +++ b/drivers/media/platform/ti/vpe/vpdma_priv.h diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index 5b1c5d96a407..5b1c5d96a407 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti/vpe/vpe_regs.h index 1a1ad5ae1228..1a1ad5ae1228 100644 --- a/drivers/media/platform/ti-vpe/vpe_regs.h +++ b/drivers/media/platform/ti/vpe/vpe_regs.h diff --git a/drivers/media/platform/via/Kconfig b/drivers/media/platform/via/Kconfig new file mode 100644 index 000000000000..8926eb0803b2 --- /dev/null +++ b/drivers/media/platform/via/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "VIA media platform drivers" + +config VIDEO_VIA_CAMERA + tristate "VIAFB camera controller support" + depends on V4L_PLATFORM_DRIVERS + depends on FB_VIA && VIDEO_DEV + select VIDEOBUF2_DMA_SG + select VIDEO_OV7670 + help + Driver support for the integrated camera controller in VIA + Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems + with ov7670 sensors. diff --git a/drivers/media/platform/via/Makefile b/drivers/media/platform/via/Makefile new file mode 100644 index 000000000000..80f747f3fffc --- /dev/null +++ b/drivers/media/platform/via/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via/via-camera.c index 95483c84c3f2..95483c84c3f2 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via/via-camera.c diff --git a/drivers/media/platform/via-camera.h b/drivers/media/platform/via/via-camera.h index 54f16318b1b3..54f16318b1b3 100644 --- a/drivers/media/platform/via-camera.h +++ b/drivers/media/platform/via/via-camera.h diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 44587dccacf1..93ef78bf62e6 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -1,8 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 +comment "Xilinx media platform drivers" + config VIDEO_XILINX tristate "Xilinx Video IP (EXPERIMENTAL)" - depends on VIDEO_V4L2 && OF && HAS_DMA + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF && HAS_DMA select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG @@ -10,9 +13,8 @@ config VIDEO_XILINX help Driver for Xilinx Video IP Pipelines -if VIDEO_XILINX - config VIDEO_XILINX_CSI2RXSS + depends on VIDEO_XILINX tristate "Xilinx CSI-2 Rx Subsystem" help Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device @@ -31,5 +33,3 @@ config VIDEO_XILINX_VTC depends on VIDEO_XILINX help Driver for the Xilinx Video Timing Controller - -endif #VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index b1baf9d7b6ec..051c60cba1e0 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/v4l2-subdev.h> #include <media/media-entity.h> +#include <media/mipi-csi2.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> @@ -115,23 +116,6 @@ #define XCSI_DEFAULT_WIDTH 1920 #define XCSI_DEFAULT_HEIGHT 1080 -/* MIPI CSI-2 Data Types from spec */ -#define XCSI_DT_YUV4228B 0x1e -#define XCSI_DT_YUV42210B 0x1f -#define XCSI_DT_RGB444 0x20 -#define XCSI_DT_RGB555 0x21 -#define XCSI_DT_RGB565 0x22 -#define XCSI_DT_RGB666 0x23 -#define XCSI_DT_RGB888 0x24 -#define XCSI_DT_RAW6 0x28 -#define XCSI_DT_RAW7 0x29 -#define XCSI_DT_RAW8 0x2a -#define XCSI_DT_RAW10 0x2b -#define XCSI_DT_RAW12 0x2c -#define XCSI_DT_RAW14 0x2d -#define XCSI_DT_RAW16 0x2e -#define XCSI_DT_RAW20 0x2f - #define XCSI_VCX_START 4 #define XCSI_MAX_VC 4 #define XCSI_MAX_VCX 16 @@ -183,32 +167,32 @@ static const struct xcsi2rxss_event xcsi2rxss_events[] = { * and media bus formats */ static const u32 xcsi2dt_mbus_lut[][2] = { - { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 }, - { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 }, - { XCSI_DT_RGB444, 0 }, - { XCSI_DT_RGB555, 0 }, - { XCSI_DT_RGB565, 0 }, - { XCSI_DT_RGB666, 0 }, - { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, - { XCSI_DT_RAW6, 0 }, - { XCSI_DT_RAW7, 0 }, - { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, - { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, - { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, - { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, - { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, - { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, - { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, - { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, - { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, - { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, - { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, - { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, - { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, - { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, - { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, - { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, - { XCSI_DT_RAW20, 0 }, + { MIPI_CSI2_DT_YUV422_8B, MEDIA_BUS_FMT_UYVY8_1X16 }, + { MIPI_CSI2_DT_YUV422_10B, MEDIA_BUS_FMT_UYVY10_1X20 }, + { MIPI_CSI2_DT_RGB444, 0 }, + { MIPI_CSI2_DT_RGB555, 0 }, + { MIPI_CSI2_DT_RGB565, 0 }, + { MIPI_CSI2_DT_RGB666, 0 }, + { MIPI_CSI2_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, + { MIPI_CSI2_DT_RAW6, 0 }, + { MIPI_CSI2_DT_RAW7, 0 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { MIPI_CSI2_DT_RAW20, 0 }, }; /** @@ -791,7 +775,7 @@ static int xcsi2rxss_set_format(struct v4l2_subdev *sd, * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format. */ dt = xcsi2rxss_get_dt(fmt->format.code); - if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) { + if (dt != xcsi2rxss->datatype && dt != MIPI_CSI2_DT_RAW8) { dev_dbg(xcsi2rxss->dev, "Unsupported media bus format"); /* set the default format for the data type */ fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, @@ -823,8 +807,8 @@ static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, /* RAW8 dt packets are available in all DT configurations */ if (code->index < 4) { n = code->index; - dt = XCSI_DT_RAW8; - } else if (state->datatype != XCSI_DT_RAW8) { + dt = MIPI_CSI2_DT_RAW8; + } else if (state->datatype != MIPI_CSI2_DT_RAW8) { n = code->index - 4; dt = state->datatype; } else { @@ -895,22 +879,22 @@ static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) } switch (xcsi2rxss->datatype) { - case XCSI_DT_YUV4228B: - case XCSI_DT_RGB444: - case XCSI_DT_RGB555: - case XCSI_DT_RGB565: - case XCSI_DT_RGB666: - case XCSI_DT_RGB888: - case XCSI_DT_RAW6: - case XCSI_DT_RAW7: - case XCSI_DT_RAW8: - case XCSI_DT_RAW10: - case XCSI_DT_RAW12: - case XCSI_DT_RAW14: + case MIPI_CSI2_DT_YUV422_8B: + case MIPI_CSI2_DT_RGB444: + case MIPI_CSI2_DT_RGB555: + case MIPI_CSI2_DT_RGB565: + case MIPI_CSI2_DT_RGB666: + case MIPI_CSI2_DT_RGB888: + case MIPI_CSI2_DT_RAW6: + case MIPI_CSI2_DT_RAW7: + case MIPI_CSI2_DT_RAW8: + case MIPI_CSI2_DT_RAW10: + case MIPI_CSI2_DT_RAW12: + case MIPI_CSI2_DT_RAW14: break; - case XCSI_DT_YUV42210B: - case XCSI_DT_RAW16: - case XCSI_DT_RAW20: + case MIPI_CSI2_DT_YUV422_10B: + case MIPI_CSI2_DT_RAW16: + case MIPI_CSI2_DT_RAW20: if (!en_csi_v20) { ret = -EINVAL; dev_dbg(dev, "enable csi v2 for this pixel format"); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index d29e29645e04..cca03bd2cc42 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -5,69 +5,17 @@ menuconfig RADIO_ADAPTERS bool "Radio Adapters" - depends on VIDEO_V4L2 + depends on VIDEO_DEV depends on MEDIA_RADIO_SUPPORT default y help Say Y here to enable selecting AM/FM radio adapters. -if RADIO_ADAPTERS && VIDEO_V4L2 - -config RADIO_TEA575X - tristate - -source "drivers/media/radio/si470x/Kconfig" - -config RADIO_SI4713 - tristate "Silicon Labs Si4713 FM Radio with RDS Transmitter support" - depends on VIDEO_V4L2 - -source "drivers/media/radio/si4713/Kconfig" - -config RADIO_SI476X - tristate "Silicon Laboratories Si476x I2C FM Radio" - depends on I2C && VIDEO_V4L2 - depends on MFD_SI476X_CORE - depends on SND_SOC - select SND_SOC_SI476X - help - Choose Y here if you have this FM radio chip. - - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux 2 API. Information on - this API and pointers to "v4l2" programs may be found at - <file:Documentation/userspace-api/media/index.rst>. - - To compile this driver as a module, choose M here: the - module will be called radio-si476x. - -config USB_MR800 - tristate "AverMedia MR 800 USB FM radio support" - depends on USB && VIDEO_V4L2 - help - Say Y here if you want to connect this type of radio to your - computer's USB port. Note that the audio is not digital, and - you must connect the line out connector to a sound card or a - set of speakers. - - To compile this driver as a module, choose M here: the - module will be called radio-mr800. - -config USB_DSBR - tristate "D-Link/GemTek USB FM radio support" - depends on USB && VIDEO_V4L2 - help - Say Y here if you want to connect this type of radio to your - computer's USB port. Note that the audio is not digital, and - you must connect the line out connector to a sound card or a - set of speakers. - - To compile this driver as a module, choose M here: the - module will be called dsbr100. +if RADIO_ADAPTERS config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_V4L2 && PCI + depends on PCI select RADIO_TEA575X help Choose Y here if you have this radio card. This card may also be @@ -81,6 +29,18 @@ config RADIO_MAXIRADIO To compile this driver as a module, choose M here: the module will be called radio-maxiradio. +config RADIO_SAA7706H + tristate "SAA7706H Car Radio DSP" + depends on I2C + help + Say Y here if you want to use the SAA7706H Car radio Digital + Signal Processor, found for instance on the Russellville development + board. On the russellville the device is connected to internal + timberdale I2C bus. + + To compile this driver as a module, choose M here: the + module will be called SAA7706H. + config RADIO_SHARK tristate "Griffin radioSHARK USB radio receiver" depends on USB @@ -116,45 +76,32 @@ config RADIO_SHARK2 To compile this driver as a module, choose M here: the module will be called radio-shark2. -config USB_KEENE - tristate "Keene FM Transmitter USB support" - depends on USB && VIDEO_V4L2 - help - Say Y here if you want to connect this type of FM transmitter - to your computer's USB port. - - To compile this driver as a module, choose M here: the - module will be called radio-keene. +config RADIO_SI4713 + tristate "Silicon Labs Si4713 FM Radio with RDS Transmitter support" -config USB_RAREMONO - tristate "Thanko's Raremono AM/FM/SW radio support" - depends on USB && VIDEO_V4L2 +config RADIO_SI476X + tristate "Silicon Laboratories Si476x I2C FM Radio" + depends on I2C + depends on MFD_SI476X_CORE + depends on SND_SOC + select SND_SOC_SI476X help - The 'Thanko's Raremono' device contains the Si4734 chip from Silicon Labs Inc. - It is one of the very few or perhaps the only consumer USB radio device - to receive the AM/FM/SW bands. + Choose Y here if you have this FM radio chip. - Say Y here if you want to connect this type of AM/FM/SW receiver - to your computer's USB port. + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux 2 API. Information on + this API and pointers to "v4l2" programs may be found at + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the - module will be called radio-raremono. - -config USB_MA901 - tristate "Masterkit MA901 USB FM radio support" - depends on USB && VIDEO_V4L2 - help - Say Y here if you want to connect this type of radio to your - computer's USB port. Note that the audio is not digital, and - you must connect the line out connector to a sound card or a - set of speakers or headphones. + module will be called radio-si476x. - To compile this driver as a module, choose M here: the - module will be called radio-ma901. +config RADIO_TEA575X + tristate config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" - depends on I2C && VIDEO_V4L2 + depends on I2C help Say Y here if you want to use the TEA5764 FM chip found in EZX phones. This FM chip is present in EZX phones from Motorola, @@ -171,21 +118,9 @@ config RADIO_TEA5764_XTAL Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N here if TEA5764 reference frequency is connected in FREQIN. -config RADIO_SAA7706H - tristate "SAA7706H Car Radio DSP" - depends on I2C && VIDEO_V4L2 - help - Say Y here if you want to use the SAA7706H Car radio Digital - Signal Processor, found for instance on the Russellville development - board. On the russellville the device is connected to internal - timberdale I2C bus. - - To compile this driver as a module, choose M here: the - module will be called SAA7706H. - config RADIO_TEF6862 tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" - depends on I2C && VIDEO_V4L2 + depends on I2C help Say Y here if you want to use the TEF6862 Car Radio Enhanced Selectivity Tuner, found for instance on the Russellville development @@ -197,7 +132,7 @@ config RADIO_TEF6862 config RADIO_TIMBERDALE tristate "Enable the Timberdale radio driver" - depends on MFD_TIMBERDALE && VIDEO_V4L2 + depends on MFD_TIMBERDALE depends on I2C # for RADIO_SAA7706H select RADIO_TEF6862 select RADIO_SAA7706H @@ -208,7 +143,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" - depends on I2C && VIDEO_V4L2 + depends on I2C select MFD_CORE select MFD_WL1273_CORE select FW_LOADER @@ -223,96 +158,88 @@ config RADIO_WL1273 To compile this driver as a module, choose M here: the module will be called radio-wl1273. -# TI's ST based wl128x FM radio -source "drivers/media/radio/wl128x/Kconfig" +config USB_DSBR + tristate "D-Link/GemTek USB FM radio support" + depends on USB + help + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers. -# -# ISA drivers configuration -# + To compile this driver as a module, choose M here: the + module will be called dsbr100. -menuconfig V4L_RADIO_ISA_DRIVERS - bool "ISA radio devices" - depends on ISA || COMPILE_TEST +config USB_KEENE + tristate "Keene FM Transmitter USB support" + depends on USB help - Say Y here to enable support for these ISA drivers. - -if V4L_RADIO_ISA_DRIVERS + Say Y here if you want to connect this type of FM transmitter + to your computer's USB port. -config RADIO_ISA - depends on ISA || COMPILE_TEST - tristate + To compile this driver as a module, choose M here: the + module will be called radio-keene. -config RADIO_CADET - tristate "ADS Cadet AM/FM Tuner" - depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 +config USB_MA901 + tristate "Masterkit MA901 USB FM radio support" + depends on USB help - Choose Y here if you have one of these AM/FM radio cards, and then - fill in the port address below. + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers or headphones. To compile this driver as a module, choose M here: the - module will be called radio-cadet. + module will be called radio-ma901. -config RADIO_RTRACK - tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 - select RADIO_ISA +config USB_MR800 + tristate "AverMedia MR 800 USB FM radio support" + depends on USB help - Choose Y here if you have one of these FM radio cards, and then fill - in the port address below. + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers. - Note that newer AIMSlab RadioTrack cards have a different chipset - and are not supported by this driver. For these cards, use the - RadioTrack II driver below. + To compile this driver as a module, choose M here: the + module will be called radio-mr800. - If you have a GemTeks combined (PnP) sound- and radio card you must - use this driver as a module and setup the card with isapnptools. - You must also pass the module a suitable io parameter, 0x248 has - been reported to be used by these cards. +config USB_RAREMONO + tristate "Thanko's Raremono AM/FM/SW radio support" + depends on USB + help + The 'Thanko's Raremono' device contains the Si4734 chip from Silicon Labs Inc. + It is one of the very few or perhaps the only consumer USB radio device + to receive the AM/FM/SW bands. - More information is contained in the file - <file:Documentation/driver-api/media/drivers/radiotrack.rst>. + Say Y here if you want to connect this type of AM/FM/SW receiver + to your computer's USB port. To compile this driver as a module, choose M here: the - module will be called radio-aimslab. + module will be called radio-raremono. -config RADIO_RTRACK_PORT - hex "RadioTrack i/o port (0x20f or 0x30f)" - depends on RADIO_RTRACK=y - default "30f" - help - Enter either 0x30f or 0x20f here. The card default is 0x30f, if you - haven't changed the jumper setting on the card. +source "drivers/media/radio/si470x/Kconfig" +source "drivers/media/radio/si4713/Kconfig" -config RADIO_RTRACK2 - tristate "AIMSlab RadioTrack II support" - depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 - select RADIO_ISA - help - Choose Y here if you have this FM radio card, and then fill in the - port address below. +# TI's ST based wl128x FM radio - Note: this driver hasn't been tested since a long time due to lack - of hardware. If you have this hardware, then please contact the - linux-media mailinglist. +source "drivers/media/radio/wl128x/Kconfig" - To compile this driver as a module, choose M here: the - module will be called radio-rtrack2. +# +# ISA drivers configuration +# -config RADIO_RTRACK2_PORT - hex "RadioTrack II i/o port (0x20c or 0x30c)" - depends on RADIO_RTRACK2=y - default "30c" +menuconfig V4L_RADIO_ISA_DRIVERS + bool "ISA radio devices" + depends on ISA || COMPILE_TEST help - Enter either 0x30c or 0x20c here. The card default is 0x30c, if you - haven't changed the jumper setting on the card. + Say Y here to enable support for these ISA drivers. + +if V4L_RADIO_ISA_DRIVERS config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help Choose Y here if you have one of these FM radio cards, and then fill @@ -330,10 +257,19 @@ config RADIO_AZTECH_PORT haven't changed the setting of jumper JP3 on the card. Removing the jumper sets the card to 0x358. +config RADIO_CADET + tristate "ADS Cadet AM/FM Tuner" + depends on ISA || COMPILE_TEST + help + Choose Y here if you have one of these AM/FM radio cards, and then + fill in the port address below. + + To compile this driver as a module, choose M here: the + module will be called radio-cadet. + config RADIO_GEMTEK tristate "GemTek Radio card (or compatible) support" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help Choose Y here if you have this FM radio card, and then fill in the @@ -371,10 +307,14 @@ config RADIO_GEMTEK_PROBE following ports will be probed: 0x20c, 0x30c, 0x24c, 0x34c, 0x248 and 0x28c. +config RADIO_ISA + depends on ISA || COMPILE_TEST + tristate + config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" depends on ISA || COMPILE_TEST - depends on ISA_DMA_API && VIDEO_V4L2 && SND + depends on ISA_DMA_API && SND select SND_ISA select SND_MIRO help @@ -386,10 +326,63 @@ config RADIO_MIROPCM20 To compile this driver as a module, choose M here: the module will be called radio-miropcm20. +config RADIO_RTRACK + tristate "AIMSlab RadioTrack (aka RadioReveal) support" + depends on ISA || COMPILE_TEST + select RADIO_ISA + help + Choose Y here if you have one of these FM radio cards, and then fill + in the port address below. + + Note that newer AIMSlab RadioTrack cards have a different chipset + and are not supported by this driver. For these cards, use the + RadioTrack II driver below. + + If you have a GemTeks combined (PnP) sound- and radio card you must + use this driver as a module and setup the card with isapnptools. + You must also pass the module a suitable io parameter, 0x248 has + been reported to be used by these cards. + + More information is contained in the file + <file:Documentation/driver-api/media/drivers/radiotrack.rst>. + + To compile this driver as a module, choose M here: the + module will be called radio-aimslab. + +config RADIO_RTRACK2 + tristate "AIMSlab RadioTrack II support" + depends on ISA || COMPILE_TEST + select RADIO_ISA + help + Choose Y here if you have this FM radio card, and then fill in the + port address below. + + Note: this driver hasn't been tested since a long time due to lack + of hardware. If you have this hardware, then please contact the + linux-media mailinglist. + + To compile this driver as a module, choose M here: the + module will be called radio-rtrack2. + +config RADIO_RTRACK2_PORT + hex "RadioTrack II i/o port (0x20c or 0x30c)" + depends on RADIO_RTRACK2=y + default "30c" + help + Enter either 0x30c or 0x20c here. The card default is 0x30c, if you + haven't changed the jumper setting on the card. + +config RADIO_RTRACK_PORT + hex "RadioTrack i/o port (0x20f or 0x30f)" + depends on RADIO_RTRACK=y + default "30f" + help + Enter either 0x30f or 0x20f here. The card default is 0x30f, if you + haven't changed the jumper setting on the card. + config RADIO_SF16FMI tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 help Choose Y here if you have one of these FM radio cards. @@ -399,7 +392,6 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16-FMR2/SF16-FMD2 Radio" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_TEA575X help Choose Y here if you have one of these FM radio cards. @@ -410,7 +402,6 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help Choose Y here if you have this FM radio card. @@ -425,7 +416,6 @@ config RADIO_TERRATEC config RADIO_TRUST tristate "Trust FM radio card" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help This is a driver for the Trust FM radio cards. Say Y if you have @@ -449,7 +439,6 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help Choose Y here if you have one of these FM radio cards, and then fill @@ -462,13 +451,6 @@ config RADIO_TYPHOON To compile this driver as a module, choose M here: the module will be called radio-typhoon. -config RADIO_TYPHOON_PORT - hex "Typhoon I/O port (0x316 or 0x336)" - depends on RADIO_TYPHOON=y - default "316" - help - Enter the I/O port of your Typhoon or EcoRadio radio card. - config RADIO_TYPHOON_MUTEFREQ int "Typhoon frequency set when muting the device (kHz)" depends on RADIO_TYPHOON=y @@ -481,10 +463,16 @@ config RADIO_TYPHOON_MUTEFREQ the device is muted. There should be no local radio station at that frequency. +config RADIO_TYPHOON_PORT + hex "Typhoon I/O port (0x316 or 0x336)" + depends on RADIO_TYPHOON=y + default "316" + help + Enter the I/O port of your Typhoon or EcoRadio radio card. + config RADIO_ZOLTRIX tristate "Zoltrix Radio" depends on ISA || COMPILE_TEST - depends on VIDEO_V4L2 select RADIO_ISA help Choose Y here if you have one of these FM radio cards, and then fill diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 53c7ae135460..cfb6af7d3bc3 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -3,36 +3,39 @@ # Makefile for the kernel character device drivers. # -obj-$(CONFIG_RADIO_ISA) += radio-isa.o +shark2-objs := radio-shark2.o radio-tea5777.o + +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o +obj-$(CONFIG_RADIO_CADET) += radio-cadet.o +obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o +obj-$(CONFIG_RADIO_ISA) += radio-isa.o +obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o +obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o +obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o +obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o -obj-$(CONFIG_RADIO_CADET) += radio-cadet.o -obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o -obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o -obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o -obj-$(CONFIG_RADIO_SHARK) += radio-shark.o obj-$(CONFIG_RADIO_SHARK2) += shark2.o -obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o -obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o -obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o -obj-$(CONFIG_RADIO_TRUST) += radio-trust.o -obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o -obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o -obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_RADIO_SHARK) += radio-shark.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_RADIO_SI4713) += si4713/ -obj-$(CONFIG_USB_MR800) += radio-mr800.o -obj-$(CONFIG_USB_KEENE) += radio-keene.o -obj-$(CONFIG_USB_MA901) += radio-ma901.o +obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o +obj-$(CONFIG_RADIO_TEA575X) += tea575x.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o -obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o +obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o +obj-$(CONFIG_RADIO_TRUST) += radio-trust.o +obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ -obj-$(CONFIG_RADIO_TEA575X) += tea575x.o -obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o +obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o -shark2-objs := radio-shark2.o radio-tea5777.o +obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_USB_KEENE) += radio-keene.o +obj-$(CONFIG_USB_MA901) += radio-ma901.o +obj-$(CONFIG_USB_MR800) += radio-mr800.o +obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 54a40d60e4fd..1fb88c2b916c 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -275,7 +275,7 @@ static int __init fmi_init(void) struct v4l2_device *v4l2_dev = &fmi->v4l2_dev; struct v4l2_ctrl_handler *hdl = &fmi->hdl; int res, i; - int probe_ports[] = { 0, 0x284, 0x384 }; + static const int probe_ports[] = { 0, 0x284, 0x384 }; if (io < 0) { for (i = 0; i < ARRAY_SIZE(probe_ports); i++) { diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig index 7161bd6cd13c..9f7d35b04a13 100644 --- a/drivers/media/radio/si470x/Kconfig +++ b/drivers/media/radio/si470x/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config RADIO_SI470X tristate "Silicon Labs Si470x FM Radio Receiver support" - depends on VIDEO_V4L2 + depends on VIDEO_DEV help This is a driver for devices with the Silicon Labs SI470x chip (either via USB or I2C buses). diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig index d5ae3388d3db..3e7713872e3f 100644 --- a/drivers/media/radio/wl128x/Kconfig +++ b/drivers/media/radio/wl128x/Kconfig @@ -4,7 +4,7 @@ # config RADIO_WL128X tristate "Texas Instruments WL128x FM Radio" - depends on VIDEO_V4L2 && RFKILL && TTY && TI_ST + depends on VIDEO_DEV && RFKILL && TTY && TI_ST depends on GPIOLIB || COMPILE_TEST help Choose Y here if you have this FM radio chip. diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 6142484d5cb4..8a316de70e6c 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -23,6 +23,7 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/nospec.h> +#include <linux/jiffies.h> #include "fmdrv.h" #include "fmdrv_v4l2.h" @@ -342,7 +343,7 @@ static void send_tasklet(struct tasklet_struct *t) return; /* Check, is there any timeout happened to last transmitted packet */ - if ((jiffies - fmdev->last_tx_jiffies) > FM_DRV_TX_TIMEOUT) { + if (time_is_before_jiffies(fmdev->last_tx_jiffies + FM_DRV_TX_TIMEOUT)) { fmerr("TX timeout occurred\n"); atomic_set(&fmdev->tx_cnt, 1); } diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index c111af820ae4..f560fc38895f 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -15,15 +15,6 @@ menuconfig RC_CORE Say Y when you have a TV or an IR device. if RC_CORE -source "drivers/media/rc/keymaps/Kconfig" - -config LIRC - bool "LIRC user interface" - help - Enable this option to enable the Linux Infrared Remote - Control user interface (e.g. /dev/lirc*). This interface - passes raw IR to and from userspace, which is needed for - IR transmitting (aka "blasting") and for the lirc daemon. config BPF_LIRC_MODE2 bool "Support for eBPF programs attached to lirc devices" @@ -38,10 +29,45 @@ config BPF_LIRC_MODE2 These eBPF programs can be used to decode IR into scancodes, for IR protocols not supported by the kernel decoders. +config LIRC + bool "LIRC user interface" + help + Enable this option to enable the Linux Infrared Remote + Control user interface (e.g. /dev/lirc*). This interface + passes raw IR to and from userspace, which is needed for + IR transmitting (aka "blasting") and for the lirc daemon. + +source "drivers/media/rc/keymaps/Kconfig" + menuconfig RC_DECODERS bool "Remote controller decoders" if RC_DECODERS + +config IR_IMON_DECODER + tristate "Enable IR raw decoder for the iMON protocol" + help + Enable this option if you have iMON PAD or Antec Veris infrared + remote control and you would like to use it with a raw IR + receiver, or if you wish to use an encoder to transmit this IR. + +config IR_JVC_DECODER + tristate "Enable IR raw decoder for the JVC protocol" + select BITREVERSE + + help + Enable this option if you have an infrared remote control which + uses the JVC protocol, and you need software decoding support. + +config IR_MCE_KBD_DECODER + tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol" + select BITREVERSE + + help + Enable this option if you have a Microsoft Remote Keyboard for + Windows Media Center Edition, which you would like to use with + a raw IR receiver in your system. + config IR_NEC_DECODER tristate "Enable IR raw decoder for the NEC protocol" select BITREVERSE @@ -66,21 +92,17 @@ config IR_RC6_DECODER Enable this option if you have an infrared remote control which uses the RC6 protocol, and you need software decoding support. -config IR_JVC_DECODER - tristate "Enable IR raw decoder for the JVC protocol" - select BITREVERSE - +config IR_RCMM_DECODER + tristate "Enable IR raw decoder for the RC-MM protocol" help - Enable this option if you have an infrared remote control which - uses the JVC protocol, and you need software decoding support. - -config IR_SONY_DECODER - tristate "Enable IR raw decoder for the Sony protocol" - select BITREVERSE + Enable this option when you have IR with RC-MM protocol, and + you need the software decoder. The driver supports 12, + 24 and 32 bits RC-MM variants. You can enable or disable the + different modes using the following RC protocol keywords: + 'rc-mm-12', 'rc-mm-24' and 'rc-mm-32'. - help - Enable this option if you have an infrared remote control which - uses the Sony protocol, and you need software decoding support. + To compile this driver as a module, choose M here: the module + will be called ir-rcmm-decoder. config IR_SANYO_DECODER tristate "Enable IR raw decoder for the Sanyo protocol" @@ -100,14 +122,13 @@ config IR_SHARP_DECODER uses the Sharp protocol (Sharp, Denon), and you need software decoding support. -config IR_MCE_KBD_DECODER - tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol" +config IR_SONY_DECODER + tristate "Enable IR raw decoder for the Sony protocol" select BITREVERSE help - Enable this option if you have a Microsoft Remote Keyboard for - Windows Media Center Edition, which you would like to use with - a raw IR receiver in your system. + Enable this option if you have an infrared remote control which + uses the Sony protocol, and you need software decoding support. config IR_XMP_DECODER tristate "Enable IR raw decoder for the XMP protocol" @@ -117,25 +138,6 @@ config IR_XMP_DECODER Enable this option if you have IR with XMP protocol, and if the IR is decoded in software -config IR_IMON_DECODER - tristate "Enable IR raw decoder for the iMON protocol" - help - Enable this option if you have iMON PAD or Antec Veris infrared - remote control and you would like to use it with a raw IR - receiver, or if you wish to use an encoder to transmit this IR. - -config IR_RCMM_DECODER - tristate "Enable IR raw decoder for the RC-MM protocol" - help - Enable this option when you have IR with RC-MM protocol, and - you need the software decoder. The driver supports 12, - 24 and 32 bits RC-MM variants. You can enable or disable the - different modes using the following RC protocol keywords: - 'rc-mm-12', 'rc-mm-24' and 'rc-mm-32'. - - To compile this driver as a module, choose M here: the module - will be called ir-rcmm-decoder. - endif #RC_DECODERS menuconfig RC_DEVICES @@ -143,23 +145,6 @@ menuconfig RC_DEVICES if RC_DEVICES -config RC_ATI_REMOTE - tristate "ATI / X10 based USB RF remote controls" - depends on USB - help - Say Y here if you want to use an X10 based USB remote control. - These are RF remotes with USB receivers. - - Such devices include the ATI remote that comes with many of ATI's - All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote, - Medion RF remote, and SnapStream FireFly remote. - - This driver provides mouse pointer, left and right mouse buttons, - and maps all the other remote buttons to keypress events. - - To compile this driver as a module, choose M here: the module will be - called ati_remote. - config IR_ENE tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)" depends on PNP || COMPILE_TEST @@ -173,6 +158,37 @@ config IR_ENE To compile this driver as a module, choose M here: the module will be called ene_ir. +config IR_FINTEK + tristate "Fintek Consumer Infrared Transceiver" + depends on PNP || COMPILE_TEST + help + Say Y here to enable support for integrated infrared receiver + /transceiver made by Fintek. This chip is found on assorted + Jetway motherboards (and of course, possibly others). + + To compile this driver as a module, choose M here: the + module will be called fintek-cir. + +config IR_GPIO_CIR + tristate "GPIO IR remote control" + depends on (OF && GPIOLIB) || COMPILE_TEST + help + Say Y if you want to use GPIO based IR Receiver. + + To compile this driver as a module, choose M here: the module will + be called gpio-ir-recv. + +config IR_GPIO_TX + tristate "GPIO IR Bit Banging Transmitter" + depends on LIRC + depends on (OF && GPIOLIB) || COMPILE_TEST + help + Say Y if you want to a GPIO based IR transmitter. This is a + bit banging driver. + + To compile this driver as a module, choose M here: the module will + be called gpio-ir-tx. + config IR_HIX5HD2 tristate "Hisilicon hix5hd2 IR remote control" depends on (OF && HAS_IOMEM) || COMPILE_TEST @@ -183,6 +199,33 @@ config IR_HIX5HD2 If you're not sure, select N here +config IR_IGORPLUGUSB + tristate "IgorPlug-USB IR Receiver" + depends on USB + help + Say Y here if you want to use the IgorPlug-USB IR Receiver by + Igor Cesko. This device is included on the Fit-PC2. + + Note that this device can only record bursts of 36 IR pulses and + spaces, which is not enough for the NEC, Sanyo and RC-6 protocol. + + To compile this driver as a module, choose M here: the module will + be called igorplugusb. + +config IR_IGUANA + tristate "IguanaWorks USB IR Transceiver" + depends on USB + help + Say Y here if you want to use the IguanaWorks USB IR Transceiver. + Both infrared receive and send are supported. If you want to + change the ID or the pin config, use the user space driver from + IguanaWorks. + + Only firmware 0x0205 and later is supported. + + To compile this driver as a module, choose M here: the module will + be called iguanair. + config IR_IMON tristate "SoundGraph iMON Receiver and Display" depends on USB @@ -203,16 +246,6 @@ config IR_IMON_RAW To compile this driver as a module, choose M here: the module will be called imon_raw. -config IR_MCEUSB - tristate "Windows Media Center Ed. eHome Infrared Transceiver" - depends on USB - help - Say Y here if you want to use a Windows Media Center Edition - eHome Infrared Transceiver. - - To compile this driver as a module, choose M here: the - module will be called mceusb. - config IR_ITE_CIR tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver" depends on PNP || COMPILE_TEST @@ -225,16 +258,15 @@ config IR_ITE_CIR To compile this driver as a module, choose M here: the module will be called ite-cir. -config IR_FINTEK - tristate "Fintek Consumer Infrared Transceiver" - depends on PNP || COMPILE_TEST +config IR_MCEUSB + tristate "Windows Media Center Ed. eHome Infrared Transceiver" + depends on USB help - Say Y here to enable support for integrated infrared receiver - /transceiver made by Fintek. This chip is found on assorted - Jetway motherboards (and of course, possibly others). + Say Y here if you want to use a Windows Media Center Edition + eHome Infrared Transceiver. To compile this driver as a module, choose M here: the - module will be called fintek-cir. + module will be called mceusb. config IR_MESON tristate "Amlogic Meson IR remote receiver" @@ -278,6 +310,18 @@ config IR_NUVOTON To compile this driver as a module, choose M here: the module will be called nuvoton-cir. +config IR_PWM_TX + tristate "PWM IR transmitter" + depends on LIRC + depends on PWM + depends on OF || COMPILE_TEST + help + Say Y if you want to use a PWM based IR transmitter. This is + more power efficient than the bit banging gpio driver. + + To compile this driver as a module, choose M here: the module will + be called pwm-ir-tx. + config IR_REDRAT3 tristate "RedRat3 IR Transceiver" depends on USB @@ -289,6 +333,31 @@ config IR_REDRAT3 To compile this driver as a module, choose M here: the module will be called redrat3. +config IR_RX51 + tristate "Nokia N900 IR transmitter diode" + depends on (OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS || COMPILE_TEST) && RC_CORE + help + Say Y or M here if you want to enable support for the IR + transmitter diode built in the Nokia N900 (RX51) device. + + The driver uses omap DM timers for generating the carrier + wave and pulses. + +config IR_SERIAL + tristate "Homebrew Serial Port Receiver" + help + Say Y if you want to use Homebrew Serial Port Receivers and + Transceivers. + + To compile this driver as a module, choose M here: the module will + be called serial-ir. + +config IR_SERIAL_TRANSMITTER + bool "Serial Port Transmitter" + depends on IR_SERIAL + help + Serial Port Transmitter support + config IR_SPI tristate "SPI connected IR LED" depends on SPI && LIRC @@ -309,47 +378,24 @@ config IR_STREAMZAP To compile this driver as a module, choose M here: the module will be called streamzap. -config IR_WINBOND_CIR - tristate "Winbond IR remote control" - depends on (X86 && PNP) || COMPILE_TEST - select NEW_LEDS - select LEDS_CLASS - select BITREVERSE - help - Say Y here if you want to use the IR remote functionality found - in some Winbond SuperI/O chips. Currently only the WPCD376I - chip is supported (included in some Intel Media series - motherboards). - - To compile this driver as a module, choose M here: the module will - be called winbond_cir. - -config IR_IGORPLUGUSB - tristate "IgorPlug-USB IR Receiver" - depends on USB +config IR_SUNXI + tristate "SUNXI IR remote control" + depends on ARCH_SUNXI || COMPILE_TEST help - Say Y here if you want to use the IgorPlug-USB IR Receiver by - Igor Cesko. This device is included on the Fit-PC2. - - Note that this device can only record bursts of 36 IR pulses and - spaces, which is not enough for the NEC, Sanyo and RC-6 protocol. + Say Y if you want to use sunXi internal IR Controller To compile this driver as a module, choose M here: the module will - be called igorplugusb. + be called sunxi-ir. -config IR_IGUANA - tristate "IguanaWorks USB IR Transceiver" +config IR_TOY + tristate "Infrared Toy and IR Droid" depends on USB help - Say Y here if you want to use the IguanaWorks USB IR Transceiver. - Both infrared receive and send are supported. If you want to - change the ID or the pin config, use the user space driver from - IguanaWorks. - - Only firmware 0x0205 and later is supported. + Say Y here if you want to use the Infrared Toy or IR Droid, USB + versions. - To compile this driver as a module, choose M here: the module will - be called iguanair. + To compile this driver as a module, choose M here: the module will be + called ir_toy. config IR_TTUSBIR tristate "TechnoTrend USB IR Receiver" @@ -363,17 +409,37 @@ config IR_TTUSBIR To compile this driver as a module, choose M here: the module will be called ttusbir. -config IR_RX51 - tristate "Nokia N900 IR transmitter diode" - depends on (OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS || COMPILE_TEST) && RC_CORE +config IR_WINBOND_CIR + tristate "Winbond IR remote control" + depends on (X86 && PNP) || COMPILE_TEST + select NEW_LEDS + select LEDS_CLASS + select BITREVERSE help - Say Y or M here if you want to enable support for the IR - transmitter diode built in the Nokia N900 (RX51) device. + Say Y here if you want to use the IR remote functionality found + in some Winbond SuperI/O chips. Currently only the WPCD376I + chip is supported (included in some Intel Media series + motherboards). - The driver uses omap DM timers for generating the carrier - wave and pulses. + To compile this driver as a module, choose M here: the module will + be called winbond_cir. -source "drivers/media/rc/img-ir/Kconfig" +config RC_ATI_REMOTE + tristate "ATI / X10 based USB RF remote controls" + depends on USB + help + Say Y here if you want to use an X10 based USB remote control. + These are RF remotes with USB receivers. + + Such devices include the ATI remote that comes with many of ATI's + All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote, + Medion RF remote, and SnapStream FireFly remote. + + This driver provides mouse pointer, left and right mouse buttons, + and maps all the other remote buttons to keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote. config RC_LOOPBACK tristate "Remote Control Loopback Driver" @@ -387,38 +453,6 @@ config RC_LOOPBACK To compile this driver as a module, choose M here: the module will be called rc_loopback. -config IR_GPIO_CIR - tristate "GPIO IR remote control" - depends on (OF && GPIOLIB) || COMPILE_TEST - help - Say Y if you want to use GPIO based IR Receiver. - - To compile this driver as a module, choose M here: the module will - be called gpio-ir-recv. - -config IR_GPIO_TX - tristate "GPIO IR Bit Banging Transmitter" - depends on LIRC - depends on (OF && GPIOLIB) || COMPILE_TEST - help - Say Y if you want to a GPIO based IR transmitter. This is a - bit banging driver. - - To compile this driver as a module, choose M here: the module will - be called gpio-ir-tx. - -config IR_PWM_TX - tristate "PWM IR transmitter" - depends on LIRC - depends on PWM - depends on OF || COMPILE_TEST - help - Say Y if you want to use a PWM based IR transmitter. This is - more power efficient than the bit banging gpio driver. - - To compile this driver as a module, choose M here: the module will - be called pwm-ir-tx. - config RC_ST tristate "ST remote control receiver" depends on ARCH_STI || COMPILE_TEST @@ -429,30 +463,6 @@ config RC_ST If you're not sure, select N here. -config IR_SUNXI - tristate "SUNXI IR remote control" - depends on ARCH_SUNXI || COMPILE_TEST - help - Say Y if you want to use sunXi internal IR Controller - - To compile this driver as a module, choose M here: the module will - be called sunxi-ir. - -config IR_SERIAL - tristate "Homebrew Serial Port Receiver" - help - Say Y if you want to use Homebrew Serial Port Receivers and - Transceivers. - - To compile this driver as a module, choose M here: the module will - be called serial-ir. - -config IR_SERIAL_TRANSMITTER - bool "Serial Port Transmitter" - depends on IR_SERIAL - help - Serial Port Transmitter support - config RC_XBOX_DVD tristate "Xbox DVD Movie Playback Kit" depends on USB @@ -463,15 +473,7 @@ config RC_XBOX_DVD To compile this driver as a module, choose M here: the module will be called xbox_remote. -config IR_TOY - tristate "Infrared Toy and IR Droid" - depends on USB - help - Say Y here if you want to use the Infrared Toy or IR Droid, USB - versions. - - To compile this driver as a module, choose M here: the module will be - called ir_toy. +source "drivers/media/rc/img-ir/Kconfig" endif #RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 378d62d21e06..a9285266e944 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -2,51 +2,56 @@ obj-y += keymaps/ -obj-$(CONFIG_RC_CORE) += rc-core.o rc-core-y := rc-main.o rc-ir-raw.o rc-core-$(CONFIG_LIRC) += lirc_dev.o rc-core-$(CONFIG_MEDIA_CEC_RC) += keymaps/rc-cec.o rc-core-$(CONFIG_BPF_LIRC_MODE2) += bpf-lirc.o + +obj-$(CONFIG_RC_CORE) += rc-core.o + +# IR decoders - please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o +obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o +obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o -obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o -obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o +obj-$(CONFIG_IR_RCMM_DECODER) += ir-rcmm-decoder.o obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o -obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o +obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o -obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o -obj-$(CONFIG_IR_RCMM_DECODER) += ir-rcmm-decoder.o -# stand-alone IR receivers/transmitters -obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o +# stand-alone IR receivers/transmitters - please keep it alphabetically +# sorted by Kconfig name (e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_IR_ENE) += ene_ir.o +obj-$(CONFIG_IR_FINTEK) += fintek-cir.o +obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o +obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o +obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o +obj-$(CONFIG_IR_IGUANA) += iguanair.o +obj-$(CONFIG_IR_IMG) += img-ir/ obj-$(CONFIG_IR_IMON) += imon.o obj-$(CONFIG_IR_IMON_RAW) += imon_raw.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o -obj-$(CONFIG_IR_FINTEK) += fintek-cir.o obj-$(CONFIG_IR_MESON) += meson-ir.o obj-$(CONFIG_IR_MESON_TX) += meson-ir-tx.o +obj-$(CONFIG_IR_MTK) += mtk-cir.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o -obj-$(CONFIG_IR_ENE) += ene_ir.o +obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o obj-$(CONFIG_IR_REDRAT3) += redrat3.o obj-$(CONFIG_IR_RX51) += ir-rx51.o +obj-$(CONFIG_IR_SERIAL) += serial_ir.o obj-$(CONFIG_IR_SPI) += ir-spi.o obj-$(CONFIG_IR_STREAMZAP) += streamzap.o +obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o +obj-$(CONFIG_IR_TOY) += ir_toy.o +obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o +obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o -obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o -obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o -obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o -obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o -obj-$(CONFIG_IR_IGUANA) += iguanair.o -obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o obj-$(CONFIG_RC_ST) += st_rc.o -obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o -obj-$(CONFIG_IR_IMG) += img-ir/ -obj-$(CONFIG_IR_SERIAL) += serial_ir.o -obj-$(CONFIG_IR_MTK) += mtk-cir.o obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o -obj-$(CONFIG_IR_TOY) += ir_toy.o diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index b0d580566e4e..3fb0968efd57 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -287,7 +287,7 @@ static void fintek_process_rx_ir_data(struct fintek_dev *fintek) if (fintek->rem) fintek->parser_state = PARSE_IRDATA; else - ir_raw_event_reset(fintek->rdev); + ir_raw_event_overflow(fintek->rdev); break; case SUBCMD: fintek->rem = fintek_cmdsize(fintek->cmd, sample); diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c index c6cd2e6d8e65..a50701cfbbd7 100644 --- a/drivers/media/rc/gpio-ir-tx.c +++ b/drivers/media/rc/gpio-ir-tx.c @@ -48,11 +48,29 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) return 0; } +static void delay_until(ktime_t until) +{ + /* + * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on + * m68k ndelay(s64) does not compile; so use s32 rather than s64. + */ + s32 delta; + + while (true) { + delta = ktime_us_delta(until, ktime_get()); + if (delta <= 0) + return; + + /* udelay more than 1ms may not work */ + delta = min(delta, 1000); + udelay(delta); + } +} + static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf, uint count) { ktime_t edge; - s32 delta; int i; local_irq_disable(); @@ -63,9 +81,7 @@ static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf, gpiod_set_value(gpio_ir->gpio, !(i % 2)); edge = ktime_add_us(edge, txbuf[i]); - delta = ktime_us_delta(edge, ktime_get()); - if (delta > 0) - udelay(delta); + delay_until(edge); } gpiod_set_value(gpio_ir->gpio, 0); @@ -97,9 +113,7 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, if (i % 2) { // space edge = ktime_add_us(edge, txbuf[i]); - delta = ktime_us_delta(edge, ktime_get()); - if (delta > 0) - udelay(delta); + delay_until(edge); } else { // pulse ktime_t last = ktime_add_us(edge, txbuf[i]); diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index 3e9988ee785f..b40dbf500186 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -67,7 +67,7 @@ static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len) if (overflow > 0) { dev_warn(ir->dev, "receive overflow, at least %u lost", overflow); - ir_raw_event_reset(ir->rc); + ir_raw_event_overflow(ir->rc); } do { diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index f8d080e41f4c..c9cb8277723f 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -109,7 +109,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len) break; case CMD_RX_OVERFLOW: dev_warn(ir->dev, "receive overflow\n"); - ir_raw_event_reset(ir->rc); + ir_raw_event_overflow(ir->rc); break; default: dev_warn(ir->dev, "control code %02x received\n", diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index e0be6471afe5..4ff954b11dc7 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -194,7 +194,7 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) * IR_INTS availably since logic would not clear * fifo when overflow, drv do the job */ - ir_raw_event_reset(priv->rdev); + ir_raw_event_overflow(priv->rdev); symb_num = readl_relaxed(priv->base + IR_DATAH); for (i = 0; i < symb_num; i++) readl_relaxed(priv->base + IR_DATAL); diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c index 41dbbef27fa6..dc68f64e7b51 100644 --- a/drivers/media/rc/ir-imon-decoder.c +++ b/drivers/media/rc/ir-imon-decoder.c @@ -95,7 +95,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) struct imon_dec *data = &dev->raw->imon; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 470f2e1fd507..8b10954d2b6b 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -40,7 +40,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct jvc_dec *data = &dev->raw->jvc; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index 1524dc0fc566..66e8feb9a569 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -221,7 +221,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_scancode lsc = {}; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index b4c3e4baf34d..37b99432ad0d 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -44,7 +44,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 address, not_address, command, not_command; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index d58b6226afeb..82d7f6ad2338 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -45,7 +45,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) enum rc_proto protocol; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 0657ad5eef48..3b2c8bab3e73 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -85,7 +85,7 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) enum rc_proto protocol; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c index fd9ec69a3718..a8a34436fe85 100644 --- a/drivers/media/rc/ir-rcmm-decoder.c +++ b/drivers/media/rc/ir-rcmm-decoder.c @@ -69,7 +69,7 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index bfc181be1044..2bc98c342882 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -51,8 +51,8 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 command, not_command; if (!is_timing_event(ev)) { - if (ev.reset) { - dev_dbg(&dev->dev, "SANYO event reset received. reset to state 0\n"); + if (ev.overflow) { + dev_dbg(&dev->dev, "SANYO event overflow received. reset to state 0\n"); data->state = STATE_INACTIVE; } return 0; diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index d09c38c07dbd..3d8488c39c56 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -41,7 +41,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 msg, echo, address, command, scancode; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index d760d52abaa2..bb25867ecb5e 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -39,7 +39,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 device, subdevice, function; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c index ff94f48bda32..dc36b68739cb 100644 --- a/drivers/media/rc/ir-xmp-decoder.c +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -37,7 +37,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) struct xmp_dec *data = &dev->raw->xmp; if (!is_timing_event(ev)) { - if (ev.reset) + if (ev.overflow) data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 7e98e7e3aace..196806709259 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -458,7 +458,7 @@ static int irtoy_probe(struct usb_interface *intf, err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL); if (err != 0) { dev_err(irtoy->dev, "fail to submit in urb: %d\n", err); - return err; + goto free_rcdev; } err = irtoy_setup(irtoy); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 4f77d4ebacdc..fcfadd7ea31c 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -238,7 +238,7 @@ static irqreturn_t ite_cir_isr(int irq, void *data) /* Check for RX overflow */ if (iflags & ITE_IRQ_RX_FIFO_OVERRUN) { dev_warn(&dev->rdev->dev, "receive overflow\n"); - ir_raw_event_reset(dev->rdev); + ir_raw_event_overflow(dev->rdev); } /* check for the receive interrupt */ diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 5fe5c9e1a46d..f513ff5caf4e 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -1,5 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ + +# Please keep keymaps alphabetically sorted by directory name +#(e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_RC_MAP) += \ + rc-adstech-dvb-t-pci.o \ rc-alink-dtu-m.o \ rc-anysee.o \ rc-apac-viewcomp.o \ @@ -9,17 +13,17 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-ati-tv-wonder-hd-600.o \ rc-ati-x10.o \ rc-avermedia-a16d.o \ - rc-avermedia.o \ rc-avermedia-cardbus.o \ rc-avermedia-dvbt.o \ rc-avermedia-m135a.o \ rc-avermedia-m733a-rm-k6.o \ + rc-avermedia.o \ rc-avermedia-rm-ks.o \ rc-avertv-303.o \ rc-azurewave-ad-tu700.o \ rc-beelink-gs1.o \ - rc-behold.o \ rc-behold-columbus.o \ + rc-behold.o \ rc-budget-ci-old.o \ rc-cinergy-1400.o \ rc-cinergy.o \ @@ -39,8 +43,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-dvico-portable.o \ rc-em-terratec.o \ rc-encore-enltv2.o \ - rc-encore-enltv.o \ rc-encore-enltv-fm53.o \ + rc-encore-enltv.o \ rc-evga-indtube.o \ rc-eztv.o \ rc-flydvb.o \ @@ -50,6 +54,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-geekbox.o \ rc-genius-tvgo-a11mce.o \ rc-gotview7135.o \ + rc-hauppauge.o \ rc-hisi-poplar.o \ rc-hisi-tv-demo.o \ rc-imon-mce.o \ @@ -67,14 +72,14 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-leadtek-y04g0051.o \ rc-lme2510.o \ rc-manli.o \ - rc-mecool-kii-pro.o \ rc-mecool-kiii-pro.o \ - rc-medion-x10.o \ + rc-mecool-kii-pro.o \ rc-medion-x10-digitainer.o \ + rc-medion-x10.o \ rc-medion-x10-or2x.o \ rc-minix-neo.o \ - rc-msi-digivox-ii.o \ rc-msi-digivox-iii.o \ + rc-msi-digivox-ii.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ rc-nebula.o \ @@ -87,20 +92,20 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-pinnacle-color.o \ rc-pinnacle-grey.o \ rc-pinnacle-pctv-hd.o \ - rc-pixelview.o \ - rc-pixelview-mk12.o \ rc-pixelview-002t.o \ + rc-pixelview-mk12.o \ rc-pixelview-new.o \ + rc-pixelview.o \ rc-powercolor-real-angel.o \ rc-proteus-2309.o \ rc-purpletv.o \ rc-pv951.o \ - rc-hauppauge.o \ rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ rc-reddo.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ + rc-su3000.o \ rc-tanix-tx3mini.o \ rc-tanix-tx5max.o \ rc-tbs-nec.o \ @@ -109,16 +114,16 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-terratec-cinergy-c-pci.o \ rc-terratec-cinergy-s2-hd.o \ rc-terratec-cinergy-xs.o \ - rc-terratec-slim.o \ rc-terratec-slim-2.o \ + rc-terratec-slim.o \ rc-tevii-nec.o \ rc-tivo.o \ - rc-total-media-in-hand.o \ rc-total-media-in-hand-02.o \ + rc-total-media-in-hand.o \ rc-trekstor.o \ rc-tt-1500.o \ - rc-twinhan-dtv-cab-ci.o \ rc-twinhan1027.o \ + rc-twinhan-dtv-cab-ci.o \ rc-vega-s9x.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ @@ -128,8 +133,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-wetek-play2.o \ rc-winfast.o \ rc-winfast-usbii-deluxe.o \ - rc-su3000.o \ + rc-x96max.o \ rc-xbox-360.o \ rc-xbox-dvd.o \ - rc-x96max.o \ rc-zx-irdec.o diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index c7c5157725f8..765375bda0c6 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -41,17 +41,16 @@ void lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_fh *fh; int sample; - /* Packet start */ - if (ev.reset) { + /* Receiver overflow, data missing */ + if (ev.overflow) { /* - * Userspace expects a long space event before the start of - * the signal to use as a sync. This may be done with repeat - * packets and normal samples. But if a reset has been sent - * then we assume that a long time has passed, so we send a - * space with the maximum time value. + * Send lirc overflow message. This message is unknown to + * lircd, but it will interpret this as a long space as + * long as the value is set to high value. This resets its + * decoder state. */ - sample = LIRC_SPACE(LIRC_VALUE_MASK); - dev_dbg(&dev->dev, "delivering reset sync space to lirc_dev\n"); + sample = LIRC_OVERFLOW(LIRC_VALUE_MASK); + dev_dbg(&dev->dev, "delivering overflow to lirc_dev\n"); /* Carrier reports */ } else if (ev.carrier_report) { @@ -60,32 +59,25 @@ void lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) /* Packet end */ } else if (ev.timeout) { - if (dev->gap) - return; - dev->gap_start = ktime_get(); - dev->gap = true; - dev->gap_duration = ev.duration; sample = LIRC_TIMEOUT(ev.duration); dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample); /* Normal sample */ } else { - if (dev->gap) { - dev->gap_duration += ktime_to_us(ktime_sub(ktime_get(), - dev->gap_start)); + if (dev->gap_start) { + u64 duration = ktime_us_delta(ktime_get(), + dev->gap_start); /* Cap by LIRC_VALUE_MASK */ - dev->gap_duration = min_t(u64, dev->gap_duration, - LIRC_VALUE_MASK); + duration = min_t(u64, duration, LIRC_VALUE_MASK); spin_lock_irqsave(&dev->lirc_fh_lock, flags); list_for_each_entry(fh, &dev->lirc_fh, list) - kfifo_put(&fh->rawir, - LIRC_SPACE(dev->gap_duration)); + kfifo_put(&fh->rawir, LIRC_SPACE(duration)); spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); - dev->gap = false; + dev->gap_start = 0; } sample = ev.pulse ? LIRC_PULSE(ev.duration) : diff --git a/drivers/media/rc/meson-ir-tx.c b/drivers/media/rc/meson-ir-tx.c index c22cd26a5c07..63e1dbf0a4e9 100644 --- a/drivers/media/rc/meson-ir-tx.c +++ b/drivers/media/rc/meson-ir-tx.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * meson-ir-tx.c - Amlogic Meson IR TX driver * * Copyright (c) 2021, SberDevices. All Rights Reserved. diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c index 840e7aec5c21..27b7412d02a5 100644 --- a/drivers/media/rc/mtk-cir.c +++ b/drivers/media/rc/mtk-cir.c @@ -24,7 +24,8 @@ * Register to setting ok count whose unit based on hardware sampling period * indicating IR receiving completion and then making IRQ fires */ -#define MTK_OK_COUNT(x) (((x) & GENMASK(23, 16)) << 16) +#define MTK_OK_COUNT_MASK (GENMASK(22, 16)) +#define MTK_OK_COUNT(x) ((x) << 16) /* Bit to enable IR hardware function */ #define MTK_IR_EN BIT(0) @@ -202,25 +203,24 @@ static inline void mtk_irq_enable(struct mtk_ir *ir, u32 mask) static irqreturn_t mtk_ir_irq(int irqno, void *dev_id) { + struct ir_raw_event rawir = {}; struct mtk_ir *ir = dev_id; - u8 wid = 0; u32 i, j, val; - struct ir_raw_event rawir = {}; + u8 wid; /* - * Reset decoder state machine explicitly is required - * because 1) the longest duration for space MTK IR hardware - * could record is not safely long. e.g 12ms if rx resolution - * is 46us by default. There is still the risk to satisfying - * every decoder to reset themselves through long enough - * trailing spaces and 2) the IRQ handler guarantees that - * start of IR message is always contained in and starting - * from register mtk_chkdata_reg(ir, i). + * Each pulse and space is encoded as a single byte, each byte + * alternating between pulse and space. If a pulse or space is longer + * than can be encoded in a single byte, it is encoded as the maximum + * value 0xff. + * + * If a space is longer than ok_count (about 23ms), the value is + * encoded as zero, and all following bytes are zero. Any IR that + * follows will be presented in the next interrupt. + * + * If there are more than 68 (=MTK_CHKDATA_SZ * 4) pulses and spaces, + * then the only the first 68 will be presented; the rest is lost. */ - ir_raw_event_reset(ir->rc); - - /* First message must be pulse */ - rawir.pulse = false; /* Handle all pulse and space IR controller captures */ for (i = 0 ; i < MTK_CHKDATA_SZ ; i++) { @@ -228,7 +228,8 @@ static irqreturn_t mtk_ir_irq(int irqno, void *dev_id) dev_dbg(ir->dev, "@reg%d=0x%08x\n", i, val); for (j = 0 ; j < 4 ; j++) { - wid = (val & (MTK_WIDTH_MASK << j * 8)) >> j * 8; + wid = val & MTK_WIDTH_MASK; + val >>= 8; rawir.pulse = !rawir.pulse; rawir.duration = wid * (MTK_IR_SAMPLE + 1); ir_raw_event_store_with_filter(ir->rc, &rawir); @@ -268,7 +269,7 @@ static irqreturn_t mtk_ir_irq(int irqno, void *dev_id) static const struct mtk_ir_data mt7623_data = { .regs = mt7623_regs, .fields = mt7623_fields, - .ok_count = 0xf, + .ok_count = 3, .hw_period = 0xff, .div = 4, }; @@ -276,7 +277,7 @@ static const struct mtk_ir_data mt7623_data = { static const struct mtk_ir_data mt7622_data = { .regs = mt7622_regs, .fields = mt7622_fields, - .ok_count = 0xf, + .ok_count = 3, .hw_period = 0xffff, .div = 32, }; @@ -400,7 +401,7 @@ static int mtk_ir_probe(struct platform_device *pdev) mtk_w32_mask(ir, MTK_DG_CNT(1), MTK_DG_CNT_MASK, MTK_IRTHD); /* Enable IR and PWM */ - val = mtk_r32(ir, MTK_CONFIG_HIGH_REG); + val = mtk_r32(ir, MTK_CONFIG_HIGH_REG) & ~MTK_OK_COUNT_MASK; val |= MTK_OK_COUNT(ir->data->ok_count) | MTK_PWM_EN | MTK_IR_EN; mtk_w32(ir, val, MTK_CONFIG_HIGH_REG); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 8a37f083fe3d..2214d41ef579 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -742,7 +742,7 @@ static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt) nvt->pkts = 0; nvt_clear_cir_fifo(nvt); - ir_raw_event_reset(nvt->rdev); + ir_raw_event_overflow(nvt->rdev); } /* copy data from hardware rx fifo into driver buffer */ diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 62f032dffd33..ef1e95e1af7f 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -190,7 +190,7 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration) /* Returns true if event is normal pulse/space event */ static inline bool is_timing_event(struct ir_raw_event ev) { - return !ev.carrier_report && !ev.reset; + return !ev.carrier_report && !ev.overflow; } #define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space") diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index c65bba4ec473..16e33d7eaaa2 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -35,8 +35,6 @@ static int ir_raw_event_thread(void *data) !is_transition(&ev, &raw->prev_ev)) dev_warn_once(&dev->dev, "two consecutive events of type %s", TO_STR(ev.pulse)); - if (raw->prev_ev.reset && ev.pulse == 0) - dev_warn_once(&dev->dev, "timing event after reset should be pulse"); } list_for_each_entry(handler, &ir_raw_handler_list, list) if (dev->enabled_protocols & diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 6441879fcba1..b356041c5c00 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -112,7 +112,11 @@ static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) rawir.pulse = i % 2 ? false : true; rawir.duration = txbuf[i]; - ir_raw_event_store_with_filter(dev, &rawir); + /* simulate overflow if ridiculously long pulse was sent */ + if (rawir.pulse && rawir.duration > MS_TO_US(50)) + ir_raw_event_overflow(dev); + else + ir_raw_event_store_with_filter(dev, &rawir); } if (lodev->carrierreport) { diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 4e419dbbacd3..19e987a048cc 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -111,7 +111,7 @@ static irqreturn_t st_rc_rx_interrupt(int irq, void *data) int_status = readl(dev->rx_base + IRB_RX_INT_STATUS); if (unlikely(int_status & IRB_RX_OVERRUN_INT)) { /* discard the entire collection in case of errors! */ - ir_raw_event_reset(dev->rdev); + ir_raw_event_overflow(dev->rdev); dev_info(dev->dev, "IR RX overrun\n"); writel(IRB_RX_OVERRUN_INT, dev->rx_base + IRB_RX_INT_CLEAR); diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 391a591c1b75..b631a81e58bb 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -126,7 +126,7 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) } if (status & REG_RXSTA_ROI) { - ir_raw_event_reset(ir->rc); + ir_raw_event_overflow(ir->rc); } else if (status & REG_RXSTA_RPE) { ir_raw_event_set_idle(ir->rc, true); ir_raw_event_handle(ir->rc); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 94efb035d21b..25884a79985c 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -470,7 +470,7 @@ wbcir_irq_handler(int irqno, void *cookie) /* RX overflow? (read clears bit) */ if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) { data->rxstate = WBCIR_RXSTATE_ERROR; - ir_raw_event_reset(data->dev); + ir_raw_event_overflow(data->dev); } /* TX underflow? */ diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index 857ef4ace6e9..4656afae5bb4 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -1,25 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -if VIDEO_V4L2 +if VIDEO_DEV && SPI comment "SPI I2C drivers auto-selected by 'Autoselect ancillary drivers'" depends on MEDIA_HIDE_ANCILLARY_SUBDRV && SPI -menu "SPI helper chips" - visible if !MEDIA_HIDE_ANCILLARY_SUBDRV - -config VIDEO_GS1662 - tristate "Gennum Serializers video" - depends on SPI && VIDEO_V4L2 - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - Enable the GS1662 driver which serializes video streams. - -endmenu - -endif - -if SPI menu "Media SPI Adapters" config CXD2880_SPI_DRV @@ -29,6 +13,14 @@ config CXD2880_SPI_DRV help Choose if you would like to have SPI interface support for Sony CXD2880. +config VIDEO_GS1662 + tristate "Gennum Serializers video" + depends on SPI && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + Enable the GS1662 driver which serializes video streams. + endmenu endif diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile index 9f45787d680d..6ac7adc64124 100644 --- a/drivers/media/spi/Makefile +++ b/drivers/media/spi/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_GS1662) += gs1662.o -obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/cxd2880 + +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o +obj-$(CONFIG_VIDEO_GS1662) += gs1662.o diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig index e27d6602545d..51cf27834df0 100644 --- a/drivers/media/test-drivers/Kconfig +++ b/drivers/media/test-drivers/Kconfig @@ -6,13 +6,9 @@ menuconfig V4L_TEST_DRIVERS if V4L_TEST_DRIVERS -source "drivers/media/test-drivers/vimc/Kconfig" - -source "drivers/media/test-drivers/vivid/Kconfig" - config VIDEO_VIM2M tristate "Virtual Memory-to-Memory Driver" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select MEDIA_CONTROLLER @@ -22,6 +18,8 @@ config VIDEO_VIM2M framework. source "drivers/media/test-drivers/vicodec/Kconfig" +source "drivers/media/test-drivers/vimc/Kconfig" +source "drivers/media/test-drivers/vivid/Kconfig" endif #V4L_TEST_DRIVERS diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile index 9f0e4ebb2efe..ff390b687189 100644 --- a/drivers/media/test-drivers/Makefile +++ b/drivers/media/test-drivers/Makefile @@ -3,8 +3,12 @@ # Makefile for the test drivers. # -obj-$(CONFIG_VIDEO_VIMC) += vimc/ -obj-$(CONFIG_VIDEO_VIVID) += vivid/ -obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o -obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ -obj-$(CONFIG_DVB_VIDTV) += vidtv/ +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) + +obj-$(CONFIG_DVB_VIDTV) += vidtv/ + +obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ +obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o +obj-$(CONFIG_VIDEO_VIMC) += vimc/ +obj-$(CONFIG_VIDEO_VIVID) += vivid/ diff --git a/drivers/media/test-drivers/vicodec/Kconfig b/drivers/media/test-drivers/vicodec/Kconfig index d77c67810c73..a7a828eec2a4 100644 --- a/drivers/media/test-drivers/vicodec/Kconfig +++ b/drivers/media/test-drivers/vicodec/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_VICODEC tristate "Virtual Codec Driver" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select MEDIA_CONTROLLER diff --git a/drivers/media/test-drivers/vidtv/Kconfig b/drivers/media/test-drivers/vidtv/Kconfig index 22c4fd39461f..e511e51c0b5b 100644 --- a/drivers/media/test-drivers/vidtv/Kconfig +++ b/drivers/media/test-drivers/vidtv/Kconfig @@ -7,5 +7,4 @@ config DVB_VIDTV validate the existing APIs in the media subsystem. It can also aid developers working on userspace applications. - When in doubt, say N. diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.c b/drivers/media/test-drivers/vidtv/vidtv_s302m.c index d79b65854627..9da18eac04b5 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_s302m.c +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.c @@ -455,6 +455,9 @@ struct vidtv_encoder e->name = kstrdup(args.name, GFP_KERNEL); e->encoder_buf = vzalloc(VIDTV_S302M_BUF_SZ); + if (!e->encoder_buf) + goto out_kfree_e; + e->encoder_buf_sz = VIDTV_S302M_BUF_SZ; e->encoder_buf_offset = 0; @@ -467,10 +470,8 @@ struct vidtv_encoder e->is_video_encoder = false; ctx = kzalloc(priv_sz, GFP_KERNEL); - if (!ctx) { - kfree(e); - return NULL; - } + if (!ctx) + goto out_kfree_buf; e->ctx = ctx; ctx->last_duration = 0; @@ -498,6 +499,14 @@ struct vidtv_encoder e->next = NULL; return e; + +out_kfree_buf: + vfree(e->encoder_buf); + +out_kfree_e: + kfree(e->name); + kfree(e); + return NULL; } void vidtv_s302m_encoder_destroy(struct vidtv_encoder *e) diff --git a/drivers/media/test-drivers/vimc/Kconfig b/drivers/media/test-drivers/vimc/Kconfig index da4b2ad6e40c..0d5169819cac 100644 --- a/drivers/media/test-drivers/vimc/Kconfig +++ b/drivers/media/test-drivers/vimc/Kconfig @@ -1,12 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_VIMC tristate "Virtual Media Controller Driver (VIMC)" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select FONT_SUPPORT select FONT_8x16 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC + select VIDEOBUF2_DMA_CONTIG select VIDEO_V4L2_TPG help Skeleton driver for Virtual Media Controller diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index 5e9fd902cd37..d1e2d0739c00 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -7,6 +7,7 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> #include <media/videobuf2-vmalloc.h> #include "vimc-common.h" @@ -423,14 +424,18 @@ static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, /* Initialize the vb2 queue */ q = &vcap->queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_DMABUF; + if (vimc_allocator == VIMC_ALLOCATOR_VMALLOC) + q->io_modes |= VB2_USERPTR; q->drv_priv = vcap; q->buf_struct_size = sizeof(struct vimc_cap_buffer); q->ops = &vimc_cap_qops; - q->mem_ops = &vb2_vmalloc_memops; + q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG + ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 2; q->lock = &vcap->lock; + q->dev = v4l2_dev->dev; ret = vb2_queue_init(q); if (ret) { diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index a289434e75ba..ba1930772589 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -35,6 +35,13 @@ #define VIMC_PIX_FMT_MAX_CODES 8 +extern unsigned int vimc_allocator; + +enum vimc_allocator_type { + VIMC_ALLOCATOR_VMALLOC = 0, + VIMC_ALLOCATOR_DMA_CONTIG = 1, +}; + /** * vimc_colorimetry_clamp - Adjust colorimetry parameters * diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 4b0ae6f51d76..06edf9d4d92c 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -5,6 +5,7 @@ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> */ +#include <linux/dma-mapping.h> #include <linux/font.h> #include <linux/init.h> #include <linux/module.h> @@ -15,6 +16,12 @@ #include "vimc-common.h" +unsigned int vimc_allocator; +module_param_named(allocator, vimc_allocator, uint, 0444); +MODULE_PARM_DESC(allocator, " memory allocator selection, default is 0.\n" + "\t\t 0 == vmalloc\n" + "\t\t 1 == dma-contig"); + #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ @@ -278,6 +285,9 @@ static int vimc_probe(struct platform_device *pdev) tpg_set_font(font->data); + if (vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG) + dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); if (!vimc) return -ENOMEM; diff --git a/drivers/media/test-drivers/vivid/Kconfig b/drivers/media/test-drivers/vivid/Kconfig index c3abde2986b2..318799d317ba 100644 --- a/drivers/media/test-drivers/vivid/Kconfig +++ b/drivers/media/test-drivers/vivid/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_VIVID tristate "Virtual Video Test Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FB + depends on VIDEO_DEV && !SPARC32 && !SPARC64 && FB depends on HAS_DMA select FONT_SUPPORT select FONT_8x16 diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index 45f96706edde..176b72cb143b 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -307,7 +307,7 @@ struct vivid_dev { bool dqbuf_error; bool req_validate_error; bool seq_wrap; - bool time_wrap; + u64 time_wrap; u64 time_wrap_offset; unsigned perc_dropped_buffers; enum vivid_signal_mode std_signal_mode[MAX_INPUTS]; @@ -437,6 +437,7 @@ struct vivid_dev { bool touch_cap_seq_resync; u32 touch_cap_seq_start; u32 touch_cap_seq_count; + u32 touch_cap_with_seq_wrap_count; bool touch_cap_streaming; struct v4l2_fract timeperframe_tch_cap; struct v4l2_pix_format tch_format; @@ -524,7 +525,9 @@ struct vivid_dev { struct task_struct *kthread_sdr_cap; unsigned long jiffies_sdr_cap; u32 sdr_cap_seq_offset; + u32 sdr_cap_seq_start; u32 sdr_cap_seq_count; + u32 sdr_cap_with_seq_wrap_count; bool sdr_cap_seq_resync; /* RDS generator */ diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 8dc50fe22972..e7516dc1227b 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -1084,7 +1084,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_display_present = { static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_streaming); - u64 rem; switch (ctrl->id) { case VIVID_CID_DQBUF_ERROR: @@ -1122,20 +1121,10 @@ static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_TIME_WRAP: dev->time_wrap = ctrl->val; - if (ctrl->val == 0) { - dev->time_wrap_offset = 0; - break; - } - /* - * We want to set the time 16 seconds before the 32 bit tv_sec - * value of struct timeval would wrap around. So first we - * calculate ktime_get_ns() % ((1 << 32) * NSEC_PER_SEC), and - * then we set the offset to ((1 << 32) - 16) * NSEC_PER_SEC). - */ - div64_u64_rem(ktime_get_ns(), - 0x100000000ULL * NSEC_PER_SEC, &rem); - dev->time_wrap_offset = - (0x100000000ULL - 16) * NSEC_PER_SEC - rem; + if (dev->time_wrap == 1) + dev->time_wrap = (1ULL << 63) - NSEC_PER_SEC * 16ULL; + else if (dev->time_wrap == 2) + dev->time_wrap = ((1ULL << 31) - 16) * NSEC_PER_SEC; break; } return 0; @@ -1208,13 +1197,20 @@ static const struct v4l2_ctrl_config vivid_ctrl_seq_wrap = { .step = 1, }; +static const char * const vivid_ctrl_time_wrap_strings[] = { + "None", + "64 Bit", + "32 Bit", + NULL, +}; + static const struct v4l2_ctrl_config vivid_ctrl_time_wrap = { .ops = &vivid_streaming_ctrl_ops, .id = VIVID_CID_TIME_WRAP, .name = "Wrap Timestamp", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(vivid_ctrl_time_wrap_strings) - 2, + .qmenu = vivid_ctrl_time_wrap_strings, }; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 9da730ccfa94..690daada7db4 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -18,6 +18,7 @@ #include <linux/freezer.h> #include <linux/random.h> #include <linux/v4l2-dv-timings.h> +#include <linux/jiffies.h> #include <asm/div64.h> #include <media/videobuf2-vmalloc.h> #include <media/v4l2-dv-timings.h> @@ -719,8 +720,7 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, if (!vid_cap_buf && !vbi_cap_buf && !meta_cap_buf) goto update_mv; - f_time = dev->cap_frame_period * dev->vid_cap_seq_count + - dev->cap_stream_start + dev->time_wrap_offset; + f_time = ktime_get_ns() + dev->time_wrap_offset; if (vid_cap_buf) { v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req, @@ -813,6 +813,10 @@ static int vivid_thread_vid_cap(void *data) dev->cap_seq_resync = false; dev->jiffies_vid_cap = jiffies; dev->cap_stream_start = ktime_get_ns(); + if (dev->time_wrap) + dev->time_wrap_offset = dev->time_wrap - dev->cap_stream_start; + else + dev->time_wrap_offset = 0; vivid_cap_update_frame_period(dev); for (;;) { @@ -890,7 +894,7 @@ static int vivid_thread_vid_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (jiffies - cur_jiffies < wait_jiffies && + while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && !kthread_should_stop()) schedule(); } diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-out.c b/drivers/media/test-drivers/vivid/vivid-kthread-out.c index 79c57d14ac4e..0833e021bb11 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-out.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-out.c @@ -18,6 +18,7 @@ #include <linux/freezer.h> #include <linux/random.h> #include <linux/v4l2-dv-timings.h> +#include <linux/jiffies.h> #include <asm/div64.h> #include <media/videobuf2-vmalloc.h> #include <media/v4l2-dv-timings.h> @@ -154,12 +155,13 @@ static int vivid_thread_vid_out(void *data) /* Resets frame counters */ dev->out_seq_offset = 0; - if (dev->seq_wrap) - dev->out_seq_count = 0xffffff80U; + dev->out_seq_count = 0; dev->jiffies_vid_out = jiffies; - dev->vid_out_seq_start = dev->vbi_out_seq_start = 0; - dev->meta_out_seq_start = 0; dev->out_seq_resync = false; + if (dev->time_wrap) + dev->time_wrap_offset = dev->time_wrap - ktime_get_ns(); + else + dev->time_wrap_offset = 0; for (;;) { try_to_freeze(); @@ -233,7 +235,7 @@ static int vivid_thread_vid_out(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (jiffies - cur_jiffies < wait_jiffies && + while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && !kthread_should_stop()) schedule(); } diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c index 38fdfee79498..fa711ee36a3f 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c @@ -5,6 +5,7 @@ */ #include <linux/freezer.h> +#include <linux/jiffies.h> #include "vivid-core.h" #include "vivid-kthread-touch.h" #include "vivid-touch-cap.h" @@ -62,6 +63,10 @@ static int vivid_thread_touch_cap(void *data) dev->touch_cap_seq_count = 0; dev->touch_cap_seq_resync = false; dev->jiffies_touch_cap = jiffies; + if (dev->time_wrap) + dev->time_wrap_offset = dev->time_wrap - ktime_get_ns(); + else + dev->time_wrap_offset = 0; for (;;) { try_to_freeze(); @@ -102,6 +107,8 @@ static int vivid_thread_touch_cap(void *data) } dropped_bufs = buffers_since_start + dev->touch_cap_seq_offset - dev->touch_cap_seq_count; dev->touch_cap_seq_count = buffers_since_start + dev->touch_cap_seq_offset; + dev->touch_cap_with_seq_wrap_count = + dev->touch_cap_seq_count - dev->touch_cap_seq_start; vivid_thread_tch_cap_tick(dev, dropped_bufs); @@ -128,7 +135,7 @@ static int vivid_thread_touch_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (jiffies - cur_jiffies < wait_jiffies && + while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && !kthread_should_stop()) schedule(); } @@ -143,6 +150,7 @@ int vivid_start_generating_touch_cap(struct vivid_dev *dev) return 0; } + dev->touch_cap_seq_start = dev->seq_wrap * 128; dev->kthread_touch_cap = kthread_run(vivid_thread_touch_cap, dev, "%s-tch-cap", dev->v4l2_dev.name); diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index 265db2114671..0ae5628b86c9 100644 --- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c @@ -17,6 +17,7 @@ #include <media/v4l2-event.h> #include <media/v4l2-dv-timings.h> #include <linux/fixp-arith.h> +#include <linux/jiffies.h> #include "vivid-core.h" #include "vivid-ctrls.h" @@ -101,7 +102,7 @@ static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev) spin_unlock(&dev->slock); if (sdr_cap_buf) { - sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count; + sdr_cap_buf->vb.sequence = dev->sdr_cap_with_seq_wrap_count; v4l2_ctrl_request_setup(sdr_cap_buf->vb.vb2_buf.req_obj.req, &dev->ctrl_hdl_sdr_cap); v4l2_ctrl_request_complete(sdr_cap_buf->vb.vb2_buf.req_obj.req, @@ -131,10 +132,13 @@ static int vivid_thread_sdr_cap(void *data) /* Resets frame counters */ dev->sdr_cap_seq_offset = 0; - if (dev->seq_wrap) - dev->sdr_cap_seq_offset = 0xffffff80U; + dev->sdr_cap_seq_count = 0; dev->jiffies_sdr_cap = jiffies; dev->sdr_cap_seq_resync = false; + if (dev->time_wrap) + dev->time_wrap_offset = dev->time_wrap - ktime_get_ns(); + else + dev->time_wrap_offset = 0; for (;;) { try_to_freeze(); @@ -174,6 +178,7 @@ static int vivid_thread_sdr_cap(void *data) } dev->sdr_cap_seq_count = buffers_since_start + dev->sdr_cap_seq_offset; + dev->sdr_cap_with_seq_wrap_count = dev->sdr_cap_seq_count - dev->sdr_cap_seq_start; vivid_thread_sdr_cap_tick(dev); mutex_unlock(&dev->mutex); @@ -201,7 +206,7 @@ static int vivid_thread_sdr_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - while (jiffies - cur_jiffies < wait_jiffies && + while (time_is_after_jiffies(cur_jiffies + wait_jiffies) && !kthread_should_stop()) schedule(); } @@ -263,7 +268,7 @@ static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count) int err = 0; dprintk(dev, 1, "%s\n", __func__); - dev->sdr_cap_seq_count = 0; + dev->sdr_cap_seq_start = dev->seq_wrap * 128; if (dev->start_streaming_error) { dev->start_streaming_error = false; err = -EINVAL; diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index ebb00b128030..64e3e4cb30c2 100644 --- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c @@ -262,7 +262,7 @@ void vivid_fillbuff_tch(struct vivid_dev *dev, struct vivid_buffer *buf) __s16 *tch_buf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - buf->vb.sequence = dev->touch_cap_seq_count; + buf->vb.sequence = dev->touch_cap_with_seq_wrap_count; test_pattern = (buf->vb.sequence / TCH_SEQ_COUNT) % TEST_CASE_MAX; test_pat_idx = buf->vb.sequence % TCH_SEQ_COUNT; diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 4605bb377574..0c01b0298099 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -23,80 +23,80 @@ menu "Customize TV tuners" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT -config MEDIA_TUNER_SIMPLE - tristate "Simple tuner support" - depends on MEDIA_SUPPORT && I2C - select MEDIA_TUNER_TDA9887 +config MEDIA_TUNER_E4000 + tristate "Elonics E4000 silicon tuner" + depends on MEDIA_SUPPORT && I2C && VIDEO_DEV + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for various simple tuners. + Elonics E4000 silicon tuner driver. -config MEDIA_TUNER_TDA18250 - tristate "NXP TDA18250 silicon tuner" +config MEDIA_TUNER_FC0011 + tristate "Fitipower FC0011 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for TDA18250 tuner. + Fitipower FC0011 silicon tuner driver. -config MEDIA_TUNER_TDA8290 - tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" +config MEDIA_TUNER_FC0012 + tristate "Fitipower FC0012 silicon tuner" depends on MEDIA_SUPPORT && I2C - select MEDIA_TUNER_TDA827X - select MEDIA_TUNER_TDA18271 default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for Philips TDA8290+8275(a) tuner. + Fitipower FC0012 silicon tuner driver. -config MEDIA_TUNER_TDA827X - tristate "Philips TDA827X silicon tuner" +config MEDIA_TUNER_FC0013 + tristate "Fitipower FC0013 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A DVB-T silicon tuner module. Say Y when you want to support this tuner. + Fitipower FC0013 silicon tuner driver. -config MEDIA_TUNER_TDA18271 - tristate "NXP TDA18271 silicon tuner" +config MEDIA_TUNER_FC2580 + tristate "FCI FC2580 silicon tuner" + depends on MEDIA_SUPPORT && I2C && VIDEO_DEV + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + FCI FC2580 silicon tuner driver. + +config MEDIA_TUNER_IT913X + tristate "ITE Tech IT913x silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A silicon tuner module. Say Y when you want to support this tuner. + ITE Tech IT913x silicon tuner driver. -config MEDIA_TUNER_TDA9887 - tristate "TDA 9885/6/7 analog IF demodulator" +config MEDIA_TUNER_M88RS6000T + tristate "Montage M88RS6000 internal tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for Philips TDA9885/6/7 - analog IF demodulator. + Montage M88RS6000 internal tuner. -config MEDIA_TUNER_TEA5761 - tristate "TEA 5761 radio tuner" +config MEDIA_TUNER_MAX2165 + tristate "Maxim MAX2165 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for the Philips TEA5761 radio tuner. + A driver for the silicon tuner MAX2165 from Maxim. -config MEDIA_TUNER_TEA5767 - tristate "TEA 5767 radio tuner" +config MEDIA_TUNER_MC44S803 + tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to include support for the Philips TEA5767 radio tuner. + Say Y here to support the Freescale MC44S803 based tuners config MEDIA_TUNER_MSI001 tristate "Mirics MSi001" - depends on MEDIA_SUPPORT && SPI && VIDEO_V4L2 + depends on MEDIA_SUPPORT && SPI && VIDEO_DEV default m if !MEDIA_SUBDRV_AUTOSELECT help Mirics MSi001 silicon tuner driver. -config MEDIA_TUNER_MT20XX - tristate "Microtune 2032 / 2050 tuners" - depends on MEDIA_SUPPORT && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to include support for the MT2032 / MT2050 tuner. - config MEDIA_TUNER_MT2060 tristate "Microtune MT2060 silicon IF tuner" depends on MEDIA_SUPPORT && I2C @@ -111,12 +111,12 @@ config MEDIA_TUNER_MT2063 help A driver for the silicon IF tuner MT2063 from Microtune. -config MEDIA_TUNER_MT2266 - tristate "Microtune MT2266 silicon tuner" +config MEDIA_TUNER_MT20XX + tristate "Microtune 2032 / 2050 tuners" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A driver for the silicon baseband tuner MT2266 from Microtune. + Say Y here to include support for the MT2032 / MT2050 tuner. config MEDIA_TUNER_MT2131 tristate "Microtune MT2131 silicon tuner" @@ -125,37 +125,19 @@ config MEDIA_TUNER_MT2131 help A driver for the silicon baseband tuner MT2131 from Microtune. -config MEDIA_TUNER_QT1010 - tristate "Quantek QT1010 silicon tuner" - depends on MEDIA_SUPPORT && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A driver for the silicon tuner QT1010 from Quantek. - -config MEDIA_TUNER_XC2028 - tristate "XCeive xc2028/xc3028 tuners" - depends on MEDIA_SUPPORT && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to include support for the xc2028/xc3028 tuners. - -config MEDIA_TUNER_XC5000 - tristate "Xceive XC5000 silicon tuner" +config MEDIA_TUNER_MT2266 + tristate "Microtune MT2266 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A driver for the silicon tuner XC5000 from Xceive. - This device is only used inside a SiP called together with a - demodulator for now. + A driver for the silicon baseband tuner MT2266 from Microtune. -config MEDIA_TUNER_XC4000 - tristate "Xceive XC4000 silicon tuner" +config MEDIA_TUNER_MXL301RF + tristate "MaxLinear MxL301RF tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A driver for the silicon tuner XC4000 from Xceive. - This device is only used inside a SiP called together with a - demodulator for now. + MaxLinear MxL301RF OFDM tuner driver. config MEDIA_TUNER_MXL5005S tristate "MaxLinear MSL5005S silicon tuner" @@ -171,47 +153,49 @@ config MEDIA_TUNER_MXL5007T help A driver for the silicon tuner MxL5007T from MaxLinear. -config MEDIA_TUNER_MC44S803 - tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" +config MEDIA_TUNER_QM1D1B0004 + tristate "Sharp QM1D1B0004 tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Freescale MC44S803 based tuners + Sharp QM1D1B0004 ISDB-S tuner driver. -config MEDIA_TUNER_MAX2165 - tristate "Maxim MAX2165 silicon tuner" +config MEDIA_TUNER_QM1D1C0042 + tristate "Sharp QM1D1C0042 tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - A driver for the silicon tuner MAX2165 from Maxim. + Sharp QM1D1C0042 trellis coded 8PSK tuner driver. -config MEDIA_TUNER_TDA18218 - tristate "NXP TDA18218 silicon tuner" +config MEDIA_TUNER_QT1010 + tristate "Quantek QT1010 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - NXP TDA18218 silicon tuner driver. + A driver for the silicon tuner QT1010 from Quantek. -config MEDIA_TUNER_FC0011 - tristate "Fitipower FC0011 silicon tuner" +config MEDIA_TUNER_R820T + tristate "Rafael Micro R820T silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT + select BITREVERSE help - Fitipower FC0011 silicon tuner driver. + Rafael Micro R820T silicon tuner driver. -config MEDIA_TUNER_FC0012 - tristate "Fitipower FC0012 silicon tuner" +config MEDIA_TUNER_SI2157 + tristate "Silicon Labs Si2157 silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Fitipower FC0012 silicon tuner driver. + Silicon Labs Si2157 silicon tuner driver. -config MEDIA_TUNER_FC0013 - tristate "Fitipower FC0013 silicon tuner" +config MEDIA_TUNER_SIMPLE + tristate "Simple tuner support" depends on MEDIA_SUPPORT && I2C + select MEDIA_TUNER_TDA9887 default m if !MEDIA_SUBDRV_AUTOSELECT help - Fitipower FC0013 silicon tuner driver. + Say Y here to include support for various simple tuners. config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" @@ -221,79 +205,96 @@ config MEDIA_TUNER_TDA18212 help NXP TDA18212 silicon tuner driver. -config MEDIA_TUNER_E4000 - tristate "Elonics E4000 silicon tuner" - depends on MEDIA_SUPPORT && I2C && VIDEO_V4L2 - select REGMAP_I2C +config MEDIA_TUNER_TDA18218 + tristate "NXP TDA18218 silicon tuner" + depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Elonics E4000 silicon tuner driver. + NXP TDA18218 silicon tuner driver. -config MEDIA_TUNER_FC2580 - tristate "FCI FC2580 silicon tuner" - depends on MEDIA_SUPPORT && I2C && VIDEO_V4L2 - select REGMAP_I2C +config MEDIA_TUNER_TDA18250 + tristate "NXP TDA18250 silicon tuner" + depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - FCI FC2580 silicon tuner driver. + Say Y here to include support for TDA18250 tuner. -config MEDIA_TUNER_M88RS6000T - tristate "Montage M88RS6000 internal tuner" +config MEDIA_TUNER_TDA18271 + tristate "NXP TDA18271 silicon tuner" depends on MEDIA_SUPPORT && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Montage M88RS6000 internal tuner. + A silicon tuner module. Say Y when you want to support this tuner. -config MEDIA_TUNER_TUA9001 - tristate "Infineon TUA9001 silicon tuner" +config MEDIA_TUNER_TDA827X + tristate "Philips TDA827X silicon tuner" depends on MEDIA_SUPPORT && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Infineon TUA 9001 silicon tuner driver. + A DVB-T silicon tuner module. Say Y when you want to support this tuner. -config MEDIA_TUNER_SI2157 - tristate "Silicon Labs Si2157 silicon tuner" +config MEDIA_TUNER_TDA8290 + tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" depends on MEDIA_SUPPORT && I2C + select MEDIA_TUNER_TDA827X + select MEDIA_TUNER_TDA18271 default m if !MEDIA_SUBDRV_AUTOSELECT help - Silicon Labs Si2157 silicon tuner driver. + Say Y here to include support for Philips TDA8290+8275(a) tuner. -config MEDIA_TUNER_IT913X - tristate "ITE Tech IT913x silicon tuner" +config MEDIA_TUNER_TDA9887 + tristate "TDA 9885/6/7 analog IF demodulator" depends on MEDIA_SUPPORT && I2C - select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - ITE Tech IT913x silicon tuner driver. + Say Y here to include support for Philips TDA9885/6/7 + analog IF demodulator. -config MEDIA_TUNER_R820T - tristate "Rafael Micro R820T silicon tuner" +config MEDIA_TUNER_TEA5761 + tristate "TEA 5761 radio tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT - select BITREVERSE help - Rafael Micro R820T silicon tuner driver. + Say Y here to include support for the Philips TEA5761 radio tuner. -config MEDIA_TUNER_MXL301RF - tristate "MaxLinear MxL301RF tuner" +config MEDIA_TUNER_TEA5767 + tristate "TEA 5767 radio tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - MaxLinear MxL301RF OFDM tuner driver. + Say Y here to include support for the Philips TEA5767 radio tuner. -config MEDIA_TUNER_QM1D1C0042 - tristate "Sharp QM1D1C0042 tuner" +config MEDIA_TUNER_TUA9001 + tristate "Infineon TUA9001 silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Sharp QM1D1C0042 trellis coded 8PSK tuner driver. + Infineon TUA 9001 silicon tuner driver. -config MEDIA_TUNER_QM1D1B0004 - tristate "Sharp QM1D1B0004 tuner" +config MEDIA_TUNER_XC2028 + tristate "XCeive xc2028/xc3028 tuners" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help - Sharp QM1D1B0004 ISDB-S tuner driver. + Say Y here to include support for the xc2028/xc3028 tuners. + +config MEDIA_TUNER_XC4000 + tristate "Xceive XC4000 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the silicon tuner XC4000 from Xceive. + This device is only used inside a SiP called together with a + demodulator for now. + +config MEDIA_TUNER_XC5000 + tristate "Xceive XC5000 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for the silicon tuner XC5000 from Xceive. + This device is only used inside a SiP called together with a + demodulator for now. + endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 7b4f8423501e..bd350a285aad 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -3,46 +3,46 @@ # Makefile for common V4L/DVB tuners # +ccflags-y += -I$(srctree)/drivers/media/dvb-frontends tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o -obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o -obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o -# tuner-types will be merged into tuner-simple, in the future -obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o -obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o -obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o -obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o -obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o -obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o -obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o -obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o -obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o -obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o -obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o -obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o -obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o -obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o -obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o -obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o -obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o -obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o -obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o -obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o -obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o -obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o -obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o -obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o -obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o +obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o obj-$(CONFIG_MEDIA_TUNER_IT913X) += it913x.o -obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o +obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o +obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o +obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o +obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o +obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o +obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o +obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o +obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o -obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o +obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o +obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_QM1D1B0004) += qm1d1b0004.o -obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o +obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o +obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o +obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o +obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o +obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o +obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o +obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18250) += tda18250.o - -ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o +obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o +obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o +obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o +obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o +obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o +obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_XC2028) += xc2028.o +obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o +obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 3f1f9af92bc9..a3a8d051dc6c 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -257,7 +257,7 @@ err: /* * V4L2 API */ -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) static const struct v4l2_frequency_band bands[] = { { .type = V4L2_TUNER_RF, @@ -654,7 +654,7 @@ static int e4000_probe(struct i2c_client *client, if (ret) goto err_kfree; -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) /* Register controls */ v4l2_ctrl_handler_init(&dev->hdl, 9); dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, @@ -713,7 +713,7 @@ static int e4000_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) v4l2_ctrl_handler_free(&dev->hdl); #endif kfree(dev); diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 7639a305048f..1b5961bdf2d5 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -357,7 +357,7 @@ static const struct dvb_tuner_ops fc2580_dvb_tuner_ops = { /* * V4L2 API */ -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) static const struct v4l2_frequency_band bands[] = { { .type = V4L2_TUNER_RF, @@ -552,7 +552,7 @@ static int fc2580_probe(struct i2c_client *client, goto err_kfree; } -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) /* Register controls */ v4l2_ctrl_handler_init(&dev->hdl, 2); dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &fc2580_ctrl_ops, @@ -594,7 +594,7 @@ static int fc2580_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +#if IS_ENABLED(CONFIG_VIDEO_DEV) v4l2_ctrl_handler_free(&dev->hdl); #endif kfree(dev); diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index 0ed2c5bc082e..ff5a6c0acdd4 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1831,7 +1831,7 @@ struct tunertype tuners[] = { }, [TUNER_XC2028] = { /* Xceive 2028 */ .name = "Xceive xc2028/xc3028 tuner", - /* see tuner-xc2028.c for details */ + /* see xc2028.c for details */ }, [TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */ .name = "Thomson FE6600", diff --git a/drivers/media/tuners/tuner-xc2028-types.h b/drivers/media/tuners/xc2028-types.h index fcca39d3e006..63a03de1e97b 100644 --- a/drivers/media/tuners/tuner-xc2028-types.h +++ b/drivers/media/tuners/xc2028-types.h @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * tuner-xc2028_types + * xc2028_types * - * This file includes internal tipes to be used inside tuner-xc2028. - * Shouldn't be included outside tuner-xc2028 + * This file includes internal tipes to be used inside xc2028. + * Shouldn't be included outside xc2028 * * Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> */ diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/xc2028.c index 574c3bb135d7..69c2e1b99bf1 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/xc2028.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// tuner-xc2028 +// xc2028 // // Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> // @@ -16,8 +16,8 @@ #include <linux/slab.h> #include <asm/unaligned.h> #include "tuner-i2c.h" -#include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" +#include "xc2028.h" +#include "xc2028-types.h" #include <linux/dvb/frontend.h> #include <media/dvb_frontend.h> diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/xc2028.h index 2dd45d0765d7..072faae7a954 100644 --- a/drivers/media/tuners/tuner-xc2028.h +++ b/drivers/media/tuners/xc2028.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * tuner-xc2028 + * xc2028 * * Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> */ diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index d9606738ce43..a04dfd5799f7 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -22,7 +22,7 @@ #include "xc4000.h" #include "tuner-i2c.h" -#include "tuner-xc2028-types.h" +#include "xc2028-types.h" static int debug; module_param(debug, int, 0644); diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index f97153df3c84..8de08704f8e4 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -12,53 +12,64 @@ if MEDIA_USB_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Webcam devices" -source "drivers/media/usb/uvc/Kconfig" + +source "drivers/media/usb/cpia2/Kconfig" source "drivers/media/usb/gspca/Kconfig" source "drivers/media/usb/pwc/Kconfig" -source "drivers/media/usb/cpia2/Kconfig" -source "drivers/media/usb/zr364xx/Kconfig" -source "drivers/media/usb/stkwebcam/Kconfig" source "drivers/media/usb/s2255/Kconfig" +source "drivers/media/usb/stkwebcam/Kconfig" source "drivers/media/usb/usbtv/Kconfig" +source "drivers/media/usb/uvc/Kconfig" +source "drivers/media/usb/zr364xx/Kconfig" + endif if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" -source "drivers/media/usb/pvrusb2/Kconfig" + +source "drivers/media/usb/go7007/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" +source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/stk1160/Kconfig" -source "drivers/media/usb/go7007/Kconfig" + endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Analog/digital TV USB devices" + source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" source "drivers/media/usb/tm6000/Kconfig" -endif +endif if I2C && MEDIA_DIGITAL_TV_SUPPORT comment "Digital TV USB devices" -source "drivers/media/usb/dvb-usb/Kconfig" + +source "drivers/media/usb/as102/Kconfig" +source "drivers/media/usb/b2c2/Kconfig" source "drivers/media/usb/dvb-usb-v2/Kconfig" +source "drivers/media/usb/dvb-usb/Kconfig" +source "drivers/media/usb/siano/Kconfig" source "drivers/media/usb/ttusb-budget/Kconfig" source "drivers/media/usb/ttusb-dec/Kconfig" -source "drivers/media/usb/siano/Kconfig" -source "drivers/media/usb/b2c2/Kconfig" -source "drivers/media/usb/as102/Kconfig" + endif if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Webcam, TV (analog/digital) USB devices" + source "drivers/media/usb/em28xx/Kconfig" + endif if MEDIA_SDR_SUPPORT comment "Software defined radio USB devices" + source "drivers/media/usb/airspy/Kconfig" source "drivers/media/usb/hackrf/Kconfig" source "drivers/media/usb/msi2500/Kconfig" + endif endif #MEDIA_USB_SUPPORT diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 3eaff3149ef4..044bd46c799c 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -3,24 +3,34 @@ # Makefile for the USB media device drivers # -# DVB USB-only drivers -obj-y += ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ -obj-y += zr364xx/ stkwebcam/ s2255/ +# DVB USB-only drivers. Please keep it alphabetically sorted by directory name +# (e. g. LC_ALL=C sort Makefile) +obj-y += b2c2/ +obj-y += dvb-usb/ +obj-y += dvb-usb-v2/ +obj-y += s2255/ +obj-y += siano/ +obj-y += stkwebcam/ +obj-y += ttusb-budget/ +obj-y += ttusb-dec/ +obj-y += zr364xx/ -obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ -obj-$(CONFIG_USB_GSPCA) += gspca/ -obj-$(CONFIG_USB_PWC) += pwc/ -obj-$(CONFIG_USB_AIRSPY) += airspy/ -obj-$(CONFIG_USB_HACKRF) += hackrf/ -obj-$(CONFIG_USB_MSI2500) += msi2500/ -obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_DVB_AS102) += as102/ +obj-$(CONFIG_USB_AIRSPY) += airspy/ +obj-$(CONFIG_USB_GSPCA) += gspca/ +obj-$(CONFIG_USB_HACKRF) += hackrf/ +obj-$(CONFIG_USB_MSI2500) += msi2500/ +obj-$(CONFIG_USB_PWC) += pwc/ +obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ -obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ +obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ +obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ +obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_STK1160) += stk1160/ -obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ -obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ -obj-$(CONFIG_VIDEO_GO7007) += go7007/ -obj-$(CONFIG_DVB_AS102) += as102/ diff --git a/drivers/media/usb/airspy/Kconfig b/drivers/media/usb/airspy/Kconfig index 458345217f78..0662d8701c44 100644 --- a/drivers/media/usb/airspy/Kconfig +++ b/drivers/media/usb/airspy/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_AIRSPY tristate "AirSpy" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC help This is a video4linux2 driver for AirSpy SDR device. diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 05cc6c48c26f..31799b6ff91f 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -2,12 +2,12 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on I2C && INPUT && DVB_CORE && USB && VIDEO_DEV select MEDIA_CONTROLLER select MEDIA_CONTROLLER_DVB select I2C_ALGOBIT select VIDEO_TVEEPROM - select VIDEOBUF2_VMALLOC if VIDEO_V4L2 + select VIDEOBUF2_VMALLOC if VIDEO_DEV select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT @@ -22,7 +22,7 @@ config VIDEO_AU0828 config VIDEO_AU0828_V4L2 bool "Auvitek AU0828 v4l2 analog video support" depends on VIDEO_AU0828 - depends on VIDEO_V4L2=y || VIDEO_V4L2=VIDEO_AU0828 + depends on VIDEO_DEV=y || VIDEO_DEV=VIDEO_AU0828 select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TUNER default y diff --git a/drivers/media/usb/cpia2/Kconfig b/drivers/media/usb/cpia2/Kconfig index e2c18ab0262b..da2c6862b4a2 100644 --- a/drivers/media/usb/cpia2/Kconfig +++ b/drivers/media/usb/cpia2/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CPIA2 tristate "CPiA2 Video For Linux" - depends on VIDEO_DEV && USB && VIDEO_V4L2 + depends on USB && VIDEO_DEV help This is the video4linux driver for cameras based on Vision's CPiA2 (Colour Processor Interface ASIC), such as the Digital Blue QX5 diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 60ca8b9d070b..4eadc9539b4c 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -14,6 +14,8 @@ config DVB_USB_V2 Say Y if you own a USB DVB device. +if DVB_USB_V2 + config DVB_USB_AF9015 tristate "Afatech AF9015 DVB-T USB2.0 support" depends on DVB_USB_V2 && I2C_MUX @@ -40,7 +42,7 @@ config DVB_USB_AF9035 select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) + select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_DEV) select MEDIA_TUNER_IT913X if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. @@ -87,6 +89,17 @@ config DVB_USB_CE6230 help Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver +config DVB_USB_DVBSKY + tristate "DVBSky USB support" + depends on DVB_USB_V2 + select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the USB receivers from DVBSky. + config DVB_USB_EC168 tristate "E3C EC168 DVB-T USB2.0 support" depends on DVB_USB_V2 @@ -139,12 +152,12 @@ config DVB_USB_RTL28XXU select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT select DVB_RTL2830 select DVB_RTL2832 - select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT && VIDEO_V4L2) + select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT && VIDEO_DEV) select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_E4000 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) + select MEDIA_TUNER_E4000 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_DEV) select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) + select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_DEV) select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT @@ -154,17 +167,6 @@ config DVB_USB_RTL28XXU help Say Y here to support the Realtek RTL28xxU DVB USB receiver. -config DVB_USB_DVBSKY - tristate "DVBSky USB support" - depends on DVB_USB_V2 - select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT - select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT - select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to support the USB receivers from DVBSky. - config DVB_USB_ZD1301 tristate "ZyDAS ZD1301" depends on DVB_USB_V2 @@ -172,3 +174,5 @@ config DVB_USB_ZD1301 select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the ZyDAS ZD1301 DVB USB receiver. + +endif diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 7498110142e4..f10fe27e2a4d 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -22,15 +22,7 @@ config DVB_USB_DEBUG Say Y if you want to enable debugging. See modinfo dvb-usb (and the appropriate drivers) for debug levels. -config DVB_USB_DIB3000MC - tristate - depends on DVB_USB - select DVB_DIB3000MC - help - This is a module with helper functions for accessing the - DIB3000MC from USB DVB devices. It must be a separate module - in case DVB_USB is built-in and DVB_DIB3000MC is a module, - and gets selected automatically when needed. +if DVB_USB config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" @@ -41,84 +33,37 @@ config DVB_USB_A800 help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. -config DVB_USB_DIBUSB_MB - tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" +config DVB_USB_AF9005 + tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - select DVB_DIB3000MB - depends on DVB_DIB3000MC || !DVB_DIB3000MC select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help - Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by - DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator. - - For an up-to-date list of devices supported by this driver, have a look - on the Linux-DVB Wiki at www.linuxtv.org. - - Say Y if you own such a device and want to use it. You should build it as - a module. + Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver + and the TerraTec Cinergy T USB XE (Rev.1) -config DVB_USB_DIBUSB_MB_FAULTY - bool "Support faulty USB IDs" - depends on DVB_USB_DIBUSB_MB +config DVB_USB_AF9005_REMOTE + tristate "Afatech AF9005 default remote control support" + depends on DVB_USB_AF9005 help - Support for faulty USB IDs due to an invalid EEPROM on some Artec devices. + Say Y here to support the default remote control decoding for the + Afatech AF9005 based receiver. -config DVB_USB_DIBUSB_MC - tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" +config DVB_USB_AZ6027 + tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB - select DVB_USB_DIB3000MC - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help - Support for USB2.0 DVB-T receivers based on reference designs made by - DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator. - - For an up-to-date list of devices supported by this driver, have a look - on the Linux-DVB Wiki at www.linuxtv.org. - - Say Y if you own such a device and want to use it. You should build it as - a module. + Say Y here to support the AZ6027 device -config DVB_USB_DIB0700 - tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" +config DVB_USB_CINERGY_T2 + tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" depends on DVB_USB - select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT - select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT - select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT - select DVB_USB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT - select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT - select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT - select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA18250 if MEDIA_SUBDRV_AUTOSELECT help - Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The - USB bridge is also present in devices having the DiB7700 DVB-T-USB - silicon. This chip can be found in devices offered by Hauppauge, - Avermedia and other big and small companies. - - For an up-to-date list of devices supported by this driver, have a look - on the LinuxTV Wiki at www.linuxtv.org. - - Say Y if you own such a device and want to use it. You should build it as - a module. + Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers -config DVB_USB_UMT_010 - tristate "HanfTek UMT-010 DVB-T USB2.0 support" - depends on DVB_USB - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - select DVB_USB_DIB3000MC - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT - select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. + Say Y if you own such a device and want to use it. config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" @@ -150,8 +95,8 @@ config DVB_USB_CXUSB config DVB_USB_CXUSB_ANALOG bool "Analog support for the Conexant USB2.0 hybrid reference design" - depends on DVB_USB_CXUSB && VIDEO_V4L2 - depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_USB_CXUSB + depends on DVB_USB_CXUSB && VIDEO_DEV + depends on VIDEO_DEV=y || VIDEO_DEV=DVB_USB_CXUSB select VIDEO_CX25840 select VIDEOBUF2_VMALLOC help @@ -159,87 +104,93 @@ config DVB_USB_CXUSB_ANALOG USB2.0 hybrid reference design. Currently this mode is supported only on a Medion MD95700 device. -config DVB_USB_M920X - tristate "Uli m920x DVB-T USB2.0 support" +config DVB_USB_DIB0700 + tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB - select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_USB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18250 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. - Currently, only devices with a product id of - "DTV USB MINI" (in cold state) are supported. - Firmware required. + Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The + USB bridge is also present in devices having the DiB7700 DVB-T-USB + silicon. This chip can be found in devices offered by Hauppauge, + Avermedia and other big and small companies. -config DVB_USB_DIGITV - tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" - depends on DVB_USB - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT - select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. + For an up-to-date list of devices supported by this driver, have a look + on the LinuxTV Wiki at www.linuxtv.org. -config DVB_USB_VP7045 - tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support" + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIB3000MC + tristate depends on DVB_USB + select DVB_DIB3000MC help - Say Y here to support the - - TwinhanDTV Alpha (stick) (VP-7045), - TwinhanDTV MagicBox II (VP-7046), - DigitalNow TinyUSB 2 DVB-t, - DigitalRise USB 2.0 Ter (Beetle) and - TYPHOON DVB-T USB DRIVE - - DVB-T USB2.0 receivers. + This is a module with helper functions for accessing the + DIB3000MC from USB DVB devices. It must be a separate module + in case DVB_USB is built-in and DVB_DIB3000MC is a module, + and gets selected automatically when needed. -config DVB_USB_VP702X - tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support" +config DVB_USB_DIBUSB_MB + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_DIB3000MB + depends on DVB_DIB3000MC || !DVB_DIB3000MC + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the + Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by + DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator. - TwinhanDTV StarBox, - DigitalRise USB Starbox and - TYPHOON DVB-S USB 2.0 BOX + For an up-to-date list of devices supported by this driver, have a look + on the Linux-DVB Wiki at www.linuxtv.org. - DVB-S USB2.0 receivers. + Say Y if you own such a device and want to use it. You should build it as + a module. -config DVB_USB_GP8PSK - tristate "GENPIX 8PSK->USB module support" - depends on DVB_USB +config DVB_USB_DIBUSB_MB_FAULTY + bool "Support faulty USB IDs" + depends on DVB_USB_DIBUSB_MB help - Say Y here to support the - GENPIX 8psk module - - DVB-S USB2.0 receivers. + Support for faulty USB IDs due to an invalid EEPROM on some Artec devices. -config DVB_USB_NOVA_T_USB2 - tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" +config DVB_USB_DIBUSB_MC + tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_USB_DIB3000MC - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. + Support for USB2.0 DVB-T receivers based on reference designs made by + DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator. -config DVB_USB_TTUSB2 - tristate "Pinnacle 400e DVB-S USB2.0 support" + For an up-to-date list of devices supported by this driver, have a look + on the Linux-DVB Wiki at www.linuxtv.org. + + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIGITV + tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB - select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver and - the TechnoTrend CT-3650 CI DVB-C/T USB2.0 receiver. The - firmware protocol used by this module is similar to the one used by the - old ttusb-driver - that's why the module is called dvb-usb-ttusb2. + Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. config DVB_USB_DTT200U tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)" @@ -251,43 +202,13 @@ config DVB_USB_DTT200U The WT-220U and its clones are pen-sized. -config DVB_USB_OPERA1 - tristate "Opera1 DVB-S USB2.0 receiver" - depends on DVB_USB - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to support the Opera DVB-S USB2.0 receiver. - -config DVB_USB_AF9005 - tristate "Afatech AF9005 DVB-T USB1.1 support" +config DVB_USB_DTV5100 + tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB - select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver - and the TerraTec Cinergy T USB XE (Rev.1) - -config DVB_USB_AF9005_REMOTE - tristate "Afatech AF9005 default remote control support" - depends on DVB_USB_AF9005 - help - Say Y here to support the default remote control decoding for the - Afatech AF9005 based receiver. - -config DVB_USB_PCTV452E - tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" - depends on DVB_USB - select TTPCI_EEPROM - select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT - select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT - help - Support for external USB adapter designed by Pinnacle, - shipped under the brand name 'PCTV HDTV Pro USB'. - Also supports TT Connect S2-3600/3650 cards. - Say Y if you own such a device and want to use it. + Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. config DVB_USB_DW2102 tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" @@ -312,29 +233,59 @@ config DVB_USB_DW2102 Say Y here to support the DvbWorld, TeVii, Prof, TechnoTrend DVB-S/S2 USB2.0 receivers. -config DVB_USB_CINERGY_T2 - tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" +config DVB_USB_GP8PSK + tristate "GENPIX 8PSK->USB module support" depends on DVB_USB help - Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers + Say Y here to support the + GENPIX 8psk module - Say Y if you own such a device and want to use it. + DVB-S USB2.0 receivers. -config DVB_USB_DTV5100 - tristate "AME DTV-5100 USB2.0 DVB-T support" +config DVB_USB_M920X + tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB - select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. + Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. + Currently, only devices with a product id of + "DTV USB MINI" (in cold state) are supported. + Firmware required. -config DVB_USB_AZ6027 - tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" +config DVB_USB_NOVA_T_USB2 + tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB + select DVB_USB_DIB3000MC + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. + +config DVB_USB_OPERA1 + tristate "Opera1 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the Opera DVB-S USB2.0 receiver. + +config DVB_USB_PCTV452E + tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" + depends on DVB_USB + select TTPCI_EEPROM + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the AZ6027 device + Support for external USB adapter designed by Pinnacle, + shipped under the brand name 'PCTV HDTV Pro USB'. + Also supports TT Connect S2-3600/3650 cards. + Say Y if you own such a device and want to use it. config DVB_USB_TECHNISAT_USB2 tristate "Technisat DVB-S/S2 USB2.0 support" @@ -343,3 +294,56 @@ config DVB_USB_TECHNISAT_USB2 select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Technisat USB2 DVB-S/S2 device + +config DVB_USB_TTUSB2 + tristate "Pinnacle 400e DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver and + the TechnoTrend CT-3650 CI DVB-C/T USB2.0 receiver. The + firmware protocol used by this module is similar to the one used by the + old ttusb-driver - that's why the module is called dvb-usb-ttusb2. + +config DVB_USB_UMT_010 + tristate "HanfTek UMT-010 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_USB_DIB3000MC + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + help + Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. + +config DVB_USB_VP702X + tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support" + depends on DVB_USB + help + Say Y here to support the + + TwinhanDTV StarBox, + DigitalRise USB Starbox and + TYPHOON DVB-S USB 2.0 BOX + + DVB-S USB2.0 receivers. + +config DVB_USB_VP7045 + tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support" + depends on DVB_USB + help + Say Y here to support the + + TwinhanDTV Alpha (stick) (VP-7045), + TwinhanDTV MagicBox II (VP-7046), + DigitalNow TinyUSB 2 DVB-t, + DigitalRise USB 2.0 Ter (Beetle) and + TYPHOON DVB-T USB DRIVE + + DVB-T USB2.0 receivers. + +endif diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 7707de7bae7c..265b960db499 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -35,7 +35,7 @@ #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "tuner-simple.h" #include "mxl5005s.h" #include "max2165.h" diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 710c1afe3e85..08fcf120daf1 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -12,7 +12,7 @@ #include "dib9000.h" #include "mt2060.h" #include "mt2266.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "xc5000.h" #include "xc4000.h" #include "s5h1411.h" diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index b451ce3cb169..ae25d2cbfdfe 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3936,6 +3936,8 @@ static int em28xx_usb_probe(struct usb_interface *intf, goto err_free; } + kref_init(&dev->ref); + dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -4036,6 +4038,8 @@ static int em28xx_usb_probe(struct usb_interface *intf, } if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) { + kref_init(&dev->dev_next->ref); + dev->dev_next->ts = SECONDARY_TS; dev->dev_next->alt = -1; dev->dev_next->is_audio_only = has_vendor_audio && @@ -4090,12 +4094,8 @@ static int em28xx_usb_probe(struct usb_interface *intf, em28xx_write_reg(dev, 0x0b, 0x82); mdelay(100); } - - kref_init(&dev->dev_next->ref); } - kref_init(&dev->ref); - request_modules(dev); /* @@ -4150,11 +4150,8 @@ static void em28xx_usb_disconnect(struct usb_interface *intf) em28xx_close_extension(dev); - if (dev->dev_next) { - em28xx_close_extension(dev->dev_next); + if (dev->dev_next) em28xx_release_resources(dev->dev_next); - } - em28xx_release_resources(dev); if (dev->dev_next) { diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 255395959255..b9a8d3fbad1a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -26,7 +26,7 @@ #include <linux/i2c.h> #include <linux/jiffies.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include <media/v4l2-common.h> #include <media/tuner.h> diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index ab167cd1f400..7fc0b68a4a22 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -41,7 +41,7 @@ #include <media/v4l2-fh.h> #include <media/i2c/ir-kbd-i2c.h> #include <media/rc-core.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include "xc5000.h" #include "em28xx-reg.h" diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index c742cc88fac5..1fa6f10ee157 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -504,6 +504,7 @@ static int s2250_probe(struct i2c_client *client, u8 *data; struct go7007 *go = i2c_get_adapdata(adapter); struct go7007_usb *usb = go->hpi_context; + int err = -EIO; audio = i2c_new_dummy_device(adapter, TLV320_ADDRESS >> 1); if (IS_ERR(audio)) @@ -532,11 +533,8 @@ static int s2250_probe(struct i2c_client *client, V4L2_CID_HUE, -512, 511, 1, 0); sd->ctrl_handler = &state->hdl; if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; + err = state->hdl.error; + goto fail; } state->std = V4L2_STD_NTSC; @@ -600,7 +598,7 @@ fail: i2c_unregister_device(audio); v4l2_ctrl_handler_free(&state->hdl); kfree(state); - return -EIO; + return err; } static int s2250_remove(struct i2c_client *client) diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index 2ce85ab38db5..9a6bd87fce03 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -191,7 +191,7 @@ static int go7007_snd_free(struct snd_device *device) return 0; } -static struct snd_device_ops go7007_snd_device_ops = { +static const struct snd_device_ops go7007_snd_device_ops = { .dev_free = go7007_snd_free, }; diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index dca4e16ed133..9c1939ce6be4 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig USB_GSPCA tristate "GSPCA based webcams" - depends on VIDEO_V4L2 + depends on VIDEO_DEV depends on INPUT || INPUT=n select VIDEOBUF2_VMALLOC help @@ -16,16 +16,11 @@ menuconfig USB_GSPCA To compile this driver as modules, choose M here: the module will be called gspca_main. - -if USB_GSPCA && VIDEO_V4L2 - -source "drivers/media/usb/gspca/m5602/Kconfig" -source "drivers/media/usb/gspca/stv06xx/Kconfig" -source "drivers/media/usb/gspca/gl860/Kconfig" +if USB_GSPCA && VIDEO_DEV config USB_GSPCA_BENQ tristate "Benq USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for the Benq DC E300 camera. @@ -34,7 +29,7 @@ config USB_GSPCA_BENQ config USB_GSPCA_CONEX tristate "Conexant Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Conexant chip. @@ -43,7 +38,7 @@ config USB_GSPCA_CONEX config USB_GSPCA_CPIA1 tristate "cpia CPiA (version 1) Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for USB cameras based on the cpia CPiA chip. Note that you need at least version 0.6.4 of libv4l for @@ -54,7 +49,7 @@ config USB_GSPCA_CPIA1 config USB_GSPCA_DTCS033 tristate "DTCS033 (Scopium) USB Astro-Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for the Scopium camera for planetary astrophotography. @@ -64,7 +59,7 @@ config USB_GSPCA_DTCS033 config USB_GSPCA_ETOMS tristate "Etoms USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Etoms chip. @@ -73,7 +68,7 @@ config USB_GSPCA_ETOMS config USB_GSPCA_FINEPIX tristate "Fujifilm FinePix USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the FinePix chip. @@ -82,7 +77,7 @@ config USB_GSPCA_FINEPIX config USB_GSPCA_JEILINJ tristate "Jeilin JPEG USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on this Jeilin chip. @@ -91,7 +86,7 @@ config USB_GSPCA_JEILINJ config USB_GSPCA_JL2005BCD tristate "JL2005B/C/D USB V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based the JL2005B, JL2005C, or JL2005D chip. @@ -101,7 +96,7 @@ config USB_GSPCA_JL2005BCD config USB_GSPCA_KINECT tristate "Kinect sensor device USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for the Microsoft Kinect sensor device. @@ -110,7 +105,7 @@ config USB_GSPCA_KINECT config USB_GSPCA_KONICA tristate "Konica USB Camera V4L2 driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Konica chip. @@ -119,7 +114,7 @@ config USB_GSPCA_KONICA config USB_GSPCA_MARS tristate "Mars USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Mars chip. @@ -128,7 +123,7 @@ config USB_GSPCA_MARS config USB_GSPCA_MR97310A tristate "Mars-Semi MR97310A USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the MR97310A chip. @@ -137,7 +132,7 @@ config USB_GSPCA_MR97310A config USB_GSPCA_NW80X tristate "Divio based (NW80x) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the NW80x chips. @@ -146,7 +141,7 @@ config USB_GSPCA_NW80X config USB_GSPCA_OV519 tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on one of these: OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF @@ -156,7 +151,7 @@ config USB_GSPCA_OV519 config USB_GSPCA_OV534 tristate "OV534 OV772x USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the OV534 chip and sensor OV772x (e.g. Sony Playstation EYE) @@ -166,7 +161,7 @@ config USB_GSPCA_OV534 config USB_GSPCA_OV534_9 tristate "OV534 OV965x USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the OV534 chip and sensor OV965x (e.g. Hercules Dualpix) @@ -176,7 +171,7 @@ config USB_GSPCA_OV534_9 config USB_GSPCA_PAC207 tristate "Pixart PAC207 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the PAC207 chip. @@ -185,7 +180,7 @@ config USB_GSPCA_PAC207 config USB_GSPCA_PAC7302 tristate "Pixart PAC7302 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the PAC7302 chip. @@ -194,7 +189,7 @@ config USB_GSPCA_PAC7302 config USB_GSPCA_PAC7311 tristate "Pixart PAC7311 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the PAC7311 chip. @@ -203,7 +198,7 @@ config USB_GSPCA_PAC7311 config USB_GSPCA_SE401 tristate "SE401 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Endpoints (formerly known as AOX) se401 chip. @@ -213,7 +208,7 @@ config USB_GSPCA_SE401 config USB_GSPCA_SN9C2028 tristate "SONIX Dual-Mode USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want streaming support for Sonix SN9C2028 cameras. These are supported as stillcams in libgphoto2/camlibs/sonix. @@ -223,7 +218,7 @@ config USB_GSPCA_SN9C2028 config USB_GSPCA_SN9C20X tristate "SN9C20X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the sn9c20x chips (SN9C201 and SN9C202). @@ -233,7 +228,7 @@ config USB_GSPCA_SN9C20X config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Sonix chips with Bayer format (SN9C101, SN9C102 and SN9C103). @@ -243,7 +238,7 @@ config USB_GSPCA_SONIXB config USB_GSPCA_SONIXJ tristate "SONIX JPEG USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Sonix chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110). @@ -251,9 +246,18 @@ config USB_GSPCA_SONIXJ To compile this driver as a module, choose M here: the module will be called gspca_sonixj +config USB_GSPCA_SPCA1528 + tristate "SPCA1528 USB Camera Driver" + depends on VIDEO_DEV && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA1528 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca1528. + config USB_GSPCA_SPCA500 tristate "SPCA500 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA500 chip. @@ -262,7 +266,7 @@ config USB_GSPCA_SPCA500 config USB_GSPCA_SPCA501 tristate "SPCA501 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA501 chip. @@ -271,7 +275,7 @@ config USB_GSPCA_SPCA501 config USB_GSPCA_SPCA505 tristate "SPCA505 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA505 chip. @@ -280,7 +284,7 @@ config USB_GSPCA_SPCA505 config USB_GSPCA_SPCA506 tristate "SPCA506 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA506 chip. @@ -289,7 +293,7 @@ config USB_GSPCA_SPCA506 config USB_GSPCA_SPCA508 tristate "SPCA508 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA508 chip. @@ -298,25 +302,16 @@ config USB_GSPCA_SPCA508 config USB_GSPCA_SPCA561 tristate "SPCA561 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SPCA561 chip. To compile this driver as a module, choose M here: the module will be called gspca_spca561. -config USB_GSPCA_SPCA1528 - tristate "SPCA1528 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help - Say Y here if you want support for cameras based on the SPCA1528 chip. - - To compile this driver as a module, choose M here: the - module will be called gspca_spca1528. - config USB_GSPCA_SQ905 tristate "SQ Technologies SQ905 based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SQ905 chip. @@ -325,7 +320,7 @@ config USB_GSPCA_SQ905 config USB_GSPCA_SQ905C tristate "SQ Technologies SQ905C based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SQ905C chip. @@ -334,7 +329,7 @@ config USB_GSPCA_SQ905C config USB_GSPCA_SQ930X tristate "SQ Technologies SQ930X based USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the SQ930X chip. @@ -343,7 +338,7 @@ config USB_GSPCA_SQ930X config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the STK014 chip. @@ -352,7 +347,7 @@ config USB_GSPCA_STK014 config USB_GSPCA_STK1135 tristate "Syntek STK1135 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the STK1135 chip. @@ -361,7 +356,7 @@ config USB_GSPCA_STK1135 config USB_GSPCA_STV0680 tristate "STV0680 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the STV0680 chip. @@ -370,7 +365,7 @@ config USB_GSPCA_STV0680 config USB_GSPCA_SUNPLUS tristate "SUNPLUS USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the Sunplus SPCA504(abc) SPCA533 SPCA536 chips. @@ -380,7 +375,7 @@ config USB_GSPCA_SUNPLUS config USB_GSPCA_T613 tristate "T613 (JPEG Compliance) USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the T613 chip. @@ -389,7 +384,7 @@ config USB_GSPCA_T613 config USB_GSPCA_TOPRO tristate "TOPRO USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the TP6800 and TP6810 Topro chips. @@ -399,7 +394,7 @@ config USB_GSPCA_TOPRO config USB_GSPCA_TOUPTEK tristate "Touptek USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the ToupTek UCMOS / AmScope MU series camera. @@ -409,7 +404,7 @@ config USB_GSPCA_TOUPTEK config USB_GSPCA_TV8532 tristate "TV8532 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the TV8531 chip. @@ -418,7 +413,7 @@ config USB_GSPCA_TV8532 config USB_GSPCA_VC032X tristate "VC032X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the VC032X chip. @@ -427,7 +422,7 @@ config USB_GSPCA_VC032X config USB_GSPCA_VICAM tristate "ViCam USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for the 3com homeconnect camera (vicam). @@ -437,7 +432,7 @@ config USB_GSPCA_VICAM config USB_GSPCA_XIRLINK_CIT tristate "Xirlink C-It USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for Xirlink C-It bases cameras. @@ -446,11 +441,15 @@ config USB_GSPCA_XIRLINK_CIT config USB_GSPCA_ZC3XX tristate "ZC3XX USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the ZC3XX chip. To compile this driver as a module, choose M here: the module will be called gspca_zc3xx. +source "drivers/media/usb/gspca/gl860/Kconfig" +source "drivers/media/usb/gspca/m5602/Kconfig" +source "drivers/media/usb/gspca/stv06xx/Kconfig" + endif diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile index 3e3ecbffdf9f..a35c45006130 100644 --- a/drivers/media/usb/gspca/Makefile +++ b/drivers/media/usb/gspca/Makefile @@ -1,51 +1,51 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_USB_GSPCA) += gspca_main.o -obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o -obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o -obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o -obj-$(CONFIG_USB_GSPCA_DTCS033) += gspca_dtcs033.o -obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o -obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o -obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o +obj-$(CONFIG_USB_GSPCA) += gspca_main.o +obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o +obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o +obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o +obj-$(CONFIG_USB_GSPCA_DTCS033) += gspca_dtcs033.o +obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o +obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o +obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o obj-$(CONFIG_USB_GSPCA_JL2005BCD) += gspca_jl2005bcd.o -obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o -obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o -obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o +obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o +obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o +obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o -obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o -obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o -obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o -obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o -obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o -obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o -obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o -obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o +obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o +obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o +obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o +obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o +obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o -obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o -obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o -obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o -obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o -obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o -obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o -obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o -obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o -obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o +obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o +obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o +obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o +obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o +obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o +obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o +obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o +obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o -obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o -obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o -obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o -obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o -obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o -obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o -obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o -obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o -obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o -obj-$(CONFIG_USB_GSPCA_TOUPTEK) += gspca_touptek.o -obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o -obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o -obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o +obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o +obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o +obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o +obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o +obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o +obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o +obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o +obj-$(CONFIG_USB_GSPCA_TOUPTEK) += gspca_touptek.o +obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o +obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o -obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o +obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o gspca_main-objs := gspca.o autogain_functions.o gspca_benq-objs := benq.o @@ -95,6 +95,6 @@ gspca_vicam-objs := vicam.o gspca_xirlink_cit-objs := xirlink_cit.o gspca_zc3xx-objs := zc3xx.o -obj-$(CONFIG_USB_M5602) += m5602/ +obj-$(CONFIG_USB_M5602) += m5602/ obj-$(CONFIG_USB_STV06XX) += stv06xx/ -obj-$(CONFIG_USB_GL860) += gl860/ +obj-$(CONFIG_USB_GL860) += gl860/ diff --git a/drivers/media/usb/gspca/gl860/Kconfig b/drivers/media/usb/gspca/gl860/Kconfig index 2dfd2704c915..e5a35ca72b60 100644 --- a/drivers/media/usb/gspca/gl860/Kconfig +++ b/drivers/media/usb/gspca/gl860/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_GL860 tristate "GL860 USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the GL860 chip. diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index ca12f33f3e12..a408fcc3a060 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -166,7 +166,9 @@ static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *)gspca_dev; int i = 0; int retval; - unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; + static const unsigned char regs_to_read[] = { + 0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f + }; gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); /* Read the first ID byte once for warmup */ diff --git a/drivers/media/usb/gspca/m5602/Kconfig b/drivers/media/usb/gspca/m5602/Kconfig index 0a250652d717..d616408b67d9 100644 --- a/drivers/media/usb/gspca/m5602/Kconfig +++ b/drivers/media/usb/gspca/m5602/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_M5602 tristate "ALi USB m5602 Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA + depends on VIDEO_DEV && USB_GSPCA help Say Y here if you want support for cameras based on the ALi m5602 connected to various image sensors. diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 2e8c3ef51ca3..608be0d64f94 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -794,7 +794,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { gspca_dev->image_len += n; - n = 0; } else { gspca_frame_add(gspca_dev, INTER_PACKET, data, n); } diff --git a/drivers/media/usb/hackrf/Kconfig b/drivers/media/usb/hackrf/Kconfig index 2267cebfdecb..1cf9b4d3a514 100644 --- a/drivers/media/usb/hackrf/Kconfig +++ b/drivers/media/usb/hackrf/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_HACKRF tristate "HackRF" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC help This is a video4linux2 driver for HackRF SDR device. diff --git a/drivers/media/usb/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig index 617400b27314..ee45a89aa607 100644 --- a/drivers/media/usb/hdpvr/Kconfig +++ b/drivers/media/usb/hdpvr/Kconfig @@ -2,7 +2,7 @@ config VIDEO_HDPVR tristate "Hauppauge HD PVR support" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV help This is a video4linux driver for Hauppauge's HD PVR USB device. diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 563128d11731..60e57e0f1927 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -308,7 +308,6 @@ static int hdpvr_start_streaming(struct hdpvr_device *dev) dev->status = STATUS_STREAMING; - INIT_WORK(&dev->worker, hdpvr_transmit_buffers); schedule_work(&dev->worker); v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, @@ -1165,6 +1164,9 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, bool ac3 = dev->flags & HDPVR_FLAG_AC3_CAP; int res; + // initialize dev->worker + INIT_WORK(&dev->worker, hdpvr_transmit_buffers); + dev->cur_std = V4L2_STD_525_60; dev->width = 720; dev->height = 480; diff --git a/drivers/media/usb/msi2500/Kconfig b/drivers/media/usb/msi2500/Kconfig index b403603bcc81..c2ded6482a5b 100644 --- a/drivers/media/usb/msi2500/Kconfig +++ b/drivers/media/usb/msi2500/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_MSI2500 tristate "Mirics MSi2500" - depends on VIDEO_V4L2 && SPI + depends on VIDEO_DEV && SPI select VIDEOBUF2_VMALLOC select MEDIA_TUNER_MSI001 diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig index e6a4f730591b..f2b64e49c5a2 100644 --- a/drivers/media/usb/pvrusb2/Kconfig +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/usb/pwc/Kconfig b/drivers/media/usb/pwc/Kconfig index 7cebf6314a67..2078bd5ecf41 100644 --- a/drivers/media/usb/pwc/Kconfig +++ b/drivers/media/usb/pwc/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_PWC tristate "USB Philips Cameras" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC help Say Y or M here if you want to use one of these Philips & OEM diff --git a/drivers/media/usb/pwc/pwc-uncompress.c b/drivers/media/usb/pwc/pwc-uncompress.c index 68bc3829c6b3..faf44cdeb268 100644 --- a/drivers/media/usb/pwc/pwc-uncompress.c +++ b/drivers/media/usb/pwc/pwc-uncompress.c @@ -41,7 +41,7 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) memcpy(raw_frame->cmd, pdev->cmd_buf, 4); memcpy(raw_frame+1, yuv, pdev->frame_size); vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, - pdev->frame_size + sizeof(struct pwc_raw_frame)); + struct_size(raw_frame, rawframe, pdev->frame_size)); return 0; } diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig index e4a0c914d9c3..889593b21889 100644 --- a/drivers/media/usb/s2255/Kconfig +++ b/drivers/media/usb/s2255/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_S2255 tristate "USB Sensoray 2255 video capture device" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC help Say Y here if you want support for the Sensoray 2255 USB device. diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index 4e1698f78818..ce717502ea4c 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -403,7 +403,7 @@ static void stk1160_disconnect(struct usb_interface *interface) /* Here is the only place where isoc get released */ stk1160_uninit_isoc(dev); - stk1160_clear_queue(dev); + stk1160_clear_queue(dev, VB2_BUF_STATE_ERROR); video_unregister_device(&dev->vdev); v4l2_device_disconnect(&dev->v4l2_dev); diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 6a4eb616d516..a1f785a5ffd8 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -232,7 +232,11 @@ static int stk1160_start_streaming(struct stk1160 *dev) /* submit urbs and enables IRQ */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL); + struct stk1160_urb *stk_urb = &dev->isoc_ctl.urb_ctl[i]; + + dma_sync_sgtable_for_device(stk1160_get_dmadev(dev), stk_urb->sgt, + DMA_FROM_DEVICE); + rc = usb_submit_urb(dev->isoc_ctl.urb_ctl[i].urb, GFP_KERNEL); if (rc) { stk1160_err("cannot submit urb[%d] (%d)\n", i, rc); goto out_uninit; @@ -258,7 +262,7 @@ out_uninit: stk1160_uninit_isoc(dev); out_stop_hw: usb_set_interface(dev->udev, 0, 0); - stk1160_clear_queue(dev); + stk1160_clear_queue(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l_lock); @@ -306,7 +310,7 @@ static int stk1160_stop_streaming(struct stk1160 *dev) stk1160_stop_hw(dev); - stk1160_clear_queue(dev); + stk1160_clear_queue(dev, VB2_BUF_STATE_ERROR); stk1160_dbg("streaming stopped\n"); @@ -745,7 +749,7 @@ static const struct video_device v4l_template = { /********************************************************************/ /* Must be called with both v4l_lock and vb_queue_lock hold */ -void stk1160_clear_queue(struct stk1160 *dev) +void stk1160_clear_queue(struct stk1160 *dev, enum vb2_buffer_state vb2_state) { struct stk1160_buffer *buf; unsigned long flags; @@ -756,7 +760,7 @@ void stk1160_clear_queue(struct stk1160 *dev) buf = list_first_entry(&dev->avail_bufs, struct stk1160_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); stk1160_dbg("buffer [%p/%d] aborted\n", buf, buf->vb.vb2_buf.index); } @@ -766,7 +770,7 @@ void stk1160_clear_queue(struct stk1160 *dev) buf = dev->isoc_ctl.buf; dev->isoc_ctl.buf = NULL; - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, vb2_state); stk1160_dbg("buffer [%p/%d] aborted\n", buf, buf->vb.vb2_buf.index); } diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 202b084f65a2..4e966f6bf608 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -295,7 +295,9 @@ static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb) static void stk1160_isoc_irq(struct urb *urb) { int i, rc; - struct stk1160 *dev = urb->context; + struct stk1160_urb *stk_urb = urb->context; + struct stk1160 *dev = stk_urb->dev; + struct device *dma_dev = stk1160_get_dmadev(dev); switch (urb->status) { case 0: @@ -310,6 +312,10 @@ static void stk1160_isoc_irq(struct urb *urb) return; } + invalidate_kernel_vmap_range(stk_urb->transfer_buffer, + urb->transfer_buffer_length); + dma_sync_sgtable_for_cpu(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE); + stk1160_process_isoc(dev, urb); /* Reset urb buffers */ @@ -318,6 +324,7 @@ static void stk1160_isoc_irq(struct urb *urb) urb->iso_frame_desc[i].actual_length = 0; } + dma_sync_sgtable_for_device(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc) stk1160_err("urb re-submit failed (%d)\n", rc); @@ -347,49 +354,41 @@ void stk1160_cancel_isoc(struct stk1160 *dev) * We don't care for NULL pointer since * usb_kill_urb allows it. */ - usb_kill_urb(dev->isoc_ctl.urb[i]); + usb_kill_urb(dev->isoc_ctl.urb_ctl[i].urb); } stk1160_dbg("all urbs killed\n"); } +static void stk_free_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb) +{ + struct device *dma_dev = stk1160_get_dmadev(dev); + + dma_vunmap_noncontiguous(dma_dev, stk_urb->transfer_buffer); + dma_free_noncontiguous(dma_dev, stk_urb->urb->transfer_buffer_length, + stk_urb->sgt, DMA_FROM_DEVICE); + usb_free_urb(stk_urb->urb); + + stk_urb->transfer_buffer = NULL; + stk_urb->sgt = NULL; + stk_urb->urb = NULL; + stk_urb->dev = NULL; + stk_urb->dma = 0; +} + /* * Releases urb and transfer buffers * Obviusly, associated urb must be killed before releasing it. */ void stk1160_free_isoc(struct stk1160 *dev) { - struct urb *urb; int i, num_bufs = dev->isoc_ctl.num_bufs; stk1160_dbg("freeing %d urb buffers...\n", num_bufs); - for (i = 0; i < num_bufs; i++) { - - urb = dev->isoc_ctl.urb[i]; - if (urb) { - - if (dev->isoc_ctl.transfer_buffer[i]) { -#ifndef CONFIG_DMA_NONCOHERENT - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); -#else - kfree(dev->isoc_ctl.transfer_buffer[i]); -#endif - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } + for (i = 0; i < num_bufs; i++) + stk_free_urb(dev, &dev->isoc_ctl.urb_ctl[i]); - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; dev->isoc_ctl.num_bufs = 0; stk1160_dbg("all urb buffers freed\n"); @@ -405,6 +404,41 @@ void stk1160_uninit_isoc(struct stk1160 *dev) stk1160_free_isoc(dev); } +static int stk1160_fill_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb, + int sb_size, int max_packets) +{ + struct device *dma_dev = stk1160_get_dmadev(dev); + + stk_urb->urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!stk_urb->urb) + return -ENOMEM; + stk_urb->sgt = dma_alloc_noncontiguous(dma_dev, sb_size, + DMA_FROM_DEVICE, GFP_KERNEL, 0); + + /* + * If the buffer allocation failed, we exit but return 0 since + * we allow the driver working with less buffers + */ + if (!stk_urb->sgt) + goto free_urb; + + stk_urb->transfer_buffer = dma_vmap_noncontiguous(dma_dev, sb_size, + stk_urb->sgt); + if (!stk_urb->transfer_buffer) + goto free_sgt; + + stk_urb->dma = stk_urb->sgt->sgl->dma_address; + stk_urb->dev = dev; + return 0; +free_sgt: + dma_free_noncontiguous(dma_dev, sb_size, stk_urb->sgt, DMA_FROM_DEVICE); + stk_urb->sgt = NULL; +free_urb: + usb_free_urb(stk_urb->urb); + stk_urb->urb = NULL; + + return 0; +} /* * Allocate URBs */ @@ -412,6 +446,7 @@ int stk1160_alloc_isoc(struct stk1160 *dev) { struct urb *urb; int i, j, k, sb_size, max_packets, num_bufs; + int ret; /* * It may be necessary to release isoc here, @@ -429,62 +464,39 @@ int stk1160_alloc_isoc(struct stk1160 *dev) dev->isoc_ctl.buf = NULL; dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; - dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - stk1160_err("out of memory for urb array\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *), - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { - stk1160_err("out of memory for usb transfers\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } /* allocate urbs and transfer buffers */ for (i = 0; i < num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) + ret = stk1160_fill_urb(dev, &dev->isoc_ctl.urb_ctl[i], + sb_size, max_packets); + if (ret) goto free_i_bufs; - dev->isoc_ctl.urb[i] = urb; - -#ifndef CONFIG_DMA_NONCOHERENT - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); -#else - dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL); -#endif - if (!dev->isoc_ctl.transfer_buffer[i]) { - stk1160_err("cannot alloc %d bytes for tx[%d] buffer\n", - sb_size, i); + urb = dev->isoc_ctl.urb_ctl[i].urb; + + if (!urb) { /* Not enough transfer buffers, so just give up */ if (i < STK1160_MIN_BUFS) goto free_i_bufs; goto nomore_tx_bufs; } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + memset(dev->isoc_ctl.urb_ctl[i].transfer_buffer, 0, sb_size); /* * FIXME: Where can I get the endpoint? */ urb->dev = dev->udev; urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO); - urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i]; + urb->transfer_buffer = dev->isoc_ctl.urb_ctl[i].transfer_buffer; urb->transfer_buffer_length = sb_size; urb->complete = stk1160_isoc_irq; - urb->context = dev; + urb->context = &dev->isoc_ctl.urb_ctl[i]; urb->interval = 1; urb->start_frame = 0; urb->number_of_packets = max_packets; -#ifndef CONFIG_DMA_NONCOHERENT urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; -#else - urb->transfer_flags = URB_ISO_ASAP; -#endif + urb->transfer_dma = dev->isoc_ctl.urb_ctl[i].dma; k = 0; for (j = 0; j < max_packets; j++) { @@ -508,18 +520,16 @@ nomore_tx_bufs: * enough to work fine, so we just free the extra urb, * store the allocated count and keep going, fingers crossed! */ - usb_free_urb(dev->isoc_ctl.urb[i]); - dev->isoc_ctl.urb[i] = NULL; - stk1160_warn("%d urbs allocated. Trying to continue...\n", i - 1); + stk1160_warn("%d urbs allocated. Trying to continue...\n", i); - dev->isoc_ctl.num_bufs = i - 1; + dev->isoc_ctl.num_bufs = i; return 0; free_i_bufs: /* Save the allocated buffers so far, so we can properly free them */ - dev->isoc_ctl.num_bufs = i+1; + dev->isoc_ctl.num_bufs = i; stk1160_free_isoc(dev); return -ENOMEM; } diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h index a31ea1c80f25..7b498d14ed7a 100644 --- a/drivers/media/usb/stk1160/stk1160.h +++ b/drivers/media/usb/stk1160/stk1160.h @@ -16,6 +16,8 @@ #include <media/videobuf2-v4l2.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> #define STK1160_VERSION "0.9.5" #define STK1160_VERSION_NUM 0x000905 @@ -84,6 +86,14 @@ struct stk1160_buffer { unsigned int pos; /* current pos inside buffer */ }; +struct stk1160_urb { + struct urb *urb; + char *transfer_buffer; + struct sg_table *sgt; + struct stk1160 *dev; + dma_addr_t dma; +}; + struct stk1160_isoc_ctl { /* max packet size of isoc transaction */ int max_pkt_size; @@ -91,11 +101,7 @@ struct stk1160_isoc_ctl { /* number of allocated urbs */ int num_bufs; - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; + struct stk1160_urb urb_ctl[STK1160_NUM_BUFS]; /* current buffer */ struct stk1160_buffer *buf; @@ -166,7 +172,7 @@ struct regval { int stk1160_vb2_setup(struct stk1160 *dev); int stk1160_video_register(struct stk1160 *dev); void stk1160_video_unregister(struct stk1160 *dev); -void stk1160_clear_queue(struct stk1160 *dev); +void stk1160_clear_queue(struct stk1160 *dev, enum vb2_buffer_state vb2_state); /* Provided by stk1160-video.c */ int stk1160_alloc_isoc(struct stk1160 *dev); @@ -189,3 +195,8 @@ void stk1160_select_input(struct stk1160 *dev); /* Provided by stk1160-ac97.c */ void stk1160_ac97_setup(struct stk1160 *dev); + +static inline struct device *stk1160_get_dmadev(struct stk1160 *dev) +{ + return bus_to_hcd(dev->udev->bus)->self.sysdev; +} diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig index 775a5151539c..d94d023f1aa0 100644 --- a/drivers/media/usb/stkwebcam/Kconfig +++ b/drivers/media/usb/stkwebcam/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_STKWEBCAM tristate "USB Syntek DC1125 Camera support" - depends on VIDEO_V4L2 + depends on VIDEO_DEV help Say Y here if you want to use this type of camera. Supported devices are typically found in some Asus laptops, diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 9f445e6ab5fa..5b822214ccc5 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -114,6 +114,13 @@ static const struct dmi_system_id stk_upside_down_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") } }, + { + .ident = "ASUS A6JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + } + }, {} }; diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c index 5358cd8c4603..98f4a63adc2a 100644 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -17,7 +17,7 @@ #include "tm6000.h" #include "tm6000-regs.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "xc5000.h" #define TM6000_BOARD_UNKNOWN 0 diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index 4990fa886d7a..8c2725e4105b 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -16,7 +16,7 @@ #include <media/tuner.h> -#include "tuner-xc2028.h" +#include "xc2028.h" #include "xc5000.h" MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c index b37782d6f79c..7554b93b82e6 100644 --- a/drivers/media/usb/tm6000/tm6000-i2c.c +++ b/drivers/media/usb/tm6000/tm6000-i2c.c @@ -15,7 +15,7 @@ #include "tm6000-regs.h" #include <media/v4l2-common.h> #include <media/tuner.h> -#include "tuner-xc2028.h" +#include "xc2028.h" /* ----------------------------------------------------------- */ diff --git a/drivers/media/usb/usbtv/Kconfig b/drivers/media/usb/usbtv/Kconfig index 84799c7203d3..578a0e693f8b 100644 --- a/drivers/media/usb/usbtv/Kconfig +++ b/drivers/media/usb/usbtv/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_USBTV tristate "USBTV007 video capture support" - depends on VIDEO_V4L2 && SND + depends on VIDEO_DEV && SND select SND_PCM select VIDEOBUF2_VMALLOC diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig index 4c2f4a3216f2..ca51ee8e45f3 100644 --- a/drivers/media/usb/uvc/Kconfig +++ b/drivers/media/usb/uvc/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_VIDEO_CLASS tristate "USB Video Class (UVC)" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF2_VMALLOC help Support for the USB Video Class (UVC). Currently only video diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 5f394d4efc21..dda0f0aa78b8 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2845,6 +2845,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX | UVC_QUIRK_BUILTIN_ISIGHT) }, + /* Apple FaceTime HD Camera (Built-In) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8514, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, /* Apple Built-In iSight via iBridge */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig index 49b4257487bb..a9fb02566c4b 100644 --- a/drivers/media/usb/zr364xx/Kconfig +++ b/drivers/media/usb/zr364xx/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_ZR364XX tristate "USB ZR364XX Camera support" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select VIDEOBUF_GEN select VIDEOBUF_VMALLOC help diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 6ee75c6c820e..1be9a2cc947a 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -3,17 +3,9 @@ # Generic video config states # -# Enable the V4L2 core and API -config VIDEO_V4L2 - tristate - depends on (I2C || I2C=n) && VIDEO_DEV - select RATIONAL - select VIDEOBUF2_V4L2 if VIDEOBUF2_CORE - default (I2C || I2C=n) && VIDEO_DEV - config VIDEO_V4L2_I2C bool - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV default y config VIDEO_V4L2_SUBDEV_API @@ -64,7 +56,7 @@ config V4L2_MEM2MEM_DEV # Used by LED subsystem flash drivers config V4L2_FLASH_LED_CLASS tristate "V4L2 flash API for LED flash class devices" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_DEV && VIDEO_V4L2_SUBDEV_API depends on LEDS_CLASS_FLASH select V4L2_ASYNC help diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 83fac5c746f5..41d91bd10cf2 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -3,37 +3,39 @@ # Makefile for the V4L2 core # +ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +ccflags-y += -I$(srctree)/drivers/media/tuners + tuner-objs := tuner-core.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ v4l2-event.o v4l2-subdev.o v4l2-common.o \ v4l2-ctrls-core.o v4l2-ctrls-api.o \ v4l2-ctrls-request.o v4l2-ctrls-defs.o + +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) videodev-$(CONFIG_COMPAT) += v4l2-compat-ioctl32.o -videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o videodev-$(CONFIG_MEDIA_CONTROLLER) += v4l2-mc.o videodev-$(CONFIG_SPI) += v4l2-spi.o +videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o -obj-$(CONFIG_VIDEO_V4L2) += videodev.o -obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o -obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o -obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o - -obj-$(CONFIG_VIDEO_TUNER) += tuner.o - -obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o -obj-$(CONFIG_V4L2_H264) += v4l2-h264.o -obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o +# Please keep it alphabetically sorted by Kconfig name +# (e. g. LC_ALL=C sort Makefile) +obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o - +obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o +obj-$(CONFIG_V4L2_H264) += v4l2-h264.o obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o +obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o +obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o -obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o -obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o +obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o +obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o -ccflags-y += -I$(srctree)/drivers/media/dvb-frontends -ccflags-y += -I$(srctree)/drivers/media/tuners +obj-$(CONFIG_VIDEO_TUNER) += tuner.o +obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 12d1e0c33c3c..ad9224a18853 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -35,7 +35,7 @@ #include "tda8290.h" #include "tea5761.h" #include "tea5767.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "tuner-simple.h" #include "tda9887.h" #include "xc5000.h" diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 54abe5245dcc..8968cec8454e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -112,7 +112,9 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_mpeg2_picture *p_mpeg2_picture; struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quant; struct v4l2_ctrl_vp8_frame *p_vp8_frame; + struct v4l2_ctrl_vp9_frame *p_vp9_frame; struct v4l2_ctrl_fwht_params *p_fwht_params; + struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix; void *p = ptr.p + idx * ctrl->elem_size; if (ctrl->p_def.p_const) @@ -152,6 +154,13 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, p_vp8_frame = p; p_vp8_frame->num_dct_parts = 1; break; + case V4L2_CTRL_TYPE_VP9_FRAME: + p_vp9_frame = p; + p_vp9_frame->profile = 0; + p_vp9_frame->bit_depth = 8; + p_vp9_frame->flags |= V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING | + V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING; + break; case V4L2_CTRL_TYPE_FWHT_PARAMS: p_fwht_params = p; p_fwht_params->version = V4L2_FWHT_VERSION; @@ -160,6 +169,15 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, p_fwht_params->flags = V4L2_FWHT_FL_PIXENC_YUV | (2 << V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); break; + case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + p_h264_scaling_matrix = p; + /* + * The default (flat) H.264 scaling matrix when none are + * specified in the bitstream, this is according to formulas + * (7-8) and (7-9) of the specification. + */ + memset(p_h264_scaling_matrix, 16, sizeof(*p_h264_scaling_matrix)); + break; } } @@ -382,7 +400,7 @@ validate_vp9_seg_params(struct v4l2_vp9_segmentation *seg) } for (i = 0; i < ARRAY_SIZE(seg->feature_data); i++) { - const int range[] = { 255, 63, 3, 0 }; + static const int range[] = { 255, 63, 3, 0 }; for (j = 0; j < ARRAY_SIZE(seg->feature_data[j]); j++) { if (seg->feature_data[i][j] < -range[j] || diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index d03ace324db0..d00237ee4cae 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -511,7 +511,7 @@ static int get_index(struct video_device *vdev) for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_devices[i] != NULL && video_devices[i]->v4l2_dev == vdev->v4l2_dev) { - set_bit(video_devices[i]->index, used); + __set_bit(video_devices[i]->index, used); } } @@ -519,7 +519,7 @@ static int get_index(struct video_device *vdev) } #define SET_VALID_IOCTL(ops, cmd, op) \ - do { if ((ops)->op) set_bit(_IOC_NR(cmd), valid_ioctls); } while (0) + do { if ((ops)->op) __set_bit(_IOC_NR(cmd), valid_ioctls); } while (0) /* This determines which ioctls are actually implemented in the driver. It's a one-time thing which simplifies video_ioctl2 as it can just do @@ -562,73 +562,73 @@ static void determine_valid_ioctls(struct video_device *vdev) /* vfl_type and vfl_dir independent ioctls */ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); - set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); /* Note: the control handler can also be passed through the filehandle, and that can't be tested here. If the bit for these control ioctls is set, then the ioctl is valid. But if it is 0, then it can still be valid if the filehandle passed the control handler. */ if (vdev->ctrl_handler || ops->vidioc_queryctrl) - set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl) - set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_querymenu) - set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); if (!is_tch) { SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); } SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); #ifdef CONFIG_VIDEO_ADV_DEBUG - set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls); #endif /* yes, really vidioc_subscribe_event */ SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) - set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); if (is_vid) { /* video specific ioctls */ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap || ops->vidioc_enum_fmt_vid_overlay)) || (is_tx && ops->vidioc_enum_fmt_vid_out)) - set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane || ops->vidioc_g_fmt_vid_overlay)) || (is_tx && (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane || ops->vidioc_g_fmt_vid_out_overlay))) - set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_s_fmt_vid_cap || ops->vidioc_s_fmt_vid_cap_mplane || ops->vidioc_s_fmt_vid_overlay)) || (is_tx && (ops->vidioc_s_fmt_vid_out || ops->vidioc_s_fmt_vid_out_mplane || ops->vidioc_s_fmt_vid_out_overlay))) - set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_try_fmt_vid_cap || ops->vidioc_try_fmt_vid_cap_mplane || ops->vidioc_try_fmt_vid_overlay)) || (is_tx && (ops->vidioc_try_fmt_vid_out || ops->vidioc_try_fmt_vid_out_mplane || ops->vidioc_try_fmt_vid_out_overlay))) - set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf); @@ -642,11 +642,11 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); if (ops->vidioc_g_selection) { - set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); } if (ops->vidioc_s_selection) - set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); } @@ -669,17 +669,17 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_g_fmt_sliced_vbi_cap)) || (is_tx && (ops->vidioc_g_fmt_vbi_out || ops->vidioc_g_fmt_sliced_vbi_out))) - set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_s_fmt_vbi_cap || ops->vidioc_s_fmt_sliced_vbi_cap)) || (is_tx && (ops->vidioc_s_fmt_vbi_out || ops->vidioc_s_fmt_sliced_vbi_out))) - set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_try_fmt_vbi_cap || ops->vidioc_try_fmt_sliced_vbi_cap)) || (is_tx && (ops->vidioc_try_fmt_vbi_out || ops->vidioc_try_fmt_sliced_vbi_out))) - set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); } else if (is_tch) { /* touch specific ioctls */ @@ -724,15 +724,15 @@ static void determine_valid_ioctls(struct video_device *vdev) if (is_vid || is_vbi || is_meta) { /* ioctls valid for video, vbi and metadata */ if (ops->vidioc_s_std) - set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std); if (is_rx) { SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); if (is_io_mc) { - set_bit(_IOC_NR(VIDIOC_ENUMINPUT), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_G_INPUT), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_S_INPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_ENUMINPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_INPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_INPUT), valid_ioctls); } else { SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); @@ -746,9 +746,9 @@ static void determine_valid_ioctls(struct video_device *vdev) } if (is_tx) { if (is_io_mc) { - set_bit(_IOC_NR(VIDIOC_ENUMOUTPUT), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_G_OUTPUT), valid_ioctls); - set_bit(_IOC_NR(VIDIOC_S_OUTPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_ENUMOUTPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_OUTPUT), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_S_OUTPUT), valid_ioctls); } else { SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); @@ -759,7 +759,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); } if (ops->vidioc_g_parm || ops->vidioc_g_std) - set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); + __set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 00457e1e93f6..afceb35e500c 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -119,11 +119,11 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep, enum v4l2_mbus_type bus_type) { - struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2; + struct v4l2_mbus_config_mipi_csi2 *bus = &vep->bus.mipi_csi2; bool have_clk_lane = false, have_data_lanes = false, have_lane_polarities = false; unsigned int flags = 0, lanes_used = 0; - u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES]; + u32 array[1 + V4L2_MBUS_CSI2_MAX_DATA_LANES]; u32 clock_lane = 0; unsigned int num_data_lanes = 0; bool use_default_lane_mapping = false; @@ -136,7 +136,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, use_default_lane_mapping = true; num_data_lanes = min_t(u32, bus->num_data_lanes, - V4L2_FWNODE_CSI2_MAX_DATA_LANES); + V4L2_MBUS_CSI2_MAX_DATA_LANES); clock_lane = bus->clock_lane; if (clock_lane) @@ -155,7 +155,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, rval = fwnode_property_count_u32(fwnode, "data-lanes"); if (rval > 0) { num_data_lanes = - min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval); + min_t(int, V4L2_MBUS_CSI2_MAX_DATA_LANES, rval); fwnode_property_read_u32_array(fwnode, "data-lanes", array, num_data_lanes); @@ -207,13 +207,11 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, if (fwnode_property_present(fwnode, "clock-noncontinuous")) { flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; pr_debug("non-continuous clock\n"); - } else { - flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; } if (bus_type == V4L2_MBUS_CSI2_DPHY || - bus_type == V4L2_MBUS_CSI2_CPHY || lanes_used || - have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) { + bus_type == V4L2_MBUS_CSI2_CPHY || + lanes_used || have_clk_lane || flags) { /* Only D-PHY has a clock lane. */ unsigned int dfl_data_lane_index = bus_type == V4L2_MBUS_CSI2_DPHY; @@ -263,7 +261,7 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep, enum v4l2_mbus_type bus_type) { - struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel; + struct v4l2_mbus_config_parallel *bus = &vep->bus.parallel; unsigned int flags = 0; u32 v; @@ -369,7 +367,7 @@ v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep, enum v4l2_mbus_type bus_type) { - struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1; + struct v4l2_mbus_config_mipi_csi1 *bus = &vep->bus.mipi_csi1; u32 v; if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) { @@ -896,25 +894,8 @@ static int v4l2_fwnode_reference_parse(struct device *dev, int ret; for (index = 0; - !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), - prop, NULL, 0, - index, &args)); - index++) - fwnode_handle_put(args.fwnode); - - if (!index) - return -ENOENT; - - /* - * Note that right now both -ENODATA and -ENOENT may signal - * out-of-bounds access. Return the error in cases other than that. - */ - if (ret != -ENOENT && ret != -ENODATA) - return ret; - - for (index = 0; - !fwnode_property_get_reference_args(dev_fwnode(dev), prop, NULL, - 0, index, &args); + !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), prop, + NULL, 0, index, &args)); index++) { struct v4l2_async_subdev *asd; @@ -930,7 +911,12 @@ static int v4l2_fwnode_reference_parse(struct device *dev, } } - return 0; + /* -ENOENT here means successful parsing */ + if (ret != -ENOENT) + return ret; + + /* Return -ENOENT if no references were found */ + return index ? 0 : -ENOENT; } /* diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 9ac557b8e146..96e307fe3aab 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -279,8 +279,8 @@ static void v4l_print_format(const void *arg, bool write_only) const struct v4l2_vbi_format *vbi; const struct v4l2_sliced_vbi_format *sliced; const struct v4l2_window *win; - const struct v4l2_sdr_format *sdr; const struct v4l2_meta_format *meta; + u32 pixelformat; u32 planes; unsigned i; @@ -299,8 +299,9 @@ static void v4l_print_format(const void *arg, bool write_only) case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: mp = &p->fmt.pix_mp; + pixelformat = mp->pixelformat; pr_cont(", width=%u, height=%u, format=%p4cc, field=%s, colorspace=%d, num_planes=%u, flags=0x%x, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n", - mp->width, mp->height, &mp->pixelformat, + mp->width, mp->height, &pixelformat, prt_names(mp->field, v4l2_field_names), mp->colorspace, mp->num_planes, mp->flags, mp->ycbcr_enc, mp->quantization, mp->xfer_func); @@ -343,14 +344,15 @@ static void v4l_print_format(const void *arg, bool write_only) break; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: - sdr = &p->fmt.sdr; - pr_cont(", pixelformat=%p4cc\n", &sdr->pixelformat); + pixelformat = p->fmt.sdr.pixelformat; + pr_cont(", pixelformat=%p4cc\n", &pixelformat); break; case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: meta = &p->fmt.meta; + pixelformat = meta->dataformat; pr_cont(", dataformat=%p4cc, buffersize=%u\n", - &meta->dataformat, meta->buffersize); + &pixelformat, meta->buffersize); break; } } @@ -1388,6 +1390,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break; case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break; + case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break; + case V4L2_PIX_FMT_NV12M_10BE_8L128: descr = "10-bit NV12M (8x128 Linear, BE)"; break; default: /* Compressed formats */ diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index e2654b422334..675e22895ebe 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -585,19 +585,14 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); -int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct v4l2_buffer *buf) +static void v4l2_m2m_adjust_mem_offset(struct vb2_queue *vq, + struct v4l2_buffer *buf) { - struct vb2_queue *vq; - int ret = 0; - unsigned int i; - - vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_querybuf(vq, buf); - /* Adjust MMAP memory offsets for the CAPTURE queue */ if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { + unsigned int i; + for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE; @@ -605,8 +600,23 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, buf->m.offset += DST_QUEUE_OFF_BASE; } } +} - return ret; +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = vb2_querybuf(vq, buf); + if (ret) + return ret; + + /* Adjust MMAP memory offsets for the CAPTURE queue */ + v4l2_m2m_adjust_mem_offset(vq, buf); + + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); @@ -763,6 +773,9 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, if (ret) return ret; + /* Adjust MMAP memory offsets for the CAPTURE queue */ + v4l2_m2m_adjust_mem_offset(vq, buf); + /* * If the capture queue is streaming, but streaming hasn't started * on the device, but was asked to stop, mark the previously queued @@ -784,9 +797,17 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { struct vb2_queue *vq; + int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); + ret = vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); + if (ret) + return ret; + + /* Adjust MMAP memory offsets for the CAPTURE queue */ + v4l2_m2m_adjust_mem_offset(vq, buf); + + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); @@ -795,9 +816,17 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, { struct video_device *vdev = video_devdata(file); struct vb2_queue *vq; + int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); + ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); + if (ret) + return ret; + + /* Adjust MMAP memory offsets for the CAPTURE queue */ + v4l2_m2m_adjust_mem_offset(vq, buf); + + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 5d27a27cc2f2..30eb50407db5 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -318,13 +318,6 @@ static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, sd->ops->pad->get_mbus_config(sd, pad, config); } -static int call_set_mbus_config(struct v4l2_subdev *sd, unsigned int pad, - struct v4l2_mbus_config *config) -{ - return check_pad(sd, pad) ? : - sd->ops->pad->get_mbus_config(sd, pad, config); -} - static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .get_fmt = call_get_fmt, .set_fmt = call_set_fmt, @@ -338,7 +331,6 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .dv_timings_cap = call_dv_timings_cap, .enum_dv_timings = call_enum_dv_timings, .get_mbus_config = call_get_mbus_config, - .set_mbus_config = call_set_mbus_config, }; static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = { diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index e201e5976f34..40408a88f464 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -149,20 +149,6 @@ struct mtk_smi_larb { /* larb: local arbiter */ unsigned char *bank; }; -int mtk_smi_larb_get(struct device *larbdev) -{ - int ret = pm_runtime_resume_and_get(larbdev); - - return (ret < 0) ? ret : 0; -} -EXPORT_SYMBOL_GPL(mtk_smi_larb_get); - -void mtk_smi_larb_put(struct device *larbdev) -{ - pm_runtime_put_sync(larbdev); -} -EXPORT_SYMBOL_GPL(mtk_smi_larb_put); - static int mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) { diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index b81cfa74edb7..1fd6a0c6e1d8 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -36,8 +36,6 @@ source "drivers/staging/media/rkvdec/Kconfig" source "drivers/staging/media/sunxi/Kconfig" -source "drivers/staging/media/tegra-vde/Kconfig" - source "drivers/staging/media/zoran/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 7e2c86e3695d..66d6f6d51c86 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ -obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ diff --git a/drivers/staging/media/atomisp/Kconfig b/drivers/staging/media/atomisp/Kconfig index aeed5803dfb1..2c8d7fdcc5f7 100644 --- a/drivers/staging/media/atomisp/Kconfig +++ b/drivers/staging/media/atomisp/Kconfig @@ -11,7 +11,7 @@ menuconfig INTEL_ATOMISP config VIDEO_ATOMISP tristate "Intel Atom Image Signal Processor Driver" - depends on VIDEO_V4L2 && INTEL_ATOMISP + depends on VIDEO_DEV && INTEL_ATOMISP depends on PMIC_OPREGION select IOSF_MBI select VIDEOBUF_VMALLOC diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig index a772b833a85f..e726101b24e4 100644 --- a/drivers/staging/media/atomisp/i2c/Kconfig +++ b/drivers/staging/media/atomisp/i2c/Kconfig @@ -6,7 +6,7 @@ config VIDEO_ATOMISP_OV2722 tristate "OVT ov2722 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the OVT OV2722 raw camera. @@ -18,7 +18,7 @@ config VIDEO_ATOMISP_OV2722 config VIDEO_ATOMISP_GC2235 tristate "Galaxy gc2235 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the OVT GC2235 raw camera. @@ -40,7 +40,7 @@ config VIDEO_ATOMISP_MSRLIST_HELPER config VIDEO_ATOMISP_MT9M114 tristate "Aptina mt9m114 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the Micron mt9m114 1.3 Mpixel camera. @@ -52,7 +52,7 @@ config VIDEO_ATOMISP_MT9M114 config VIDEO_ATOMISP_GC0310 tristate "GC0310 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the Galaxycore GC0310 0.3MP sensor. @@ -60,7 +60,7 @@ config VIDEO_ATOMISP_GC0310 config VIDEO_ATOMISP_OV2680 tristate "Omnivision OV2680 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the Omnivision OV2680 raw camera. @@ -72,7 +72,7 @@ config VIDEO_ATOMISP_OV2680 config VIDEO_ATOMISP_OV5693 tristate "Omnivision ov5693 sensor support" depends on ACPI - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor-level driver for the Micron ov5693 5 Mpixel camera. @@ -88,7 +88,7 @@ config VIDEO_ATOMISP_OV5693 config VIDEO_ATOMISP_LM3554 tristate "LM3554 flash light driver" depends on ACPI - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C help This is a Video4Linux2 sub-dev driver for the LM3554 flash light driver. diff --git a/drivers/staging/media/atomisp/pci/atomisp_acc.c b/drivers/staging/media/atomisp/pci/atomisp_acc.c index 9a1751895ab0..28cb271663c4 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_acc.c +++ b/drivers/staging/media/atomisp/pci/atomisp_acc.c @@ -439,6 +439,18 @@ int atomisp_acc_s_mapped_arg(struct atomisp_sub_device *asd, return 0; } +static void atomisp_acc_unload_some_extensions(struct atomisp_sub_device *asd, + int i, + struct atomisp_acc_fw *acc_fw) +{ + while (--i >= 0) { + if (acc_fw->flags & acc_flag_to_pipe[i].flag) { + atomisp_css_unload_acc_extension(asd, acc_fw->fw, + acc_flag_to_pipe[i].pipe_id); + } + } +} + /* * Appends the loaded acceleration binary extensions to the * current ISP mode. Must be called just before sh_css_start(). @@ -479,16 +491,20 @@ int atomisp_acc_load_extensions(struct atomisp_sub_device *asd) acc_fw->fw, acc_flag_to_pipe[i].pipe_id, acc_fw->type); - if (ret) + if (ret) { + atomisp_acc_unload_some_extensions(asd, i, acc_fw); goto error; + } ext_loaded = true; } } ret = atomisp_css_set_acc_parameters(acc_fw); - if (ret < 0) + if (ret < 0) { + atomisp_acc_unload_some_extensions(asd, i, acc_fw); goto error; + } } if (!ext_loaded) @@ -497,6 +513,7 @@ int atomisp_acc_load_extensions(struct atomisp_sub_device *asd) ret = atomisp_css_update_stream(asd); if (ret) { dev_err(isp->dev, "%s: update stream failed.\n", __func__); + atomisp_acc_unload_extensions(asd); goto error; } @@ -504,13 +521,6 @@ int atomisp_acc_load_extensions(struct atomisp_sub_device *asd) return 0; error: - while (--i >= 0) { - if (acc_fw->flags & acc_flag_to_pipe[i].flag) { - atomisp_css_unload_acc_extension(asd, acc_fw->fw, - acc_flag_to_pipe[i].pipe_id); - } - } - list_for_each_entry_continue_reverse(acc_fw, &asd->acc.fw, list) { if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT && acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index 1173be0e72b0..781a11cca599 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -963,7 +963,7 @@ int atomisp_css_irq_translate(struct atomisp_device *isp, void atomisp_css_rx_get_irq_info(enum mipi_port_id port, unsigned int *infos) { -#ifndef IS_ISP2401 +#ifndef ISP2401 ia_css_isys_rx_get_irq_info(port, infos); #else *infos = 0; @@ -973,7 +973,7 @@ void atomisp_css_rx_get_irq_info(enum mipi_port_id port, void atomisp_css_rx_clear_irq_info(enum mipi_port_id port, unsigned int infos) { -#ifndef IS_ISP2401 +#ifndef ISP2401 ia_css_isys_rx_clear_irq_info(port, infos); #endif } diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 1cc581074ba7..7e47db82de07 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -38,7 +38,7 @@ enum clock_rate { #define ELDO_CTRL_REG 0x12 #define ELDO1_SEL_REG 0x19 -#define ELDO1_1P8V 0x16 +#define ELDO1_1P6V 0x12 #define ELDO1_CTRL_SHIFT 0x00 #define ELDO2_SEL_REG 0x1a @@ -89,7 +89,7 @@ struct gmin_subdev { u8 pwm_i2c_addr; /* For PMIC AXP */ - int eldo1_sel_reg, eldo1_1p8v, eldo1_ctrl_shift; + int eldo1_sel_reg, eldo1_1p6v, eldo1_ctrl_shift; int eldo2_sel_reg, eldo2_1p8v, eldo2_ctrl_shift; }; @@ -118,6 +118,10 @@ static const char *pmic_name[] = { [PMIC_CRYSTALCOVE] = "Crystal Cove PMIC", }; +static DEFINE_MUTEX(gmin_regulator_mutex); +static int gmin_v1p8_enable_count; +static int gmin_v2p8_enable_count; + /* The atomisp uses type==0 for the end-of-list marker, so leave space. */ static struct intel_v4l2_subdev_table pdata_subdevs[MAX_SUBDEVS + 1]; @@ -536,7 +540,7 @@ static int gmin_subdev_add(struct gmin_subdev *gs) struct i2c_client *client = v4l2_get_subdevdata(gs->subdev); struct device *dev = &client->dev; struct acpi_device *adev = ACPI_COMPANION(dev); - int ret, clock_num = -1; + int ret, default_val, clock_num = -1; dev_info(dev, "%s: ACPI path is %pfw\n", __func__, dev_fwnode(dev)); @@ -544,7 +548,20 @@ static int gmin_subdev_add(struct gmin_subdev *gs) gs->clock_src = gmin_get_var_int(dev, false, "ClkSrc", VLV2_CLK_PLL_19P2MHZ); - gs->csi_port = gmin_get_var_int(dev, false, "CsiPort", 0); + /* + * Get ACPI _PR0 derived clock here already because it is used + * to determine the csi_port default. + */ + if (acpi_device_power_manageable(adev)) + clock_num = atomisp_get_acpi_power(dev); + + /* Compare clock to CsiPort 1 pmc-clock used in the CHT/BYT reference designs */ + if (IS_ISP2401) + default_val = clock_num == 4 ? 1 : 0; + else + default_val = clock_num == 0 ? 1 : 0; + + gs->csi_port = gmin_get_var_int(dev, false, "CsiPort", default_val); gs->csi_lanes = gmin_get_var_int(dev, false, "CsiLanes", 1); gs->gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); @@ -625,11 +642,7 @@ static int gmin_subdev_add(struct gmin_subdev *gs) * otherwise. */ - /* Try first to use ACPI to get the clock resource */ - if (acpi_device_power_manageable(adev)) - clock_num = atomisp_get_acpi_power(dev); - - /* Fall-back use EFI and/or DMI match */ + /* If getting the clock from _PR0 above failed, fall-back to EFI and/or DMI match */ if (clock_num < 0) clock_num = gmin_get_var_int(dev, false, "CamClk", 0); @@ -681,9 +694,9 @@ static int gmin_subdev_add(struct gmin_subdev *gs) break; case PMIC_AXP: - gs->eldo1_1p8v = gmin_get_var_int(dev, false, + gs->eldo1_1p6v = gmin_get_var_int(dev, false, "eldo1_1p8v", - ELDO1_1P8V); + ELDO1_1P6V); gs->eldo1_sel_reg = gmin_get_var_int(dev, false, "eldo1_sel_reg", ELDO1_SEL_REG); @@ -741,13 +754,28 @@ static int axp_regulator_set(struct device *dev, struct gmin_subdev *gs, val = on ? 1 << shift : 0; - ret = gmin_i2c_write(dev, gs->pwm_i2c_addr, sel_reg, val, 1 << shift); + ret = gmin_i2c_write(dev, gs->pwm_i2c_addr, ctrl_reg, val, 1 << shift); if (ret) return ret; return 0; } +/* + * Some boards contain a hw-bug where turning eldo2 back on after having turned + * it off causes the CPLM3218 ambient-light-sensor on the image-sensor's I2C bus + * to crash, hanging the bus. Do not turn eldo2 off on these systems. + */ +static const struct dmi_system_id axp_leave_eldo2_on_ids[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TrekStor"), + DMI_MATCH(DMI_PRODUCT_NAME, "SurfTab duo W1 10.1 (VT4)"), + }, + }, + { } +}; + static int axp_v1p8_on(struct device *dev, struct gmin_subdev *gs) { int ret; @@ -763,13 +791,8 @@ static int axp_v1p8_on(struct device *dev, struct gmin_subdev *gs) */ usleep_range(110, 150); - ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p8v, - ELDO_CTRL_REG, gs->eldo1_ctrl_shift, true); - if (ret) - return ret; - - ret = axp_regulator_set(dev, gs, gs->eldo2_sel_reg, gs->eldo2_1p8v, - ELDO_CTRL_REG, gs->eldo2_ctrl_shift, false); + ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p6v, + ELDO_CTRL_REG, gs->eldo1_ctrl_shift, true); return ret; } @@ -777,11 +800,14 @@ static int axp_v1p8_off(struct device *dev, struct gmin_subdev *gs) { int ret; - ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p8v, + ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p6v, ELDO_CTRL_REG, gs->eldo1_ctrl_shift, false); if (ret) return ret; + if (dmi_check_system(axp_leave_eldo2_on_ids)) + return 0; + ret = axp_regulator_set(dev, gs, gs->eldo2_sel_reg, gs->eldo2_1p8v, ELDO_CTRL_REG, gs->eldo2_ctrl_shift, false); return ret; @@ -851,38 +877,58 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) gs->v1p8_on = on; + ret = 0; + mutex_lock(&gmin_regulator_mutex); + if (on) { + gmin_v1p8_enable_count++; + if (gmin_v1p8_enable_count > 1) + goto out; /* Already on */ + } else { + gmin_v1p8_enable_count--; + if (gmin_v1p8_enable_count > 0) + goto out; /* Still needed */ + } + if (gs->v1p8_gpio >= 0) gpio_set_value(gs->v1p8_gpio, on); if (gs->v1p8_reg) { regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000); if (on) - return regulator_enable(gs->v1p8_reg); + ret = regulator_enable(gs->v1p8_reg); else - return regulator_disable(gs->v1p8_reg); + ret = regulator_disable(gs->v1p8_reg); + + goto out; } switch (pmic_id) { case PMIC_AXP: if (on) - return axp_v1p8_on(subdev->dev, gs); + ret = axp_v1p8_on(subdev->dev, gs); else - return axp_v1p8_off(subdev->dev, gs); + ret = axp_v1p8_off(subdev->dev, gs); + break; case PMIC_TI: value = on ? LDO_1P8V_ON : LDO_1P8V_OFF; - return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, - LDO10_REG, value, 0xff); + ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, + LDO10_REG, value, 0xff); + break; case PMIC_CRYSTALCOVE: value = on ? CRYSTAL_ON : CRYSTAL_OFF; - return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, - CRYSTAL_1P8V_REG, value, 0xff); + ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, + CRYSTAL_1P8V_REG, value, 0xff); + break; default: - dev_err(subdev->dev, "Couldn't set power mode for v1p2\n"); + dev_err(subdev->dev, "Couldn't set power mode for v1p8\n"); + ret = -EINVAL; } - return -EINVAL; +out: + mutex_unlock(&gmin_regulator_mutex); + return ret; } static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) @@ -908,37 +954,57 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) return 0; gs->v2p8_on = on; + ret = 0; + mutex_lock(&gmin_regulator_mutex); + if (on) { + gmin_v2p8_enable_count++; + if (gmin_v2p8_enable_count > 1) + goto out; /* Already on */ + } else { + gmin_v2p8_enable_count--; + if (gmin_v2p8_enable_count > 0) + goto out; /* Still needed */ + } + if (gs->v2p8_gpio >= 0) gpio_set_value(gs->v2p8_gpio, on); if (gs->v2p8_reg) { regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000); if (on) - return regulator_enable(gs->v2p8_reg); + ret = regulator_enable(gs->v2p8_reg); else - return regulator_disable(gs->v2p8_reg); + ret = regulator_disable(gs->v2p8_reg); + + goto out; } switch (pmic_id) { case PMIC_AXP: - return axp_regulator_set(subdev->dev, gs, ALDO1_SEL_REG, - ALDO1_2P8V, ALDO1_CTRL3_REG, - ALDO1_CTRL3_SHIFT, on); + ret = axp_regulator_set(subdev->dev, gs, ALDO1_SEL_REG, + ALDO1_2P8V, ALDO1_CTRL3_REG, + ALDO1_CTRL3_SHIFT, on); + break; case PMIC_TI: value = on ? LDO_2P8V_ON : LDO_2P8V_OFF; - return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, - LDO9_REG, value, 0xff); + ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, + LDO9_REG, value, 0xff); + break; case PMIC_CRYSTALCOVE: value = on ? CRYSTAL_ON : CRYSTAL_OFF; - return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, - CRYSTAL_2P8V_REG, value, 0xff); + ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr, + CRYSTAL_2P8V_REG, value, 0xff); + break; default: - dev_err(subdev->dev, "Couldn't set power mode for v1p2\n"); + dev_err(subdev->dev, "Couldn't set power mode for v2p8\n"); + ret = -EINVAL; } - return -EINVAL; +out: + mutex_unlock(&gmin_regulator_mutex); + return ret; } static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 1b240891a6e2..49ccfb1646da 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -25,6 +25,7 @@ #include <linux/delay.h> #include <linux/dmi.h> #include <linux/interrupt.h> +#include <linux/bits.h> #include <asm/iosf_mbi.h> @@ -626,11 +627,11 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp) * IRQ, if so, waiting for it to be served */ pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - irq = irq & 1 << INTR_IIR; + irq &= BIT(INTR_IIR); pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq); pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - if (!(irq & (1 << INTR_IIR))) + if (!(irq & BIT(INTR_IIR))) goto done; atomisp_css2_hw_store_32(MRFLD_INTR_CLEAR_REG, 0xFFFFFFFF); @@ -643,11 +644,11 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp) return -EAGAIN; } else { pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - irq = irq & 1 << INTR_IIR; + irq &= BIT(INTR_IIR); pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq); pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - if (!(irq & (1 << INTR_IIR))) { + if (!(irq & BIT(INTR_IIR))) { atomisp_css2_hw_store_32(MRFLD_INTR_ENABLE_REG, 0x0); goto done; } @@ -666,7 +667,7 @@ done: * HW sighting:4568410. */ pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - irq &= ~(1 << INTR_IER); + irq &= ~BIT(INTR_IER); pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq); atomisp_msi_irq_uninit(isp); @@ -1467,7 +1468,7 @@ static bool is_valid_device(struct pci_dev *pdev, const struct pci_device_id *id * remove the if once the driver become generic */ -#if defined(ISP2400) +#ifndef ISP2401 if (IS_ISP2401) { dev_err(&pdev->dev, "Support for %s (ISP2401) was disabled at compile time\n", name); @@ -1549,7 +1550,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i start = pci_resource_start(pdev, ATOM_ISP_PCI_BAR); dev_dbg(&pdev->dev, "start: 0x%x\n", start); - err = pcim_iomap_regions(pdev, 1 << ATOM_ISP_PCI_BAR, pci_name(pdev)); + err = pcim_iomap_regions(pdev, BIT(ATOM_ISP_PCI_BAR), pci_name(pdev)); if (err) { dev_err(&pdev->dev, "Failed to I/O memory remapping (%d)\n", err); goto ioremap_fail; @@ -1838,11 +1839,11 @@ load_fw_fail: */ pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - irq = irq & 1 << INTR_IIR; + irq &= BIT(INTR_IIR); pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq); pci_read_config_dword(pdev, PCI_INTERRUPT_CTRL, &irq); - irq &= ~(1 << INTR_IER); + irq &= ~BIT(INTR_IER); pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, irq); atomisp_msi_irq_uninit(isp); @@ -1854,7 +1855,7 @@ load_fw_fail: dev_err(&pdev->dev, "Failed to switch off ISP\n"); atomisp_dev_alloc_fail: - pcim_iounmap_regions(pdev, 1 << ATOM_ISP_PCI_BAR); + pcim_iounmap_regions(pdev, BIT(ATOM_ISP_PCI_BAR)); ioremap_fail: return err; diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index 6a5ee4607089..c1cda16f2dc0 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -39,7 +39,7 @@ struct hmm_bo_device bo_device; struct hmm_pool dynamic_pool; struct hmm_pool reserved_pool; -static ia_css_ptr dummy_ptr; +static ia_css_ptr dummy_ptr = mmgr_EXCEPTION; static bool hmm_initialized; struct _hmm_mem_stat hmm_mem_stat; @@ -209,7 +209,7 @@ int hmm_init(void) void hmm_cleanup(void) { - if (!dummy_ptr) + if (dummy_ptr == mmgr_EXCEPTION) return; sysfs_remove_group(&atomisp_dev->kobj, atomisp_attribute_group); @@ -288,7 +288,8 @@ void hmm_free(ia_css_ptr virt) dev_dbg(atomisp_dev, "%s: free 0x%08x\n", __func__, virt); - WARN_ON(!virt); + if (WARN_ON(virt == mmgr_EXCEPTION)) + return; bo = hmm_bo_device_search_start(&bo_device, (unsigned int)virt); diff --git a/drivers/staging/media/atomisp/pci/ia_css_acc_types.h b/drivers/staging/media/atomisp/pci/ia_css_acc_types.h index d0ce2f8ba653..a20879aedef6 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_acc_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_acc_types.h @@ -24,6 +24,7 @@ #include <type_support.h> #include <platform_support.h> #include <debug_global.h> +#include <linux/bits.h> #include "ia_css_types.h" #include "ia_css_frame_format.h" @@ -466,7 +467,7 @@ struct ia_css_acc_fw { enum ia_css_sp_sleep_mode { SP_DISABLE_SLEEP_MODE = 0, - SP_SLEEP_AFTER_FRAME = 1 << 0, - SP_SLEEP_AFTER_IRQ = 1 << 1 + SP_SLEEP_AFTER_FRAME = BIT(0), + SP_SLEEP_AFTER_IRQ = BIT(1), }; #endif /* _IA_CSS_ACC_TYPES_H */ diff --git a/drivers/staging/media/atomisp/pci/ia_css_env.h b/drivers/staging/media/atomisp/pci/ia_css_env.h index 3b89bbd837a0..42bb1ec1c22d 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_env.h +++ b/drivers/staging/media/atomisp/pci/ia_css_env.h @@ -18,6 +18,7 @@ #include <type_support.h> #include <linux/stdarg.h> /* va_list */ +#include <linux/bits.h> #include "ia_css_types.h" #include "ia_css_acc_types.h" @@ -28,10 +29,10 @@ /* Memory allocation attributes, for use in ia_css_css_mem_env. */ enum ia_css_mem_attr { - IA_CSS_MEM_ATTR_CACHED = 1 << 0, - IA_CSS_MEM_ATTR_ZEROED = 1 << 1, - IA_CSS_MEM_ATTR_PAGEALIGN = 1 << 2, - IA_CSS_MEM_ATTR_CONTIGUOUS = 1 << 3, + IA_CSS_MEM_ATTR_CACHED = BIT(0), + IA_CSS_MEM_ATTR_ZEROED = BIT(1), + IA_CSS_MEM_ATTR_PAGEALIGN = BIT(2), + IA_CSS_MEM_ATTR_CONTIGUOUS = BIT(3), }; /* Environment with function pointers for local IA memory allocation. diff --git a/drivers/staging/media/atomisp/pci/ia_css_event_public.h b/drivers/staging/media/atomisp/pci/ia_css_event_public.h index 76219d741d2e..b052648d4fc2 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_event_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_event_public.h @@ -24,6 +24,7 @@ #include <ia_css_err.h> /* ia_css_err */ #include <ia_css_types.h> /* ia_css_pipe */ #include <ia_css_timer.h> /* ia_css_timer */ +#include <linux/bits.h> /* The event type, distinguishes the kind of events that * can are generated by the CSS system. @@ -35,38 +36,38 @@ * 4) "enum ia_css_event_type convert_event_sp_to_host_domain" (sh_css.c) */ enum ia_css_event_type { - IA_CSS_EVENT_TYPE_OUTPUT_FRAME_DONE = 1 << 0, + IA_CSS_EVENT_TYPE_OUTPUT_FRAME_DONE = BIT(0), /** Output frame ready. */ - IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE = 1 << 1, + IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE = BIT(1), /** Second output frame ready. */ - IA_CSS_EVENT_TYPE_VF_OUTPUT_FRAME_DONE = 1 << 2, + IA_CSS_EVENT_TYPE_VF_OUTPUT_FRAME_DONE = BIT(2), /** Viewfinder Output frame ready. */ - IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE = 1 << 3, + IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE = BIT(3), /** Second viewfinder Output frame ready. */ - IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE = 1 << 4, + IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE = BIT(4), /** Indication that 3A statistics are available. */ - IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE = 1 << 5, + IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE = BIT(5), /** Indication that DIS statistics are available. */ - IA_CSS_EVENT_TYPE_PIPELINE_DONE = 1 << 6, + IA_CSS_EVENT_TYPE_PIPELINE_DONE = BIT(6), /** Pipeline Done event, sent after last pipeline stage. */ - IA_CSS_EVENT_TYPE_FRAME_TAGGED = 1 << 7, + IA_CSS_EVENT_TYPE_FRAME_TAGGED = BIT(7), /** Frame tagged. */ - IA_CSS_EVENT_TYPE_INPUT_FRAME_DONE = 1 << 8, + IA_CSS_EVENT_TYPE_INPUT_FRAME_DONE = BIT(8), /** Input frame ready. */ - IA_CSS_EVENT_TYPE_METADATA_DONE = 1 << 9, + IA_CSS_EVENT_TYPE_METADATA_DONE = BIT(9), /** Metadata ready. */ - IA_CSS_EVENT_TYPE_LACE_STATISTICS_DONE = 1 << 10, + IA_CSS_EVENT_TYPE_LACE_STATISTICS_DONE = BIT(10), /** Indication that LACE statistics are available. */ - IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE = 1 << 11, + IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE = BIT(11), /** Extension stage complete. */ - IA_CSS_EVENT_TYPE_TIMER = 1 << 12, + IA_CSS_EVENT_TYPE_TIMER = BIT(12), /** Timer event for measuring the SP side latencies. It contains the 32-bit timer value from the SP */ - IA_CSS_EVENT_TYPE_PORT_EOF = 1 << 13, + IA_CSS_EVENT_TYPE_PORT_EOF = BIT(13), /** End Of Frame event, sent when in buffered sensor mode. */ - IA_CSS_EVENT_TYPE_FW_WARNING = 1 << 14, + IA_CSS_EVENT_TYPE_FW_WARNING = BIT(14), /** Performance warning encounter by FW */ - IA_CSS_EVENT_TYPE_FW_ASSERT = 1 << 15, + IA_CSS_EVENT_TYPE_FW_ASSERT = BIT(15), /** Assertion hit by FW */ }; diff --git a/drivers/staging/media/atomisp/pci/ia_css_irq.h b/drivers/staging/media/atomisp/pci/ia_css_irq.h index 3b81a39cfe97..26b1b3c8ba62 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_irq.h +++ b/drivers/staging/media/atomisp/pci/ia_css_irq.h @@ -23,6 +23,7 @@ #include "ia_css_err.h" #include "ia_css_pipe_public.h" #include "ia_css_input_port.h" +#include <linux/bits.h> /* Interrupt types, these enumerate all supported interrupt types. */ @@ -46,49 +47,49 @@ enum ia_css_irq_type { * (SW) interrupts */ enum ia_css_irq_info { - IA_CSS_IRQ_INFO_CSS_RECEIVER_ERROR = 1 << 0, + IA_CSS_IRQ_INFO_CSS_RECEIVER_ERROR = BIT(0), /** the css receiver has encountered an error */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_FIFO_OVERFLOW = 1 << 1, + IA_CSS_IRQ_INFO_CSS_RECEIVER_FIFO_OVERFLOW = BIT(1), /** the FIFO in the csi receiver has overflown */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF = 1 << 2, + IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF = BIT(2), /** the css receiver received the start of frame */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF = 1 << 3, + IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF = BIT(3), /** the css receiver received the end of frame */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_SOL = 1 << 4, + IA_CSS_IRQ_INFO_CSS_RECEIVER_SOL = BIT(4), /** the css receiver received the start of line */ - IA_CSS_IRQ_INFO_EVENTS_READY = 1 << 5, + IA_CSS_IRQ_INFO_EVENTS_READY = BIT(5), /** One or more events are available in the PSYS event queue */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_EOL = 1 << 6, + IA_CSS_IRQ_INFO_CSS_RECEIVER_EOL = BIT(6), /** the css receiver received the end of line */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_SIDEBAND_CHANGED = 1 << 7, + IA_CSS_IRQ_INFO_CSS_RECEIVER_SIDEBAND_CHANGED = BIT(7), /** the css receiver received a change in side band signals */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_GEN_SHORT_0 = 1 << 8, + IA_CSS_IRQ_INFO_CSS_RECEIVER_GEN_SHORT_0 = BIT(8), /** generic short packets (0) */ - IA_CSS_IRQ_INFO_CSS_RECEIVER_GEN_SHORT_1 = 1 << 9, + IA_CSS_IRQ_INFO_CSS_RECEIVER_GEN_SHORT_1 = BIT(9), /** generic short packets (1) */ - IA_CSS_IRQ_INFO_IF_PRIM_ERROR = 1 << 10, + IA_CSS_IRQ_INFO_IF_PRIM_ERROR = BIT(10), /** the primary input formatter (A) has encountered an error */ - IA_CSS_IRQ_INFO_IF_PRIM_B_ERROR = 1 << 11, + IA_CSS_IRQ_INFO_IF_PRIM_B_ERROR = BIT(11), /** the primary input formatter (B) has encountered an error */ - IA_CSS_IRQ_INFO_IF_SEC_ERROR = 1 << 12, + IA_CSS_IRQ_INFO_IF_SEC_ERROR = BIT(12), /** the secondary input formatter has encountered an error */ - IA_CSS_IRQ_INFO_STREAM_TO_MEM_ERROR = 1 << 13, + IA_CSS_IRQ_INFO_STREAM_TO_MEM_ERROR = BIT(13), /** the stream-to-memory device has encountered an error */ - IA_CSS_IRQ_INFO_SW_0 = 1 << 14, + IA_CSS_IRQ_INFO_SW_0 = BIT(14), /** software interrupt 0 */ - IA_CSS_IRQ_INFO_SW_1 = 1 << 15, + IA_CSS_IRQ_INFO_SW_1 = BIT(15), /** software interrupt 1 */ - IA_CSS_IRQ_INFO_SW_2 = 1 << 16, + IA_CSS_IRQ_INFO_SW_2 = BIT(16), /** software interrupt 2 */ - IA_CSS_IRQ_INFO_ISP_BINARY_STATISTICS_READY = 1 << 17, + IA_CSS_IRQ_INFO_ISP_BINARY_STATISTICS_READY = BIT(17), /** ISP binary statistics are ready */ - IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR = 1 << 18, + IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR = BIT(18), /** the input system in in error */ - IA_CSS_IRQ_INFO_IF_ERROR = 1 << 19, + IA_CSS_IRQ_INFO_IF_ERROR = BIT(19), /** the input formatter in in error */ - IA_CSS_IRQ_INFO_DMA_ERROR = 1 << 20, + IA_CSS_IRQ_INFO_DMA_ERROR = BIT(20), /** the dma in in error */ - IA_CSS_IRQ_INFO_ISYS_EVENTS_READY = 1 << 21, + IA_CSS_IRQ_INFO_ISYS_EVENTS_READY = BIT(21), /** end-of-frame events are ready in the isys_event queue */ }; @@ -103,23 +104,23 @@ enum ia_css_irq_info { * different receiver types, or possibly none in case of tests systems. */ enum ia_css_rx_irq_info { - IA_CSS_RX_IRQ_INFO_BUFFER_OVERRUN = 1U << 0, /** buffer overrun */ - IA_CSS_RX_IRQ_INFO_ENTER_SLEEP_MODE = 1U << 1, /** entering sleep mode */ - IA_CSS_RX_IRQ_INFO_EXIT_SLEEP_MODE = 1U << 2, /** exited sleep mode */ - IA_CSS_RX_IRQ_INFO_ECC_CORRECTED = 1U << 3, /** ECC corrected */ - IA_CSS_RX_IRQ_INFO_ERR_SOT = 1U << 4, + IA_CSS_RX_IRQ_INFO_BUFFER_OVERRUN = BIT(0), /** buffer overrun */ + IA_CSS_RX_IRQ_INFO_ENTER_SLEEP_MODE = BIT(1), /** entering sleep mode */ + IA_CSS_RX_IRQ_INFO_EXIT_SLEEP_MODE = BIT(2), /** exited sleep mode */ + IA_CSS_RX_IRQ_INFO_ECC_CORRECTED = BIT(3), /** ECC corrected */ + IA_CSS_RX_IRQ_INFO_ERR_SOT = BIT(4), /** Start of transmission */ - IA_CSS_RX_IRQ_INFO_ERR_SOT_SYNC = 1U << 5, /** SOT sync (??) */ - IA_CSS_RX_IRQ_INFO_ERR_CONTROL = 1U << 6, /** Control (??) */ - IA_CSS_RX_IRQ_INFO_ERR_ECC_DOUBLE = 1U << 7, /** Double ECC */ - IA_CSS_RX_IRQ_INFO_ERR_CRC = 1U << 8, /** CRC error */ - IA_CSS_RX_IRQ_INFO_ERR_UNKNOWN_ID = 1U << 9, /** Unknown ID */ - IA_CSS_RX_IRQ_INFO_ERR_FRAME_SYNC = 1U << 10,/** Frame sync error */ - IA_CSS_RX_IRQ_INFO_ERR_FRAME_DATA = 1U << 11,/** Frame data error */ - IA_CSS_RX_IRQ_INFO_ERR_DATA_TIMEOUT = 1U << 12,/** Timeout occurred */ - IA_CSS_RX_IRQ_INFO_ERR_UNKNOWN_ESC = 1U << 13,/** Unknown escape seq. */ - IA_CSS_RX_IRQ_INFO_ERR_LINE_SYNC = 1U << 14,/** Line Sync error */ - IA_CSS_RX_IRQ_INFO_INIT_TIMEOUT = 1U << 15, + IA_CSS_RX_IRQ_INFO_ERR_SOT_SYNC = BIT(5), /** SOT sync (??) */ + IA_CSS_RX_IRQ_INFO_ERR_CONTROL = BIT(6), /** Control (??) */ + IA_CSS_RX_IRQ_INFO_ERR_ECC_DOUBLE = BIT(7), /** Double ECC */ + IA_CSS_RX_IRQ_INFO_ERR_CRC = BIT(8), /** CRC error */ + IA_CSS_RX_IRQ_INFO_ERR_UNKNOWN_ID = BIT(9), /** Unknown ID */ + IA_CSS_RX_IRQ_INFO_ERR_FRAME_SYNC = BIT(10), /** Frame sync error */ + IA_CSS_RX_IRQ_INFO_ERR_FRAME_DATA = BIT(11), /** Frame data error */ + IA_CSS_RX_IRQ_INFO_ERR_DATA_TIMEOUT = BIT(12), /** Timeout occurred */ + IA_CSS_RX_IRQ_INFO_ERR_UNKNOWN_ESC = BIT(13), /** Unknown escape seq. */ + IA_CSS_RX_IRQ_INFO_ERR_LINE_SYNC = BIT(14), /** Line Sync error */ + IA_CSS_RX_IRQ_INFO_INIT_TIMEOUT = BIT(15), }; /* Interrupt info structure. This structure contains information about an diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.c index 562662ab8a44..a70bce1179da 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.c @@ -44,7 +44,7 @@ ia_css_macc1_5_vmem_encode( unsigned int size) { unsigned int i, j, k, idx; - unsigned int idx_map[] = { + static const unsigned int idx_map[] = { 0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8 }; diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h index e37ef4232c55..fff89e9b4b01 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h +++ b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h @@ -20,6 +20,7 @@ #include <type_support.h> #include <linux/stdarg.h> +#include <linux/bits.h> #include "ia_css_types.h" #include "ia_css_binary.h" #include "ia_css_frame_public.h" @@ -53,21 +54,21 @@ extern int dbg_level; * Values can be combined to dump a combination of sets. */ enum ia_css_debug_enable_param_dump { - IA_CSS_DEBUG_DUMP_FPN = 1 << 0, /** FPN table */ - IA_CSS_DEBUG_DUMP_OB = 1 << 1, /** OB table */ - IA_CSS_DEBUG_DUMP_SC = 1 << 2, /** Shading table */ - IA_CSS_DEBUG_DUMP_WB = 1 << 3, /** White balance */ - IA_CSS_DEBUG_DUMP_DP = 1 << 4, /** Defect Pixel */ - IA_CSS_DEBUG_DUMP_BNR = 1 << 5, /** Bayer Noise Reductions */ - IA_CSS_DEBUG_DUMP_S3A = 1 << 6, /** 3A Statistics */ - IA_CSS_DEBUG_DUMP_DE = 1 << 7, /** De Mosaicing */ - IA_CSS_DEBUG_DUMP_YNR = 1 << 8, /** Luma Noise Reduction */ - IA_CSS_DEBUG_DUMP_CSC = 1 << 9, /** Color Space Conversion */ - IA_CSS_DEBUG_DUMP_GC = 1 << 10, /** Gamma Correction */ - IA_CSS_DEBUG_DUMP_TNR = 1 << 11, /** Temporal Noise Reduction */ - IA_CSS_DEBUG_DUMP_ANR = 1 << 12, /** Advanced Noise Reduction */ - IA_CSS_DEBUG_DUMP_CE = 1 << 13, /** Chroma Enhancement */ - IA_CSS_DEBUG_DUMP_ALL = 1 << 14 /** Dump all device parameters */ + IA_CSS_DEBUG_DUMP_FPN = BIT(0), /** FPN table */ + IA_CSS_DEBUG_DUMP_OB = BIT(1), /** OB table */ + IA_CSS_DEBUG_DUMP_SC = BIT(2), /** Shading table */ + IA_CSS_DEBUG_DUMP_WB = BIT(3), /** White balance */ + IA_CSS_DEBUG_DUMP_DP = BIT(4), /** Defect Pixel */ + IA_CSS_DEBUG_DUMP_BNR = BIT(5), /** Bayer Noise Reductions */ + IA_CSS_DEBUG_DUMP_S3A = BIT(6), /** 3A Statistics */ + IA_CSS_DEBUG_DUMP_DE = BIT(7), /** De Mosaicing */ + IA_CSS_DEBUG_DUMP_YNR = BIT(8), /** Luma Noise Reduction */ + IA_CSS_DEBUG_DUMP_CSC = BIT(9), /** Color Space Conversion */ + IA_CSS_DEBUG_DUMP_GC = BIT(10), /** Gamma Correction */ + IA_CSS_DEBUG_DUMP_TNR = BIT(11), /** Temporal Noise Reduction */ + IA_CSS_DEBUG_DUMP_ANR = BIT(12), /** Advanced Noise Reduction */ + IA_CSS_DEBUG_DUMP_CE = BIT(13), /** Chroma Enhancement */ + IA_CSS_DEBUG_DUMP_ALL = BIT(14), /** Dump all device parameters */ }; #define IA_CSS_ERROR(fmt, ...) \ diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index 94149647b98b..dd688f8ab649 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -56,7 +56,11 @@ static struct firmware_header *firmware_header; * which will be replaced with the actual RELEASE_VERSION * during package generation. Please do not modify */ +#ifdef ISP2401 static const char *release_version = STR(irci_stable_candrpv_0415_20150521_0458); +#else +static const char *release_version = STR(irci_stable_candrpv_0415_20150423_1753); +#endif #define MAX_FW_REL_VER_NAME 300 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---"; diff --git a/drivers/staging/media/hantro/Kconfig b/drivers/staging/media/hantro/Kconfig index 3c5d833322c8..0172a6822ec2 100644 --- a/drivers/staging/media/hantro/Kconfig +++ b/drivers/staging/media/hantro/Kconfig @@ -2,7 +2,7 @@ config VIDEO_HANTRO tristate "Hantro VPU driver" depends on ARCH_MXC || ARCH_ROCKCHIP || ARCH_AT91 || ARCH_SUNXI || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/staging/media/hantro/TODO b/drivers/staging/media/hantro/TODO index fa0c94057007..1d7fed936019 100644 --- a/drivers/staging/media/hantro/TODO +++ b/drivers/staging/media/hantro/TODO @@ -4,10 +4,3 @@ the uABI, it will be required to have the driver in staging. For this reason, we are keeping this driver in staging for now. - -* Add support for the S_SELECTION API. - See the comment for VEPU_REG_ENC_OVER_FILL_STRM_OFFSET. - -* Instead of having a DMA bounce buffer, it could be possible to use a - normal buffer and memmove() the payload to make space for the header. - This might need to use extra JPEG markers for padding reasons. diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 06d0f3597694..357f83b86809 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -259,7 +259,6 @@ struct hantro_ctx { /* Specific for particular codec modes. */ union { struct hantro_h264_dec_hw_ctx h264_dec; - struct hantro_jpeg_enc_hw_ctx jpeg_enc; struct hantro_mpeg2_dec_hw_ctx mpeg2_dec; struct hantro_vp8_dec_hw_ctx vp8_dec; struct hantro_hevc_dec_hw_ctx hevc_dec; diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 6a51f39dde56..dc768884cb79 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -219,21 +219,15 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) if (ret) return ret; + dst_vq->bidirectional = true; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; /* - * When encoding, the CAPTURE queue doesn't need dma memory, - * as the CPU needs to create the JPEG frames, from the - * hardware-produced JPEG payload. - * - * For the DMA destination buffer, we use a bounce buffer. + * The Kernel needs access to the JPEG destination buffer for the + * JPEG encoder to fill in the JPEG headers. */ - if (ctx->is_encoder) { - dst_vq->mem_ops = &vb2_vmalloc_memops; - } else { - dst_vq->bidirectional = true; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | - DMA_ATTR_NO_KERNEL_MAPPING; - } + if (!ctx->is_encoder) + dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; @@ -332,6 +326,11 @@ static const struct v4l2_ctrl_ops hantro_hevc_ctrl_ops = { .s_ctrl = hantro_hevc_s_ctrl, }; +#define HANTRO_JPEG_ACTIVE_MARKERS (V4L2_JPEG_ACTIVE_MARKER_APP0 | \ + V4L2_JPEG_ACTIVE_MARKER_COM | \ + V4L2_JPEG_ACTIVE_MARKER_DQT | \ + V4L2_JPEG_ACTIVE_MARKER_DHT) + static const struct hantro_ctrl controls[] = { { .codec = HANTRO_JPEG_ENCODER, @@ -344,6 +343,22 @@ static const struct hantro_ctrl controls[] = { .ops = &hantro_jpeg_ctrl_ops, }, }, { + .codec = HANTRO_JPEG_ENCODER, + .cfg = { + .id = V4L2_CID_JPEG_ACTIVE_MARKER, + .max = HANTRO_JPEG_ACTIVE_MARKERS, + .def = HANTRO_JPEG_ACTIVE_MARKERS, + /* + * Changing the set of active markers/segments also + * messes up the alignment of the JPEG header, which + * is needed to allow the hardware to write directly + * to the output buffer. Implementing this introduces + * a lot of complexity for little gain, as the markers + * enabled is already the minimum required set. + */ + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, + }, { .codec = HANTRO_MPEG2_DECODER, .cfg = { .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, @@ -615,7 +630,9 @@ static const struct of_device_id of_hantro_match[] = { { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, #endif #ifdef CONFIG_VIDEO_HANTRO_IMX8M + { .compatible = "nxp,imx8mm-vpu-g1", .data = &imx8mm_vpu_g1_variant, }, { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, + { .compatible = "nxp,imx8mq-vpu-g1", .data = &imx8mq_vpu_g1_variant }, { .compatible = "nxp,imx8mq-vpu-g2", .data = &imx8mq_vpu_g2_variant }, #endif #ifdef CONFIG_VIDEO_HANTRO_SAMA5D4 @@ -890,6 +907,15 @@ static int hantro_probe(struct platform_device *pdev) match = of_match_node(of_hantro_match, pdev->dev.of_node); vpu->variant = match->data; + /* + * Support for nxp,imx8mq-vpu is kept for backwards compatibility + * but it's deprecated. Please update your DTS file to use + * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead. + */ + if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu")) + dev_warn(&pdev->dev, "%s compatible is deprecated\n", + match->compatible); + INIT_DELAYED_WORK(&vpu->watchdog_work, hantro_watchdog); vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks, diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c index 99d8ea7543da..c524af41baf5 100644 --- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c +++ b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c @@ -255,24 +255,11 @@ static void set_params(struct hantro_ctx *ctx) hantro_reg_write(vpu, &g2_apf_threshold, 8); } -static int find_ref_pic_index(const struct v4l2_hevc_dpb_entry *dpb, int pic_order_cnt) -{ - int i; - - for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - if (dpb[i].pic_order_cnt[0] == pic_order_cnt) - return i; - } - - return 0x0; -} - static void set_ref_pic_list(struct hantro_ctx *ctx) { const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; struct hantro_dev *vpu = ctx->dev; const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; u32 list0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; u32 list1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; static const struct hantro_reg ref_pic_regs0[] = { @@ -316,11 +303,11 @@ static void set_ref_pic_list(struct hantro_ctx *ctx) /* List 0 contains: short term before, short term after and long term */ j = 0; for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list0); i++) - list0[j++] = find_ref_pic_index(dpb, decode_params->poc_st_curr_before[i]); + list0[j++] = decode_params->poc_st_curr_before[i]; for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list0); i++) - list0[j++] = find_ref_pic_index(dpb, decode_params->poc_st_curr_after[i]); + list0[j++] = decode_params->poc_st_curr_after[i]; for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list0); i++) - list0[j++] = find_ref_pic_index(dpb, decode_params->poc_lt_curr[i]); + list0[j++] = decode_params->poc_lt_curr[i]; /* Fill the list, copying over and over */ i = 0; @@ -329,11 +316,11 @@ static void set_ref_pic_list(struct hantro_ctx *ctx) j = 0; for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list1); i++) - list1[j++] = find_ref_pic_index(dpb, decode_params->poc_st_curr_after[i]); + list1[j++] = decode_params->poc_st_curr_after[i]; for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list1); i++) - list1[j++] = find_ref_pic_index(dpb, decode_params->poc_st_curr_before[i]); + list1[j++] = decode_params->poc_st_curr_before[i]; for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list1); i++) - list1[j++] = find_ref_pic_index(dpb, decode_params->poc_lt_curr[i]); + list1[j++] = decode_params->poc_lt_curr[i]; i = 0; while (j < ARRAY_SIZE(list1)) @@ -433,7 +420,7 @@ static int set_ref(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; - if (dpb[i].rps == V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR) + if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index 1450013d3685..12d69503d6ba 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -18,29 +18,44 @@ static void hantro_h1_set_src_img_ctrl(struct hantro_dev *vpu, struct hantro_ctx *ctx) { - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; + u32 overfill_r, overfill_b; u32 reg; - reg = H1_REG_IN_IMG_CTRL_ROW_LEN(pix_fmt->width) - | H1_REG_IN_IMG_CTRL_OVRFLR_D4(0) - | H1_REG_IN_IMG_CTRL_OVRFLB_D4(0) + /* + * The format width and height are already macroblock aligned + * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination + * format width and height can be further modified by + * .vidioc_s_selection(), and the width is 4-aligned. + */ + overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; + overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; + + reg = H1_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width) + | H1_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) + | H1_REG_IN_IMG_CTRL_OVRFLB(overfill_b) | H1_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); vepu_write_relaxed(vpu, reg, H1_REG_IN_IMG_CTRL); } static void hantro_h1_jpeg_enc_set_buffers(struct hantro_dev *vpu, struct hantro_ctx *ctx, - struct vb2_buffer *src_buf) + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) { struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; dma_addr_t src[3]; + u32 size_left; + + size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; + if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) + size_left = 0; WARN_ON(pix_fmt->num_planes > 3); - vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.dma, + vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + ctx->vpu_dst_fmt->header_size, H1_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.size, - H1_REG_STR_BUF_LIMIT); + vepu_write_relaxed(vpu, size_left, H1_REG_STR_BUF_LIMIT); if (pix_fmt->num_planes == 1) { src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); @@ -112,7 +127,8 @@ int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) H1_REG_ENC_CTRL); hantro_h1_set_src_img_ctrl(vpu, ctx); - hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); + hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf); hantro_h1_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, jpeg_ctx.hw_chroma_qtable); @@ -145,13 +161,6 @@ void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx) u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - /* - * TODO: Rework the JPEG encoder to eliminate the need - * for a bounce buffer. - */ - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->jpeg_enc.bounce_buffer.cpu, bytesused); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, ctx->vpu_dst_fmt->header_size + bytesused); } diff --git a/drivers/staging/media/hantro/hantro_h1_regs.h b/drivers/staging/media/hantro/hantro_h1_regs.h index d6e9825bb5c7..30e7e7b920b5 100644 --- a/drivers/staging/media/hantro/hantro_h1_regs.h +++ b/drivers/staging/media/hantro/hantro_h1_regs.h @@ -47,7 +47,7 @@ #define H1_REG_IN_IMG_CTRL 0x03c #define H1_REG_IN_IMG_CTRL_ROW_LEN(x) ((x) << 12) #define H1_REG_IN_IMG_CTRL_OVRFLR_D4(x) ((x) << 10) -#define H1_REG_IN_IMG_CTRL_OVRFLB_D4(x) ((x) << 6) +#define H1_REG_IN_IMG_CTRL_OVRFLB(x) ((x) << 6) #define H1_REG_IN_IMG_CTRL_FMT(x) ((x) << 2) #define H1_REG_ENC_CTRL0 0x040 #define H1_REG_ENC_CTRL0_INIT_QP(x) ((x) << 26) diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 4a19ae8940b9..ed018e293ba0 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -43,15 +43,6 @@ struct hantro_aux_buf { unsigned long attrs; }; -/** - * struct hantro_jpeg_enc_hw_ctx - * - * @bounce_buffer: Bounce buffer - */ -struct hantro_jpeg_enc_hw_ctx { - struct hantro_aux_buf bounce_buffer; -}; - /* Max. number of entries in the DPB (HW limitation). */ #define HANTRO_H264_DPB_SIZE 16 @@ -299,6 +290,8 @@ enum hantro_enc_fmt { ROCKCHIP_VPU_ENC_FMT_UYVY422 = 3, }; +extern const struct hantro_variant imx8mm_vpu_g1_variant; +extern const struct hantro_variant imx8mq_vpu_g1_variant; extern const struct hantro_variant imx8mq_vpu_g2_variant; extern const struct hantro_variant imx8mq_vpu_variant; extern const struct hantro_variant px30_vpu_variant; @@ -327,8 +320,6 @@ void hantro_g1_reset(struct hantro_ctx *ctx); int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx); -int hantro_jpeg_enc_init(struct hantro_ctx *ctx); -void hantro_jpeg_enc_exit(struct hantro_ctx *ctx); void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx); void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx); diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c index df62fbdff7c9..d07b1b449b61 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ b/drivers/staging/media/hantro/hantro_jpeg.c @@ -6,21 +6,23 @@ * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) * Copyright (C) 2014 Philipp Zabel, Pengutronix */ -#include <linux/dma-mapping.h> + +#include <linux/align.h> +#include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/string.h> #include "hantro_jpeg.h" #include "hantro.h" -#define LUMA_QUANT_OFF 7 -#define CHROMA_QUANT_OFF 72 -#define HEIGHT_OFF 141 -#define WIDTH_OFF 143 +#define LUMA_QUANT_OFF 25 +#define CHROMA_QUANT_OFF 90 +#define HEIGHT_OFF 159 +#define WIDTH_OFF 161 -#define HUFF_LUMA_DC_OFF 160 -#define HUFF_LUMA_AC_OFF 193 -#define HUFF_CHROMA_DC_OFF 376 -#define HUFF_CHROMA_AC_OFF 409 +#define HUFF_LUMA_DC_OFF 178 +#define HUFF_LUMA_AC_OFF 211 +#define HUFF_CHROMA_DC_OFF 394 +#define HUFF_CHROMA_AC_OFF 427 /* Default tables from JPEG ITU-T.81 * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 @@ -47,7 +49,7 @@ static const unsigned char chroma_q_table[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 }; -static const unsigned char zigzag[64] = { +static const unsigned char zigzag[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, @@ -58,7 +60,7 @@ static const unsigned char zigzag[64] = { 53, 60, 61, 54, 47, 55, 62, 63 }; -static const u32 hw_reorder[64] = { +static const u32 hw_reorder[] = { 0, 8, 16, 24, 1, 9, 17, 25, 32, 40, 48, 56, 33, 41, 49, 57, 2, 10, 18, 26, 3, 11, 19, 27, @@ -140,10 +142,15 @@ static const unsigned char chroma_ac_table[] = { * and we'll use fixed offsets to change the width, height * quantization tables, etc. */ -static const unsigned char hantro_jpeg_header[JPEG_HEADER_SIZE] = { +static const unsigned char hantro_jpeg_header[] = { /* SOI */ 0xff, 0xd8, + /* JFIF-APP0 */ + 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, + /* DQT */ 0xff, 0xdb, 0x00, 0x84, @@ -242,11 +249,29 @@ static const unsigned char hantro_jpeg_header[JPEG_HEADER_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* COM */ + 0xff, 0xfe, 0x00, 0x03, 0x00, + /* SOS */ 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, }; +/* + * JPEG_HEADER_SIZE is used in other parts of the driver in lieu of + * "sizeof(hantro_jpeg_header)". The two must be equal. + */ +static_assert(sizeof(hantro_jpeg_header) == JPEG_HEADER_SIZE); + +/* + * hantro_jpeg_header is padded with a COM segment, so that the payload + * of the SOS segment (the entropy-encoded image scan), which should + * trail the whole header, is 8-byte aligned for the hardware to write + * to directly. + */ +static_assert(IS_ALIGNED(sizeof(hantro_jpeg_header), 8), + "Hantro JPEG header size needs to be 8-byte aligned."); + static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) { unsigned int temp; @@ -267,7 +292,10 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, { int i; - for (i = 0; i < 64; i++) { + BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); + + for (i = 0; i < JPEG_QUANT_SIZE; i++) { file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } @@ -286,6 +314,11 @@ static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) else scale = 200 - 2 * ctx->quality; + BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); + jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, ctx->hw_luma_qtable, luma_q_table, scale); jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, @@ -313,30 +346,3 @@ void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) jpeg_set_quality(ctx); } - -int hantro_jpeg_enc_init(struct hantro_ctx *ctx) -{ - ctx->jpeg_enc.bounce_buffer.size = - ctx->dst_fmt.plane_fmt[0].sizeimage - - ctx->vpu_dst_fmt->header_size; - - ctx->jpeg_enc.bounce_buffer.cpu = - dma_alloc_attrs(ctx->dev->dev, - ctx->jpeg_enc.bounce_buffer.size, - &ctx->jpeg_enc.bounce_buffer.dma, - GFP_KERNEL, - DMA_ATTR_ALLOC_SINGLE_PAGES); - if (!ctx->jpeg_enc.bounce_buffer.cpu) - return -ENOMEM; - - return 0; -} - -void hantro_jpeg_enc_exit(struct hantro_ctx *ctx) -{ - dma_free_attrs(ctx->dev->dev, - ctx->jpeg_enc.bounce_buffer.size, - ctx->jpeg_enc.bounce_buffer.cpu, - ctx->jpeg_enc.bounce_buffer.dma, - DMA_ATTR_ALLOC_SINGLE_PAGES); -} diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h index 035ab25b803f..0b49d0b82caa 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ b/drivers/staging/media/hantro/hantro_jpeg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -#define JPEG_HEADER_SIZE 601 +#define JPEG_HEADER_SIZE 624 #define JPEG_QUANT_SIZE 64 struct hantro_jpeg_ctx { diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index e595905b3bd7..67148ba346f5 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -554,6 +554,80 @@ vidioc_s_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) return hantro_set_fmt_cap(fh_to_ctx(priv), &f->fmt.pix_mp); } +static int vidioc_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + + /* Crop only supported on source. */ + if (!ctx->is_encoder || + sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = ctx->src_fmt.width; + sel->r.height = ctx->src_fmt.height; + break; + case V4L2_SEL_TGT_CROP: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = ctx->dst_fmt.width; + sel->r.height = ctx->dst_fmt.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + struct v4l2_rect *rect = &sel->r; + struct vb2_queue *vq; + + /* Crop only supported on source. */ + if (!ctx->is_encoder || + sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Change not allowed if the queue is streaming. */ + vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx); + if (vb2_is_streaming(vq)) + return -EBUSY; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * We do not support offsets, and we can crop only inside + * right-most or bottom-most macroblocks. + */ + if (rect->left != 0 || rect->top != 0 || + round_up(rect->width, MB_DIM) != ctx->src_fmt.width || + round_up(rect->height, MB_DIM) != ctx->src_fmt.height) { + /* Default to full frame for incorrect settings. */ + rect->left = 0; + rect->top = 0; + rect->width = ctx->src_fmt.width; + rect->height = ctx->src_fmt.height; + } else { + /* We support widths aligned to 4 pixels and arbitrary heights. */ + rect->width = round_up(rect->width, 4); + } + + ctx->dst_fmt.width = rect->width; + ctx->dst_fmt.height = rect->height; + + return 0; +} + const struct v4l2_ioctl_ops hantro_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_framesizes = vidioc_enum_framesizes, @@ -580,6 +654,9 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = { .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, }; static int diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c index f5991b8e553a..9802508bade2 100644 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -205,13 +205,6 @@ static void imx8m_vpu_g1_reset(struct hantro_ctx *ctx) imx8m_soft_reset(vpu, RESET_G1); } -static void imx8m_vpu_g2_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - imx8m_soft_reset(vpu, RESET_G2); -} - /* * Supported codec ops. */ @@ -237,17 +230,33 @@ static const struct hantro_codec_ops imx8mq_vpu_codec_ops[] = { }, }; +static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = { [HANTRO_MODE_HEVC_DEC] = { .run = hantro_g2_hevc_dec_run, - .reset = imx8m_vpu_g2_reset, .init = hantro_hevc_dec_init, .exit = hantro_hevc_dec_exit, }, [HANTRO_MODE_VP9_DEC] = { .run = hantro_g2_vp9_dec_run, .done = hantro_g2_vp9_dec_done, - .reset = imx8m_vpu_g2_reset, .init = hantro_vp9_dec_init, .exit = hantro_vp9_dec_exit, }, @@ -267,6 +276,8 @@ static const struct hantro_irq imx8mq_g2_irqs[] = { static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; +static const char * const imx8mq_g1_clk_names[] = { "g1" }; +static const char * const imx8mq_g2_clk_names[] = { "g2" }; const struct hantro_variant imx8mq_vpu_variant = { .dec_fmts = imx8m_vpu_dec_fmts, @@ -287,6 +298,21 @@ const struct hantro_variant imx8mq_vpu_variant = { .num_regs = ARRAY_SIZE(imx8mq_reg_names) }; +const struct hantro_variant imx8mq_vpu_g1_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .postproc_fmts = imx8m_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_g1_codec_ops, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_g1_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), +}; + const struct hantro_variant imx8mq_vpu_g2_variant = { .dec_offset = 0x0, .dec_fmts = imx8m_vpu_g2_dec_fmts, @@ -296,10 +322,20 @@ const struct hantro_variant imx8mq_vpu_g2_variant = { .postproc_ops = &hantro_g2_postproc_ops, .codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER, .codec_ops = imx8mq_vpu_g2_codec_ops, - .init = imx8mq_vpu_hw_init, - .runtime_resume = imx8mq_runtime_resume, .irqs = imx8mq_g2_irqs, .num_irqs = ARRAY_SIZE(imx8mq_g2_irqs), - .clk_names = imx8mq_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_clk_names), + .clk_names = imx8mq_g2_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names), +}; + +const struct hantro_variant imx8mm_vpu_g1_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_g1_codec_ops, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_g1_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), }; diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c index 4df16f59fb97..8395c4d48dd0 100644 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c @@ -35,18 +35,23 @@ static void rockchip_vpu2_set_src_img_ctrl(struct hantro_dev *vpu, struct hantro_ctx *ctx) { - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; + u32 overfill_r, overfill_b; u32 reg; /* - * The pix fmt width/height are already macroblock aligned - * by .vidioc_s_fmt_vid_cap_mplane() callback + * The format width and height are already macroblock aligned + * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination + * format width and height can be further modified by + * .vidioc_s_selection(), and the width is 4-aligned. */ - reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(pix_fmt->width); + overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; + overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; + + reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width); vepu_write_relaxed(vpu, reg, VEPU_REG_INPUT_LUMA_INFO); - reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(0) | - VEPU_REG_IN_IMG_CTRL_OVRFLB(0); + reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) | + VEPU_REG_IN_IMG_CTRL_OVRFLB(overfill_b); /* * This register controls the input crop, as the offset * from the right/bottom within the last macroblock. The offset from the @@ -61,17 +66,23 @@ static void rockchip_vpu2_set_src_img_ctrl(struct hantro_dev *vpu, static void rockchip_vpu2_jpeg_enc_set_buffers(struct hantro_dev *vpu, struct hantro_ctx *ctx, - struct vb2_buffer *src_buf) + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) { struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; dma_addr_t src[3]; + u32 size_left; + + size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; + if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) + size_left = 0; WARN_ON(pix_fmt->num_planes > 3); - vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.dma, + vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + ctx->vpu_dst_fmt->header_size, VEPU_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.size, - VEPU_REG_STR_BUF_LIMIT); + vepu_write_relaxed(vpu, size_left, VEPU_REG_STR_BUF_LIMIT); if (pix_fmt->num_planes == 1) { src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); @@ -132,6 +143,9 @@ int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx) memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + if (!jpeg_ctx.buffer) + return -ENOMEM; + jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -142,7 +156,8 @@ int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx) VEPU_REG_ENCODE_START); rockchip_vpu2_set_src_img_ctrl(vpu, ctx); - rockchip_vpu2_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); + rockchip_vpu2_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf); rockchip_vpu2_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, jpeg_ctx.hw_chroma_qtable); @@ -177,13 +192,6 @@ void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx) u32 bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - /* - * TODO: Rework the JPEG encoder to eliminate the need - * for a bounce buffer. - */ - memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->jpeg_enc.bounce_buffer.cpu, bytesused); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, ctx->vpu_dst_fmt->header_size + bytesused); } diff --git a/drivers/staging/media/hantro/rockchip_vpu_hw.c b/drivers/staging/media/hantro/rockchip_vpu_hw.c index c203b606e6e7..163cf92eafca 100644 --- a/drivers/staging/media/hantro/rockchip_vpu_hw.c +++ b/drivers/staging/media/hantro/rockchip_vpu_hw.c @@ -343,9 +343,7 @@ static const struct hantro_codec_ops rk3066_vpu_codec_ops[] = { [HANTRO_MODE_JPEG_ENC] = { .run = hantro_h1_jpeg_enc_run, .reset = rockchip_vpu1_enc_reset, - .init = hantro_jpeg_enc_init, .done = hantro_h1_jpeg_enc_done, - .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { .run = hantro_g1_h264_dec_run, @@ -371,9 +369,7 @@ static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { [HANTRO_MODE_JPEG_ENC] = { .run = hantro_h1_jpeg_enc_run, .reset = rockchip_vpu1_enc_reset, - .init = hantro_jpeg_enc_init, .done = hantro_h1_jpeg_enc_done, - .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { .run = hantro_g1_h264_dec_run, @@ -399,9 +395,7 @@ static const struct hantro_codec_ops rk3399_vpu_codec_ops[] = { [HANTRO_MODE_JPEG_ENC] = { .run = rockchip_vpu2_jpeg_enc_run, .reset = rockchip_vpu2_enc_reset, - .init = hantro_jpeg_enc_init, .done = rockchip_vpu2_jpeg_enc_done, - .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { .run = rockchip_vpu2_h264_dec_run, diff --git a/drivers/staging/media/hantro/sunxi_vpu_hw.c b/drivers/staging/media/hantro/sunxi_vpu_hw.c index 90633406c4eb..c0edd5856a0c 100644 --- a/drivers/staging/media/hantro/sunxi_vpu_hw.c +++ b/drivers/staging/media/hantro/sunxi_vpu_hw.c @@ -29,10 +29,10 @@ static const struct hantro_fmt sunxi_vpu_dec_fmts[] = { .frmsize = { .min_width = 48, .max_width = 3840, - .step_width = MB_DIM, + .step_width = 32, .min_height = 48, .max_height = 2160, - .step_height = MB_DIM, + .step_height = 32, }, }, }; diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig index c3bf433ba3e3..0bacac302d7e 100644 --- a/drivers/staging/media/imx/Kconfig +++ b/drivers/staging/media/imx/Kconfig @@ -4,7 +4,7 @@ config VIDEO_IMX_MEDIA depends on ARCH_MXC || COMPILE_TEST depends on HAS_DMA depends on VIDEO_DEV - depends on VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select V4L2_FWNODE select V4L2_MEM2MEM_DEV diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 19c2fc54d424..d82be898145b 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -15,5 +15,4 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o -obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx8mq-mipi-csi2.o diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index 06c94f20ecf8..5d3a337c8702 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -27,3 +27,28 @@ - i.MX7: all of the above, since it uses the imx media core - i.MX7: use Frame Interval Monitor + +- imx7-media-csi: Restrict the supported formats list to the SoC version. + + The imx7 CSI bridge can be configured to sample pixel components from the Rx + queue in single (8bpp) or double (16bpp) component modes. Image format + variants with different sample sizes (ie YUYV_2X8 vs YUYV_1X16) determine the + pixel components sampling size per each clock cycle and their packing mode + (see imx7_csi_configure() for details). + + As the imx7 CSI bridge can be interfaced with different IP blocks depending on + the SoC model it is integrated on, the Rx queue sampling size should match + the size of the samples transferred by the transmitting IP block. + + To avoid mis-configurations of the capture pipeline, the enumeration of the + supported formats should be restricted to match the pixel source transmitting + mode. + + Example: i.MX8MM SoC integrates the CSI bridge with the Samsung CSIS CSI-2 + receiver which operates in dual pixel sampling mode. The CSI bridge should + only expose the 1X16 formats variant which instructs it to operate in dual + pixel sampling mode. When the CSI bridge is instead integrated on an i.MX7, + which supports both serial and parallel input, it should expose both variants. + + This currently only applies to YUYV formats, but other formats might need + to be handled in the same way. diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index bd7f156f2d52..b2b1f4dd41d7 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -718,9 +718,10 @@ static int csi_setup(struct csi_priv *priv) /* compose mbus_config from the upstream endpoint */ mbus_cfg.type = priv->upstream_ep.bus_type; - mbus_cfg.flags = is_parallel_bus(&priv->upstream_ep) ? - priv->upstream_ep.bus.parallel.flags : - priv->upstream_ep.bus.mipi_csi2.flags; + if (is_parallel_bus(&priv->upstream_ep)) + mbus_cfg.bus.parallel = priv->upstream_ep.bus.parallel; + else + mbus_cfg.bus.mipi_csi2 = priv->upstream_ep.bus.mipi_csi2; if_fmt = *infmt; crop = priv->crop; diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 558b256ac935..c4cb558a85c6 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -303,7 +303,6 @@ static void csi2ipu_gasket_init(struct csi2_dev *csi2) static int csi2_get_active_lanes(struct csi2_dev *csi2, unsigned int *lanes) { struct v4l2_mbus_config mbus_config = { 0 }; - unsigned int num_lanes = UINT_MAX; int ret; *lanes = csi2->data_lanes; @@ -326,32 +325,14 @@ static int csi2_get_active_lanes(struct csi2_dev *csi2, unsigned int *lanes) return -EINVAL; } - switch (mbus_config.flags & V4L2_MBUS_CSI2_LANES) { - case V4L2_MBUS_CSI2_1_LANE: - num_lanes = 1; - break; - case V4L2_MBUS_CSI2_2_LANE: - num_lanes = 2; - break; - case V4L2_MBUS_CSI2_3_LANE: - num_lanes = 3; - break; - case V4L2_MBUS_CSI2_4_LANE: - num_lanes = 4; - break; - default: - num_lanes = csi2->data_lanes; - break; - } - - if (num_lanes > csi2->data_lanes) { + if (mbus_config.bus.mipi_csi2.num_data_lanes > csi2->data_lanes) { dev_err(csi2->dev, "Unsupported mbus config: too many data lanes %u\n", - num_lanes); + mbus_config.bus.mipi_csi2.num_data_lanes); return -EINVAL; } - *lanes = num_lanes; + *lanes = mbus_config.bus.mipi_csi2.num_data_lanes; return 0; } diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index 2288dadb2683..8467a1491048 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -122,6 +123,10 @@ #define BIT_DATA_FROM_MIPI BIT(22) #define BIT_MIPI_YU_SWAP BIT(21) #define BIT_MIPI_DOUBLE_CMPNT BIT(20) +#define BIT_MASK_OPTION_FIRST_FRAME (0 << 18) +#define BIT_MASK_OPTION_CSI_EN (1 << 18) +#define BIT_MASK_OPTION_SECOND_FRAME (2 << 18) +#define BIT_MASK_OPTION_ON_DATA (3 << 18) #define BIT_BASEADDR_CHG_ERR_EN BIT(9) #define BIT_BASEADDR_SWITCH_SEL BIT(5) #define BIT_BASEADDR_SWITCH_EN BIT(4) @@ -154,6 +159,11 @@ #define CSI_CSICR18 0x48 #define CSI_CSICR19 0x4c +enum imx_csi_model { + IMX7_CSI_IMX7 = 0, + IMX7_CSI_IMX8MQ, +}; + struct imx7_csi { struct device *dev; struct v4l2_subdev sd; @@ -189,6 +199,8 @@ struct imx7_csi { bool is_csi2; struct completion last_eof_completion; + + enum imx_csi_model model; }; static struct imx7_csi * @@ -486,16 +498,40 @@ static void imx7_csi_configure(struct imx7_csi *csi) cr3 |= BIT_TWO_8BIT_SENSOR; cr18 |= BIT_MIPI_DATA_FORMAT_RAW14; break; + /* - * CSI-2 sources are supposed to use the 1X16 formats, but not - * all of them comply. Support both variants. + * The CSI bridge has a 16-bit input bus. Depending on the + * connected source, data may be transmitted with 8 or 10 bits + * per clock sample (in bits [9:2] or [9:0] respectively) or + * with 16 bits per clock sample (in bits [15:0]). The data is + * then packed into a 32-bit FIFO (as shown in figure 13-11 of + * the i.MX8MM reference manual rev. 3). + * + * The data packing in a 32-bit FIFO input word is controlled by + * the CR3 TWO_8BIT_SENSOR field (also known as SENSOR_16BITS in + * the i.MX8MM reference manual). When set to 0, data packing + * groups four 8-bit input samples (bits [9:2]). When set to 1, + * data packing groups two 16-bit input samples (bits [15:0]). + * + * The register field CR18 MIPI_DOUBLE_CMPNT also needs to be + * configured according to the input format for YUV 4:2:2 data. + * The field controls the gasket between the CSI-2 receiver and + * the CSI bridge. On i.MX7 and i.MX8MM, the field must be set + * to 1 when the CSIS outputs 16-bit samples. On i.MX8MQ, the + * gasket ignores the MIPI_DOUBLE_CMPNT bit and YUV 4:2:2 always + * uses 16-bit samples. Setting MIPI_DOUBLE_CMPNT in that case + * has no effect, but doesn't cause any issue. */ case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV8_1X16: cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + cr3 |= BIT_TWO_8BIT_SENSOR; + cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B | + BIT_MIPI_DOUBLE_CMPNT; + break; } } @@ -537,6 +573,16 @@ static void imx7_csi_deinit(struct imx7_csi *csi, clk_disable_unprepare(csi->mclk); } +static void imx7_csi_baseaddr_switch_on_second_frame(struct imx7_csi *csi) +{ + u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); + + cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | + BIT_BASEADDR_CHG_ERR_EN; + cr18 |= BIT_MASK_OPTION_SECOND_FRAME; + imx7_csi_reg_write(csi, cr18, CSI_CSICR18); +} + static void imx7_csi_enable(struct imx7_csi *csi) { /* Clear the Rx FIFO and reflash the DMA controller. */ @@ -552,6 +598,9 @@ static void imx7_csi_enable(struct imx7_csi *csi) /* Enable the RxFIFO DMA and the CSI. */ imx7_csi_dmareq_rff_enable(csi); imx7_csi_hw_enable(csi); + + if (csi->model == IMX7_CSI_IMX8MQ) + imx7_csi_baseaddr_switch_on_second_frame(csi); } static void imx7_csi_disable(struct imx7_csi *csi) @@ -1155,6 +1204,8 @@ static int imx7_csi_probe(struct platform_device *pdev) if (IS_ERR(csi->regbase)) return PTR_ERR(csi->regbase); + csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev); + spin_lock_init(&csi->irqlock); mutex_init(&csi->lock); @@ -1249,8 +1300,9 @@ static int imx7_csi_remove(struct platform_device *pdev) } static const struct of_device_id imx7_csi_of_match[] = { - { .compatible = "fsl,imx7-csi" }, - { .compatible = "fsl,imx6ul-csi" }, + { .compatible = "fsl,imx8mq-csi", .data = (void *)IMX7_CSI_IMX8MQ }, + { .compatible = "fsl,imx7-csi", .data = (void *)IMX7_CSI_IMX7 }, + { .compatible = "fsl,imx6ul-csi", .data = (void *)IMX7_CSI_IMX7 }, { }, }; MODULE_DEVICE_TABLE(of, imx7_csi_of_match); diff --git a/drivers/staging/media/imx/imx8mq-mipi-csi2.c b/drivers/staging/media/imx/imx8mq-mipi-csi2.c index 7adbdd14daa9..83194328d010 100644 --- a/drivers/staging/media/imx/imx8mq-mipi-csi2.c +++ b/drivers/staging/media/imx/imx8mq-mipi-csi2.c @@ -117,7 +117,7 @@ struct csi_state { struct v4l2_async_notifier notifier; struct v4l2_subdev *src_sd; - struct v4l2_fwnode_bus_mipi_csi2 bus; + struct v4l2_mbus_config_mipi_csi2 bus; struct mutex lock; /* Protect csi2_fmt, format_mbus, state, hs_settle */ const struct csi2_pix_format *csi2_fmt; @@ -200,12 +200,13 @@ static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = { }, { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .width = 14, - }, { + }, /* YUV formats */ - .code = MEDIA_BUS_FMT_YUYV8_2X8, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, .width = 16, }, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, + .code = MEDIA_BUS_FMT_UYVY8_1X16, .width = 16, } }; @@ -398,9 +399,6 @@ static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable) struct csi_state *state = mipi_sd_to_csi2_state(sd); int ret = 0; - imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, - CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE); - if (enable) { ret = pm_runtime_resume_and_get(state->dev); if (ret < 0) @@ -696,11 +694,10 @@ err_parse: * Suspend/resume */ -static int imx8mq_mipi_csi_pm_suspend(struct device *dev, bool runtime) +static void imx8mq_mipi_csi_pm_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); - int ret = 0; mutex_lock(&state->lock); @@ -708,36 +705,19 @@ static int imx8mq_mipi_csi_pm_suspend(struct device *dev, bool runtime) imx8mq_mipi_csi_stop_stream(state); imx8mq_mipi_csi_clk_disable(state); state->state &= ~ST_POWERED; - if (!runtime) - state->state |= ST_SUSPENDED; } mutex_unlock(&state->lock); - - ret = icc_set_bw(state->icc_path, 0, 0); - if (ret) - dev_err(dev, "icc_set_bw failed with %d\n", ret); - - return ret ? -EAGAIN : 0; } -static int imx8mq_mipi_csi_pm_resume(struct device *dev, bool runtime) +static int imx8mq_mipi_csi_pm_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); int ret = 0; - ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); - if (ret) { - dev_err(dev, "icc_set_bw failed with %d\n", ret); - return ret; - } - mutex_lock(&state->lock); - if (!runtime && !(state->state & ST_SUSPENDED)) - goto unlock; - if (!(state->state & ST_POWERED)) { state->state |= ST_POWERED; ret = imx8mq_mipi_csi_clk_enable(state); @@ -758,22 +738,55 @@ unlock: static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) { - return imx8mq_mipi_csi_pm_suspend(dev, false); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + + imx8mq_mipi_csi_pm_suspend(dev); + + state->state |= ST_SUSPENDED; + + return 0; } static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) { - return imx8mq_mipi_csi_pm_resume(dev, false); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + + if (!(state->state & ST_SUSPENDED)) + return 0; + + return imx8mq_mipi_csi_pm_resume(dev); } static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) { - return imx8mq_mipi_csi_pm_suspend(dev, true); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret; + + imx8mq_mipi_csi_pm_suspend(dev); + + ret = icc_set_bw(state->icc_path, 0, 0); + if (ret) + dev_err(dev, "icc_set_bw failed with %d\n", ret); + + return ret; } static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) { - return imx8mq_mipi_csi_pm_resume(dev, true); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi_state *state = mipi_sd_to_csi2_state(sd); + int ret; + + ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); + if (ret) { + dev_err(dev, "icc_set_bw failed with %d\n", ret); + return ret; + } + + return imx8mq_mipi_csi_pm_resume(dev); } static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = { @@ -921,7 +934,7 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev) /* Enable runtime PM. */ pm_runtime_enable(dev); if (!pm_runtime_enabled(dev)) { - ret = imx8mq_mipi_csi_pm_resume(dev, true); + ret = imx8mq_mipi_csi_runtime_resume(dev); if (ret < 0) goto icc; } @@ -934,7 +947,7 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev) cleanup: pm_runtime_disable(&pdev->dev); - imx8mq_mipi_csi_pm_suspend(&pdev->dev, true); + imx8mq_mipi_csi_runtime_suspend(&pdev->dev); media_entity_cleanup(&state->sd.entity); v4l2_async_nf_unregister(&state->notifier); @@ -958,7 +971,7 @@ static int imx8mq_mipi_csi_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&state->sd); pm_runtime_disable(&pdev->dev); - imx8mq_mipi_csi_pm_suspend(&pdev->dev, true); + imx8mq_mipi_csi_runtime_suspend(&pdev->dev); media_entity_cleanup(&state->sd.entity); mutex_destroy(&state->lock); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/staging/media/ipu3/Kconfig b/drivers/staging/media/ipu3/Kconfig index 3e9640523e50..114a1d8e7cc8 100644 --- a/drivers/staging/media/ipu3/Kconfig +++ b/drivers/staging/media/ipu3/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_IPU3_IMGU tristate "Intel ipu3-imgu driver" - depends on PCI && VIDEO_V4L2 + depends on PCI && VIDEO_DEV depends on X86 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/staging/media/max96712/Kconfig b/drivers/staging/media/max96712/Kconfig index acde14fd5c4d..117fadf81bd0 100644 --- a/drivers/staging/media/max96712/Kconfig +++ b/drivers/staging/media/max96712/Kconfig @@ -3,7 +3,7 @@ config VIDEO_MAX96712 tristate "Maxim MAX96712 Quad GMSL2 Deserializer support" depends on I2C depends on OF_GPIO - depends on VIDEO_V4L2 + depends on VIDEO_DEV select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index 9bc72d9a858b..6b5abd958bff 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -30,7 +30,7 @@ struct max96712_priv { struct regmap *regmap; struct gpio_desc *gpiod_pwdn; - struct v4l2_fwnode_bus_mipi_csi2 mipi; + struct v4l2_mbus_config_mipi_csi2 mipi; struct v4l2_subdev sd; struct v4l2_ctrl_handler ctrl_handler; diff --git a/drivers/staging/media/meson/vdec/Kconfig b/drivers/staging/media/meson/vdec/Kconfig index 9e1450193392..19ffea987b89 100644 --- a/drivers/staging/media/meson/vdec/Kconfig +++ b/drivers/staging/media/meson/vdec/Kconfig @@ -2,7 +2,7 @@ config VIDEO_MESON_VDEC tristate "Amlogic video decoder driver" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && HAS_DMA depends on ARCH_MESON || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c index db7022707ff8..86ccc8937afc 100644 --- a/drivers/staging/media/meson/vdec/esparser.c +++ b/drivers/staging/media/meson/vdec/esparser.c @@ -328,7 +328,12 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) offset = esparser_get_offset(sess); - amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags); + ret = amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags); + if (ret) { + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + return ret; + } + dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n", vb->timestamp, payload_size, offset, vbuf->flags); diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c index 203d7afa085d..7d2a75653250 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.c +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c @@ -227,13 +227,16 @@ int amvdec_set_canvases(struct amvdec_session *sess, } EXPORT_SYMBOL_GPL(amvdec_set_canvases); -void amvdec_add_ts(struct amvdec_session *sess, u64 ts, - struct v4l2_timecode tc, u32 offset, u32 vbuf_flags) +int amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 vbuf_flags) { struct amvdec_timestamp *new_ts; unsigned long flags; new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL); + if (!new_ts) + return -ENOMEM; + new_ts->ts = ts; new_ts->tc = tc; new_ts->offset = offset; @@ -242,6 +245,7 @@ void amvdec_add_ts(struct amvdec_session *sess, u64 ts, spin_lock_irqsave(&sess->ts_spinlock, flags); list_add_tail(&new_ts->list, &sess->timestamps); spin_unlock_irqrestore(&sess->ts_spinlock, flags); + return 0; } EXPORT_SYMBOL_GPL(amvdec_add_ts); diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.h b/drivers/staging/media/meson/vdec/vdec_helpers.h index 88137d15aa3a..4bf3e61d081b 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.h +++ b/drivers/staging/media/meson/vdec/vdec_helpers.h @@ -56,8 +56,8 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, * @offset: offset in the VIFIFO where the associated packet was written * @flags: the vb2_v4l2_buffer flags */ -void amvdec_add_ts(struct amvdec_session *sess, u64 ts, - struct v4l2_timecode tc, u32 offset, u32 flags); +int amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 flags); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); /** diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c index eabbebab2da2..88c9d72e1c83 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.c +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -103,6 +103,18 @@ static const struct amvdec_format vdec_formats_gxl[] = { static const struct amvdec_format vdec_formats_gxm[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/gxl_vp9.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_H264, .min_buffers = 2, .max_buffers = 24, diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig index 6c254907a27b..6d1f55b09132 100644 --- a/drivers/staging/media/omap4iss/Kconfig +++ b/drivers/staging/media/omap4iss/Kconfig @@ -2,7 +2,7 @@ config VIDEO_OMAP4 tristate "OMAP 4 Camera support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_DEV && I2C depends on ARCH_OMAP4 || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API diff --git a/drivers/staging/media/rkvdec/Kconfig b/drivers/staging/media/rkvdec/Kconfig index dc7292f346fa..e963d60cc6ad 100644 --- a/drivers/staging/media/rkvdec/Kconfig +++ b/drivers/staging/media/rkvdec/Kconfig @@ -2,7 +2,7 @@ config VIDEO_ROCKCHIP_VDEC tristate "Rockchip Video Decoder driver" depends on ARCH_ROCKCHIP || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/staging/media/sunxi/cedrus/Kconfig b/drivers/staging/media/sunxi/cedrus/Kconfig index da369950bbf2..21c13f9b6e33 100644 --- a/drivers/staging/media/sunxi/cedrus/Kconfig +++ b/drivers/staging/media/sunxi/cedrus/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_SUNXI_CEDRUS tristate "Allwinner Cedrus VPU driver" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV depends on HAS_DMA depends on OF select MEDIA_CONTROLLER diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 4a4b714b0f26..68b3dcdb5df3 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -439,6 +439,8 @@ static int cedrus_probe(struct platform_device *pdev) mutex_init(&dev->dev_mutex); + INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index c345f2984041..3bc094eb497f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -24,6 +24,7 @@ #include <linux/iopoll.h> #include <linux/platform_device.h> +#include <linux/workqueue.h> #define CEDRUS_NAME "cedrus" @@ -194,6 +195,8 @@ struct cedrus_dev { struct reset_control *rstc; unsigned int capabilities; + + struct delayed_work watchdog_work; }; extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index a16c1422558f..9c7200299465 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -97,4 +97,8 @@ void cedrus_device_run(void *priv) v4l2_ctrl_request_complete(src_req, &ctx->hdl); dev->dec_ops[ctx->current_codec]->trigger(ctx); + + /* Start the watchdog timer. */ + schedule_delayed_work(&dev->watchdog_work, + msecs_to_jiffies(2000)); } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index b4173a8926d6..d8fb93035470 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -38,7 +38,7 @@ struct cedrus_h264_sram_ref_pic { #define CEDRUS_H264_FRAME_NUM 18 -#define CEDRUS_NEIGHBOR_INFO_BUF_SIZE (16 * SZ_1K) +#define CEDRUS_NEIGHBOR_INFO_BUF_SIZE (32 * SZ_1K) #define CEDRUS_MIN_PIC_INFO_BUF_SIZE (130 * SZ_1K) static void cedrus_h264_write_sram(struct cedrus_dev *dev, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index 8829a7bab07e..44f385be9f6c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -23,7 +23,7 @@ * Subsequent BSP implementations seem to double the neighbor info buffer size * for the H6 SoC, which may be related to 10 bit H265 support. */ -#define CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE (397 * SZ_1K) +#define CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE (794 * SZ_1K) #define CEDRUS_H265_ENTRY_POINTS_BUF_SIZE (4 * SZ_1K) #define CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE 160 @@ -169,7 +169,7 @@ static void cedrus_h265_ref_pic_list_write(struct cedrus_dev *dev, unsigned int index = list[i]; u8 value = list[i]; - if (dpb[index].rps == V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR) + if (dpb[index].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) value |= VE_DEC_H265_SRAM_REF_PIC_LIST_LT_REF; /* Each SRAM word gathers up to 4 references. */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 2d7663726467..a6470a89851e 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -118,6 +118,13 @@ static irqreturn_t cedrus_irq(int irq, void *data) enum vb2_buffer_state state; enum cedrus_irq_status status; + /* + * If cancel_delayed_work returns false it means watchdog already + * executed and finished the job. + */ + if (!cancel_delayed_work(&dev->watchdog_work)) + return IRQ_HANDLED; + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); if (!ctx) { v4l2_err(&dev->v4l2_dev, @@ -143,6 +150,24 @@ static irqreturn_t cedrus_irq(int irq, void *data) return IRQ_HANDLED; } +void cedrus_watchdog(struct work_struct *work) +{ + struct cedrus_dev *dev; + struct cedrus_ctx *ctx; + + dev = container_of(to_delayed_work(work), + struct cedrus_dev, watchdog_work); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) + return; + + v4l2_err(&dev->v4l2_dev, "frame processing timed out!\n"); + reset_control_reset(dev->rstc); + v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, + VB2_BUF_STATE_ERROR); +} + int cedrus_hw_suspend(struct device *device) { struct cedrus_dev *dev = dev_get_drvdata(device); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h index 45f641f0bfa2..7c92f00e36da 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h @@ -28,4 +28,6 @@ int cedrus_hw_resume(struct device *device); int cedrus_hw_probe(struct cedrus_dev *dev); void cedrus_hw_remove(struct cedrus_dev *dev); +void cedrus_watchdog(struct work_struct *work); + #endif diff --git a/drivers/staging/media/tegra-vde/Kconfig b/drivers/staging/media/tegra-vde/Kconfig deleted file mode 100644 index 0dc78afd09e0..000000000000 --- a/drivers/staging/media/tegra-vde/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config TEGRA_VDE - tristate "NVIDIA Tegra Video Decoder Engine driver" - depends on ARCH_TEGRA || COMPILE_TEST - select DMA_SHARED_BUFFER - select IOMMU_IOVA - select SRAM - help - Say Y here to enable support for the NVIDIA Tegra video decoder - driver. diff --git a/drivers/staging/media/tegra-vde/Makefile b/drivers/staging/media/tegra-vde/Makefile deleted file mode 100644 index 2827f7601de8..000000000000 --- a/drivers/staging/media/tegra-vde/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -tegra-vde-y := vde.o iommu.o dmabuf-cache.o -obj-$(CONFIG_TEGRA_VDE) += tegra-vde.o diff --git a/drivers/staging/media/tegra-vde/TODO b/drivers/staging/media/tegra-vde/TODO deleted file mode 100644 index 31aaa3e66d80..000000000000 --- a/drivers/staging/media/tegra-vde/TODO +++ /dev/null @@ -1,4 +0,0 @@ -TODO: - - Implement V4L2 API once it gains support for stateless decoders. - -Contact: Dmitry Osipenko <digetx@gmail.com> diff --git a/drivers/staging/media/tegra-vde/uapi.h b/drivers/staging/media/tegra-vde/uapi.h deleted file mode 100644 index ffb4983e5bb6..000000000000 --- a/drivers/staging/media/tegra-vde/uapi.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> */ -#ifndef _UAPI_TEGRA_VDE_H_ -#define _UAPI_TEGRA_VDE_H_ - -#include <linux/types.h> -#include <asm/ioctl.h> - -#define FLAG_B_FRAME 0x1 -#define FLAG_REFERENCE 0x2 - -struct tegra_vde_h264_frame { - __s32 y_fd; - __s32 cb_fd; - __s32 cr_fd; - __s32 aux_fd; - __u32 y_offset; - __u32 cb_offset; - __u32 cr_offset; - __u32 aux_offset; - __u32 frame_num; - __u32 flags; - - // Must be zero'ed - __u32 reserved[6]; -}; - -struct tegra_vde_h264_decoder_ctx { - __s32 bitstream_data_fd; - __u32 bitstream_data_offset; - - __u64 dpb_frames_ptr; - __u32 dpb_frames_nb; - __u32 dpb_ref_frames_with_earlier_poc_nb; - - // SPS - __u32 baseline_profile; - __u32 level_idc; - __u32 log2_max_pic_order_cnt_lsb; - __u32 log2_max_frame_num; - __u32 pic_order_cnt_type; - __u32 direct_8x8_inference_flag; - __u32 pic_width_in_mbs; - __u32 pic_height_in_mbs; - - // PPS - __u32 pic_init_qp; - __u32 deblocking_filter_control_present_flag; - __u32 constrained_intra_pred_flag; - __u32 chroma_qp_index_offset; - __u32 pic_order_present_flag; - - // Slice header - __u32 num_ref_idx_l0_active_minus1; - __u32 num_ref_idx_l1_active_minus1; - - // Must be zero'ed - __u32 reserved[11]; -}; - -#define VDE_IOCTL_BASE ('v' + 0x20) - -#define VDE_IO(nr) _IO(VDE_IOCTL_BASE, nr) -#define VDE_IOR(nr, type) _IOR(VDE_IOCTL_BASE, nr, type) -#define VDE_IOW(nr, type) _IOW(VDE_IOCTL_BASE, nr, type) -#define VDE_IOWR(nr, type) _IOWR(VDE_IOCTL_BASE, nr, type) - -#define TEGRA_VDE_DECODE_H264 0x00 - -#define TEGRA_VDE_IOCTL_DECODE_H264 \ - VDE_IOW(TEGRA_VDE_DECODE_H264, struct tegra_vde_h264_decoder_ctx) - -#endif // _UAPI_TEGRA_VDE_H_ diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c deleted file mode 100644 index a8f1a024c343..000000000000 --- a/drivers/staging/media/tegra-vde/vde.c +++ /dev/null @@ -1,1358 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * NVIDIA Tegra Video decoder driver - * - * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> - * - */ - -#include <linux/clk.h> -#include <linux/dma-buf.h> -#include <linux/genalloc.h> -#include <linux/interrupt.h> -#include <linux/iopoll.h> -#include <linux/list.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/pm_runtime.h> -#include <linux/reset.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -#include <soc/tegra/common.h> -#include <soc/tegra/pmc.h> - -#include "uapi.h" -#include "vde.h" - -#define CREATE_TRACE_POINTS -#include "trace.h" - -#define ICMDQUE_WR 0x00 -#define CMDQUE_CONTROL 0x08 -#define INTR_STATUS 0x18 -#define BSE_INT_ENB 0x40 -#define BSE_CONFIG 0x44 - -#define BSE_ICMDQUE_EMPTY BIT(3) -#define BSE_DMA_BUSY BIT(23) - -struct video_frame { - struct dma_buf_attachment *y_dmabuf_attachment; - struct dma_buf_attachment *cb_dmabuf_attachment; - struct dma_buf_attachment *cr_dmabuf_attachment; - struct dma_buf_attachment *aux_dmabuf_attachment; - dma_addr_t y_addr; - dma_addr_t cb_addr; - dma_addr_t cr_addr; - dma_addr_t aux_addr; - u32 frame_num; - u32 flags; -}; - -static void tegra_vde_writel(struct tegra_vde *vde, - u32 value, void __iomem *base, u32 offset) -{ - trace_vde_writel(vde, base, offset, value); - - writel_relaxed(value, base + offset); -} - -static u32 tegra_vde_readl(struct tegra_vde *vde, - void __iomem *base, u32 offset) -{ - u32 value = readl_relaxed(base + offset); - - trace_vde_readl(vde, base, offset, value); - - return value; -} - -static void tegra_vde_set_bits(struct tegra_vde *vde, - u32 mask, void __iomem *base, u32 offset) -{ - u32 value = tegra_vde_readl(vde, base, offset); - - tegra_vde_writel(vde, value | mask, base, offset); -} - -static int tegra_vde_wait_mbe(struct tegra_vde *vde) -{ - u32 tmp; - - return readl_relaxed_poll_timeout(vde->mbe + 0x8C, tmp, - (tmp >= 0x10), 1, 100); -} - -static int tegra_vde_alloc_bo(struct tegra_vde *vde, - struct tegra_vde_bo **ret_bo, - enum dma_data_direction dma_dir, - size_t size) -{ - struct device *dev = vde->miscdev.parent; - struct tegra_vde_bo *bo; - int err; - - bo = kzalloc(sizeof(*bo), GFP_KERNEL); - if (!bo) - return -ENOMEM; - - bo->vde = vde; - bo->size = size; - bo->dma_dir = dma_dir; - bo->dma_attrs = DMA_ATTR_WRITE_COMBINE | - DMA_ATTR_NO_KERNEL_MAPPING; - - if (!vde->domain) - bo->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS; - - bo->dma_cookie = dma_alloc_attrs(dev, bo->size, &bo->dma_handle, - GFP_KERNEL, bo->dma_attrs); - if (!bo->dma_cookie) { - dev_err(dev, "Failed to allocate DMA buffer of size: %zu\n", - bo->size); - err = -ENOMEM; - goto free_bo; - } - - err = dma_get_sgtable_attrs(dev, &bo->sgt, bo->dma_cookie, - bo->dma_handle, bo->size, bo->dma_attrs); - if (err) { - dev_err(dev, "Failed to get DMA buffer SG table: %d\n", err); - goto free_attrs; - } - - err = dma_map_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); - if (err) { - dev_err(dev, "Failed to map DMA buffer SG table: %d\n", err); - goto free_table; - } - - if (vde->domain) { - err = tegra_vde_iommu_map(vde, &bo->sgt, &bo->iova, bo->size); - if (err) { - dev_err(dev, "Failed to map DMA buffer IOVA: %d\n", err); - goto unmap_sgtable; - } - - bo->dma_addr = iova_dma_addr(&vde->iova, bo->iova); - } else { - bo->dma_addr = sg_dma_address(bo->sgt.sgl); - } - - *ret_bo = bo; - - return 0; - -unmap_sgtable: - dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); -free_table: - sg_free_table(&bo->sgt); -free_attrs: - dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle, - bo->dma_attrs); -free_bo: - kfree(bo); - - return err; -} - -static void tegra_vde_free_bo(struct tegra_vde_bo *bo) -{ - struct tegra_vde *vde = bo->vde; - struct device *dev = vde->miscdev.parent; - - if (vde->domain) - tegra_vde_iommu_unmap(vde, bo->iova); - - dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs); - - sg_free_table(&bo->sgt); - - dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle, - bo->dma_attrs); - kfree(bo); -} - -static int tegra_vde_setup_mbe_frame_idx(struct tegra_vde *vde, - unsigned int refs_nb, - bool setup_refs) -{ - u32 frame_idx_enb_mask = 0; - u32 value; - unsigned int frame_idx; - unsigned int idx; - int err; - - tegra_vde_writel(vde, 0xD0000000 | (0 << 23), vde->mbe, 0x80); - tegra_vde_writel(vde, 0xD0200000 | (0 << 23), vde->mbe, 0x80); - - err = tegra_vde_wait_mbe(vde); - if (err) - return err; - - if (!setup_refs) - return 0; - - for (idx = 0, frame_idx = 1; idx < refs_nb; idx++, frame_idx++) { - tegra_vde_writel(vde, 0xD0000000 | (frame_idx << 23), - vde->mbe, 0x80); - tegra_vde_writel(vde, 0xD0200000 | (frame_idx << 23), - vde->mbe, 0x80); - - frame_idx_enb_mask |= frame_idx << (6 * (idx % 4)); - - if (idx % 4 == 3 || idx == refs_nb - 1) { - value = 0xC0000000; - value |= (idx >> 2) << 24; - value |= frame_idx_enb_mask; - - tegra_vde_writel(vde, value, vde->mbe, 0x80); - - err = tegra_vde_wait_mbe(vde); - if (err) - return err; - - frame_idx_enb_mask = 0; - } - } - - return 0; -} - -static void tegra_vde_mbe_set_0xa_reg(struct tegra_vde *vde, int reg, u32 val) -{ - tegra_vde_writel(vde, 0xA0000000 | (reg << 24) | (val & 0xFFFF), - vde->mbe, 0x80); - tegra_vde_writel(vde, 0xA0000000 | ((reg + 1) << 24) | (val >> 16), - vde->mbe, 0x80); -} - -static int tegra_vde_wait_bsev(struct tegra_vde *vde, bool wait_dma) -{ - struct device *dev = vde->miscdev.parent; - u32 value; - int err; - - err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, - !(value & BIT(2)), 1, 100); - if (err) { - dev_err(dev, "BSEV unknown bit timeout\n"); - return err; - } - - err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, - (value & BSE_ICMDQUE_EMPTY), 1, 100); - if (err) { - dev_err(dev, "BSEV ICMDQUE flush timeout\n"); - return err; - } - - if (!wait_dma) - return 0; - - err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, - !(value & BSE_DMA_BUSY), 1, 100); - if (err) { - dev_err(dev, "BSEV DMA timeout\n"); - return err; - } - - return 0; -} - -static int tegra_vde_push_to_bsev_icmdqueue(struct tegra_vde *vde, - u32 value, bool wait_dma) -{ - tegra_vde_writel(vde, value, vde->bsev, ICMDQUE_WR); - - return tegra_vde_wait_bsev(vde, wait_dma); -} - -static void tegra_vde_setup_frameid(struct tegra_vde *vde, - struct video_frame *frame, - unsigned int frameid, - u32 mbs_width, u32 mbs_height) -{ - u32 y_addr = frame ? frame->y_addr : 0x6CDEAD00; - u32 cb_addr = frame ? frame->cb_addr : 0x6CDEAD00; - u32 cr_addr = frame ? frame->cr_addr : 0x6CDEAD00; - u32 value1 = frame ? ((mbs_width << 16) | mbs_height) : 0; - u32 value2 = frame ? ((((mbs_width + 1) >> 1) << 6) | 1) : 0; - - tegra_vde_writel(vde, y_addr >> 8, vde->frameid, 0x000 + frameid * 4); - tegra_vde_writel(vde, cb_addr >> 8, vde->frameid, 0x100 + frameid * 4); - tegra_vde_writel(vde, cr_addr >> 8, vde->frameid, 0x180 + frameid * 4); - tegra_vde_writel(vde, value1, vde->frameid, 0x080 + frameid * 4); - tegra_vde_writel(vde, value2, vde->frameid, 0x280 + frameid * 4); -} - -static void tegra_setup_frameidx(struct tegra_vde *vde, - struct video_frame *frames, - unsigned int frames_nb, - u32 mbs_width, u32 mbs_height) -{ - unsigned int idx; - - for (idx = 0; idx < frames_nb; idx++) - tegra_vde_setup_frameid(vde, &frames[idx], idx, - mbs_width, mbs_height); - - for (; idx < 17; idx++) - tegra_vde_setup_frameid(vde, NULL, idx, 0, 0); -} - -static void tegra_vde_setup_iram_entry(struct tegra_vde *vde, - unsigned int table, - unsigned int row, - u32 value1, u32 value2) -{ - u32 *iram_tables = vde->iram; - - trace_vde_setup_iram_entry(table, row, value1, value2); - - iram_tables[0x20 * table + row * 2] = value1; - iram_tables[0x20 * table + row * 2 + 1] = value2; -} - -static void tegra_vde_setup_iram_tables(struct tegra_vde *vde, - struct video_frame *dpb_frames, - unsigned int ref_frames_nb, - unsigned int with_earlier_poc_nb) -{ - struct video_frame *frame; - u32 value, aux_addr; - int with_later_poc_nb; - unsigned int i, k; - - trace_vde_ref_l0(dpb_frames[0].frame_num); - - for (i = 0; i < 16; i++) { - if (i < ref_frames_nb) { - frame = &dpb_frames[i + 1]; - - aux_addr = frame->aux_addr; - - value = (i + 1) << 26; - value |= !(frame->flags & FLAG_B_FRAME) << 25; - value |= 1 << 24; - value |= frame->frame_num; - } else { - aux_addr = 0x6ADEAD00; - value = 0x3f; - } - - tegra_vde_setup_iram_entry(vde, 0, i, value, aux_addr); - tegra_vde_setup_iram_entry(vde, 1, i, value, aux_addr); - tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); - tegra_vde_setup_iram_entry(vde, 3, i, value, aux_addr); - } - - if (!(dpb_frames[0].flags & FLAG_B_FRAME)) - return; - - if (with_earlier_poc_nb >= ref_frames_nb) - return; - - with_later_poc_nb = ref_frames_nb - with_earlier_poc_nb; - - trace_vde_ref_l1(with_later_poc_nb, with_earlier_poc_nb); - - for (i = 0, k = with_earlier_poc_nb; i < with_later_poc_nb; i++, k++) { - frame = &dpb_frames[k + 1]; - - aux_addr = frame->aux_addr; - - value = (k + 1) << 26; - value |= !(frame->flags & FLAG_B_FRAME) << 25; - value |= 1 << 24; - value |= frame->frame_num; - - tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); - } - - for (k = 0; i < ref_frames_nb; i++, k++) { - frame = &dpb_frames[k + 1]; - - aux_addr = frame->aux_addr; - - value = (k + 1) << 26; - value |= !(frame->flags & FLAG_B_FRAME) << 25; - value |= 1 << 24; - value |= frame->frame_num; - - tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); - } -} - -static int tegra_vde_setup_hw_context(struct tegra_vde *vde, - struct tegra_vde_h264_decoder_ctx *ctx, - struct video_frame *dpb_frames, - dma_addr_t bitstream_data_addr, - size_t bitstream_data_size, - unsigned int macroblocks_nb) -{ - struct device *dev = vde->miscdev.parent; - u32 value; - int err; - - tegra_vde_set_bits(vde, 0x000A, vde->sxe, 0xF0); - tegra_vde_set_bits(vde, 0x000B, vde->bsev, CMDQUE_CONTROL); - tegra_vde_set_bits(vde, 0x8002, vde->mbe, 0x50); - tegra_vde_set_bits(vde, 0x000A, vde->mbe, 0xA0); - tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x14); - tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x28); - tegra_vde_set_bits(vde, 0x0A00, vde->mce, 0x08); - tegra_vde_set_bits(vde, 0x000A, vde->tfe, 0x00); - tegra_vde_set_bits(vde, 0x0005, vde->vdma, 0x04); - - tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x1C); - tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x00); - tegra_vde_writel(vde, 0x00000007, vde->vdma, 0x04); - tegra_vde_writel(vde, 0x00000007, vde->frameid, 0x200); - tegra_vde_writel(vde, 0x00000005, vde->tfe, 0x04); - tegra_vde_writel(vde, 0x00000000, vde->mbe, 0x84); - tegra_vde_writel(vde, 0x00000010, vde->sxe, 0x08); - tegra_vde_writel(vde, 0x00000150, vde->sxe, 0x54); - tegra_vde_writel(vde, 0x0000054C, vde->sxe, 0x58); - tegra_vde_writel(vde, 0x00000E34, vde->sxe, 0x5C); - tegra_vde_writel(vde, 0x063C063C, vde->mce, 0x10); - tegra_vde_writel(vde, 0x0003FC00, vde->bsev, INTR_STATUS); - tegra_vde_writel(vde, 0x0000150D, vde->bsev, BSE_CONFIG); - tegra_vde_writel(vde, 0x00000100, vde->bsev, BSE_INT_ENB); - tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x98); - tegra_vde_writel(vde, 0x00000060, vde->bsev, 0x9C); - - memset(vde->iram + 128, 0, macroblocks_nb / 2); - - tegra_setup_frameidx(vde, dpb_frames, ctx->dpb_frames_nb, - ctx->pic_width_in_mbs, ctx->pic_height_in_mbs); - - tegra_vde_setup_iram_tables(vde, dpb_frames, - ctx->dpb_frames_nb - 1, - ctx->dpb_ref_frames_with_earlier_poc_nb); - - /* - * The IRAM mapping is write-combine, ensure that CPU buffers have - * been flushed at this point. - */ - wmb(); - - tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x8C); - tegra_vde_writel(vde, bitstream_data_addr + bitstream_data_size, - vde->bsev, 0x54); - - value = ctx->pic_width_in_mbs << 11 | ctx->pic_height_in_mbs << 3; - - tegra_vde_writel(vde, value, vde->bsev, 0x88); - - err = tegra_vde_wait_bsev(vde, false); - if (err) - return err; - - err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x800003FC, false); - if (err) - return err; - - value = 0x01500000; - value |= ((vde->iram_lists_addr + 512) >> 2) & 0xFFFF; - - err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); - if (err) - return err; - - err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x840F054C, false); - if (err) - return err; - - err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x80000080, false); - if (err) - return err; - - value = 0x0E340000 | ((vde->iram_lists_addr >> 2) & 0xFFFF); - - err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); - if (err) - return err; - - value = 0x00800005; - value |= ctx->pic_width_in_mbs << 11; - value |= ctx->pic_height_in_mbs << 3; - - tegra_vde_writel(vde, value, vde->sxe, 0x10); - - value = !ctx->baseline_profile << 17; - value |= ctx->level_idc << 13; - value |= ctx->log2_max_pic_order_cnt_lsb << 7; - value |= ctx->pic_order_cnt_type << 5; - value |= ctx->log2_max_frame_num; - - tegra_vde_writel(vde, value, vde->sxe, 0x40); - - value = ctx->pic_init_qp << 25; - value |= !!(ctx->deblocking_filter_control_present_flag) << 2; - value |= !!ctx->pic_order_present_flag; - - tegra_vde_writel(vde, value, vde->sxe, 0x44); - - value = ctx->chroma_qp_index_offset; - value |= ctx->num_ref_idx_l0_active_minus1 << 5; - value |= ctx->num_ref_idx_l1_active_minus1 << 10; - value |= !!ctx->constrained_intra_pred_flag << 15; - - tegra_vde_writel(vde, value, vde->sxe, 0x48); - - value = 0x0C000000; - value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 24; - - tegra_vde_writel(vde, value, vde->sxe, 0x4C); - - value = 0x03800000; - value |= bitstream_data_size & GENMASK(19, 15); - - tegra_vde_writel(vde, value, vde->sxe, 0x68); - - tegra_vde_writel(vde, bitstream_data_addr, vde->sxe, 0x6C); - - if (vde->soc->supports_ref_pic_marking) - tegra_vde_writel(vde, vde->secure_bo->dma_addr, vde->sxe, 0x7c); - - value = 0x10000005; - value |= ctx->pic_width_in_mbs << 11; - value |= ctx->pic_height_in_mbs << 3; - - tegra_vde_writel(vde, value, vde->mbe, 0x80); - - value = 0x26800000; - value |= ctx->level_idc << 4; - value |= !ctx->baseline_profile << 1; - value |= !!ctx->direct_8x8_inference_flag; - - tegra_vde_writel(vde, value, vde->mbe, 0x80); - - tegra_vde_writel(vde, 0xF4000001, vde->mbe, 0x80); - tegra_vde_writel(vde, 0x20000000, vde->mbe, 0x80); - tegra_vde_writel(vde, 0xF4000101, vde->mbe, 0x80); - - value = 0x20000000; - value |= ctx->chroma_qp_index_offset << 8; - - tegra_vde_writel(vde, value, vde->mbe, 0x80); - - err = tegra_vde_setup_mbe_frame_idx(vde, - ctx->dpb_frames_nb - 1, - ctx->pic_order_cnt_type == 0); - if (err) { - dev_err(dev, "MBE frames setup failed %d\n", err); - return err; - } - - tegra_vde_mbe_set_0xa_reg(vde, 0, 0x000009FC); - tegra_vde_mbe_set_0xa_reg(vde, 2, 0x61DEAD00); - tegra_vde_mbe_set_0xa_reg(vde, 4, 0x62DEAD00); - tegra_vde_mbe_set_0xa_reg(vde, 6, 0x63DEAD00); - tegra_vde_mbe_set_0xa_reg(vde, 8, dpb_frames[0].aux_addr); - - value = 0xFC000000; - value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 2; - - if (!ctx->baseline_profile) - value |= !!(dpb_frames[0].flags & FLAG_REFERENCE) << 1; - - tegra_vde_writel(vde, value, vde->mbe, 0x80); - - err = tegra_vde_wait_mbe(vde); - if (err) { - dev_err(dev, "MBE programming failed %d\n", err); - return err; - } - - return 0; -} - -static void tegra_vde_decode_frame(struct tegra_vde *vde, - unsigned int macroblocks_nb) -{ - reinit_completion(&vde->decode_completion); - - tegra_vde_writel(vde, 0x00000001, vde->bsev, 0x8C); - tegra_vde_writel(vde, 0x20000000 | (macroblocks_nb - 1), - vde->sxe, 0x00); -} - -static int tegra_vde_attach_dmabuf(struct tegra_vde *vde, - int fd, - unsigned long offset, - size_t min_size, - size_t align_size, - struct dma_buf_attachment **a, - dma_addr_t *addrp, - size_t *size, - enum dma_data_direction dma_dir) -{ - struct device *dev = vde->miscdev.parent; - struct dma_buf *dmabuf; - int err; - - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) { - dev_err(dev, "Invalid dmabuf FD\n"); - return PTR_ERR(dmabuf); - } - - if (dmabuf->size & (align_size - 1)) { - dev_err(dev, "Unaligned dmabuf 0x%zX, should be aligned to 0x%zX\n", - dmabuf->size, align_size); - return -EINVAL; - } - - if ((u64)offset + min_size > dmabuf->size) { - dev_err(dev, "Too small dmabuf size %zu @0x%lX, should be at least %zu\n", - dmabuf->size, offset, min_size); - return -EINVAL; - } - - err = tegra_vde_dmabuf_cache_map(vde, dmabuf, dma_dir, a, addrp); - if (err) - goto err_put; - - *addrp = *addrp + offset; - - if (size) - *size = dmabuf->size - offset; - - return 0; - -err_put: - dma_buf_put(dmabuf); - - return err; -} - -static int tegra_vde_attach_dmabufs_to_frame(struct tegra_vde *vde, - struct video_frame *frame, - struct tegra_vde_h264_frame *src, - enum dma_data_direction dma_dir, - bool baseline_profile, - size_t lsize, size_t csize) -{ - int err; - - err = tegra_vde_attach_dmabuf(vde, src->y_fd, - src->y_offset, lsize, SZ_256, - &frame->y_dmabuf_attachment, - &frame->y_addr, - NULL, dma_dir); - if (err) - return err; - - err = tegra_vde_attach_dmabuf(vde, src->cb_fd, - src->cb_offset, csize, SZ_256, - &frame->cb_dmabuf_attachment, - &frame->cb_addr, - NULL, dma_dir); - if (err) - goto err_release_y; - - err = tegra_vde_attach_dmabuf(vde, src->cr_fd, - src->cr_offset, csize, SZ_256, - &frame->cr_dmabuf_attachment, - &frame->cr_addr, - NULL, dma_dir); - if (err) - goto err_release_cb; - - if (baseline_profile) { - frame->aux_addr = 0x64DEAD00; - return 0; - } - - err = tegra_vde_attach_dmabuf(vde, src->aux_fd, - src->aux_offset, csize, SZ_256, - &frame->aux_dmabuf_attachment, - &frame->aux_addr, - NULL, dma_dir); - if (err) - goto err_release_cr; - - return 0; - -err_release_cr: - tegra_vde_dmabuf_cache_unmap(vde, frame->cr_dmabuf_attachment, true); -err_release_cb: - tegra_vde_dmabuf_cache_unmap(vde, frame->cb_dmabuf_attachment, true); -err_release_y: - tegra_vde_dmabuf_cache_unmap(vde, frame->y_dmabuf_attachment, true); - - return err; -} - -static void tegra_vde_release_frame_dmabufs(struct tegra_vde *vde, - struct video_frame *frame, - enum dma_data_direction dma_dir, - bool baseline_profile, - bool release) -{ - if (!baseline_profile) - tegra_vde_dmabuf_cache_unmap(vde, frame->aux_dmabuf_attachment, - release); - - tegra_vde_dmabuf_cache_unmap(vde, frame->cr_dmabuf_attachment, release); - tegra_vde_dmabuf_cache_unmap(vde, frame->cb_dmabuf_attachment, release); - tegra_vde_dmabuf_cache_unmap(vde, frame->y_dmabuf_attachment, release); -} - -static int tegra_vde_validate_frame(struct device *dev, - struct tegra_vde_h264_frame *frame) -{ - if (frame->frame_num > 0x7FFFFF) { - dev_err(dev, "Bad frame_num %u\n", frame->frame_num); - return -EINVAL; - } - - return 0; -} - -static int tegra_vde_validate_h264_ctx(struct device *dev, - struct tegra_vde_h264_decoder_ctx *ctx) -{ - if (ctx->dpb_frames_nb == 0 || ctx->dpb_frames_nb > 17) { - dev_err(dev, "Bad DPB size %u\n", ctx->dpb_frames_nb); - return -EINVAL; - } - - if (ctx->level_idc > 15) { - dev_err(dev, "Bad level value %u\n", ctx->level_idc); - return -EINVAL; - } - - if (ctx->pic_init_qp > 52) { - dev_err(dev, "Bad pic_init_qp value %u\n", ctx->pic_init_qp); - return -EINVAL; - } - - if (ctx->log2_max_pic_order_cnt_lsb > 16) { - dev_err(dev, "Bad log2_max_pic_order_cnt_lsb value %u\n", - ctx->log2_max_pic_order_cnt_lsb); - return -EINVAL; - } - - if (ctx->log2_max_frame_num > 16) { - dev_err(dev, "Bad log2_max_frame_num value %u\n", - ctx->log2_max_frame_num); - return -EINVAL; - } - - if (ctx->chroma_qp_index_offset > 31) { - dev_err(dev, "Bad chroma_qp_index_offset value %u\n", - ctx->chroma_qp_index_offset); - return -EINVAL; - } - - if (ctx->pic_order_cnt_type > 2) { - dev_err(dev, "Bad pic_order_cnt_type value %u\n", - ctx->pic_order_cnt_type); - return -EINVAL; - } - - if (ctx->num_ref_idx_l0_active_minus1 > 15) { - dev_err(dev, "Bad num_ref_idx_l0_active_minus1 value %u\n", - ctx->num_ref_idx_l0_active_minus1); - return -EINVAL; - } - - if (ctx->num_ref_idx_l1_active_minus1 > 15) { - dev_err(dev, "Bad num_ref_idx_l1_active_minus1 value %u\n", - ctx->num_ref_idx_l1_active_minus1); - return -EINVAL; - } - - if (!ctx->pic_width_in_mbs || ctx->pic_width_in_mbs > 127) { - dev_err(dev, "Bad pic_width_in_mbs value %u\n", - ctx->pic_width_in_mbs); - return -EINVAL; - } - - if (!ctx->pic_height_in_mbs || ctx->pic_height_in_mbs > 127) { - dev_err(dev, "Bad pic_height_in_mbs value %u\n", - ctx->pic_height_in_mbs); - return -EINVAL; - } - - return 0; -} - -static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, - unsigned long vaddr) -{ - struct device *dev = vde->miscdev.parent; - struct tegra_vde_h264_decoder_ctx ctx; - struct tegra_vde_h264_frame *frames; - struct tegra_vde_h264_frame __user *frames_user; - struct video_frame *dpb_frames; - struct dma_buf_attachment *bitstream_data_dmabuf_attachment; - enum dma_data_direction dma_dir; - dma_addr_t bitstream_data_addr; - dma_addr_t bsev_ptr; - size_t lsize, csize; - size_t bitstream_data_size; - unsigned int macroblocks_nb; - unsigned int read_bytes; - unsigned int cstride; - unsigned int i; - long timeout; - int ret, err; - - if (copy_from_user(&ctx, (void __user *)vaddr, sizeof(ctx))) - return -EFAULT; - - ret = tegra_vde_validate_h264_ctx(dev, &ctx); - if (ret) - return ret; - - ret = tegra_vde_attach_dmabuf(vde, ctx.bitstream_data_fd, - ctx.bitstream_data_offset, - SZ_16K, SZ_16K, - &bitstream_data_dmabuf_attachment, - &bitstream_data_addr, - &bitstream_data_size, - DMA_TO_DEVICE); - if (ret) - return ret; - - frames = kmalloc_array(ctx.dpb_frames_nb, sizeof(*frames), GFP_KERNEL); - if (!frames) { - ret = -ENOMEM; - goto release_bitstream_dmabuf; - } - - dpb_frames = kcalloc(ctx.dpb_frames_nb, sizeof(*dpb_frames), - GFP_KERNEL); - if (!dpb_frames) { - ret = -ENOMEM; - goto free_frames; - } - - macroblocks_nb = ctx.pic_width_in_mbs * ctx.pic_height_in_mbs; - frames_user = u64_to_user_ptr(ctx.dpb_frames_ptr); - - if (copy_from_user(frames, frames_user, - ctx.dpb_frames_nb * sizeof(*frames))) { - ret = -EFAULT; - goto free_dpb_frames; - } - - cstride = ALIGN(ctx.pic_width_in_mbs * 8, 16); - csize = cstride * ctx.pic_height_in_mbs * 8; - lsize = macroblocks_nb * 256; - - for (i = 0; i < ctx.dpb_frames_nb; i++) { - ret = tegra_vde_validate_frame(dev, &frames[i]); - if (ret) - goto release_dpb_frames; - - dpb_frames[i].flags = frames[i].flags; - dpb_frames[i].frame_num = frames[i].frame_num; - - dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - ret = tegra_vde_attach_dmabufs_to_frame(vde, &dpb_frames[i], - &frames[i], dma_dir, - ctx.baseline_profile, - lsize, csize); - if (ret) - goto release_dpb_frames; - } - - ret = mutex_lock_interruptible(&vde->lock); - if (ret) - goto release_dpb_frames; - - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) - goto unlock; - - /* - * We rely on the VDE registers reset value, otherwise VDE - * causes bus lockup. - */ - ret = reset_control_assert(vde->rst_mc); - if (ret) { - dev_err(dev, "DEC start: Failed to assert MC reset: %d\n", - ret); - goto put_runtime_pm; - } - - ret = reset_control_reset(vde->rst); - if (ret) { - dev_err(dev, "DEC start: Failed to reset HW: %d\n", ret); - goto put_runtime_pm; - } - - ret = reset_control_deassert(vde->rst_mc); - if (ret) { - dev_err(dev, "DEC start: Failed to deassert MC reset: %d\n", - ret); - goto put_runtime_pm; - } - - ret = tegra_vde_setup_hw_context(vde, &ctx, dpb_frames, - bitstream_data_addr, - bitstream_data_size, - macroblocks_nb); - if (ret) - goto put_runtime_pm; - - tegra_vde_decode_frame(vde, macroblocks_nb); - - timeout = wait_for_completion_interruptible_timeout( - &vde->decode_completion, msecs_to_jiffies(1000)); - if (timeout == 0) { - bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10); - macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF; - read_bytes = bsev_ptr ? bsev_ptr - bitstream_data_addr : 0; - - dev_err(dev, "Decoding failed: read 0x%X bytes, %u macroblocks parsed\n", - read_bytes, macroblocks_nb); - - ret = -EIO; - } else if (timeout < 0) { - ret = timeout; - } - - /* - * At first reset memory client to avoid resetting VDE HW in the - * middle of DMA which could result into memory corruption or hang - * the whole system. - */ - err = reset_control_assert(vde->rst_mc); - if (err) - dev_err(dev, "DEC end: Failed to assert MC reset: %d\n", err); - - err = reset_control_assert(vde->rst); - if (err) - dev_err(dev, "DEC end: Failed to assert HW reset: %d\n", err); - -put_runtime_pm: - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - -unlock: - mutex_unlock(&vde->lock); - -release_dpb_frames: - while (i--) { - dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - tegra_vde_release_frame_dmabufs(vde, &dpb_frames[i], dma_dir, - ctx.baseline_profile, ret != 0); - } - -free_dpb_frames: - kfree(dpb_frames); - -free_frames: - kfree(frames); - -release_bitstream_dmabuf: - tegra_vde_dmabuf_cache_unmap(vde, bitstream_data_dmabuf_attachment, - ret != 0); - - return ret; -} - -static long tegra_vde_unlocked_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct miscdevice *miscdev = filp->private_data; - struct tegra_vde *vde = container_of(miscdev, struct tegra_vde, - miscdev); - - switch (cmd) { - case TEGRA_VDE_IOCTL_DECODE_H264: - return tegra_vde_ioctl_decode_h264(vde, arg); - } - - dev_err(miscdev->parent, "Invalid IOCTL command %u\n", cmd); - - return -ENOTTY; -} - -static int tegra_vde_release_file(struct inode *inode, struct file *filp) -{ - struct miscdevice *miscdev = filp->private_data; - struct tegra_vde *vde = container_of(miscdev, struct tegra_vde, - miscdev); - - tegra_vde_dmabuf_cache_unmap_sync(vde); - - return 0; -} - -static const struct file_operations tegra_vde_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = tegra_vde_unlocked_ioctl, - .release = tegra_vde_release_file, -}; - -static irqreturn_t tegra_vde_isr(int irq, void *data) -{ - struct tegra_vde *vde = data; - - if (completion_done(&vde->decode_completion)) - return IRQ_NONE; - - tegra_vde_set_bits(vde, 0, vde->frameid, 0x208); - complete(&vde->decode_completion); - - return IRQ_HANDLED; -} - -static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev) -{ - struct tegra_vde *vde = dev_get_drvdata(dev); - int err; - - if (!dev->pm_domain) { - err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC); - if (err) { - dev_err(dev, "Failed to power down HW: %d\n", err); - return err; - } - } - - clk_disable_unprepare(vde->clk); - reset_control_release(vde->rst); - reset_control_release(vde->rst_mc); - - return 0; -} - -static __maybe_unused int tegra_vde_runtime_resume(struct device *dev) -{ - struct tegra_vde *vde = dev_get_drvdata(dev); - int err; - - err = reset_control_acquire(vde->rst_mc); - if (err) { - dev_err(dev, "Failed to acquire mc reset: %d\n", err); - return err; - } - - err = reset_control_acquire(vde->rst); - if (err) { - dev_err(dev, "Failed to acquire reset: %d\n", err); - goto release_mc_reset; - } - - if (!dev->pm_domain) { - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC, - vde->clk, vde->rst); - if (err) { - dev_err(dev, "Failed to power up HW : %d\n", err); - goto release_reset; - } - } else { - /* - * tegra_powergate_sequence_power_up() leaves clocks enabled, - * while GENPD not. - */ - err = clk_prepare_enable(vde->clk); - if (err) { - dev_err(dev, "Failed to enable clock: %d\n", err); - goto release_reset; - } - } - - return 0; - -release_reset: - reset_control_release(vde->rst); -release_mc_reset: - reset_control_release(vde->rst_mc); - - return err; -} - -static int tegra_vde_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct tegra_vde *vde; - int irq, err; - - vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL); - if (!vde) - return -ENOMEM; - - platform_set_drvdata(pdev, vde); - - vde->soc = of_device_get_match_data(&pdev->dev); - - vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe"); - if (IS_ERR(vde->sxe)) - return PTR_ERR(vde->sxe); - - vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev"); - if (IS_ERR(vde->bsev)) - return PTR_ERR(vde->bsev); - - vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe"); - if (IS_ERR(vde->mbe)) - return PTR_ERR(vde->mbe); - - vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe"); - if (IS_ERR(vde->ppe)) - return PTR_ERR(vde->ppe); - - vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce"); - if (IS_ERR(vde->mce)) - return PTR_ERR(vde->mce); - - vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe"); - if (IS_ERR(vde->tfe)) - return PTR_ERR(vde->tfe); - - vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb"); - if (IS_ERR(vde->ppb)) - return PTR_ERR(vde->ppb); - - vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma"); - if (IS_ERR(vde->vdma)) - return PTR_ERR(vde->vdma); - - vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid"); - if (IS_ERR(vde->frameid)) - return PTR_ERR(vde->frameid); - - vde->clk = devm_clk_get(dev, NULL); - if (IS_ERR(vde->clk)) { - err = PTR_ERR(vde->clk); - dev_err(dev, "Could not get VDE clk %d\n", err); - return err; - } - - vde->rst = devm_reset_control_get_exclusive_released(dev, NULL); - if (IS_ERR(vde->rst)) { - err = PTR_ERR(vde->rst); - dev_err(dev, "Could not get VDE reset %d\n", err); - return err; - } - - vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc"); - if (IS_ERR(vde->rst_mc)) { - err = PTR_ERR(vde->rst_mc); - dev_err(dev, "Could not get MC reset %d\n", err); - return err; - } - - irq = platform_get_irq_byname(pdev, "sync-token"); - if (irq < 0) - return irq; - - err = devm_request_irq(dev, irq, tegra_vde_isr, 0, - dev_name(dev), vde); - if (err) { - dev_err(dev, "Could not request IRQ %d\n", err); - return err; - } - - err = devm_tegra_core_dev_init_opp_table_common(dev); - if (err) { - dev_err(dev, "Could initialize OPP table %d\n", err); - return err; - } - - vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0); - if (!vde->iram_pool) { - dev_err(dev, "Could not get IRAM pool\n"); - return -EPROBE_DEFER; - } - - vde->iram = gen_pool_dma_alloc(vde->iram_pool, - gen_pool_size(vde->iram_pool), - &vde->iram_lists_addr); - if (!vde->iram) { - dev_err(dev, "Could not reserve IRAM\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&vde->map_list); - mutex_init(&vde->map_lock); - mutex_init(&vde->lock); - init_completion(&vde->decode_completion); - - vde->miscdev.minor = MISC_DYNAMIC_MINOR; - vde->miscdev.name = "tegra_vde"; - vde->miscdev.fops = &tegra_vde_fops; - vde->miscdev.parent = dev; - - err = tegra_vde_iommu_init(vde); - if (err) { - dev_err(dev, "Failed to initialize IOMMU: %d\n", err); - goto err_gen_free; - } - - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 300); - - /* - * VDE partition may be left ON after bootloader, hence let's - * power-cycle it in order to put hardware into a predictable lower - * power state. - */ - err = pm_runtime_resume_and_get(dev); - if (err) - goto err_pm_runtime; - - pm_runtime_put(dev); - - err = tegra_vde_alloc_bo(vde, &vde->secure_bo, DMA_FROM_DEVICE, 4096); - if (err) { - dev_err(dev, "Failed to allocate secure BO: %d\n", err); - goto err_pm_runtime; - } - - err = misc_register(&vde->miscdev); - if (err) { - dev_err(dev, "Failed to register misc device: %d\n", err); - goto err_free_secure_bo; - } - - return 0; - -err_free_secure_bo: - tegra_vde_free_bo(vde->secure_bo); -err_pm_runtime: - pm_runtime_dont_use_autosuspend(dev); - pm_runtime_disable(dev); - - tegra_vde_iommu_deinit(vde); - -err_gen_free: - gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, - gen_pool_size(vde->iram_pool)); - - return err; -} - -static int tegra_vde_remove(struct platform_device *pdev) -{ - struct tegra_vde *vde = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - - misc_deregister(&vde->miscdev); - - tegra_vde_free_bo(vde->secure_bo); - - /* - * As it increments RPM usage_count even on errors, we don't need to - * check the returned code here. - */ - pm_runtime_get_sync(dev); - - pm_runtime_dont_use_autosuspend(dev); - pm_runtime_disable(dev); - - /* - * Balance RPM state, the VDE power domain is left ON and hardware - * is clock-gated. It's safe to reboot machine now. - */ - pm_runtime_put_noidle(dev); - clk_disable_unprepare(vde->clk); - - tegra_vde_dmabuf_cache_unmap_all(vde); - tegra_vde_iommu_deinit(vde); - - gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, - gen_pool_size(vde->iram_pool)); - - return 0; -} - -static void tegra_vde_shutdown(struct platform_device *pdev) -{ - /* - * On some devices bootloader isn't ready to a power-gated VDE on - * a warm-reboot, machine will hang in that case. - */ - pm_runtime_get_sync(&pdev->dev); -} - -static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) -{ - struct tegra_vde *vde = dev_get_drvdata(dev); - int err; - - mutex_lock(&vde->lock); - - err = pm_runtime_force_suspend(dev); - if (err < 0) - return err; - - return 0; -} - -static __maybe_unused int tegra_vde_pm_resume(struct device *dev) -{ - struct tegra_vde *vde = dev_get_drvdata(dev); - int err; - - err = pm_runtime_force_resume(dev); - if (err < 0) - return err; - - mutex_unlock(&vde->lock); - - return 0; -} - -static const struct dev_pm_ops tegra_vde_pm_ops = { - SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend, - tegra_vde_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend, - tegra_vde_pm_resume) -}; - -static const struct tegra_vde_soc tegra124_vde_soc = { - .supports_ref_pic_marking = true, -}; - -static const struct tegra_vde_soc tegra114_vde_soc = { - .supports_ref_pic_marking = true, -}; - -static const struct tegra_vde_soc tegra30_vde_soc = { - .supports_ref_pic_marking = false, -}; - -static const struct tegra_vde_soc tegra20_vde_soc = { - .supports_ref_pic_marking = false, -}; - -static const struct of_device_id tegra_vde_of_match[] = { - { .compatible = "nvidia,tegra124-vde", .data = &tegra124_vde_soc }, - { .compatible = "nvidia,tegra114-vde", .data = &tegra114_vde_soc }, - { .compatible = "nvidia,tegra30-vde", .data = &tegra30_vde_soc }, - { .compatible = "nvidia,tegra20-vde", .data = &tegra20_vde_soc }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_vde_of_match); - -static struct platform_driver tegra_vde_driver = { - .probe = tegra_vde_probe, - .remove = tegra_vde_remove, - .shutdown = tegra_vde_shutdown, - .driver = { - .name = "tegra-vde", - .of_match_table = tegra_vde_of_match, - .pm = &tegra_vde_pm_ops, - }, -}; -module_platform_driver(tegra_vde_driver); - -MODULE_DESCRIPTION("NVIDIA Tegra Video Decoder driver"); -MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/tegra-vde/vde.h b/drivers/staging/media/tegra-vde/vde.h deleted file mode 100644 index bbd42b8d9991..000000000000 --- a/drivers/staging/media/tegra-vde/vde.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * NVIDIA Tegra Video decoder driver - * - * Copyright (C) 2016-2019 GRATE-DRIVER project - */ - -#ifndef TEGRA_VDE_H -#define TEGRA_VDE_H - -#include <linux/completion.h> -#include <linux/dma-direction.h> -#include <linux/iova.h> -#include <linux/list.h> -#include <linux/miscdevice.h> -#include <linux/mutex.h> -#include <linux/types.h> - -struct clk; -struct dma_buf; -struct gen_pool; -struct iommu_group; -struct iommu_domain; -struct reset_control; -struct dma_buf_attachment; - -struct tegra_vde_soc { - bool supports_ref_pic_marking; -}; - -struct tegra_vde_bo { - struct iova *iova; - struct sg_table sgt; - struct tegra_vde *vde; - enum dma_data_direction dma_dir; - unsigned long dma_attrs; - dma_addr_t dma_handle; - dma_addr_t dma_addr; - void *dma_cookie; - size_t size; -}; - -struct tegra_vde { - void __iomem *sxe; - void __iomem *bsev; - void __iomem *mbe; - void __iomem *ppe; - void __iomem *mce; - void __iomem *tfe; - void __iomem *ppb; - void __iomem *vdma; - void __iomem *frameid; - struct mutex lock; - struct mutex map_lock; - struct list_head map_list; - struct miscdevice miscdev; - struct reset_control *rst; - struct reset_control *rst_mc; - struct gen_pool *iram_pool; - struct completion decode_completion; - struct clk *clk; - struct iommu_domain *domain; - struct iommu_group *group; - struct iova_domain iova; - struct iova *iova_resv_static_addresses; - struct iova *iova_resv_last_page; - const struct tegra_vde_soc *soc; - struct tegra_vde_bo *secure_bo; - dma_addr_t iram_lists_addr; - u32 *iram; -}; - -int tegra_vde_iommu_init(struct tegra_vde *vde); -void tegra_vde_iommu_deinit(struct tegra_vde *vde); -int tegra_vde_iommu_map(struct tegra_vde *vde, - struct sg_table *sgt, - struct iova **iovap, - size_t size); -void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova); - -int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde, - struct dma_buf *dmabuf, - enum dma_data_direction dma_dir, - struct dma_buf_attachment **ap, - dma_addr_t *addrp); -void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde, - struct dma_buf_attachment *a, - bool release); -void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde); -void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde); - -static __maybe_unused char const * -tegra_vde_reg_base_name(struct tegra_vde *vde, void __iomem *base) -{ - if (vde->sxe == base) - return "SXE"; - - if (vde->bsev == base) - return "BSEV"; - - if (vde->mbe == base) - return "MBE"; - - if (vde->ppe == base) - return "PPE"; - - if (vde->mce == base) - return "MCE"; - - if (vde->tfe == base) - return "TFE"; - - if (vde->ppb == base) - return "PPB"; - - if (vde->vdma == base) - return "VDMA"; - - if (vde->frameid == base) - return "FRAMEID"; - - return "???"; -} - -#endif /* TEGRA_VDE_H */ diff --git a/drivers/staging/media/tegra-video/Kconfig b/drivers/staging/media/tegra-video/Kconfig index 1f35da4b134e..df1b2cff2417 100644 --- a/drivers/staging/media/tegra-video/Kconfig +++ b/drivers/staging/media/tegra-video/Kconfig @@ -2,7 +2,7 @@ config VIDEO_TEGRA tristate "NVIDIA Tegra VI driver" depends on TEGRA_HOST1X - depends on VIDEO_V4L2 + depends on VIDEO_DEV select MEDIA_CONTROLLER select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE diff --git a/drivers/staging/media/zoran/Kconfig b/drivers/staging/media/zoran/Kconfig index 7874842033ca..3fb3e27e04a8 100644 --- a/drivers/staging/media/zoran/Kconfig +++ b/drivers/staging/media/zoran/Kconfig @@ -1,8 +1,19 @@ config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux (Deprecated)" - depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 + depends on PCI && I2C_ALGOBIT && VIDEO_DEV depends on !ALPHA + depends on DEBUG_FS select VIDEOBUF2_DMA_CONTIG + select VIDEO_ADV7170 if VIDEO_ZORAN_LML33R10 + select VIDEO_ADV7175 if VIDEO_ZORAN_DC10 || VIDEO_ZORAN_DC30 + select VIDEO_BT819 if VIDEO_ZORAN_LML33 + select VIDEO_BT856 if VIDEO_ZORAN_LML33 || VIDEO_ZORAN_AVS6EYES + select VIDEO_BT866 if VIDEO_ZORAN_AVS6EYES + select VIDEO_KS0127 if VIDEO_ZORAN_AVS6EYES + select VIDEO_SAA711X if VIDEO_ZORAN_BUZ || VIDEO_ZORAN_LML33R10 + select VIDEO_SAA7110 if VIDEO_ZORAN_DC10 + select VIDEO_SAA7185 if VIDEO_ZORAN_BUZ + select VIDEO_VPX3220 if VIDEO_ZORAN_DC30 help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega @@ -14,17 +25,15 @@ config VIDEO_ZORAN module will be called zr36067. config VIDEO_ZORAN_DC30 - tristate "Pinnacle/Miro DC30(+) support" + bool "Pinnacle/Miro DC30(+) support" depends on VIDEO_ZORAN - select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback card. This also supports really old DC10 cards based on the zr36050 MJPEG codec and zr36016 VFE. config VIDEO_ZORAN_ZR36060 - tristate "Zoran ZR36060" + bool "Zoran ZR36060" depends on VIDEO_ZORAN help Say Y to support Zoran boards based on 36060 chips. @@ -32,45 +41,34 @@ config VIDEO_ZORAN_ZR36060 and 33 R10 and AverMedia 6 boards. config VIDEO_ZORAN_BUZ - tristate "Iomega Buz support" + bool "Iomega Buz support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT help Support for the Iomega Buz MJPEG capture/playback card. config VIDEO_ZORAN_DC10 - tristate "Pinnacle/Miro DC10(+) support" + bool "Pinnacle/Miro DC10(+) support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT help Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback card. config VIDEO_ZORAN_LML33 - tristate "Linux Media Labs LML33 support" + bool "Linux Media Labs LML33 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT help Support for the Linux Media Labs LML33 MJPEG capture/playback card. config VIDEO_ZORAN_LML33R10 - tristate "Linux Media Labs LML33R10 support" + bool "Linux Media Labs LML33R10 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT help support for the Linux Media Labs LML33R10 MJPEG capture/playback card. config VIDEO_ZORAN_AVS6EYES - tristate "AverMedia 6 Eyes support" + bool "AverMedia 6 Eyes support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT help Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/staging/media/zoran/Makefile b/drivers/staging/media/zoran/Makefile index 7023158e3892..9603bac0195c 100644 --- a/drivers/staging/media/zoran/Makefile +++ b/drivers/staging/media/zoran/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 zr36067-objs := zoran_device.o \ - zoran_driver.o zoran_card.o + zoran_driver.o zoran_card.o videocodec.o -obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o -obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o -obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o +obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o +zr36067-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o +zr36067-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o diff --git a/drivers/staging/media/zoran/videocodec.c b/drivers/staging/media/zoran/videocodec.c index 28031d3fd757..3af7d02bd910 100644 --- a/drivers/staging/media/zoran/videocodec.c +++ b/drivers/staging/media/zoran/videocodec.c @@ -8,31 +8,21 @@ * (c) 2002 Wolfgang Scherr <scherr@net4you.at> */ -#define VIDEOCODEC_VERSION "v0.2" - #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/slab.h> -// kernel config is here (procfs flag) - -#ifdef CONFIG_PROC_FS -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#endif - #include "videocodec.h" -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); +static int videocodec_debug; +module_param(videocodec_debug, int, 0); +MODULE_PARM_DESC(videocodec_debug, "Debug level (0-4)"); #define dprintk(num, format, args...) \ do { \ - if (debug >= num) \ + if (videocodec_debug >= num) \ printk(format, ##args); \ } while (0) @@ -80,12 +70,9 @@ struct videocodec *videocodec_attach(struct videocodec_master *master) if ((master->flags & h->codec->flags) == master->flags) { dprintk(4, "%s: try '%s'\n", __func__, h->codec->name); - if (!try_module_get(h->codec->owner)) - return NULL; - codec = kmemdup(h->codec, sizeof(struct videocodec), GFP_KERNEL); if (!codec) - goto out_module_put; + goto out_kfree; res = strlen(codec->name); snprintf(codec->name + res, sizeof(codec->name) - res, "[%d]", h->attached); @@ -121,13 +108,10 @@ struct videocodec *videocodec_attach(struct videocodec_master *master) pr_err("%s: no codec found!\n", __func__); return NULL; - out_module_put: - module_put(h->codec->owner); out_kfree: kfree(codec); return NULL; } -EXPORT_SYMBOL(videocodec_attach); int videocodec_detach(struct videocodec *codec) { @@ -168,7 +152,6 @@ int videocodec_detach(struct videocodec *codec) prev->next = a->next; dprintk(4, "videocodec: delete middle\n"); } - module_put(a->codec->owner); kfree(a->codec); kfree(a); h->attached -= 1; @@ -183,7 +166,6 @@ int videocodec_detach(struct videocodec *codec) pr_err("%s: given codec not found!\n", __func__); return -EINVAL; } -EXPORT_SYMBOL(videocodec_detach); int videocodec_register(const struct videocodec *codec) { @@ -216,7 +198,6 @@ int videocodec_register(const struct videocodec *codec) return 0; } -EXPORT_SYMBOL(videocodec_register); int videocodec_unregister(const struct videocodec *codec) { @@ -263,10 +244,8 @@ int videocodec_unregister(const struct videocodec *codec) pr_err("%s: given codec not found!\n", __func__); return -EINVAL; } -EXPORT_SYMBOL(videocodec_unregister); -#ifdef CONFIG_PROC_FS -static int proc_videocodecs_show(struct seq_file *m, void *v) +int videocodec_debugfs_show(struct seq_file *m) { struct codec_list *h = codeclist_top; struct attached_list *a; @@ -293,38 +272,3 @@ static int proc_videocodecs_show(struct seq_file *m, void *v) return 0; } -#endif - -/* ===================== */ -/* hook in driver module */ -/* ===================== */ -static int __init videocodec_init(void) -{ -#ifdef CONFIG_PROC_FS - static struct proc_dir_entry *videocodec_proc_entry; -#endif - - pr_info("Linux video codec intermediate layer: %s\n", VIDEOCODEC_VERSION); - -#ifdef CONFIG_PROC_FS - videocodec_proc_entry = proc_create_single("videocodecs", 0, NULL, proc_videocodecs_show); - if (!videocodec_proc_entry) - pr_err("videocodec: can't init procfs.\n"); -#endif - return 0; -} - -static void __exit videocodec_exit(void) -{ -#ifdef CONFIG_PROC_FS - remove_proc_entry("videocodecs", NULL); -#endif -} - -module_init(videocodec_init); -module_exit(videocodec_exit); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Intermediate API module for video codecs " - VIDEOCODEC_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/zoran/videocodec.h b/drivers/staging/media/zoran/videocodec.h index 8a5003dda9f4..9dea348fee40 100644 --- a/drivers/staging/media/zoran/videocodec.h +++ b/drivers/staging/media/zoran/videocodec.h @@ -123,6 +123,7 @@ M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) #ifndef __LINUX_VIDEOCODEC_H #define __LINUX_VIDEOCODEC_H +#include <linux/debugfs.h> #include <linux/videodev2.h> #define CODEC_DO_COMPRESSION 0 @@ -233,7 +234,6 @@ struct jpeg_app_marker { }; struct videocodec { - struct module *owner; /* -- filled in by slave device during register -- */ char name[32]; unsigned long magic; /* may be used for client<->master attaching */ @@ -305,4 +305,6 @@ extern int videocodec_unregister(const struct videocodec *); /* the other calls are directly done via the videocodec structure! */ +int videocodec_debugfs_show(struct seq_file *m); + #endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h index b1ad2a2b914c..654c95fa5aba 100644 --- a/drivers/staging/media/zoran/zoran.h +++ b/drivers/staging/media/zoran/zoran.h @@ -18,6 +18,7 @@ #ifndef _BUZ_H_ #define _BUZ_H_ +#include <linux/debugfs.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/videobuf2-core.h> @@ -53,22 +54,8 @@ static inline struct zr_buffer *vb2_to_zr_buffer(struct vb2_buffer *vb) #define BUZ_NUM_STAT_COM 4 #define BUZ_MASK_STAT_COM 3 -#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ -#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ - #define BUZ_MAX_INPUT 16 -#if VIDEO_MAX_FRAME <= 32 -# define V4L_MAX_FRAME 32 -#elif VIDEO_MAX_FRAME <= 64 -# define V4L_MAX_FRAME 64 -#else -# error "Too many video frame buffers to handle" -#endif -#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) - -#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME) - #include "zr36057.h" enum card_type { @@ -295,6 +282,7 @@ struct zoran { struct list_head queued_bufs; spinlock_t queued_bufs_lock; /* Protects queued_bufs */ struct zr_buffer *inuse[BUZ_NUM_STAT_COM * 2]; + struct dentry *dbgfs_dir; }; static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) @@ -313,6 +301,6 @@ static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) #endif -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq); +int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir); void zoran_queue_exit(struct zoran *zr); int zr_set_buf(struct zoran *zr); diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c index f259585b0689..26f978a1cc72 100644 --- a/drivers/staging/media/zoran/zoran_card.c +++ b/drivers/staging/media/zoran/zoran_card.c @@ -29,6 +29,9 @@ #include "zoran.h" #include "zoran_card.h" #include "zoran_device.h" +#include "zr36016.h" +#include "zr36050.h" +#include "zr36060.h" extern const struct zoran_format zoran_formats[]; @@ -36,17 +39,6 @@ static int card[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "Card type"); -/* - * The video mem address of the video card. The driver has a little database - * for some videocards to determine it from there. If your video card is not - * in there you have either to give it to the driver as a parameter or set - * in a VIDIOCSFBUF ioctl - */ - -static unsigned long vidmem; /* default = 0 - Video memory base address */ -module_param_hw(vidmem, ulong, iomem, 0444); -MODULE_PARM_DESC(vidmem, "Default video memory base address"); - /* Default input and video norm at startup of the driver. */ static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ @@ -68,20 +60,6 @@ static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); -int v4l_nbufs = 4; -int v4l_bufsize = 864; /* Everybody should be able to work with this setting */ -module_param(v4l_nbufs, int, 0644); -MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); -module_param(v4l_bufsize, int, 0644); -MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)"); - -int jpg_nbufs = 32; -int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */ -module_param(jpg_nbufs, int, 0644); -MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use"); -module_param(jpg_bufsize, int, 0644); -MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)"); - /* 1=Pass through TV signal when device is not used */ /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ int pass_through; @@ -266,6 +244,96 @@ static const char *codecid_to_modulename(u16 codecid) return name; } +static int codec_init(struct zoran *zr, u16 codecid) +{ + switch (codecid) { + case CODEC_TYPE_ZR36060: +#ifdef CONFIG_VIDEO_ZORAN_ZR36060 + return zr36060_init_module(); +#else + pci_err(zr->pci_dev, "ZR36060 support is not enabled\n"); + return -EINVAL; +#endif + break; + case CODEC_TYPE_ZR36050: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + return zr36050_init_module(); +#else + pci_err(zr->pci_dev, "ZR36050 support is not enabled\n"); + return -EINVAL; +#endif + break; + case CODEC_TYPE_ZR36016: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + return zr36016_init_module(); +#else + pci_err(zr->pci_dev, "ZR36016 support is not enabled\n"); + return -EINVAL; +#endif + break; + } + + pci_err(zr->pci_dev, "unknown codec id %x\n", codecid); + return -EINVAL; +} + +static void codec_exit(struct zoran *zr, u16 codecid) +{ + switch (codecid) { + case CODEC_TYPE_ZR36060: +#ifdef CONFIG_VIDEO_ZORAN_ZR36060 + zr36060_cleanup_module(); +#endif + break; + case CODEC_TYPE_ZR36050: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + zr36050_cleanup_module(); +#endif + break; + case CODEC_TYPE_ZR36016: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + zr36016_cleanup_module(); +#endif + break; + } +} + +static int videocodec_init(struct zoran *zr) +{ + const char *codec_name, *vfe_name; + int result; + + codec_name = codecid_to_modulename(zr->card.video_codec); + if (codec_name) { + result = codec_init(zr, zr->card.video_codec); + if (result < 0) { + pci_err(zr->pci_dev, "failed to load video codec %s: %d\n", + codec_name, result); + return result; + } + } + vfe_name = codecid_to_modulename(zr->card.video_vfe); + if (vfe_name) { + result = codec_init(zr, zr->card.video_vfe); + if (result < 0) { + pci_err(zr->pci_dev, "failed to load video vfe %s: %d\n", + vfe_name, result); + if (codec_name) + codec_exit(zr, zr->card.video_codec); + return result; + } + } + return 0; +} + +static void videocodec_exit(struct zoran *zr) +{ + if (zr->card.video_codec != CODEC_TYPE_NONE) + codec_exit(zr, zr->card.video_codec); + if (zr->card.video_vfe != CODEC_TYPE_NONE) + codec_exit(zr, zr->card.video_vfe); +} + // struct tvnorm { // u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; // }; @@ -803,6 +871,99 @@ int zoran_check_jpg_settings(struct zoran *zr, return 0; } +static int zoran_init_video_device(struct zoran *zr, struct video_device *video_dev, int dir) +{ + int err; + + /* Now add the template and register the device unit. */ + *video_dev = zoran_template; + video_dev->v4l2_dev = &zr->v4l2_dev; + video_dev->lock = &zr->lock; + video_dev->device_caps = V4L2_CAP_STREAMING | dir; + + strscpy(video_dev->name, ZR_DEVNAME(zr), sizeof(video_dev->name)); + /* + * It's not a mem2mem device, but you can both capture and output from one and the same + * device. This should really be split up into two device nodes, but that's a job for + * another day. + */ + video_dev->vfl_dir = VFL_DIR_M2M; + zoran_queue_init(zr, &zr->vq, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + err = video_register_device(video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); + if (err < 0) + return err; + video_set_drvdata(video_dev, zr); + return 0; +} + +static void zoran_exit_video_devices(struct zoran *zr) +{ + video_unregister_device(zr->video_dev); + kfree(zr->video_dev); +} + +static int zoran_init_video_devices(struct zoran *zr) +{ + int err; + + zr->video_dev = video_device_alloc(); + if (!zr->video_dev) + return -ENOMEM; + + err = zoran_init_video_device(zr, zr->video_dev, V4L2_CAP_VIDEO_CAPTURE); + if (err) + kfree(zr->video_dev); + return err; +} + +/* + * v4l2_device_unregister() will care about removing zr->encoder/zr->decoder + * via v4l2_i2c_subdev_unregister() + */ +static int zoran_i2c_init(struct zoran *zr) +{ + int err; + + pci_info(zr->pci_dev, "Initializing i2c bus...\n"); + + err = zoran_register_i2c(zr); + if (err) { + pci_err(zr->pci_dev, "%s - cannot initialize i2c bus\n", __func__); + return err; + } + + zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, + zr->card.i2c_decoder, 0, + zr->card.addrs_decoder); + if (!zr->decoder) { + pci_err(zr->pci_dev, "Fail to get decoder %s\n", zr->card.i2c_decoder); + err = -EINVAL; + goto error_decoder; + } + + if (zr->card.i2c_encoder) { + zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, + zr->card.i2c_encoder, 0, + zr->card.addrs_encoder); + if (!zr->encoder) { + pci_err(zr->pci_dev, "Fail to get encoder %s\n", zr->card.i2c_encoder); + err = -EINVAL; + goto error_decoder; + } + } + return 0; + +error_decoder: + zoran_unregister_i2c(zr); + return err; +} + +static void zoran_i2c_exit(struct zoran *zr) +{ + zoran_unregister_i2c(zr); +} + void zoran_open_init_params(struct zoran *zr) { int i; @@ -874,17 +1035,11 @@ static int zr36057_init(struct zoran *zr) zoran_open_init_params(zr); /* allocate memory *before* doing anything to the hardware in case allocation fails */ - zr->video_dev = video_device_alloc(); - if (!zr->video_dev) { - err = -ENOMEM; - goto exit; - } zr->stat_com = dma_alloc_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), &zr->p_sc, GFP_KERNEL); if (!zr->stat_com) { - err = -ENOMEM; - goto exit_video; + return -ENOMEM; } for (j = 0; j < BUZ_NUM_STAT_COM; j++) zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ @@ -897,26 +1052,9 @@ static int zr36057_init(struct zoran *zr) goto exit_statcom; } - /* Now add the template and register the device unit. */ - *zr->video_dev = zoran_template; - zr->video_dev->v4l2_dev = &zr->v4l2_dev; - zr->video_dev->lock = &zr->lock; - zr->video_dev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - - strscpy(zr->video_dev->name, ZR_DEVNAME(zr), sizeof(zr->video_dev->name)); - /* - * It's not a mem2mem device, but you can both capture and output from one and the same - * device. This should really be split up into two device nodes, but that's a job for - * another day. - */ - zr->video_dev->vfl_dir = VFL_DIR_M2M; - - zoran_queue_init(zr, &zr->vq); - - err = video_register_device(zr->video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); - if (err < 0) + err = zoran_init_video_devices(zr); + if (err) goto exit_statcomb; - video_set_drvdata(zr->video_dev, zr); zoran_init_hardware(zr); if (!pass_through) { @@ -931,9 +1069,6 @@ exit_statcomb: dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); exit_statcom: dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), zr->stat_com, zr->p_sc); -exit_video: - kfree(zr->video_dev); -exit: return err; } @@ -945,6 +1080,8 @@ static void zoran_remove(struct pci_dev *pdev) if (!zr->initialized) goto exit_free; + debugfs_remove_recursive(zr->dbgfs_dir); + zoran_queue_exit(zr); /* unregister videocodec bus */ @@ -952,9 +1089,10 @@ static void zoran_remove(struct pci_dev *pdev) videocodec_detach(zr->codec); if (zr->vfe) videocodec_detach(zr->vfe); + videocodec_exit(zr); /* unregister i2c bus */ - zoran_unregister_i2c(zr); + zoran_i2c_exit(zr); /* disable PCI bus-mastering */ zoran_set_pci_master(zr, 0); /* put chip into reset */ @@ -965,7 +1103,7 @@ static void zoran_remove(struct pci_dev *pdev) dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); pci_release_regions(pdev); pci_disable_device(zr->pci_dev); - video_unregister_device(zr->video_dev); + zoran_exit_video_devices(zr); exit_free: v4l2_ctrl_handler_free(&zr->hdl); v4l2_device_unregister(&zr->v4l2_dev); @@ -1051,6 +1189,39 @@ static const struct v4l2_ctrl_ops zoran_video_ctrl_ops = { .s_ctrl = zoran_video_set_ctrl, }; +static int zoran_debugfs_show(struct seq_file *seq, void *v) +{ + struct zoran *zr = seq->private; + + seq_printf(seq, "Running mode %x\n", zr->running); + seq_printf(seq, "Codec mode %x\n", zr->codec_mode); + seq_printf(seq, "Norm %llx\n", zr->norm); + seq_printf(seq, "Input %d\n", zr->input); + seq_printf(seq, "Buffersize %d\n", zr->buffer_size); + + seq_printf(seq, "V4L width %dx%d\n", zr->v4l_settings.width, zr->v4l_settings.height); + seq_printf(seq, "V4L bytesperline %d\n", zr->v4l_settings.bytesperline); + + seq_printf(seq, "JPG decimation %u\n", zr->jpg_settings.decimation); + seq_printf(seq, "JPG hor_dcm %u\n", zr->jpg_settings.hor_dcm); + seq_printf(seq, "JPG ver_dcm %u\n", zr->jpg_settings.ver_dcm); + seq_printf(seq, "JPG tmp_dcm %u\n", zr->jpg_settings.tmp_dcm); + seq_printf(seq, "JPG odd_even %u\n", zr->jpg_settings.odd_even); + seq_printf(seq, "JPG crop %dx%d %d %d\n", + zr->jpg_settings.img_x, + zr->jpg_settings.img_y, + zr->jpg_settings.img_width, + zr->jpg_settings.img_height); + + seq_printf(seq, "Prepared %u\n", zr->prepared); + seq_printf(seq, "Queued %u\n", zr->queued); + + videocodec_debugfs_show(seq); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(zoran_debugfs); + /* * Scan for a Buz card (actually for the PCI controller ZR36057), * request the irq and map the io memory @@ -1063,14 +1234,22 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct videocodec_master *master_vfe = NULL; struct videocodec_master *master_codec = NULL; int card_num; - const char *codec_name, *vfe_name; unsigned int nr; int err; + pci_info(pdev, "Zoran MJPEG board driver version %s\n", ZORAN_VERSION); + + /* some mainboards might not do PCI-PCI data transfer well */ + if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) + pci_warn(pdev, "%s: chipset does not support reliable PCI-PCI DMA\n", + ZORAN_NAME); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (err) - return -ENODEV; - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + return err; + err = vb2_dma_contig_set_max_seg_size(&pdev->dev, U32_MAX); + if (err) + return err; nr = zoran_num++; if (nr >= BUZ_MAX) { @@ -1174,41 +1353,15 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } zr36057_restart(zr); - /* i2c */ - pci_info(zr->pci_dev, "Initializing i2c bus...\n"); - if (zoran_register_i2c(zr) < 0) { - pci_err(pdev, "%s - can't initialize i2c bus\n", __func__); + err = zoran_i2c_init(zr); + if (err) goto zr_free_irq; - } - - zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_decoder, 0, - zr->card.addrs_decoder); - - if (zr->card.i2c_encoder) - zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_encoder, 0, - zr->card.addrs_encoder); pci_info(zr->pci_dev, "Initializing videocodec bus...\n"); - - if (zr->card.video_codec) { - codec_name = codecid_to_modulename(zr->card.video_codec); - if (codec_name) { - result = request_module(codec_name); - if (result) - pci_err(pdev, "failed to load modules %s: %d\n", codec_name, result); - } - } - if (zr->card.video_vfe) { - vfe_name = codecid_to_modulename(zr->card.video_vfe); - if (vfe_name) { - result = request_module(vfe_name); - if (result < 0) - pci_err(pdev, "failed to load modules %s: %d\n", vfe_name, result); - } - } + err = videocodec_init(zr); + if (err) + goto zr_unreg_i2c; /* reset JPEG codec */ jpeg_codec_sleep(zr, 1); @@ -1218,15 +1371,15 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (zr->card.video_codec != 0) { master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); if (!master_codec) - goto zr_unreg_i2c; + goto zr_unreg_videocodec; zr->codec = videocodec_attach(master_codec); if (!zr->codec) { pci_err(pdev, "%s - no codec found\n", __func__); - goto zr_unreg_i2c; + goto zr_unreg_videocodec; } if (zr->codec->type != zr->card.video_codec) { pci_err(pdev, "%s - wrong codec\n", __func__); - goto zr_detach_codec; + goto zr_unreg_videocodec; } } if (zr->card.video_vfe != 0) { @@ -1253,14 +1406,19 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) zr->map_mode = ZORAN_MAP_MODE_RAW; + zr->dbgfs_dir = debugfs_create_dir(ZR_DEVNAME(zr), NULL); + debugfs_create_file("debug", 0444, zr->dbgfs_dir, zr, + &zoran_debugfs_fops); return 0; zr_detach_vfe: videocodec_detach(zr->vfe); zr_detach_codec: videocodec_detach(zr->codec); +zr_unreg_videocodec: + videocodec_exit(zr); zr_unreg_i2c: - zoran_unregister_i2c(zr); + zoran_i2c_exit(zr); zr_free_irq: btwrite(0, ZR36057_SPGPPCR); pci_free_irq(zr->pci_dev, 0, zr); @@ -1281,54 +1439,4 @@ static struct pci_driver zoran_driver = { .remove = zoran_remove, }; -static int __init zoran_init(void) -{ - int res; - - pr_info("Zoran MJPEG board driver version %s\n", ZORAN_VERSION); - - /* check the parameters we have been given, adjust if necessary */ - if (v4l_nbufs < 2) - v4l_nbufs = 2; - if (v4l_nbufs > VIDEO_MAX_FRAME) - v4l_nbufs = VIDEO_MAX_FRAME; - /* The user specifies the in KB, we want them in byte (and page aligned) */ - v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); - if (v4l_bufsize < 32768) - v4l_bufsize = 32768; - /* 2 MB is arbitrary but sufficient for the maximum possible images */ - if (v4l_bufsize > 2048 * 1024) - v4l_bufsize = 2048 * 1024; - if (jpg_nbufs < 4) - jpg_nbufs = 4; - if (jpg_nbufs > BUZ_MAX_FRAME) - jpg_nbufs = BUZ_MAX_FRAME; - jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024); - if (jpg_bufsize < 8192) - jpg_bufsize = 8192; - if (jpg_bufsize > (512 * 1024)) - jpg_bufsize = 512 * 1024; - /* Use parameter for vidmem or try to find a video card */ - if (vidmem) - pr_info("%s: Using supplied video memory base address @ 0x%lx\n", ZORAN_NAME, vidmem); - - /* some mainboards might not do PCI-PCI data transfer well */ - if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) - pr_warn("%s: chipset does not support reliable PCI-PCI DMA\n", ZORAN_NAME); - - res = pci_register_driver(&zoran_driver); - if (res) { - pr_err("Unable to register ZR36057 driver\n"); - return res; - } - - return 0; -} - -static void __exit zoran_exit(void) -{ - pci_unregister_driver(&zoran_driver); -} - -module_init(zoran_init); -module_exit(zoran_exit); +module_pci_driver(zoran_driver); diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c index 5b12a730a229..2470889a58fa 100644 --- a/drivers/staging/media/zoran/zoran_device.c +++ b/drivers/staging/media/zoran/zoran_device.c @@ -239,7 +239,7 @@ static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, wa = tvn->wa; ha = tvn->ha; - pci_info(zr->pci_dev, "set_vfe() - width = %d, height = %d\n", video_width, video_height); + pci_dbg(zr->pci_dev, "set_vfe() - width = %d, height = %d\n", video_width, video_height); if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT || @@ -664,7 +664,7 @@ void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO clear_interrupt_counters(zr); - pci_info(zr->pci_dev, "enable_jpg(MOTION_COMPRESS)\n"); + pci_dbg(zr->pci_dev, "enable_jpg(MOTION_COMPRESS)\n"); break; } @@ -693,7 +693,7 @@ void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO clear_interrupt_counters(zr); - pci_info(zr->pci_dev, "enable_jpg(MOTION_DECOMPRESS)\n"); + pci_dbg(zr->pci_dev, "enable_jpg(MOTION_DECOMPRESS)\n"); break; case BUZ_MODE_IDLE: @@ -720,7 +720,7 @@ void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) decoder_call(zr, video, s_stream, 1); encoder_call(zr, video, s_routing, 0, 0, 0); - pci_info(zr->pci_dev, "enable_jpg(IDLE)\n"); + pci_dbg(zr->pci_dev, "enable_jpg(IDLE)\n"); break; } } @@ -814,7 +814,7 @@ static void zoran_reap_stat_com(struct zoran *zr) if (zr->jpg_settings.tmp_dcm == 1) i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; else - i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2 + 1; + i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; stat_com = le32_to_cpu(zr->stat_com[i]); if ((stat_com & 1) == 0) { @@ -826,6 +826,11 @@ static void zoran_reap_stat_com(struct zoran *zr) size = (stat_com & GENMASK(22, 1)) >> 1; buf = zr->inuse[i]; + if (!buf) { + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + pci_err(zr->pci_dev, "No buffer at slot %d\n", i); + return; + } buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { diff --git a/drivers/staging/media/zoran/zoran_device.h b/drivers/staging/media/zoran/zoran_device.h index 6c5d70238228..322b04c55d41 100644 --- a/drivers/staging/media/zoran/zoran_device.h +++ b/drivers/staging/media/zoran/zoran_device.h @@ -47,9 +47,7 @@ extern void zr36057_restart(struct zoran *zr); extern const struct zoran_format zoran_formats[]; -extern int v4l_nbufs; extern int v4l_bufsize; -extern int jpg_nbufs; extern int jpg_bufsize; extern int pass_through; diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 46382e43f1bf..4304b7e21709 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -153,8 +153,6 @@ static __u32 zoran_v4l2_calc_bufsize(struct zoran_jpg_settings *settings) result <<= 1; } - if (result > jpg_bufsize) - return jpg_bufsize; if (result < 8192) return 8192; @@ -173,7 +171,7 @@ static int zoran_v4l_set_format(struct zoran *zr, int width, int height, if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { - pci_err(zr->pci_dev, "%s - wrong frame size (%dx%d)\n", __func__, width, height); + pci_dbg(zr->pci_dev, "%s - wrong frame size (%dx%d)\n", __func__, width, height); return -EINVAL; } @@ -183,7 +181,7 @@ static int zoran_v4l_set_format(struct zoran *zr, int width, int height, /* Check against available buffer size */ if (height * width * bpp > zr->buffer_size) { - pci_err(zr->pci_dev, "%s - video buffer size (%d kB) is too small\n", + pci_dbg(zr->pci_dev, "%s - video buffer size (%d kB) is too small\n", __func__, zr->buffer_size >> 10); return -EINVAL; } @@ -191,7 +189,7 @@ static int zoran_v4l_set_format(struct zoran *zr, int width, int height, /* The video front end needs 4-byte alinged line sizes */ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { - pci_err(zr->pci_dev, "%s - wrong frame alignment\n", __func__); + pci_dbg(zr->pci_dev, "%s - wrong frame alignment\n", __func__); return -EINVAL; } @@ -207,7 +205,7 @@ static int zoran_set_norm(struct zoran *zr, v4l2_std_id norm) { if (!(norm & zr->card.norms)) { - pci_err(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); + pci_dbg(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); return -EINVAL; } @@ -233,7 +231,7 @@ static int zoran_set_input(struct zoran *zr, int input) return 0; if (input < 0 || input >= zr->card.inputs) { - pci_err(zr->pci_dev, "%s - unsupported input %d\n", __func__, input); + pci_dbg(zr->pci_dev, "%s - unsupported input %d\n", __func__, input); return -EINVAL; } @@ -255,8 +253,6 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability strscpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); strscpy(cap->driver, "zoran", sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(zr->pci_dev)); - cap->device_caps = zr->video_dev->device_caps; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -402,7 +398,6 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); - zr->buffer_size = fmt->fmt.pix.sizeimage; fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return res; @@ -437,6 +432,8 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * bpp; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height; return 0; } @@ -535,7 +532,7 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) break; if (i == NUM_FORMATS) { - pci_err(zr->pci_dev, "VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", + pci_dbg(zr->pci_dev, "VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", fmt->fmt.pix.pixelformat); /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ return -EINVAL; @@ -582,6 +579,9 @@ static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) struct zoran *zr = video_drvdata(file); int res = 0; + if (zr->norm == std) + return 0; + if (zr->running != ZORAN_MAP_MODE_NONE) return -EBUSY; @@ -666,7 +666,7 @@ static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selectio if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pci_err(zr->pci_dev, "%s invalid selection type combination\n", __func__); + pci_dbg(zr->pci_dev, "%s invalid selection type combination\n", __func__); return -EINVAL; } @@ -712,7 +712,7 @@ static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selectio return -EINVAL; if (zr->map_mode == ZORAN_MAP_MODE_RAW) { - pci_err(zr->pci_dev, "VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n"); + pci_dbg(zr->pci_dev, "VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n"); return -EINVAL; } @@ -734,14 +734,6 @@ static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selectio return res; } -static int zoran_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) -{ - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return 0; -} - /* * Output is disabled temporarily * Zoran is picky about jpeg data it accepts. At least it seems to unsupport COM and APPn. @@ -749,7 +741,6 @@ static int zoran_g_parm(struct file *file, void *priv, struct v4l2_streamparm *p */ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_querycap = zoran_querycap, - .vidioc_g_parm = zoran_g_parm, .vidioc_s_selection = zoran_s_selection, .vidioc_g_selection = zoran_g_selection, .vidioc_enum_input = zoran_enum_input, @@ -785,8 +776,6 @@ static const struct v4l2_file_operations zoran_fops = { .unlocked_ioctl = video_ioctl2, .open = v4l2_fh_open, .release = vb2_fop_release, - .read = vb2_fop_read, - .write = vb2_fop_write, .mmap = vb2_fop_mmap, .poll = vb2_fop_poll, }; @@ -869,6 +858,10 @@ int zr_set_buf(struct zoran *zr) vbuf = &buf->vbuf; buf->vbuf.field = V4L2_FIELD_INTERLACED; + if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) + buf->vbuf.field = V4L2_FIELD_INTERLACED; + else + buf->vbuf.field = V4L2_FIELD_TOP; vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, zr->buffer_size); vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); zr->inuse[0] = NULL; @@ -889,6 +882,7 @@ int zr_set_buf(struct zoran *zr) return -EINVAL; } list_del(&buf->queue); + zr->buf_in_reserve--; spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); vbuf = &buf->vbuf; @@ -928,9 +922,10 @@ static int zr_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) zr->stat_com[j] = cpu_to_le32(1); zr->inuse[j] = NULL; } + zr->vbseq = 0; if (zr->map_mode != ZORAN_MAP_MODE_RAW) { - pci_info(zr->pci_dev, "START JPG\n"); + pci_dbg(zr->pci_dev, "START JPG\n"); zr36057_restart(zr); zoran_init_hardware(zr); if (zr->map_mode == ZORAN_MAP_MODE_JPG_REC) @@ -944,7 +939,7 @@ static int zr_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } - pci_info(zr->pci_dev, "START RAW\n"); + pci_dbg(zr->pci_dev, "START RAW\n"); zr36057_restart(zr); zoran_init_hardware(zr); @@ -994,7 +989,7 @@ static void zr_vb2_stop_streaming(struct vb2_queue *vq) } spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); if (zr->buf_in_reserve) - pci_err(zr->pci_dev, "Buffer remaining %d\n", zr->buf_in_reserve); + pci_dbg(zr->pci_dev, "Buffer remaining %d\n", zr->buf_in_reserve); zr->map_mode = ZORAN_MAP_MODE_RAW; } @@ -1008,7 +1003,7 @@ static const struct vb2_ops zr_video_qops = { .wait_finish = vb2_ops_wait_finish, }; -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq) +int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) { int err; @@ -1016,8 +1011,9 @@ int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq) INIT_LIST_HEAD(&zr->queued_bufs); vq->dev = &zr->pci_dev->dev; - vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vq->io_modes = VB2_USERPTR | VB2_DMABUF | VB2_MMAP | VB2_READ | VB2_WRITE; + vq->type = dir; + + vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ | VB2_WRITE; vq->drv_priv = zr; vq->buf_struct_size = sizeof(struct zr_buffer); vq->ops = &zr_video_qops; diff --git a/drivers/staging/media/zoran/zr36016.c b/drivers/staging/media/zoran/zr36016.c index 9b350a885879..26c7c32b6bc0 100644 --- a/drivers/staging/media/zoran/zr36016.c +++ b/drivers/staging/media/zoran/zr36016.c @@ -22,14 +22,14 @@ /* amount of chips attached via this driver */ static int zr36016_codecs; -/* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); +static int zr36016_debug; +module_param(zr36016_debug, int, 0); +MODULE_PARM_DESC(zr36016_debug, "Debug level (0-4)"); + #define dprintk(num, format, args...) \ do { \ - if (debug >= num) \ + if (zr36016_debug >= num) \ printk(format, ##args); \ } while (0) @@ -120,7 +120,7 @@ static u8 zr36016_read_version(struct zr36016 *ptr) static int zr36016_basic_test(struct zr36016 *ptr) { - if (debug) { + if (zr36016_debug) { int i; zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); @@ -390,7 +390,6 @@ static int zr36016_setup(struct videocodec *codec) } static const struct videocodec zr36016_codec = { - .owner = THIS_MODULE, .name = "zr36016", .magic = 0L, /* magic not used */ .flags = @@ -409,14 +408,13 @@ static const struct videocodec zr36016_codec = { HOOK IN DRIVER AS KERNEL MODULE ========================================================================= */ -static int __init zr36016_init_module(void) +int zr36016_init_module(void) { - //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION); zr36016_codecs = 0; return videocodec_register(&zr36016_codec); } -static void __exit zr36016_cleanup_module(void) +void zr36016_cleanup_module(void) { if (zr36016_codecs) { dprintk(1, @@ -425,10 +423,3 @@ static void __exit zr36016_cleanup_module(void) } videocodec_unregister(&zr36016_codec); } - -module_init(zr36016_init_module); -module_exit(zr36016_cleanup_module); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Driver module for ZR36016 video frontends"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/zoran/zr36016.h b/drivers/staging/media/zoran/zr36016.h index 1475f971cc24..04afba35669d 100644 --- a/drivers/staging/media/zoran/zr36016.h +++ b/drivers/staging/media/zoran/zr36016.h @@ -89,4 +89,6 @@ struct zr36016 { #define ZR016_SIGN 0x02 #define ZR016_YMCS 0x01 +int zr36016_init_module(void); +void zr36016_cleanup_module(void); #endif /*fndef ZR36016_H */ diff --git a/drivers/staging/media/zoran/zr36050.c b/drivers/staging/media/zoran/zr36050.c index c62af27f2683..38f7021e7b06 100644 --- a/drivers/staging/media/zoran/zr36050.c +++ b/drivers/staging/media/zoran/zr36050.c @@ -5,8 +5,6 @@ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> */ -#define ZR050_VERSION "v0.7.1" - #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -32,13 +30,13 @@ static int zr36050_codecs; /* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); +static int zr36050_debug; +module_param(zr36050_debug, int, 0); +MODULE_PARM_DESC(zr36050_debug, "Debug level (0-4)"); #define dprintk(num, format, args...) \ do { \ - if (debug >= num) \ + if (zr36050_debug >= num) \ printk(format, ##args); \ } while (0) @@ -798,7 +796,6 @@ static int zr36050_setup(struct videocodec *codec) } static const struct videocodec zr36050_codec = { - .owner = THIS_MODULE, .name = "zr36050", .magic = 0L, // magic not used .flags = @@ -817,14 +814,13 @@ static const struct videocodec zr36050_codec = { HOOK IN DRIVER AS KERNEL MODULE ========================================================================= */ -static int __init zr36050_init_module(void) +int zr36050_init_module(void) { - //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION); zr36050_codecs = 0; return videocodec_register(&zr36050_codec); } -static void __exit zr36050_cleanup_module(void) +void zr36050_cleanup_module(void) { if (zr36050_codecs) { dprintk(1, @@ -833,11 +829,3 @@ static void __exit zr36050_cleanup_module(void) } videocodec_unregister(&zr36050_codec); } - -module_init(zr36050_init_module); -module_exit(zr36050_cleanup_module); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors " - ZR050_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/zoran/zr36050.h b/drivers/staging/media/zoran/zr36050.h index 8f972d045b58..f9b58f4c77b9 100644 --- a/drivers/staging/media/zoran/zr36050.h +++ b/drivers/staging/media/zoran/zr36050.h @@ -160,4 +160,6 @@ struct zr36050 { #define ZR050_U_COMPONENT 1 #define ZR050_V_COMPONENT 2 +int zr36050_init_module(void); +void zr36050_cleanup_module(void); #endif /*fndef ZR36050_H */ diff --git a/drivers/staging/media/zoran/zr36060.c b/drivers/staging/media/zoran/zr36060.c index 1c3af11b5f24..d0c369e31c81 100644 --- a/drivers/staging/media/zoran/zr36060.c +++ b/drivers/staging/media/zoran/zr36060.c @@ -5,8 +5,6 @@ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be> */ -#define ZR060_VERSION "v0.7" - #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -34,14 +32,13 @@ static bool low_bitrate; module_param(low_bitrate, bool, 0); MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); -/* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); +static int zr36060_debug; +module_param(zr36060_debug, int, 0); +MODULE_PARM_DESC(zr36060_debug, "Debug level (0-4)"); #define dprintk(num, format, args...) \ do { \ - if (debug >= num) \ + if (zr36060_debug >= num) \ printk(format, ##args); \ } while (0) @@ -832,7 +829,6 @@ static int zr36060_setup(struct videocodec *codec) } static const struct videocodec zr36060_codec = { - .owner = THIS_MODULE, .name = "zr36060", .magic = 0L, // magic not used .flags = @@ -847,13 +843,13 @@ static const struct videocodec zr36060_codec = { // others are not used }; -static int __init zr36060_init_module(void) +int zr36060_init_module(void) { zr36060_codecs = 0; return videocodec_register(&zr36060_codec); } -static void __exit zr36060_cleanup_module(void) +void zr36060_cleanup_module(void) { if (zr36060_codecs) { dprintk(1, @@ -864,10 +860,3 @@ static void __exit zr36060_cleanup_module(void) /* however, we can't just stay alive */ videocodec_unregister(&zr36060_codec); } - -module_init(zr36060_init_module); -module_exit(zr36060_cleanup_module); - -MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>"); -MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors " ZR060_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/zoran/zr36060.h b/drivers/staging/media/zoran/zr36060.h index d2cdc26bf625..fbf5429534ac 100644 --- a/drivers/staging/media/zoran/zr36060.h +++ b/drivers/staging/media/zoran/zr36060.h @@ -198,4 +198,6 @@ struct zr36060 { #define ZR060_SR_H_SCALE2 BIT(0) #define ZR060_SR_H_SCALE4 (2 << 0) +int zr36060_init_module(void); +void zr36060_cleanup_module(void); #endif /*fndef ZR36060_H */ diff --git a/drivers/staging/most/video/Kconfig b/drivers/staging/most/video/Kconfig index e0964ca5e7b3..e16cc5e104b7 100644 --- a/drivers/staging/most/video/Kconfig +++ b/drivers/staging/most/video/Kconfig @@ -5,7 +5,7 @@ config MOST_VIDEO tristate "Video" - depends on VIDEO_V4L2 + depends on VIDEO_DEV help Say Y here if you want to commumicate via Video 4 Linux. diff --git a/drivers/staging/vc04_services/bcm2835-camera/Kconfig b/drivers/staging/vc04_services/bcm2835-camera/Kconfig index d0653d1ed3c7..dcda565f9b38 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/Kconfig +++ b/drivers/staging/vc04_services/bcm2835-camera/Kconfig @@ -2,7 +2,7 @@ config VIDEO_BCM2835 tristate "BCM2835 Camera" depends on MEDIA_SUPPORT - depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST) + depends on VIDEO_DEV && (ARCH_BCM2835 || COMPILE_TEST) select BCM2835_VCHIQ select BCM2835_VCHIQ_MMAL select VIDEOBUF2_VMALLOC diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dd58094f0b85..4fa2ddf322b4 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -448,7 +448,7 @@ config USB_CONFIGFS_F_HID config USB_CONFIGFS_F_UVC bool "USB Webcam function" depends on USB_CONFIGFS - depends on VIDEO_V4L2 + depends on VIDEO_DEV depends on VIDEO_DEV select VIDEOBUF2_DMA_SG select VIDEOBUF2_VMALLOC diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index de6668e58481..0a7b382fbe27 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -500,7 +500,7 @@ endif # or video class gadget drivers), or specific hardware, here. config USB_G_WEBCAM tristate "USB Webcam Gadget" - depends on VIDEO_V4L2 + depends on VIDEO_DEV select USB_LIBCOMPOSITE select VIDEOBUF2_DMA_SG select VIDEOBUF2_VMALLOC |