diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-04 06:59:38 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-04 06:59:38 +0300 |
commit | a98f670e41a99f53acb1fb33cee9c6abbb2e6f23 (patch) | |
tree | f8ae10a4cb91758ad7f9422053753a8c5d0f04dc /drivers/media | |
parent | ee01c4d72adffb7d424535adf630f2955748fa8b (diff) | |
parent | 938b29db3aa9c293c7c1366b16e55e308f1a1ddd (diff) | |
download | linux-a98f670e41a99f53acb1fb33cee9c6abbb2e6f23.tar.xz |
Merge tag 'media/v5.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Media documentation is now split into admin-guide, driver-api and
userspace-api books (a longstanding request from Jon);
- The media Kconfig was reorganized, in order to make easier to select
drivers and their dependencies;
- The testing drivers now has a separate directory;
- added a new driver for Rockchip Video Decoder IP;
- The atomisp staging driver was resurrected. It is meant to work with
4 generations of cameras on Atom-based laptops, tablets and cell
phones. So, it seems worth investing time to cleanup this driver and
making it in good shape.
- Added some V4L2 core ancillary routines to help with h264 codecs;
- Added an ov2740 image sensor driver;
- The si2157 gained support for Analog TV, which, in turn, added
support for some cx231xx and cx23885 boards to also support analog
standards;
- Added some V4L2 controls (V4L2_CID_CAMERA_ORIENTATION and
V4L2_CID_CAMERA_SENSOR_ROTATION) to help identifying where the camera
is located at the device;
- VIDIOC_ENUM_FMT was extended to support MC-centric devices;
- Lots of drivers improvements and cleanups.
* tag 'media/v5.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (503 commits)
media: Documentation: media: Refer to mbus format documentation from CSI-2 docs
media: s5k5baf: Replace zero-length array with flexible-array
media: i2c: imx219: Drop <linux/clk-provider.h> and <linux/clkdev.h>
media: i2c: Add ov2740 image sensor driver
media: ov8856: Implement sensor module revision identification
media: ov8856: Add devicetree support
media: dt-bindings: ov8856: Document YAML bindings
media: dvb-usb: Add Cinergy S2 PCIe Dual Port support
media: dvbdev: Fix tuner->demod media controller link
media: dt-bindings: phy: phy-rockchip-dphy-rx0: move rockchip dphy rx0 bindings out of staging
media: staging: dt-bindings: phy-rockchip-dphy-rx0: remove non-used reg property
media: atomisp: unify the version for isp2401 a0 and b0 versions
media: atomisp: update TODO with the current data
media: atomisp: adjust some code at sh_css that could be broken
media: atomisp: don't produce errs for ignored IRQs
media: atomisp: print IRQ when debugging
media: atomisp: isp_mmu: don't use kmem_cache
media: atomisp: add a notice about possible leak resources
media: atomisp: disable the dynamic and reserved pools
media: atomisp: turn on camera before setting it
...
Diffstat (limited to 'drivers/media')
294 files changed, 6349 insertions, 2746 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9dfea5c4b6ab..a6d073f2e036 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -3,42 +3,81 @@ # Multimedia device configuration # -config CEC_CORE - tristate - -config CEC_NOTIFIER - bool - -config CEC_PIN - bool - +# +# NOTE: CEC and Remote Controller support should not depend on MEDIA_SUPPORT +# source "drivers/media/rc/Kconfig" +source "drivers/media/cec/Kconfig" menuconfig MEDIA_SUPPORT tristate "Multimedia support" depends on HAS_IOMEM help - If you want to use Webcams, Video grabber devices and/or TV devices - enable this option and other options below. + If you want to use media devices, including Webcams, Video grabber + devices and/or TV devices, V4L2 codecs, etc, enable this option + and other options below. + Additional info and docs are available on the web at <https://linuxtv.org> if MEDIA_SUPPORT -comment "Multimedia core support" +config MEDIA_SUPPORT_FILTER + bool "Filter media drivers" + depends on MEDIA_SUPPORT + default y if !EMBEDDED && !EXPERT + help + Configuring the media subsystem can be complex, as there are + hundreds of drivers and other config options. + + This menu offers option that will help the Kernel's config + system to hide drivers that are out of the scope of the + user needs, and disabling core support for unused APIs. + + If not selected, all non-optional media core functionality + needed to support media drivers will be enabled. Also, all + media device drivers should be shown. + +config MEDIA_SUBDRV_AUTOSELECT + bool "Autoselect ancillary drivers (tuners, sensors, i2c, spi, frontends)" + depends on HAS_IOMEM + select I2C + select I2C_MUX + default y if MEDIA_SUPPORT_FILTER + help + By default, a media driver auto-selects all possible ancillary + devices such as tuners, sensors, video encoders/decoders and + frontends, that are used by any of the supported devices. + + This is generally the right thing to do, except when there + are strict constraints with regards to the kernel size, + like on embedded systems. + + Use this option with care, as deselecting ancillary drivers which + are, in fact, necessary will result in the lack of the needed + functionality for your device (it may not tune or may not have + the needed demodulators). + + If unsure say Y. + +menu "Media device types" # # Multimedia support - automatically enable V4L2 and DVB core # config MEDIA_CAMERA_SUPPORT - bool "Cameras/video grabbers support" + bool + prompt "Cameras and video grabbers" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help Enable support for webcams and video grabbers. Say Y when you have a webcam or a video capture grabber board. config MEDIA_ANALOG_TV_SUPPORT - bool "Analog TV support" + bool + prompt "Analog TV" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help Enable analog TV support. @@ -50,7 +89,9 @@ config MEDIA_ANALOG_TV_SUPPORT will disable support for them. config MEDIA_DIGITAL_TV_SUPPORT - bool "Digital TV support" + bool + prompt "Digital TV" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help Enable digital TV support. @@ -58,7 +99,9 @@ config MEDIA_DIGITAL_TV_SUPPORT hybrid digital TV and analog TV. config MEDIA_RADIO_SUPPORT - bool "AM/FM radio receivers/transmitters support" + bool + prompt "AM/FM radio receivers/transmitters" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help Enable AM/FM radio support. @@ -72,47 +115,65 @@ config MEDIA_RADIO_SUPPORT disable support for them. config MEDIA_SDR_SUPPORT - bool "Software defined radio support" + bool + prompt "Software defined radio" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help Enable software defined radio support. Say Y when you have a software defined radio device. -config MEDIA_CEC_SUPPORT - bool "HDMI CEC support" +config MEDIA_PLATFORM_SUPPORT + bool + prompt "Platform-specific devices" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER help - Enable support for HDMI CEC (Consumer Electronics Control), - which is an optional HDMI feature. + Enable support for complex cameras, codecs, and other hardware + that are integrated at the CPU, GPU or on Image Signalling Processor + and don't use PCI, USB or Firewire buses. - Say Y when you have an HDMI receiver, transmitter or a USB CEC - adapter that supports HDMI CEC. + This is found on Embedded hardware (SoC), on V4L2 codecs and + on some GPU and newer CPU chipsets. -source "drivers/media/cec/Kconfig" + Say Y when you want to be able so see such devices. -source "drivers/media/mc/Kconfig" +config MEDIA_TEST_SUPPORT + bool + prompt "Test drivers" if MEDIA_SUPPORT_FILTER + default y if !MEDIA_SUPPORT_FILTER + help + Those drivers should not be used on production Kernels, but + can be useful on debug ones. It enables several dummy drivers + that simulate a real hardware. Very useful to test userspace + applications and to validate if the subsystem core is doesn't + have regressions. -# -# Video4Linux support -# Only enables if one of the V4L2 types (ATV, webcam, radio) is selected -# + Say Y if you want to use some virtual test driver. + + In case of doubts, say N. + Say Y when you have a software defined radio device. +endmenu # media device types + + +menu "Media core support" + visible if !MEDIA_SUPPORT_FILTER config VIDEO_DEV - tristate - depends on MEDIA_SUPPORT - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT - default y + tristate "Video4Linux core" + default MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT || MEDIA_PLATFORM_SUPPORT || MEDIA_TEST_SUPPORT + help + Enables the V4L2 API, used by cameras, analog TV, video grabbers, + radio devices and by some input devices. -config VIDEO_V4L2_SUBDEV_API - bool "V4L2 sub-device userspace API" - depends on VIDEO_DEV && MEDIA_CONTROLLER +config MEDIA_CONTROLLER + bool "Media Controller API" + default MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_PLATFORM_SUPPORT help - Enables the V4L2 sub-device pad-level userspace API used to configure - video format, size and frame rate between hardware blocks. + Enable the media controller API used to query media devices internal + topology and configure it dynamically. This API is mostly used by camera interfaces in embedded platforms. -source "drivers/media/v4l2-core/Kconfig" - # # DVB Core # Only enables if one of DTV is selected @@ -120,98 +181,73 @@ source "drivers/media/v4l2-core/Kconfig" config DVB_CORE tristate - depends on MEDIA_SUPPORT depends on MEDIA_DIGITAL_TV_SUPPORT depends on (I2C || I2C=n) - default y + default MEDIA_DIGITAL_TV_SUPPORT select CRC32 - -config DVB_MMAP - bool "Enable DVB memory-mapped API (EXPERIMENTAL)" - depends on DVB_CORE - depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_CORE - select VIDEOBUF2_VMALLOC help - This option enables DVB experimental memory-mapped API, which - reduces the number of context switches to read DVB buffers, as - the buffers can use mmap() syscalls. + Enables the DVB API, used by Digital TV devices. Supports several + standards, including DVB, ATSC, ISDB and CMDB. - Support for it is experimental. Use with care. If unsure, - say N. +endmenu # Media core support -config DVB_NET - bool "DVB Network Support" - default (NET && INET) - depends on NET && INET && DVB_CORE - help - This option enables DVB Network Support which is a part of the DVB - standard. It is used, for example, by automatic firmware updates used - on Set-Top-Boxes. It can also be used to access the Internet via the - DVB card, if the network provider supports it. +# +# Extra per-media API core functionality - You may want to disable the network support on embedded devices. If - unsure say Y. +menu "Video4Linux options" + visible if VIDEO_DEV -# This Kconfig option is used by both PCI and USB drivers -config TTPCI_EEPROM - tristate - depends on I2C +source "drivers/media/v4l2-core/Kconfig" +endmenu + +menu "Media controller options" + visible if MEDIA_CONTROLLER + +source "drivers/media/mc/Kconfig" +endmenu + +menu "Digital TV options" + visible if DVB_CORE source "drivers/media/dvb-core/Kconfig" +endmenu -comment "Media drivers" +menu "Media drivers" -# -# V4L platform/mem2mem drivers -# +comment "Drivers filtered as selected at 'Filter media drivers'" + depends on MEDIA_SUPPORT_FILTER source "drivers/media/usb/Kconfig" source "drivers/media/pci/Kconfig" -source "drivers/media/platform/Kconfig" -source "drivers/media/mmc/Kconfig" source "drivers/media/radio/Kconfig" -comment "Supported FireWire (IEEE 1394) Adapters" - depends on DVB_CORE && FIREWIRE -source "drivers/media/firewire/Kconfig" - # Common driver options source "drivers/media/common/Kconfig" -comment "Media ancillary drivers (tuners, sensors, i2c, spi, frontends)" - -# -# Ancillary drivers (tuners, i2c, spi, frontends) -# +if MEDIA_PLATFORM_SUPPORT +source "drivers/media/platform/Kconfig" +source "drivers/media/mmc/Kconfig" +endif -config MEDIA_SUBDRV_AUTOSELECT - bool "Autoselect ancillary drivers (tuners, sensors, i2c, spi, frontends)" - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT || MEDIA_SDR_SUPPORT - depends on HAS_IOMEM - select I2C - select I2C_MUX - default y if !EMBEDDED - help - By default, a media driver auto-selects all possible ancillary - devices such as tuners, sensors, video encoders/decoders and - frontends, that are used by any of the supported devices. +if MEDIA_TEST_SUPPORT +source "drivers/media/test-drivers/Kconfig" +endif - This is generally the right thing to do, except when there - are strict constraints with regards to the kernel size, - like on embedded systems. +source "drivers/media/firewire/Kconfig" - Use this option with care, as deselecting ancillary drivers which - are, in fact, necessary will result in the lack of the needed - functionality for your device (it may not tune or may not have - the needed demodulators). +endmenu - If unsure say Y. +# +# Ancillary drivers (tuners, i2c, spi, frontends) +# config MEDIA_HIDE_ANCILLARY_SUBDRV bool depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT default y +menu "Media ancillary drivers" + config MEDIA_ATTACH bool depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT @@ -223,4 +259,6 @@ source "drivers/media/spi/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" +endmenu + endif # MEDIA_SUPPORT diff --git a/drivers/media/Makefile b/drivers/media/Makefile index f215f0a89f9e..d18357bf1346 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -29,6 +29,6 @@ obj-$(CONFIG_CEC_CORE) += cec/ # Finally, merge the drivers that require the core # -obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ spi/ +obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ spi/ test-drivers/ obj-$(CONFIG_VIDEO_DEV) += radio/ diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index c01919713ab9..eea74b7cfa8c 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -1,4 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only +config CEC_CORE + tristate + +config CEC_NOTIFIER + bool + +config CEC_PIN + bool + config MEDIA_CEC_RC bool "HDMI CEC RC integration" depends on CEC_CORE && RC_CORE @@ -11,3 +20,19 @@ config CEC_PIN_ERROR_INJ depends on CEC_PIN && DEBUG_FS help This option enables CEC error injection using debugfs. + +menuconfig MEDIA_CEC_SUPPORT + bool + prompt "HDMI CEC drivers" + default y if !MEDIA_SUPPORT_FILTER + help + Enable support for HDMI CEC (Consumer Electronics Control), + which is an optional HDMI feature. + + Say Y when you have an HDMI receiver, transmitter or a USB CEC + adapter that supports HDMI CEC. + +if MEDIA_CEC_SUPPORT +source "drivers/media/cec/platform/Kconfig" +source "drivers/media/cec/usb/Kconfig" +endif diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile index ad8677d8c896..74e80e1b3571 100644 --- a/drivers/media/cec/Makefile +++ b/drivers/media/cec/Makefile @@ -1,16 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -cec-objs := cec-core.o cec-adap.o cec-api.o - -ifeq ($(CONFIG_CEC_NOTIFIER),y) - cec-objs += cec-notifier.o -endif - -ifeq ($(CONFIG_CEC_PIN),y) - cec-objs += cec-pin.o -endif - -ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y) - cec-objs += cec-pin-error-inj.o -endif - -obj-$(CONFIG_CEC_CORE) += cec.o +obj-y += core/ platform/ usb/ diff --git a/drivers/media/cec/core/Makefile b/drivers/media/cec/core/Makefile new file mode 100644 index 000000000000..ad8677d8c896 --- /dev/null +++ b/drivers/media/cec/core/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +cec-objs := cec-core.o cec-adap.o cec-api.o + +ifeq ($(CONFIG_CEC_NOTIFIER),y) + cec-objs += cec-notifier.o +endif + +ifeq ($(CONFIG_CEC_PIN),y) + cec-objs += cec-pin.o +endif + +ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y) + cec-objs += cec-pin-error-inj.o +endif + +obj-$(CONFIG_CEC_CORE) += cec.o diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 6c95dc471d4c..6a04d19a96b2 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1734,6 +1734,10 @@ int __cec_s_log_addrs(struct cec_adapter *adap, unsigned j; log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID; + if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) { + dprintk(1, "unknown logical address type\n"); + return -EINVAL; + } if (type_mask & (1 << log_addrs->log_addr_type[i])) { dprintk(1, "duplicate logical address type\n"); return -EINVAL; @@ -1754,10 +1758,6 @@ int __cec_s_log_addrs(struct cec_adapter *adap, dprintk(1, "invalid primary device type\n"); return -EINVAL; } - if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) { - dprintk(1, "unknown logical address type\n"); - return -EINVAL; - } for (j = 0; j < feature_sz; j++) { if ((features[j] & 0x80) == 0) { if (op_is_dev_features) diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/core/cec-api.c index 17d1cb2e5f97..17d1cb2e5f97 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/core/cec-api.c diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/core/cec-core.c index 0c52e1bb3910..0c52e1bb3910 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/core/cec-core.c diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c index 517e0035fc99..517e0035fc99 100644 --- a/drivers/media/cec/cec-notifier.c +++ b/drivers/media/cec/core/cec-notifier.c diff --git a/drivers/media/cec/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c index c0088d3b8e3d..c0088d3b8e3d 100644 --- a/drivers/media/cec/cec-pin-error-inj.c +++ b/drivers/media/cec/core/cec-pin-error-inj.c diff --git a/drivers/media/cec/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h index f423db8855d9..f423db8855d9 100644 --- a/drivers/media/cec/cec-pin-priv.h +++ b/drivers/media/cec/core/cec-pin-priv.h diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 660fe111f540..660fe111f540 100644 --- a/drivers/media/cec/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c diff --git a/drivers/media/cec/cec-priv.h b/drivers/media/cec/core/cec-priv.h index 9bbd05053d42..9bbd05053d42 100644 --- a/drivers/media/cec/cec-priv.h +++ b/drivers/media/cec/core/cec-priv.h diff --git a/drivers/media/cec/platform/Kconfig b/drivers/media/cec/platform/Kconfig new file mode 100644 index 000000000000..350533cd8261 --- /dev/null +++ b/drivers/media/cec/platform/Kconfig @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Platform drivers + +config CEC_CROS_EC + tristate "ChromeOS EC CEC driver" + depends on CROS_EC + select CEC_CORE + select CEC_NOTIFIER + select CROS_EC_PROTO + help + If you say yes here you will get support for the + ChromeOS Embedded Controller's CEC. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_MESON_AO + tristate "Amlogic Meson AO CEC driver" + depends on ARCH_MESON || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for Amlogic Meson SoCs AO CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + +config CEC_MESON_G12A_AO + tristate "Amlogic Meson G12A AO CEC driver" + depends on ARCH_MESON || COMPILE_TEST + depends on COMMON_CLK && OF + select REGMAP + select REGMAP_MMIO + select CEC_CORE + select CEC_NOTIFIER + ---help--- + This is a driver for Amlogic Meson G12A SoCs AO CEC interface. + This driver if for the new AO-CEC module found in G12A SoCs, + usually named AO_CEC_B in documentation. + It uses the generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_GPIO + tristate "Generic GPIO-based CEC driver" + depends on PREEMPTION || COMPILE_TEST + select CEC_CORE + select CEC_PIN + select CEC_NOTIFIER + select GPIOLIB + help + This is a generic GPIO-based CEC driver. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_SAMSUNG_S5P + tristate "Samsung S5P CEC driver" + depends on ARCH_EXYNOS || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for Samsung S5P HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_STI + tristate "STMicroelectronics STiH4xx HDMI CEC driver" + depends on ARCH_STI || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for STIH4xx HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_STM32 + tristate "STMicroelectronics STM32 HDMI CEC driver" + depends on ARCH_STM32 || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + select CEC_CORE + help + This is a driver for STM32 interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_TEGRA + tristate "Tegra HDMI CEC driver" + depends on ARCH_TEGRA || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for the Tegra HDMI CEC interface. It uses the + generic CEC framework interface. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_SECO + tristate "SECO Boards HDMI CEC driver" + depends on (X86 || IA64) || COMPILE_TEST + depends on PCI && DMI + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for SECO Boards integrated CEC interface. + Selecting it will enable support for this device. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. + +config CEC_SECO_RC + bool "SECO Boards IR RC5 support" + depends on CEC_SECO + depends on RC_CORE=y || RC_CORE = CEC_SECO + help + If you say yes here you will get support for the + SECO Boards Consumer-IR in seco-cec driver. + The embedded controller supports RC5 protocol only, default mapping + is set to rc-hauppauge. diff --git a/drivers/media/cec/platform/Makefile b/drivers/media/cec/platform/Makefile new file mode 100644 index 000000000000..3a947159b25a --- /dev/null +++ b/drivers/media/cec/platform/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the CEC platform device drivers. +# + +# 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_TEGRA) += tegra/ + diff --git a/drivers/media/platform/cec-gpio/Makefile b/drivers/media/cec/platform/cec-gpio/Makefile index a40c621dbd24..a40c621dbd24 100644 --- a/drivers/media/platform/cec-gpio/Makefile +++ b/drivers/media/cec/platform/cec-gpio/Makefile diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index 42d2c2cd9a78..c8c4efc83f5f 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -31,12 +31,12 @@ struct cec_gpio { ktime_t v5_ts; }; -static bool cec_gpio_read(struct cec_adapter *adap) +static int cec_gpio_read(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); if (cec->cec_is_low) - return false; + return 0; return gpiod_get_value(cec->cec_gpio); } @@ -71,9 +71,10 @@ static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; - bool is_high = gpiod_get_value(cec->v5_gpio); + int val = gpiod_get_value(cec->v5_gpio); + bool is_high = val > 0; - if (is_high == cec->v5_is_high) + if (val < 0 || is_high == cec->v5_is_high) return IRQ_HANDLED; cec->v5_ts = ktime_get(); cec->v5_is_high = is_high; @@ -91,9 +92,10 @@ static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv) static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; - bool is_high = gpiod_get_value(cec->hpd_gpio); + int val = gpiod_get_value(cec->hpd_gpio); + bool is_high = val > 0; - if (is_high == cec->hpd_is_high) + if (val < 0 || is_high == cec->hpd_is_high) return IRQ_HANDLED; cec->hpd_ts = ktime_get(); cec->hpd_is_high = is_high; @@ -103,8 +105,10 @@ static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) static irqreturn_t cec_gpio_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; + int val = gpiod_get_value(cec->cec_gpio); - cec_pin_changed(cec->adap, gpiod_get_value(cec->cec_gpio)); + if (val >= 0) + cec_pin_changed(cec->adap, val > 0); return IRQ_HANDLED; } diff --git a/drivers/media/cec/platform/cros-ec/Makefile b/drivers/media/cec/platform/cros-ec/Makefile new file mode 100644 index 000000000000..d7e3511078ef --- /dev/null +++ b/drivers/media/cec/platform/cros-ec/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_CROS_EC) += cros-ec-cec.o diff --git a/drivers/media/platform/cros-ec-cec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 0e7e2772f08f..0e7e2772f08f 100644 --- a/drivers/media/platform/cros-ec-cec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c diff --git a/drivers/media/cec/platform/meson/Makefile b/drivers/media/cec/platform/meson/Makefile new file mode 100644 index 000000000000..34fc5d444d0e --- /dev/null +++ b/drivers/media/cec/platform/meson/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_MESON_AO) += ao-cec.o +obj-$(CONFIG_CEC_MESON_G12A_AO) += ao-cec-g12a.o diff --git a/drivers/media/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c index 891533060d49..891533060d49 100644 --- a/drivers/media/platform/meson/ao-cec-g12a.c +++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/cec/platform/meson/ao-cec.c index 09aff82c3773..09aff82c3773 100644 --- a/drivers/media/platform/meson/ao-cec.c +++ b/drivers/media/cec/platform/meson/ao-cec.c diff --git a/drivers/media/platform/s5p-cec/Makefile b/drivers/media/cec/platform/s5p/Makefile index bd0103b91bee..92bf7b8557c5 100644 --- a/drivers/media/platform/s5p-cec/Makefile +++ b/drivers/media/cec/platform/s5p/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec.o +obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p-cec.o s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h b/drivers/media/cec/platform/s5p/exynos_hdmi_cec.h index 325db8c432bd..325db8c432bd 100644 --- a/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h +++ b/drivers/media/cec/platform/s5p/exynos_hdmi_cec.h diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c b/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c index eb981ebce362..eb981ebce362 100644 --- a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c +++ b/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c diff --git a/drivers/media/platform/s5p-cec/regs-cec.h b/drivers/media/cec/platform/s5p/regs-cec.h index 447f717028a2..447f717028a2 100644 --- a/drivers/media/platform/s5p-cec/regs-cec.h +++ b/drivers/media/cec/platform/s5p/regs-cec.h diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/cec/platform/s5p/s5p_cec.c index 2a3e7ffefe0a..2a3e7ffefe0a 100644 --- a/drivers/media/platform/s5p-cec/s5p_cec.c +++ b/drivers/media/cec/platform/s5p/s5p_cec.c diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/cec/platform/s5p/s5p_cec.h index 34d033b20f96..34d033b20f96 100644 --- a/drivers/media/platform/s5p-cec/s5p_cec.h +++ b/drivers/media/cec/platform/s5p/s5p_cec.h diff --git a/drivers/media/cec/platform/seco/Makefile b/drivers/media/cec/platform/seco/Makefile new file mode 100644 index 000000000000..aa1ca8ccdb8b --- /dev/null +++ b/drivers/media/cec/platform/seco/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_SECO) += seco-cec.o diff --git a/drivers/media/platform/seco-cec/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index 2ff62a488b27..075dd79beb6f 100644 --- a/drivers/media/platform/seco-cec/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -343,7 +343,7 @@ static const struct cec_adap_ops secocec_cec_adap_ops = { .adap_transmit = secocec_adap_transmit, }; -#ifdef CONFIG_VIDEO_SECO_RC +#ifdef CONFIG_CEC_SECO_RC static int secocec_ir_probe(void *priv) { struct secocec_data *cec = priv; diff --git a/drivers/media/platform/seco-cec/seco-cec.h b/drivers/media/cec/platform/seco/seco-cec.h index 843de8c7dfd4..843de8c7dfd4 100644 --- a/drivers/media/platform/seco-cec/seco-cec.h +++ b/drivers/media/cec/platform/seco/seco-cec.h diff --git a/drivers/media/cec/platform/sti/Makefile b/drivers/media/cec/platform/sti/Makefile new file mode 100644 index 000000000000..26ec5ba1c633 --- /dev/null +++ b/drivers/media/cec/platform/sti/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_STI) += stih-cec.o diff --git a/drivers/media/platform/sti/cec/stih-cec.c b/drivers/media/cec/platform/sti/stih-cec.c index f0c73e64b586..f0c73e64b586 100644 --- a/drivers/media/platform/sti/cec/stih-cec.c +++ b/drivers/media/cec/platform/sti/stih-cec.c diff --git a/drivers/media/cec/platform/stm32/Makefile b/drivers/media/cec/platform/stm32/Makefile new file mode 100644 index 000000000000..b7597a00befa --- /dev/null +++ b/drivers/media/cec/platform/stm32/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_STM32) += stm32-cec.o diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index ea4b1ebfca99..ea4b1ebfca99 100644 --- a/drivers/media/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c diff --git a/drivers/media/cec/platform/tegra/Makefile b/drivers/media/cec/platform/tegra/Makefile new file mode 100644 index 000000000000..275d1c019d49 --- /dev/null +++ b/drivers/media/cec/platform/tegra/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_TEGRA) += tegra_cec.o diff --git a/drivers/media/platform/tegra-cec/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index 1ac0c70a5981..1ac0c70a5981 100644 --- a/drivers/media/platform/tegra-cec/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c diff --git a/drivers/media/platform/tegra-cec/tegra_cec.h b/drivers/media/cec/platform/tegra/tegra_cec.h index 8c370be38e1e..8c370be38e1e 100644 --- a/drivers/media/platform/tegra-cec/tegra_cec.h +++ b/drivers/media/cec/platform/tegra/tegra_cec.h diff --git a/drivers/media/cec/usb/Kconfig b/drivers/media/cec/usb/Kconfig new file mode 100644 index 000000000000..3f3a5c75287a --- /dev/null +++ b/drivers/media/cec/usb/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# USB drivers + +if USB_SUPPORT && TTY +source "drivers/media/cec/usb/pulse8/Kconfig" +source "drivers/media/cec/usb/rainshadow/Kconfig" +endif diff --git a/drivers/media/cec/usb/Makefile b/drivers/media/cec/usb/Makefile new file mode 100644 index 000000000000..e4183d1bfa9a --- /dev/null +++ b/drivers/media/cec/usb/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the CEC USB device drivers. +# +obj-$(CONFIG_USB_PULSE8_CEC) += pulse8/ +obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow/ diff --git a/drivers/media/usb/pulse8-cec/Kconfig b/drivers/media/cec/usb/pulse8/Kconfig index e802d30dbbee..a0224ef80e6c 100644 --- a/drivers/media/usb/pulse8-cec/Kconfig +++ b/drivers/media/cec/usb/pulse8/Kconfig @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_PULSE8_CEC tristate "Pulse Eight HDMI CEC" - depends on USB_ACM select CEC_CORE + select USB + select USB_ACM select SERIO select SERIO_SERPORT help diff --git a/drivers/media/usb/pulse8-cec/Makefile b/drivers/media/cec/usb/pulse8/Makefile index 7816c68bf928..7816c68bf928 100644 --- a/drivers/media/usb/pulse8-cec/Makefile +++ b/drivers/media/cec/usb/pulse8/Makefile diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/cec/usb/pulse8/pulse8-cec.c index beae6aa12638..beae6aa12638 100644 --- a/drivers/media/usb/pulse8-cec/pulse8-cec.c +++ b/drivers/media/cec/usb/pulse8/pulse8-cec.c diff --git a/drivers/media/usb/rainshadow-cec/Kconfig b/drivers/media/cec/usb/rainshadow/Kconfig index b481c5157d7e..c9ef2c192b17 100644 --- a/drivers/media/usb/rainshadow-cec/Kconfig +++ b/drivers/media/cec/usb/rainshadow/Kconfig @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config USB_RAINSHADOW_CEC tristate "RainShadow Tech HDMI CEC" - depends on USB_ACM select CEC_CORE + select USB + select USB_ACM select SERIO select SERIO_SERPORT help diff --git a/drivers/media/usb/rainshadow-cec/Makefile b/drivers/media/cec/usb/rainshadow/Makefile index 47b33c574c3e..47b33c574c3e 100644 --- a/drivers/media/usb/rainshadow-cec/Makefile +++ b/drivers/media/cec/usb/rainshadow/Makefile diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c index ee870ea1a886..ee870ea1a886 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 1990b7f09454..4ea03b7899a8 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -14,7 +14,7 @@ config VIDEO_TVEEPROM depends on I2C config CYPRESS_FIRMWARE - tristate "Cypress firmware helper routines" + tristate depends on USB source "drivers/media/common/videobuf2/Kconfig" diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig index 90e038d5ffd9..6ffac618417b 100644 --- a/drivers/media/dvb-core/Kconfig +++ b/drivers/media/dvb-core/Kconfig @@ -3,6 +3,32 @@ # DVB device configuration # +config DVB_MMAP + bool "Enable DVB memory-mapped API (EXPERIMENTAL)" + depends on DVB_CORE + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_CORE + select VIDEOBUF2_VMALLOC + help + This option enables DVB experimental memory-mapped API, which + reduces the number of context switches to read DVB buffers, as + the buffers can use mmap() syscalls. + + Support for it is experimental. Use with care. If unsure, + say N. + +config DVB_NET + bool "DVB Network Support" + default (NET && INET) + depends on NET && INET && DVB_CORE + help + This option enables DVB Network Support which is a part of the DVB + standard. It is used, for example, by automatic firmware updates used + on Set-Top-Boxes. It can also be used to access the Internet via the + DVB card, if the network provider supports it. + + You may want to disable the network support on embedded devices. If + unsure say Y. + config DVB_MAX_ADAPTERS int "maximum number of DVB/ATSC adapters" depends on DVB_CORE @@ -19,6 +45,7 @@ config DVB_MAX_ADAPTERS config DVB_DYNAMIC_MINORS bool "Dynamic DVB minor allocation" depends on DVB_CORE + default y help If you say Y here, the DVB subsystem will use dynamic minor allocation for any device that uses the DVB major number. diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 80b6a71aa33e..959fa2820259 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -707,9 +707,10 @@ int dvb_create_media_graph(struct dvb_adapter *adap, } if (ntuner && ndemod) { - pad_source = media_get_pad_index(tuner, true, + /* NOTE: first found tuner source pad presumed correct */ + pad_source = media_get_pad_index(tuner, false, PAD_SIGNAL_ANALOG); - if (pad_source) + if (pad_source < 0) return -EINVAL; ret = media_create_pad_links(mdev, MEDIA_ENT_F_TUNER, diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index a29e9ddf9c82..643b851a6b60 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -1,4 +1,8 @@ -comment "DVB Frontend drivers hidden by 'Autoselect ancillary drivers'" +# SPDX-License-Identifier: GPL-2.0 + +if MEDIA_DIGITAL_TV_SUPPORT + +comment "DVB Frontend drivers auto-selected by 'Autoselect ancillary drivers'" depends on MEDIA_HIDE_ANCILLARY_SUBDRV menu "Customise DVB Frontends" @@ -943,9 +947,15 @@ config DVB_SP2 help CIMaX SP2/SP2HF Common Interface module. +endmenu # Customise DVB Frontends + +endif # MEDIA_DIGITAL_TV_SUPPORT + comment "Tools to develop new frontends" + depends on MEDIA_TEST_SUPPORT config DVB_DUMMY_FE tristate "Dummy frontend driver" - depends on DVB_CORE -endmenu + depends on MEDIA_TEST_SUPPORT && DVB_CORE + help + Dummy skeleton frontend driver. diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c index f87e27481ea7..d5b1b3788e39 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -685,7 +685,7 @@ static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe) int ret; struct cxd2880_priv *priv; struct cxd2880_dvbt_tpsinfo info; - enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ; + enum cxd2880_dtv_bandwidth bw; u32 pre_ber_rate = 0; u32 post_ber_rate = 0; u32 ucblock_rate = 0; diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h index 9118a7a48ef2..3f129efa21de 100644 --- a/drivers/media/dvb-frontends/dib3000.h +++ b/drivers/media/dvb-frontends/dib3000.h @@ -14,7 +14,7 @@ * Amaury Demol from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef DIB3000_H diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c index 46ed0e20c8fa..0f0480d8576d 100644 --- a/drivers/media/dvb-frontends/dib3000mb.c +++ b/drivers/media/dvb-frontends/dib3000mb.c @@ -14,7 +14,7 @@ * Amaury Demol from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 0a4875b391d9..7e8e5c308d1c 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1093,7 +1093,7 @@ static int init_hi(struct drxk_state *state) static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable) { - int status = -1; + int status; u16 sio_pdr_mclk_cfg = 0; u16 sio_pdr_mdx_cfg = 0; u16 err_cfg = 0; diff --git a/drivers/media/dvb-frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h index 907254b85708..bb85a6e27076 100644 --- a/drivers/media/dvb-frontends/eds1547.h +++ b/drivers/media/dvb-frontends/eds1547.h @@ -3,7 +3,7 @@ * * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) * -* see Documentation/media/dvb-drivers/dvb-usb.rst for more information +* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef EDS1547 diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 6c4adec58174..d3c330e035c4 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -846,6 +846,7 @@ static int lgdt3306a_fe_sleep(struct dvb_frontend *fe) static int lgdt3306a_init(struct dvb_frontend *fe) { struct lgdt3306a_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; u8 val; int ret; @@ -997,6 +998,9 @@ static int lgdt3306a_init(struct dvb_frontend *fe) ret = lgdt3306a_sleep(state); lg_chkerr(ret); + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + fail: return ret; } @@ -1597,6 +1601,7 @@ static int lgdt3306a_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct lgdt3306a_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; u16 strength = 0; int ret = 0; @@ -1637,6 +1642,15 @@ static int lgdt3306a_read_status(struct dvb_frontend *fe, default: ret = -EINVAL; } + + if (*status & FE_HAS_SYNC) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = lgdt3306a_calculate_snr_x100(state) * 10; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } } return ret; } diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index d2c28dcf6b42..f204e715bc59 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -980,6 +980,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); + if (ret) + goto err; } dev_dbg(&client->dev, "carrier offset=%d\n", @@ -1898,7 +1900,7 @@ static int m88ds3103_probe(struct i2c_client *client, if (ret) goto err_kfree; dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1; - dev_err(&client->dev, "dt addr is 0x%02x", dev->dt_addr); + dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr); dev->dt_client = i2c_new_dummy_device(client->adapter, dev->dt_addr); diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 7d93a1617e86..212312d20ff6 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -193,7 +193,7 @@ void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) { - u8 val = 0xff; + u8 val; u8 mask, pos; extract_mask_pos(label, &mask, &pos); diff --git a/drivers/media/dvb-frontends/z0194a.h b/drivers/media/dvb-frontends/z0194a.h index 21442905d116..3446ccbf3c1c 100644 --- a/drivers/media/dvb-frontends/z0194a.h +++ b/drivers/media/dvb-frontends/z0194a.h @@ -3,7 +3,7 @@ * * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) * -* see Documentation/media/dvb-drivers/dvb-usb.rst for more information +* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef Z0194A diff --git a/drivers/media/firewire/Kconfig b/drivers/media/firewire/Kconfig index e7837da5905b..0c1f326f581f 100644 --- a/drivers/media/firewire/Kconfig +++ b/drivers/media/firewire/Kconfig @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only +if DVB_CORE && FIREWIRE +comment "FireWire (IEEE 1394) Adapters" + config DVB_FIREDTV tristate "FireDTV and FloppyDTV" - depends on DVB_CORE && FIREWIRE help Support for DVB receivers from Digital Everywhere which are connected via IEEE 1394 (FireWire). @@ -18,3 +20,4 @@ config DVB_FIREDTV_INPUT def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) endif # DVB_FIREDTV +endif # DVB_CORE && FIREWIRE diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 125d596c13dd..da11036ad804 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -5,6 +5,9 @@ if VIDEO_V4L2 +comment "IR I2C driver auto-selected by 'Autoselect ancillary drivers'" + depends on MEDIA_SUBDRV_AUTOSELECT && I2C && RC_CORE + config VIDEO_IR_I2C tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT || EXPERT depends on I2C && RC_CORE @@ -19,17 +22,18 @@ config VIDEO_IR_I2C In doubt, say Y. # -# Encoder / Decoder module configuration +# V4L2 I2C drivers that aren't related with Camera support # -comment "I2C drivers hidden by 'Autoselect ancillary drivers'" +comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'" depends on MEDIA_HIDE_ANCILLARY_SUBDRV +# +# Encoder / Decoder module configuration +# -menu "I2C Encoders, decoders, sensors and other helper chips" +menu "Audio decoders, processors and mixers" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV -comment "Audio decoders, processors and mixers" - config VIDEO_TVAUDIO tristate "Simple audio decoder chips" depends on VIDEO_V4L2 && I2C @@ -62,11 +66,13 @@ config VIDEO_TDA9840 config VIDEO_TDA1997X tristate "NXP TDA1997x HDMI receiver" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + 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. @@ -185,8 +191,10 @@ config VIDEO_SONY_BTF_MPX To compile this driver as a module, choose M here: the module will be called sony-btf-mpx. +endmenu -comment "RDS decoders" +menu "RDS decoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV config VIDEO_SAA6588 tristate "SAA6588 Radio Chip RDS decoder support" @@ -199,12 +207,16 @@ config VIDEO_SAA6588 To compile this driver as a module, choose M here: the module will be called saa6588. +endmenu -comment "Video decoders" +menu "Video decoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV config VIDEO_ADV7180 tristate "Analog Devices ADV7180 decoder" - depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB && VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help Support for the Analog Devices ADV7180 video decoder. @@ -223,8 +235,10 @@ config VIDEO_ADV7183 config VIDEO_ADV748X tristate "Analog Devices ADV748x decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C depends on OF + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C select V4L2_FWNODE help @@ -236,8 +250,10 @@ config VIDEO_ADV748X config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + 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 @@ -260,7 +276,9 @@ config VIDEO_ADV7604_CEC config VIDEO_ADV7842 tristate "Analog Devices ADV7842 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select HDMI help Support for the Analog Devices ADV7842 video decoder. @@ -347,7 +365,9 @@ config VIDEO_SAA711X config VIDEO_TC358743 tristate "Toshiba TC358743 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select HDMI select V4L2_FWNODE help @@ -457,7 +477,10 @@ config VIDEO_SAA717X source "drivers/media/i2c/cx25840/Kconfig" -comment "Video encoders" +endmenu + +menu "Video encoders" + visible if !MEDIA_HIDE_ANCILLARY_SUBDRV config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" @@ -515,8 +538,10 @@ config VIDEO_ADV7393 config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + 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. @@ -536,7 +561,10 @@ config VIDEO_ADV7511_CEC config VIDEO_AD9389B tristate "Analog Devices AD9389B encoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help Support for the Analog Devices AD9389B video encoder. @@ -559,8 +587,124 @@ config VIDEO_THS8200 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 + 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 + 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 + help + Support for TI THS7303/53 video amplifier -comment "Camera sensor devices" + 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 +# + +menu "Camera sensor devices" + visible if MEDIA_CAMERA_SUPPORT config VIDEO_APTINA_PLL tristate @@ -568,12 +712,11 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate -if MEDIA_CAMERA_SUPPORT - config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CONTROLLER + 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 Hynix @@ -584,8 +727,10 @@ config VIDEO_HI556 config VIDEO_IMX214 tristate "Sony IMX214 sensor support" - depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB && I2C && VIDEO_V4L2 depends on V4L2_FWNODE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -596,7 +741,9 @@ config VIDEO_IMX214 config VIDEO_IMX219 tristate "Sony IMX219 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 Sony @@ -607,7 +754,9 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -617,7 +766,9 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C help This is a V4L2 sensor driver for the Sony IMX274 @@ -625,7 +776,9 @@ config VIDEO_IMX274 config VIDEO_IMX290 tristate "Sony IMX290 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C select V4L2_FWNODE help @@ -637,7 +790,9 @@ config VIDEO_IMX290 config VIDEO_IMX319 tristate "Sony IMX319 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX319 camera. @@ -647,7 +802,9 @@ config VIDEO_IMX319 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -678,7 +835,8 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" - depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -689,7 +847,8 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" - depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -698,10 +857,25 @@ config VIDEO_OV2685 To compile this driver as a module, choose M here: the module will be called ov2685. +config VIDEO_OV2740 + tristate "OmniVision OV2740 sensor support" + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV2740 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2740. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF - depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB && VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Omnivision @@ -710,7 +884,9 @@ config VIDEO_OV5640 config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -721,7 +897,9 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -742,8 +920,9 @@ config VIDEO_OV6650 config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CONTROLLER + 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 @@ -754,8 +933,9 @@ config VIDEO_OV5670 config VIDEO_OV5675 tristate "OmniVision OV5675 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CONTROLLER + 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 @@ -777,7 +957,9 @@ config VIDEO_OV5695 config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -826,7 +1008,9 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -844,7 +1028,9 @@ config VIDEO_OV9640 config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_SCCB help This is a V4L2 sensor driver for the Omnivision @@ -852,7 +1038,9 @@ config VIDEO_OV9650 config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -870,14 +1058,18 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 && VIDEO_V4L2_SUBDEV_API + 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 @@ -893,7 +1085,9 @@ config VIDEO_MT9M111 config VIDEO_MT9P031 tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEO_APTINA_PLL help This is a Video4Linux2 sensor driver for the Aptina @@ -901,7 +1095,9 @@ config VIDEO_MT9P031 config VIDEO_MT9T001 tristate "Aptina MT9T001 support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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. @@ -926,7 +1122,9 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C select V4L2_FWNODE help @@ -951,7 +1149,9 @@ config VIDEO_SR030PC30 config VIDEO_NOON010PC30 tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This driver supports NOON010PC30 CIF camera from Siliconfile @@ -969,21 +1169,27 @@ config VIDEO_RJ54N1 config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. config VIDEO_S5K4ECGX tristate "Samsung S5K4ECGX sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select CRC32 help This is a V4L2 sensor driver for Samsung S5K4ECGX 5M @@ -991,7 +1197,9 @@ config VIDEO_S5K4ECGX config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a V4L2 sensor driver for Samsung S5K5BAF 2M @@ -1002,28 +1210,32 @@ source "drivers/media/i2c/et8ek8/Kconfig" config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" - depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && SPI && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. -endif -comment "Lens drivers" +endmenu -if MEDIA_CAMERA_SUPPORT +menu "Lens drivers" + visible if MEDIA_CAMERA_SUPPORT config VIDEO_AD5820 tristate "AD5820 lens voice coil support" - depends on GPIOLIB && I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on GPIOLIB && I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER help This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). config VIDEO_AK7375 tristate "AK7375 lens voice coil support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a driver for the AK7375 camera lens voice coil. AK7375 is a 12 bit DAC with 120mA output current sink @@ -1032,8 +1244,9 @@ config VIDEO_AK7375 config VIDEO_DW9714 tristate "DW9714 lens voice coil support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a driver for the DW9714 camera lens voice coil. DW9714 is a 10 bit DAC with 120mA output current sink @@ -1042,30 +1255,32 @@ config VIDEO_DW9714 config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a driver for the DW9807 camera lens voice coil. DW9807 is a 10 bit DAC with 100mA output current sink capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. -endif - -comment "Flash devices" +endmenu -if MEDIA_CAMERA_SUPPORT +menu "Flash devices" + visible if MEDIA_CAMERA_SUPPORT config VIDEO_ADP1653 tristate "ADP1653 flash support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER help This is a driver for the ADP1653 flash controller. It is used for example in Nokia N900. config VIDEO_LM3560 tristate "LM3560 dual flash driver support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER select REGMAP_I2C help This is a driver for the lm3560 dual flash controllers. It controls @@ -1073,112 +1288,12 @@ config VIDEO_LM3560 config VIDEO_LM3646 tristate "LM3646 dual flash driver support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER select REGMAP_I2C help This is a driver for the lm3646 dual flash controllers. It controls flash, torch LEDs. - -endif - -comment "Video improvement chips" - -config VIDEO_UPD64031A - tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_V4L2 && I2C - 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. - -comment "Audio/Video compression chips" - -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. - -comment "SDR tuner chips" - -config SDR_MAX2175 - tristate "Maxim 2175 RF to Bits tuner" - depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C - select REGMAP_I2C - 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. - -comment "Miscellaneous helper chips" - -config VIDEO_THS7303 - tristate "THS7303/53 Video Amplifier" - depends on VIDEO_V4L2 && I2C - 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 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - 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 -endif +endif # VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 77bf7d0b691f..993acab81b2c 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o +obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 0de946fe2109..e2e935f78986 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -997,14 +997,14 @@ static void cx23885_initialize(struct i2c_client *client) */ cx25840_write4(client, 0x404, 0x0010253e); - /* CC on - Undocumented Register */ + /* CC on - VBI_LINE_CTRL3, FLD_VBI_MD_LINE12 */ cx25840_write(client, state->vbi_regs_offset + 0x42f, 0x66); /* HVR-1250 / HVR1850 DIF related */ /* Power everything up */ cx25840_write4(client, 0x130, 0x0); - /* Undocumented */ + /* SRC_COMB_CFG */ if (is_cx23888(state)) cx25840_write4(client, 0x454, 0x6628021F); else @@ -1486,24 +1486,24 @@ static int set_input(struct i2c_client *client, cx25840_write4(client, 0x410, 0xffff0dbf); cx25840_write4(client, 0x414, 0x00137d03); - cx25840_write4(client, state->vbi_regs_offset + 0x42c, - 0x42600000); - cx25840_write4(client, state->vbi_regs_offset + 0x430, - 0x0000039b); - cx25840_write4(client, state->vbi_regs_offset + 0x438, - 0x00000000); - - cx25840_write4(client, state->vbi_regs_offset + 0x440, - 0xF8E3E824); - cx25840_write4(client, state->vbi_regs_offset + 0x444, - 0x401040dc); - cx25840_write4(client, state->vbi_regs_offset + 0x448, - 0xcd3f02a0); - cx25840_write4(client, state->vbi_regs_offset + 0x44c, - 0x161f1000); - cx25840_write4(client, state->vbi_regs_offset + 0x450, - 0x00000802); - + if (is_cx23888(state)) { + /* 888 MISC_TIM_CTRL */ + cx25840_write4(client, 0x42c, 0x42600000); + /* 888 FIELD_COUNT */ + cx25840_write4(client, 0x430, 0x0000039b); + /* 888 VSCALE_CTRL */ + cx25840_write4(client, 0x438, 0x00000000); + /* 888 DFE_CTRL1 */ + cx25840_write4(client, 0x440, 0xF8E3E824); + /* 888 DFE_CTRL2 */ + cx25840_write4(client, 0x444, 0x401040dc); + /* 888 DFE_CTRL3 */ + cx25840_write4(client, 0x448, 0xcd3f02a0); + /* 888 PLL_CTRL */ + cx25840_write4(client, 0x44c, 0x161f1000); + /* 888 HTL_CTRL */ + cx25840_write4(client, 0x450, 0x00000802); + } cx25840_write4(client, 0x91c, 0x01000000); cx25840_write4(client, 0x8e0, 0x03063870); cx25840_write4(client, 0x8d4, 0x7FFF0024); diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig index 1c6909874d56..afcc4ea764f6 100644 --- a/drivers/media/i2c/et8ek8/Kconfig +++ b/drivers/media/i2c/et8ek8/Kconfig @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_ET8EK8 tristate "ET8EK8 camera sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a driver for the Toshiba ET8EK8 5 MP camera sensor. diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index cb03bdec1f9c..f64c0ef7a897 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -15,8 +15,6 @@ */ #include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/clkdev.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -112,6 +110,14 @@ #define IMX219_TESTP_BLUE_DEFAULT 0 #define IMX219_TESTP_GREENB_DEFAULT 0 +/* IMX219 native and active pixel array size. */ +#define IMX219_NATIVE_WIDTH 3296U +#define IMX219_NATIVE_HEIGHT 2480U +#define IMX219_PIXEL_ARRAY_LEFT 8U +#define IMX219_PIXEL_ARRAY_TOP 8U +#define IMX219_PIXEL_ARRAY_WIDTH 3280U +#define IMX219_PIXEL_ARRAY_HEIGHT 2464U + struct imx219_reg { u16 address; u8 val; @@ -129,6 +135,9 @@ struct imx219_mode { /* Frame height */ unsigned int height; + /* Analog crop rectangle. */ + struct v4l2_rect crop; + /* V-timing */ unsigned int vts_def; @@ -463,6 +472,12 @@ static const struct imx219_mode supported_modes[] = { /* 8MPix 15fps mode */ .width = 3280, .height = 2464, + .crop = { + .left = 0, + .top = 0, + .width = 3280, + .height = 2464 + }, .vts_def = IMX219_VTS_15FPS, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), @@ -473,6 +488,12 @@ static const struct imx219_mode supported_modes[] = { /* 1080P 30fps cropped */ .width = 1920, .height = 1080, + .crop = { + .left = 680, + .top = 692, + .width = 1920, + .height = 1080 + }, .vts_def = IMX219_VTS_30FPS_1080P, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), @@ -483,6 +504,12 @@ static const struct imx219_mode supported_modes[] = { /* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, + .crop = { + .left = 0, + .top = 0, + .width = 3280, + .height = 2464 + }, .vts_def = IMX219_VTS_30FPS_BINNED, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), @@ -493,6 +520,12 @@ static const struct imx219_mode supported_modes[] = { /* 640x480 30fps mode */ .width = 640, .height = 480, + .crop = { + .left = 1000, + .top = 752, + .width = 1280, + .height = 960 + }, .vts_def = IMX219_VTS_30FPS_640x480, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), @@ -654,6 +687,7 @@ static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct imx219 *imx219 = to_imx219(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_rect *try_crop; mutex_lock(&imx219->mutex); @@ -664,6 +698,13 @@ static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) MEDIA_BUS_FMT_SRGGB10_1X10); try_fmt->field = V4L2_FIELD_NONE; + /* Initialize try_crop rectangle. */ + try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + try_crop->top = IMX219_PIXEL_ARRAY_TOP; + try_crop->left = IMX219_PIXEL_ARRAY_LEFT; + try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; + try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT; + mutex_unlock(&imx219->mutex); return 0; @@ -781,7 +822,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code)) + if (fse->code != imx219_get_format_code(imx219, fse->code)) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -928,6 +969,56 @@ static int imx219_set_framefmt(struct imx219 *imx219) return -EINVAL; } +static const struct v4l2_rect * +__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&imx219->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx219->mode->crop; + } + + return NULL; +} + +static int imx219_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx219 *imx219 = to_imx219(sd); + + mutex_lock(&imx219->mutex); + sel->r = *__imx219_get_pad_crop(imx219, cfg, sel->pad, + sel->which); + mutex_unlock(&imx219->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX219_NATIVE_WIDTH; + sel->r.height = IMX219_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = IMX219_PIXEL_ARRAY_TOP; + sel->r.left = IMX219_PIXEL_ARRAY_LEFT; + sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static int imx219_start_streaming(struct imx219 *imx219) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); @@ -1152,6 +1243,7 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = { .enum_mbus_code = imx219_enum_mbus_code, .get_fmt = imx219_get_pad_format, .set_fmt = imx219_set_pad_format, + .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, }; @@ -1171,11 +1263,12 @@ static int imx219_init_controls(struct imx219 *imx219) struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); struct v4l2_ctrl_handler *ctrl_hdlr; unsigned int height = imx219->mode->height; + struct v4l2_fwnode_device_properties props; int exposure_max, exposure_def, hblank; int i, ret; ctrl_hdlr = &imx219->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 11); if (ret) return ret; @@ -1254,6 +1347,15 @@ static int imx219_init_controls(struct imx219 *imx219) goto error; } + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, + &props); + if (ret) + goto error; + imx219->sd.ctrl_handler = ctrl_hdlr; return 0; diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig index e573482f269f..6f0ef33b7ee1 100644 --- a/drivers/media/i2c/m5mols/Kconfig +++ b/drivers/media/i2c/m5mols/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_M5MOLS tristate "Fujitsu M-5MOLS 8MP sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 506a30e69ced..03b4ed3a61b8 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1194,7 +1194,7 @@ static const struct v4l2_ctrl_ops max2175_ctrl_ops = { /* * I2S output enable/disable configuration. This is a private control. - * Refer to Documentation/media/v4l-drivers/max2175.rst for more details. + * Refer to Documentation/userspace-api/media/drivers/max2175.rst for more details. */ static const struct v4l2_ctrl_config max2175_i2s_en = { .ops = &max2175_ctrl_ops, @@ -1210,7 +1210,7 @@ static const struct v4l2_ctrl_config max2175_i2s_en = { /* * HSLS value control LO freq adjacent location configuration. - * Refer to Documentation/media/v4l-drivers/max2175.rst for more details. + * Refer to Documentation/userspace-api/media/drivers/max2175.rst for more details. */ static const struct v4l2_ctrl_config max2175_hsls = { .ops = &max2175_ctrl_ops, @@ -1226,7 +1226,7 @@ static const struct v4l2_ctrl_config max2175_hsls = { /* * Rx modes below are a set of preset configurations that decides the tuner's * sck and sample rate of transmission. They are separate for EU & NA regions. - * Refer to Documentation/media/v4l-drivers/max2175.rst for more details. + * Refer to Documentation/userspace-api/media/drivers/max2175.rst for more details. */ static const char * const max2175_ctrl_eu_rx_modes[] = { [MAX2175_EU_FM_1_2] = "EU FM 1.2", diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index aac6f77afa0f..236ad2c816b7 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -7,6 +7,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #define OV13858_REG_VALUE_08BIT 1 #define OV13858_REG_VALUE_16BIT 2 @@ -1589,6 +1590,7 @@ static const struct v4l2_subdev_internal_ops ov13858_internal_ops = { static int ov13858_init_controls(struct ov13858 *ov13858) { struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd); + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; s64 vblank_def; @@ -1600,7 +1602,7 @@ static int ov13858_init_controls(struct ov13858 *ov13858) int ret; ctrl_hdlr = &ov13858->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -1666,6 +1668,15 @@ static int ov13858_init_controls(struct ov13858 *ov13858) goto error; } + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov13858_ctrl_ops, + &props); + if (ret) + goto error; + ov13858->sd.ctrl_handler = ctrl_hdlr; return 0; diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c new file mode 100644 index 000000000000..2dd2609db873 --- /dev/null +++ b/drivers/media/i2c/ov2740.c @@ -0,0 +1,1016 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 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 OV2740_LINK_FREQ_360MHZ 360000000ULL +#define OV2740_SCLK 72000000LL +#define OV2740_MCLK 19200000 +#define OV2740_DATA_LANES 2 +#define OV2740_RGB_DEPTH 10 + +#define OV2740_REG_CHIP_ID 0x300a +#define OV2740_CHIP_ID 0x2740 + +#define OV2740_REG_MODE_SELECT 0x0100 +#define OV2740_MODE_STANDBY 0x00 +#define OV2740_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV2740_REG_VTS 0x380e +#define OV2740_VTS_DEF 0x088a +#define OV2740_VTS_MIN 0x0460 +#define OV2740_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV2740_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV2740_REG_EXPOSURE 0x3500 +#define OV2740_EXPOSURE_MIN 8 +#define OV2740_EXPOSURE_MAX_MARGIN 8 +#define OV2740_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV2740_REG_ANALOG_GAIN 0x3508 +#define OV2740_ANAL_GAIN_MIN 128 +#define OV2740_ANAL_GAIN_MAX 1983 +#define OV2740_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV2740_REG_MWB_R_GAIN 0x500a +#define OV2740_REG_MWB_G_GAIN 0x500c +#define OV2740_REG_MWB_B_GAIN 0x500e +#define OV2740_DGTL_GAIN_MIN 0 +#define OV2740_DGTL_GAIN_MAX 4095 +#define OV2740_DGTL_GAIN_STEP 1 +#define OV2740_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV2740_REG_TEST_PATTERN 0x5040 +#define OV2740_TEST_PATTERN_ENABLE BIT(7) +#define OV2740_TEST_PATTERN_BAR_SHIFT 2 + +enum { + OV2740_LINK_FREQ_360MHZ_INDEX, +}; + +struct ov2740_reg { + u16 address; + u8 val; +}; + +struct ov2740_reg_list { + u32 num_of_regs; + const struct ov2740_reg *regs; +}; + +struct ov2740_link_freq_config { + const struct ov2740_reg_list reg_list; +}; + +struct ov2740_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 ov2740_reg_list reg_list; +}; + +static const struct ov2740_reg mipi_data_rate_720mbps[] = { + {0x0103, 0x01}, + {0x0302, 0x4b}, + {0x030d, 0x4b}, + {0x030e, 0x02}, + {0x030a, 0x01}, + {0x0312, 0x11}, +}; + +static const struct ov2740_reg mode_1932x1092_regs[] = { + {0x3000, 0x00}, + {0x3018, 0x32}, + {0x3031, 0x0a}, + {0x3080, 0x08}, + {0x3083, 0xB4}, + {0x3103, 0x00}, + {0x3104, 0x01}, + {0x3106, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x44}, + {0x3502, 0x40}, + {0x3503, 0x88}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x00}, + {0x3512, 0x20}, + {0x3632, 0x00}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3645, 0x13}, + {0x3646, 0x81}, + {0x3636, 0x10}, + {0x3651, 0x0a}, + {0x3656, 0x02}, + {0x3659, 0x04}, + {0x365a, 0xda}, + {0x365b, 0xa2}, + {0x365c, 0x04}, + {0x365d, 0x1d}, + {0x365e, 0x1a}, + {0x3662, 0xd7}, + {0x3667, 0x78}, + {0x3669, 0x0a}, + {0x366a, 0x92}, + {0x3700, 0x54}, + {0x3702, 0x10}, + {0x3706, 0x42}, + {0x3709, 0x30}, + {0x370b, 0xc2}, + {0x3714, 0x63}, + {0x3715, 0x01}, + {0x3716, 0x00}, + {0x371a, 0x3e}, + {0x3732, 0x0e}, + {0x3733, 0x10}, + {0x375f, 0x0e}, + {0x3768, 0x30}, + {0x3769, 0x44}, + {0x376a, 0x22}, + {0x377b, 0x20}, + {0x377c, 0x00}, + {0x377d, 0x0c}, + {0x3798, 0x00}, + {0x37a1, 0x55}, + {0x37a8, 0x6d}, + {0x37c2, 0x04}, + {0x37c5, 0x00}, + {0x37c8, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x40}, + {0x380c, 0x04}, + {0x380d, 0x38}, + {0x380e, 0x04}, + {0x380f, 0x60}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x3822, 0x84}, + {0x3829, 0x00}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x04}, + {0x3836, 0x01}, + {0x3837, 0x08}, + {0x3839, 0x01}, + {0x383a, 0x00}, + {0x383b, 0x08}, + {0x383c, 0x00}, + {0x3f0b, 0x00}, + {0x4001, 0x20}, + {0x4009, 0x07}, + {0x4003, 0x10}, + {0x4010, 0xe0}, + {0x4016, 0x00}, + {0x4017, 0x10}, + {0x4044, 0x02}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4320, 0x80}, + {0x4322, 0x00}, + {0x4323, 0x00}, + {0x4324, 0x00}, + {0x4325, 0x00}, + {0x4326, 0x00}, + {0x4327, 0x00}, + {0x4328, 0x00}, + {0x4329, 0x00}, + {0x432c, 0x03}, + {0x432d, 0x81}, + {0x4501, 0x84}, + {0x4502, 0x40}, + {0x4503, 0x18}, + {0x4504, 0x04}, + {0x4508, 0x02}, + {0x4601, 0x10}, + {0x4800, 0x00}, + {0x4816, 0x52}, + {0x4837, 0x16}, + {0x5000, 0x7f}, + {0x5001, 0x00}, + {0x5005, 0x38}, + {0x501e, 0x0d}, + {0x5040, 0x00}, + {0x5901, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x8c}, + {0x380a, 0x04}, + {0x380b, 0x44}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x01}, +}; + +static const char * const ov2740_test_pattern_menu[] = { + "Disabled", + "Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar", +}; + +static const s64 link_freq_menu_items[] = { + OV2740_LINK_FREQ_360MHZ, +}; + +static const struct ov2740_link_freq_config link_freq_configs[] = { + [OV2740_LINK_FREQ_360MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps), + .regs = mipi_data_rate_720mbps, + } + }, +}; + +static const struct ov2740_mode supported_modes[] = { + { + .width = 1932, + .height = 1092, + .hts = 1080, + .vts_def = OV2740_VTS_DEF, + .vts_min = OV2740_VTS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1932x1092_regs), + .regs = mode_1932x1092_regs, + }, + .link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX, + }, +}; + +struct ov2740 { + 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 ov2740_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct ov2740 *to_ov2740(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov2740, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV2740_DATA_LANES; + + do_div(pixel_rate, OV2740_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, OV2740_SCLK); + + return ppl; +} + +static int ov2740_read_reg(struct ov2740 *ov2740, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret = 0; + + if (len > sizeof(data_buf)) + 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[sizeof(data_buf) - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return ret < 0 ? ret : -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov2740_write_reg(struct ov2740 *ov2740, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + u8 buf[6]; + int ret = 0; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int ov2740_write_reg_list(struct ov2740 *ov2740, + const struct ov2740_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + unsigned int i; + int ret = 0; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov2740_write_reg(ov2740, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov2740_update_digital_gain(struct ov2740 *ov2740, u32 d_gain) +{ + int ret = 0; + + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_R_GAIN, 2, d_gain); + if (ret) + return ret; + + ret = ov2740_write_reg(ov2740, OV2740_REG_MWB_G_GAIN, 2, d_gain); + if (ret) + return ret; + + return ov2740_write_reg(ov2740, OV2740_REG_MWB_B_GAIN, 2, d_gain); +} + +static int ov2740_test_pattern(struct ov2740 *ov2740, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV2740_TEST_PATTERN_BAR_SHIFT | + OV2740_TEST_PATTERN_ENABLE; + + return ov2740_write_reg(ov2740, OV2740_REG_TEST_PATTERN, 1, pattern); +} + +static int ov2740_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov2740 *ov2740 = container_of(ctrl->handler, + struct ov2740, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->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 = ov2740->cur_mode->height + ctrl->val - + OV2740_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov2740->exposure, + ov2740->exposure->minimum, + exposure_max, ov2740->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 = ov2740_write_reg(ov2740, OV2740_REG_ANALOG_GAIN, 2, + ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov2740_update_digital_gain(ov2740, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov2740_write_reg(ov2740, OV2740_REG_EXPOSURE, 3, + ctrl->val << 4); + break; + + case V4L2_CID_VBLANK: + ret = ov2740_write_reg(ov2740, OV2740_REG_VTS, 2, + ov2740->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov2740_test_pattern(ov2740, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov2740_ctrl_ops = { + .s_ctrl = ov2740_set_ctrl, +}; + +static int ov2740_init_controls(struct ov2740 *ov2740) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct ov2740_mode *cur_mode; + s64 exposure_max, h_blank, pixel_rate; + u32 vblank_min, vblank_max, vblank_default; + int size; + int ret = 0; + + ctrl_hdlr = &ov2740->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov2740->mutex; + cur_mode = ov2740->cur_mode; + size = ARRAY_SIZE(link_freq_menu_items); + + ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_LINK_FREQ, + size - 1, 0, + link_freq_menu_items); + if (ov2740->link_freq) + ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(OV2740_LINK_FREQ_360MHZ_INDEX); + ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + vblank_min = cur_mode->vts_min - cur_mode->height; + vblank_max = OV2740_VTS_MAX - cur_mode->height; + vblank_default = cur_mode->vts_def - cur_mode->height; + ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + + h_blank = to_pixels_per_line(cur_mode->hts, cur_mode->link_freq_index); + h_blank -= cur_mode->width; + ov2740->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (ov2740->hblank) + ov2740->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV2740_ANAL_GAIN_MIN, OV2740_ANAL_GAIN_MAX, + OV2740_ANAL_GAIN_STEP, OV2740_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX, + OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT); + exposure_max = cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN; + ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_EXPOSURE, + OV2740_EXPOSURE_MIN, exposure_max, + OV2740_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov2740_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2740_test_pattern_menu) - 1, + 0, 0, ov2740_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov2740->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov2740_update_pad_format(const struct ov2740_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 ov2740_start_streaming(struct ov2740 *ov2740) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + const struct ov2740_reg_list *reg_list; + int link_freq_index; + int ret = 0; + + link_freq_index = ov2740->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = ov2740_write_reg_list(ov2740, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov2740->cur_mode->reg_list; + ret = ov2740_write_reg_list(ov2740, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov2740->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STREAMING); + if (ret) + dev_err(&client->dev, "failed to start streaming"); + + return ret; +} + +static void ov2740_stop_streaming(struct ov2740 *ov2740) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + + if (ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STANDBY)) + dev_err(&client->dev, "failed to stop streaming"); +} + +static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov2740 *ov2740 = to_ov2740(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov2740->streaming == enable) + return 0; + + mutex_lock(&ov2740->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov2740->mutex); + return ret; + } + + ret = ov2740_start_streaming(ov2740); + if (ret) { + enable = 0; + ov2740_stop_streaming(ov2740); + pm_runtime_put(&client->dev); + } + } else { + ov2740_stop_streaming(ov2740); + pm_runtime_put(&client->dev); + } + + ov2740->streaming = enable; + mutex_unlock(&ov2740->mutex); + + return ret; +} + +static int __maybe_unused ov2740_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2740 *ov2740 = to_ov2740(sd); + + mutex_lock(&ov2740->mutex); + if (ov2740->streaming) + ov2740_stop_streaming(ov2740); + + mutex_unlock(&ov2740->mutex); + + return 0; +} + +static int __maybe_unused ov2740_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2740 *ov2740 = to_ov2740(sd); + int ret = 0; + + mutex_lock(&ov2740->mutex); + if (!ov2740->streaming) + goto exit; + + ret = ov2740_start_streaming(ov2740); + if (ret) { + ov2740->streaming = false; + ov2740_stop_streaming(ov2740); + } + +exit: + mutex_unlock(&ov2740->mutex); + return ret; +} + +static int ov2740_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov2740 *ov2740 = to_ov2740(sd); + const struct ov2740_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(&ov2740->mutex); + ov2740_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov2740->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov2740->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov2740->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(ov2740->vblank, + mode->vts_min - mode->height, + OV2740_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, + h_blank); + } + mutex_unlock(&ov2740->mutex); + + return 0; +} + +static int ov2740_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov2740 *ov2740 = to_ov2740(sd); + + mutex_lock(&ov2740->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov2740->sd, cfg, + fmt->pad); + else + ov2740_update_pad_format(ov2740->cur_mode, &fmt->format); + + mutex_unlock(&ov2740->mutex); + + return 0; +} + +static int ov2740_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov2740_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + 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 ov2740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov2740 *ov2740 = to_ov2740(sd); + + mutex_lock(&ov2740->mutex); + ov2740_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&ov2740->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov2740_video_ops = { + .s_stream = ov2740_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov2740_pad_ops = { + .set_fmt = ov2740_set_format, + .get_fmt = ov2740_get_format, + .enum_mbus_code = ov2740_enum_mbus_code, + .enum_frame_size = ov2740_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov2740_subdev_ops = { + .video = &ov2740_video_ops, + .pad = &ov2740_pad_ops, +}; + +static const struct media_entity_operations ov2740_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov2740_internal_ops = { + .open = ov2740_open, +}; + +static int ov2740_identify_module(struct ov2740 *ov2740) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + int ret; + u32 val; + + ret = ov2740_read_reg(ov2740, OV2740_REG_CHIP_ID, 3, &val); + if (ret) + return ret; + + if (val != OV2740_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV2740_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int ov2740_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) + return ret; + + if (mclk != OV2740_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 != OV2740_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 ov2740_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2740 *ov2740 = to_ov2740(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(&ov2740->mutex); + + return 0; +} + +static int ov2740_probe(struct i2c_client *client) +{ + struct ov2740 *ov2740; + int ret = 0; + + ret = ov2740_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + ov2740 = devm_kzalloc(&client->dev, sizeof(*ov2740), GFP_KERNEL); + if (!ov2740) + return -ENOMEM; + + 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); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov2740->sd.internal_ops = &ov2740_internal_ops; + ov2740->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov2740->sd.entity.ops = &ov2740_subdev_entity_ops; + ov2740->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov2740->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov2740->sd.entity, 1, &ov2740->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_common(&ov2740->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(&ov2740->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); + mutex_destroy(&ov2740->mutex); + + return ret; +} + +static const struct dev_pm_ops ov2740_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov2740_acpi_ids[] = { + {"INT3474"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); +#endif + +static struct i2c_driver ov2740_i2c_driver = { + .driver = { + .name = "ov2740", + .pm = &ov2740_pm_ops, + .acpi_match_table = ACPI_PTR(ov2740_acpi_ids), + }, + .probe_new = ov2740_probe, + .remove = ov2740_remove, +}; + +module_i2c_driver(ov2740_i2c_driver); + +MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); +MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); +MODULE_DESCRIPTION("OmniVision OV2740 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 854031f0b64a..2fe4a7ac0592 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -3093,8 +3093,8 @@ static int ov5640_probe(struct i2c_client *client) free_ctrls: v4l2_ctrl_handler_free(&sensor->ctrls.handler); entity_cleanup: - mutex_destroy(&sensor->lock); media_entity_cleanup(&sensor->sd.entity); + mutex_destroy(&sensor->lock); return ret; } @@ -3104,9 +3104,9 @@ static int ov5640_remove(struct i2c_client *client) struct ov5640_dev *sensor = to_ov5640_dev(sd); v4l2_async_unregister_subdev(&sensor->sd); - mutex_destroy(&sensor->lock); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); + mutex_destroy(&sensor->lock); return 0; } diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 041fcbb4eebd..f26252e35e08 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -7,6 +7,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #define OV5670_REG_CHIP_ID 0x300a #define OV5670_CHIP_ID 0x005670 @@ -2059,6 +2060,8 @@ static const struct v4l2_ctrl_ops ov5670_ctrl_ops = { /* Initialize control handlers */ static int ov5670_init_controls(struct ov5670 *ov5670) { + struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 vblank_max; s64 vblank_def; @@ -2067,7 +2070,7 @@ static int ov5670_init_controls(struct ov5670 *ov5670) int ret; ctrl_hdlr = &ov5670->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -2129,6 +2132,15 @@ static int ov5670_init_controls(struct ov5670 *ov5670) goto error; } + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov5670_ctrl_ops, + &props); + if (ret) + goto error; + ov5670->sd.ctrl_handler = ctrl_hdlr; return 0; diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index 8655842af275..4ca27675cc5a 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -3,10 +3,13 @@ #include <asm/unaligned.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/delay.h> +#include <linux/gpio/consumer.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> @@ -18,7 +21,7 @@ #define OV8856_LINK_FREQ_360MHZ 360000000ULL #define OV8856_LINK_FREQ_180MHZ 180000000ULL #define OV8856_SCLK 144000000ULL -#define OV8856_MCLK 19200000 +#define OV8856_XVCLK_19_2 19200000 #define OV8856_DATA_LANES 4 #define OV8856_RGB_DEPTH 10 @@ -29,6 +32,19 @@ #define OV8856_MODE_STANDBY 0x00 #define OV8856_MODE_STREAMING 0x01 +/* module revisions */ +#define OV8856_2A_MODULE 0x01 +#define OV8856_1B_MODULE 0x02 + +/* the OTP read-out buffer is at 0x7000 and 0xf is the offset + * of the byte in the OTP that means the module revision + */ +#define OV8856_MODULE_REVISION 0x700f +#define OV8856_OTP_MODE_CTRL 0x3d84 +#define OV8856_OTP_LOAD_CTRL 0x3d81 +#define OV8856_OTP_MODE_AUTO 0x00 +#define OV8856_OTP_LOAD_CTRL_ENABLE BIT(0) + /* vertical-timings from sensor */ #define OV8856_REG_VTS 0x380e #define OV8856_VTS_MAX 0x7fff @@ -64,6 +80,12 @@ #define to_ov8856(_sd) container_of(_sd, struct ov8856, sd) +static const char * const ov8856_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + enum { OV8856_LINK_FREQ_720MBPS, OV8856_LINK_FREQ_360MBPS, @@ -566,6 +588,10 @@ struct ov8856 { struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)]; + /* V4L2 Controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; @@ -908,6 +934,57 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) return ret; } +static int __ov8856_power_on(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + int ret; + + if (is_acpi_node(dev_fwnode(&client->dev))) + return 0; + + ret = clk_prepare_enable(ov8856->xvclk); + if (ret < 0) { + dev_err(&client->dev, "failed to enable xvclk\n"); + return ret; + } + + if (ov8856->reset_gpio) { + gpiod_set_value_cansleep(ov8856->reset_gpio, 1); + usleep_range(1000, 2000); + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names), + ov8856->supplies); + if (ret < 0) { + dev_err(&client->dev, "failed to enable regulators\n"); + goto disable_clk; + } + + gpiod_set_value_cansleep(ov8856->reset_gpio, 0); + usleep_range(1500, 1800); + + return 0; + +disable_clk: + gpiod_set_value_cansleep(ov8856->reset_gpio, 1); + clk_disable_unprepare(ov8856->xvclk); + + return ret; +} + +static void __ov8856_power_off(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + + if (is_acpi_node(dev_fwnode(&client->dev))) + return; + + gpiod_set_value_cansleep(ov8856->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names), + ov8856->supplies); + clk_disable_unprepare(ov8856->xvclk); +} + static int __maybe_unused ov8856_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -918,6 +995,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev) if (ov8856->streaming) ov8856_stop_streaming(ov8856); + __ov8856_power_off(ov8856); mutex_unlock(&ov8856->mutex); return 0; @@ -931,6 +1009,8 @@ static int __maybe_unused ov8856_resume(struct device *dev) int ret; mutex_lock(&ov8856->mutex); + + __ov8856_power_on(ov8856); if (ov8856->streaming) { ret = ov8856_start_streaming(ov8856); if (ret) { @@ -1089,32 +1169,96 @@ static int ov8856_identify_module(struct ov8856 *ov8856) return -ENXIO; } + ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); + if (ret) + return ret; + + ret = ov8856_write_reg(ov8856, OV8856_OTP_MODE_CTRL, + OV8856_REG_VALUE_08BIT, OV8856_OTP_MODE_AUTO); + if (ret) { + dev_err(&client->dev, "failed to set otp mode"); + return ret; + } + + ret = ov8856_write_reg(ov8856, OV8856_OTP_LOAD_CTRL, + OV8856_REG_VALUE_08BIT, + OV8856_OTP_LOAD_CTRL_ENABLE); + if (ret) { + dev_err(&client->dev, "failed to enable load control"); + return ret; + } + + ret = ov8856_read_reg(ov8856, OV8856_MODULE_REVISION, + OV8856_REG_VALUE_08BIT, &val); + if (ret) { + dev_err(&client->dev, "failed to read module revision"); + return ret; + } + + dev_info(&client->dev, "OV8856 revision %x (%s) at address 0x%02x\n", + val, + val == OV8856_2A_MODULE ? "2A" : + val == OV8856_1B_MODULE ? "1B" : "unknown revision", + client->addr); + + ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY); + if (ret) { + dev_err(&client->dev, "failed to exit streaming mode"); + return ret; + } + return 0; } -static int ov8856_check_hwcfg(struct device *dev) +static int ov8856_get_hwcfg(struct ov8856 *ov8856, 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; + u32 xvclk_rate; int ret; unsigned int i, j; if (!fwnode) return -ENXIO; - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); if (ret) return ret; - if (mclk != OV8856_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; + if (!is_acpi_node(fwnode)) { + ov8856->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov8856->xvclk)) { + dev_err(dev, "could not get xvclk clock (%pe)\n", + ov8856->xvclk); + return PTR_ERR(ov8856->xvclk); + } + + clk_set_rate(ov8856->xvclk, xvclk_rate); + xvclk_rate = clk_get_rate(ov8856->xvclk); } + if (xvclk_rate != OV8856_XVCLK_19_2) + dev_warn(dev, "external clock rate %u is unsupported", + xvclk_rate); + + ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ov8856->reset_gpio)) + return PTR_ERR(ov8856->reset_gpio); + + for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++) + ov8856->supplies[i].supply = ov8856_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names), + ov8856->supplies); + if (ret) + return ret; + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) return -ENXIO; @@ -1169,6 +1313,8 @@ static int ov8856_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); mutex_destroy(&ov8856->mutex); + __ov8856_power_off(ov8856); + return 0; } @@ -1177,22 +1323,29 @@ static int ov8856_probe(struct i2c_client *client) struct ov8856 *ov8856; int ret; - ret = ov8856_check_hwcfg(&client->dev); + ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL); + if (!ov8856) + return -ENOMEM; + + ret = ov8856_get_hwcfg(ov8856, &client->dev); if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", + dev_err(&client->dev, "failed to get HW configuration: %d", ret); return ret; } - ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL); - if (!ov8856) - return -ENOMEM; - v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); + + ret = __ov8856_power_on(ov8856); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + return ret; + } + ret = ov8856_identify_module(ov8856); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); - return ret; + goto probe_power_off; } mutex_init(&ov8856->mutex); @@ -1238,6 +1391,9 @@ probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler); mutex_destroy(&ov8856->mutex); +probe_power_off: + __ov8856_power_off(ov8856); + return ret; } @@ -1254,11 +1410,18 @@ static const struct acpi_device_id ov8856_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids); #endif +static const struct of_device_id ov8856_of_match[] = { + { .compatible = "ovti,ov8856" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov8856_of_match); + static struct i2c_driver ov8856_i2c_driver = { .driver = { .name = "ov8856", .pm = &ov8856_pm_ops, .acpi_match_table = ACPI_PTR(ov8856_acpi_ids), + .of_match_table = ov8856_of_match, }, .probe_new = ov8856_probe, .remove = ov8856_remove, diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index cdfe008ba39f..42584a088273 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -281,7 +281,7 @@ struct s5k5baf_fw { u16 id; u16 offset; } seq[0]; - u16 data[0]; + u16 data[]; }; struct s5k5baf { diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig index fcaa7f9494a8..6893b532824f 100644 --- a/drivers/media/i2c/smiapp/Kconfig +++ b/drivers/media/i2c/smiapp/Kconfig @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_SMIAPP tristate "SMIA++/SMIA sensor support" - depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK - depends on MEDIA_CAMERA_SUPPORT + depends on I2C && VIDEO_V4L2 && HAVE_CLK + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEO_SMIAPP_PLL select V4L2_FWNODE help diff --git a/drivers/media/mc/Kconfig b/drivers/media/mc/Kconfig index 3b9795cfcb36..4815b9dde9af 100644 --- a/drivers/media/mc/Kconfig +++ b/drivers/media/mc/Kconfig @@ -1,17 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + # # Media controller -# Selectable only for webcam/grabbers, as other drivers don't use it # -config MEDIA_CONTROLLER - bool "Media Controller API" - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT - help - Enable the media controller API used to query media devices internal - topology and configure it dynamically. - - This API is mostly used by camera interfaces in embedded platforms. - config MEDIA_CONTROLLER_DVB bool "Enable Media controller for DVB (EXPERIMENTAL)" depends on MEDIA_CONTROLLER && DVB_CORE @@ -21,8 +13,8 @@ config MEDIA_CONTROLLER_DVB This is currently experimental. config MEDIA_CONTROLLER_REQUEST_API - bool "Enable Media controller Request API (EXPERIMENTAL)" - depends on MEDIA_CONTROLLER && STAGING_MEDIA + bool + depends on MEDIA_CONTROLLER help DO NOT ENABLE THIS OPTION UNLESS YOU KNOW WHAT YOU'RE DOING. @@ -31,3 +23,6 @@ config MEDIA_CONTROLLER_REQUEST_API There is currently no intention to provide API or ABI stability for this new API as of yet. + +comment "Please notice that the enabled Media controller Request API is EXPERIMENTAL" + depends on MEDIA_CONTROLLER_REQUEST_API diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 211279c5fd77..12b45e669bcc 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -386,7 +386,7 @@ int media_entity_get_fwnode_pad(struct media_entity *entity, if (ret) return ret; - ret = entity->ops->get_fwnode_pad(&endpoint); + ret = entity->ops->get_fwnode_pad(entity, &endpoint); if (ret < 0) return ret; diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig index de0528c6994a..75aa6de08d53 100644 --- a/drivers/media/mmc/Kconfig +++ b/drivers/media/mmc/Kconfig @@ -1,3 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -comment "Supported MMC/SDIO adapters" source "drivers/media/mmc/siano/Kconfig" diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig index 1919f6fea8b1..570696019a9e 100644 --- a/drivers/media/mmc/siano/Kconfig +++ b/drivers/media/mmc/siano/Kconfig @@ -2,6 +2,8 @@ # # Siano Mobile Silicon Digital TV device configuration # +comment "MMC/SDIO DVB adapters" + depends on DVB_CORE && HAS_DMA && MMC config SMS_SDIO_DRV tristate "Siano SMS1xxx based MDTV via SDIO interface" diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index dcb3719f440e..2cd8e328dda9 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -if PCI && MEDIA_SUPPORT + +if PCI menuconfig MEDIA_PCI_SUPPORT bool "Media PCI Adapters" @@ -56,5 +57,16 @@ endif source "drivers/media/pci/intel/ipu3/Kconfig" +config VIDEO_PCI_SKELETON + tristate "Skeleton PCI V4L2 driver" + depends on SAMPLES + depends on MEDIA_TEST_SUPPORT + depends on PCI && VIDEO_V4L2 + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_DMA_CONTIG + help + Enable build of the skeleton PCI driver, used as a reference + when developing new drivers. + endif #MEDIA_PCI_SUPPORT endif #PCI diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index 75d172a6f54c..3f56decbb681 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -17,7 +17,7 @@ config VIDEO_BT848 help Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in - <file:Documentation/media/v4l-drivers/bttv.rst> for more information. + <file:Documentation/admin-guide/media/bttv.rst> for more information. To compile this driver as a module, choose M here: the module will be called bttv. diff --git a/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig index e0e7df460a92..d8d9ea6b09bc 100644 --- a/drivers/media/pci/cobalt/Kconfig +++ b/drivers/media/pci/cobalt/Kconfig @@ -1,11 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_COBALT tristate "Cisco Cobalt support" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && I2C depends on PCI_MSI && MTD_COMPLEX_MAPPINGS depends on (GPIOLIB && DRM_I2C_ADV7511=n) || COMPILE_TEST depends on SND depends on MTD + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select I2C_ALGOBIT select SND_PCM select VIDEO_ADV7604 diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index fa57e12f2ac8..4864def20676 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -466,14 +466,24 @@ static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { static const struct v4l2_fmtdesc formats[] = { - { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, - "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "HM12 (YUV 4:1:1)", + .pixelformat = V4L2_PIX_FMT_HM12, }, - { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, - "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } + { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = V4L2_FMT_FLAG_COMPRESSED, + .description = "MPEG", + .pixelformat = V4L2_PIX_FMT_MPEG, }, - { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, - "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 } + { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "UYVY 4:2:2", + .pixelformat = V4L2_PIX_FMT_UYVY, }, }; diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index 3178df3c4922..c41bae118415 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -845,7 +845,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* * Audio related reset according to - * Documentation/media/v4l-drivers/cx2341x.rst + * Documentation/driver-api/media/drivers/cx2341x-devel.rst */ if (atomic_read(&cx->ana_capturing) == 0) cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, @@ -853,7 +853,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* * Number of lines for Field 1 & Field 2 according to - * Documentation/media/v4l-drivers/cx2341x.rst + * Documentation/driver-api/media/drivers/cx2341x-devel.rst * Field 1 is 312 for 625 line systems in BT.656 * Field 2 is 313 for 625 line systems in BT.656 */ diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 8e5a2c580821..570a4a09c387 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -703,8 +703,19 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_HAUPPAUGE_HVR5525] = { .name = "Hauppauge WinTV-HVR5525", + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + } }, }, [CX23885_BOARD_VIEWCAST_260E] = { .name = "ViewCast 260e", @@ -757,32 +768,61 @@ struct cx23885_board cx23885_boards[] = { } }, }, [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB] = { - .name = "Hauppauge WinTV-QuadHD-DVB", + .name = "Hauppauge WinTV-QuadHD-DVB", + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + } }, }, [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885] = { - .name = "Hauppauge WinTV-QuadHD-DVB(885)", + .name = "Hauppauge WinTV-QuadHD-DVB(885)", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, }, [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC] = { - .name = "Hauppauge WinTV-QuadHD-ATSC", + .name = "Hauppauge WinTV-QuadHD-ATSC", + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + } }, }, [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885] = { - .name = "Hauppauge WinTV-QuadHD-ATSC(885)", + .name = "Hauppauge WinTV-QuadHD-ATSC(885)", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, }, [CX23885_BOARD_HAUPPAUGE_HVR1265_K4] = { .name = "Hauppauge WinTV-HVR-1265(161111)", .porta = CX23885_ANALOG_VIDEO, .portc = CX23885_MPEG_DVB, .tuner_type = TUNER_ABSENT, - .force_bff = 1, .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + }, { .type = CX23885_VMUX_COMPOSITE1, .vmux = CX25840_VIN7_CH3 | CX25840_VIN4_CH2 | @@ -2350,6 +2390,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR5525: case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: case CX23885_BOARD_HAUPPAUGE_HVR1290: diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 494751a067a3..45c2f4afceb8 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2314,6 +2314,12 @@ static int dvb_register(struct cx23885_tsport *port) goto frontend_detach; } port->i2c_client_tuner = client_tuner; + + dev->ts1.analog_fe.tuner_priv = client_tuner; + memcpy(&dev->ts1.analog_fe.ops.tuner_ops, + &fe0->dvb.frontend->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + break; } break; @@ -2367,6 +2373,16 @@ static int dvb_register(struct cx23885_tsport *port) goto frontend_detach; } port->i2c_client_tuner = client_tuner; + + /* we only attach tuner for analog on the 888 version */ + if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) { + pr_info("%s(): QUADHD_DVB analog setup\n", + __func__); + dev->ts1.analog_fe.tuner_priv = client_tuner; + memcpy(&dev->ts1.analog_fe.ops.tuner_ops, + &fe0->dvb.frontend->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } break; /* port c - terrestrial/cable */ @@ -2456,6 +2472,16 @@ static int dvb_register(struct cx23885_tsport *port) goto frontend_detach; } port->i2c_client_tuner = client_tuner; + + /* we only attach tuner for analog on the 888 version */ + if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) { + pr_info("%s(): QUADHD_ATSC analog setup\n", + __func__); + dev->ts1.analog_fe.tuner_priv = client_tuner; + memcpy(&dev->ts1.analog_fe.ops.tuner_ops, + &fe0->dvb.frontend->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } break; /* port c - terrestrial/cable */ @@ -2527,6 +2553,11 @@ static int dvb_register(struct cx23885_tsport *port) goto frontend_detach; } port->i2c_client_tuner = client_tuner; + + dev->ts1.analog_fe.tuner_priv = client_tuner; + memcpy(&dev->ts1.analog_fe.ops.tuner_ops, + &fe0->dvb.frontend->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); break; } break; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 000c108b94fd..440d108b7ddd 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -253,7 +253,10 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) || + (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) || + (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR5525) || (dev->board == CX23885_BOARD_MYGICA_X8507) || (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) || (dev->board == CX23885_BOARD_VIEWCAST_260E) || @@ -636,8 +639,18 @@ static int vidioc_querycap(struct file *file, void *priv, V4L2_CAP_AUDIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS; - if (dev->tuner_type != TUNER_ABSENT) + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: cap->capabilities |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + break; + } return 0; } @@ -883,8 +896,17 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct cx23885_dev *dev = video_drvdata(file); - if (dev->tuner_type == TUNER_ABSENT) - return -EINVAL; + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + break; + default: + if (dev->tuner_type == TUNER_ABSENT) + return -EINVAL; + break; + } if (0 != t->index) return -EINVAL; @@ -899,8 +921,17 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct cx23885_dev *dev = video_drvdata(file); - if (dev->tuner_type == TUNER_ABSENT) - return -EINVAL; + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + break; + default: + if (dev->tuner_type == TUNER_ABSENT) + return -EINVAL; + break; + } if (0 != t->index) return -EINVAL; /* Update the A/V core */ @@ -914,9 +945,17 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct cx23885_dev *dev = video_drvdata(file); - if (dev->tuner_type == TUNER_ABSENT) - return -EINVAL; - + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + break; + default: + if (dev->tuner_type == TUNER_ABSENT) + return -EINVAL; + break; + } f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; @@ -930,8 +969,17 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency struct v4l2_ctrl *mute; int old_mute_val = 1; - if (dev->tuner_type == TUNER_ABSENT) - return -EINVAL; + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + break; + default: + if (dev->tuner_type == TUNER_ABSENT) + return -EINVAL; + break; + } if (unlikely(f->tuner != 0)) return -EINVAL; @@ -996,7 +1044,10 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || - (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4)) + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR5525) || + (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) || + (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC)) fe = &dev->ts1.analog_fe; if (fe && fe->ops.tuner_ops.set_analog_params) { @@ -1027,6 +1078,9 @@ int cx23885_set_frequency(struct file *file, void *priv, case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: ret = cx23885_set_freq_via_ops(dev, f); break; default: @@ -1302,8 +1356,18 @@ int cx23885_video_register(struct cx23885_dev *dev) dev->video_dev->queue = &dev->vb2_vidq; dev->video_dev->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_VIDEO_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: dev->video_dev->device_caps |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + dev->video_dev->device_caps |= V4L2_CAP_TUNER; + } + err = video_register_device(dev->video_dev, VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) { @@ -1320,8 +1384,17 @@ int cx23885_video_register(struct cx23885_dev *dev) dev->vbi_dev->queue = &dev->vb2_vbiq; dev->vbi_dev->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_VBI_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + switch (dev->board) { /* i2c device tuners */ + case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: + case CX23885_BOARD_HAUPPAUGE_HVR5525: + case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: dev->vbi_dev->device_caps |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + dev->vbi_dev->device_caps |= V4L2_CAP_TUNER; + } err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->nr]); if (err < 0) { diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index dcadf78657d6..48c8a3429542 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1070,8 +1070,7 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) mutex_lock(&devlist); cx88_ir_fini(core); if (core->i2c_rc == 0) { - if (core->i2c_rtc) - i2c_unregister_device(core->i2c_rtc); + i2c_unregister_device(core->i2c_rtc); i2c_del_adapter(&core->i2c_adap); } list_del(&core->devlist); diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index c7c2acd55266..7e0fed9cd200 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -638,7 +638,7 @@ void cx88_i2c_init_ir(struct cx88_core *core) I2C_SMBUS_READ, 0, I2C_SMBUS_QUICK, NULL) >= 0) { info.addr = *addrp; - i2c_new_device(&core->i2c_adap, &info); + i2c_new_client_device(&core->i2c_adap, &info); break; } } diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index 6aabc45aa93c..ba0e9660a047 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1385,7 +1385,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, }; request_module("rtc-isl1208"); - core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); + core->i2c_rtc = i2c_new_client_device(&core->i2c_adap, &rtc_info); } /* fall-through */ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index dab34fb85c09..169efd558e45 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -15,7 +15,6 @@ config DVB_DDBRIDGE select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT select DVB_MXL5XX if MEDIA_SUBDRV_AUTOSELECT select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT - select DVB_DUMMY_FE if MEDIA_SUBDRV_AUTOSELECT help Support for cards with the Digital Devices PCI express bridge: - Octopus PCIe Bridge diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index 2b77c8d0eb2e..5e7eab81173b 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -7,7 +7,7 @@ ddbridge-objs := ddbridge-main.o ddbridge-core.o ddbridge-ci.o \ ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o \ ddbridge-sx8.o -obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o +obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o ddbridge-dummy-fe.o ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ ccflags-y += -I $(srctree)/drivers/media/tuners/ diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 7a2d19682fe3..7cabb9e9ffe2 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -50,7 +50,7 @@ #include "stv6111.h" #include "lnbh25.h" #include "cxd2099.h" -#include "dvb_dummy_fe.h" +#include "ddbridge-dummy-fe.h" /****************************************************************************/ @@ -1265,7 +1265,7 @@ static int demod_attach_dummy(struct ddb_input *input) struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; struct device *dev = input->port->dev->dev; - dvb->fe = dvb_attach(dvb_dummy_fe_qam_attach); + dvb->fe = dvb_attach(ddbridge_dummy_fe_qam_attach); if (!dvb->fe) { dev_err(dev, "QAM dummy attach failed!\n"); return -ENODEV; diff --git a/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c new file mode 100644 index 000000000000..6868a0c4fc82 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Dummy Frontend + * + * Written by Emard <emard@softhome.net> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <media/dvb_frontend.h> +#include "ddbridge-dummy-fe.h" + +struct ddbridge_dummy_fe_state { + struct dvb_frontend frontend; +}; + +static int ddbridge_dummy_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + + return 0; +} + +static int ddbridge_dummy_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int ddbridge_dummy_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + *strength = 0; + return 0; +} + +static int ddbridge_dummy_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int ddbridge_dummy_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +/* + * Should only be implemented if it actually reads something from the hardware. + * Also, it should check for the locks, in order to avoid report wrong data + * to userspace. + */ +static int ddbridge_dummy_fe_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + return 0; +} + +static int ddbridge_dummy_fe_set_frontend(struct dvb_frontend *fe) +{ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int ddbridge_dummy_fe_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int ddbridge_dummy_fe_init(struct dvb_frontend *fe) +{ + return 0; +} + +static void ddbridge_dummy_fe_release(struct dvb_frontend *fe) +{ + struct ddbridge_dummy_fe_state *state = fe->demodulator_priv; + + kfree(state); +} + +static const struct dvb_frontend_ops ddbridge_dummy_fe_qam_ops; + +struct dvb_frontend *ddbridge_dummy_fe_qam_attach(void) +{ + struct ddbridge_dummy_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ddbridge_dummy_fe_state), GFP_KERNEL); + if (!state) + return NULL; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &ddbridge_dummy_fe_qam_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + return &state->frontend; +} +EXPORT_SYMBOL(ddbridge_dummy_fe_qam_attach); + +static const struct dvb_frontend_ops ddbridge_dummy_fe_qam_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "ddbridge dummy DVB-C", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + /* symbol_rate_min: SACLK/64 == (XIN/2)/64 */ + .symbol_rate_min = (57840000 / 2) / 64, + .symbol_rate_max = (57840000 / 2) / 4, /* SACLK/4 */ + .caps = FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO | + FE_CAN_INVERSION_AUTO + }, + + .release = ddbridge_dummy_fe_release, + + .init = ddbridge_dummy_fe_init, + .sleep = ddbridge_dummy_fe_sleep, + + .set_frontend = ddbridge_dummy_fe_set_frontend, + .get_frontend = ddbridge_dummy_fe_get_frontend, + + .read_status = ddbridge_dummy_fe_read_status, + .read_ber = ddbridge_dummy_fe_read_ber, + .read_signal_strength = ddbridge_dummy_fe_read_signal_strength, + .read_snr = ddbridge_dummy_fe_read_snr, + .read_ucblocks = ddbridge_dummy_fe_read_ucblocks, +}; + +MODULE_DESCRIPTION("ddbridge dummy Frontend"); +MODULE_AUTHOR("Emard"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ddbridge/ddbridge-dummy-fe.h b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.h new file mode 100644 index 000000000000..ddf189c09524 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Dummy Frontend + * + * Written by Emard <emard@softhome.net> + */ + +#ifndef DDBRIDGE_DUMMY_FE_H +#define DDBRIDGE_DUMMY_FE_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +struct dvb_frontend *ddbridge_dummy_fe_qam_attach(void); + +#endif // DDBRIDGE_DUMMY_FE_H diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index f35bba16b60e..82d7f17e6a02 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -2,9 +2,9 @@ config VIDEO_IPU3_CIO2 tristate "Intel ipu3-cio2 driver" depends on VIDEO_V4L2 && PCI - depends on VIDEO_V4L2_SUBDEV_API depends on (X86 && ACPI) || COMPILE_TEST - depends on MEDIA_CONTROLLER + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE select VIDEOBUF2_DMA_SG diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 137853944e46..35dccb31174c 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -920,14 +920,15 @@ static int ivtv_g_selection(struct file *file, void *fh, static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { static const struct v4l2_fmtdesc hm12 = { - 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, - "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, - { 0, 0, 0, 0 } + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "HM12 (YUV 4:2:0)", + .pixelformat = V4L2_PIX_FMT_HM12, }; static const struct v4l2_fmtdesc mpeg = { - 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, - "MPEG", V4L2_PIX_FMT_MPEG, - { 0, 0, 0, 0 } + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = V4L2_FMT_FLAG_COMPRESSED, + .description = "MPEG", + .pixelformat = V4L2_PIX_FMT_MPEG, }; struct ivtv *itv = fh2id(fh)->itv; struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; @@ -946,14 +947,15 @@ static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdes static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { static const struct v4l2_fmtdesc hm12 = { - 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0, - "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, - { 0, 0, 0, 0 } + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, + .description = "HM12 (YUV 4:2:0)", + .pixelformat = V4L2_PIX_FMT_HM12, }; static const struct v4l2_fmtdesc mpeg = { - 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FMT_FLAG_COMPRESSED, - "MPEG", V4L2_PIX_FMT_MPEG, - { 0, 0, 0, 0 } + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, + .flags = V4L2_FMT_FLAG_COMPRESSED, + .description = "MPEG", + .pixelformat = V4L2_PIX_FMT_MPEG, }; struct ivtv *itv = fh2id(fh)->itv; struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c index e78ca1f26e68..2da94be5b373 100644 --- a/drivers/media/pci/mantis/mantis_dvb.c +++ b/drivers/media/pci/mantis/mantis_dvb.c @@ -135,7 +135,7 @@ static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) int mantis_dvb_init(struct mantis_pci *mantis) { struct mantis_hwconfig *config = mantis->hwconfig; - int result = -1; + int result; dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter"); diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig index b37da612dd0c..fed1f4a01817 100644 --- a/drivers/media/pci/meye/Kconfig +++ b/drivers/media/pci/meye/Kconfig @@ -7,7 +7,7 @@ config VIDEO_MEYE help This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in - <file:Documentation/media/v4l-drivers/meye.rst> for more information. + <file:Documentation/admin-guide/media/meye.rst> for more information. If you say Y or M here, you need to say Y or M to "Sony Laptop Extras" in the misc device section. diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 9aea7c30380b..8610eb473b39 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -982,7 +982,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) if (dev->init_data.name) info.platform_data = &dev->init_data; - i2c_new_device(&dev->i2c_adap, &info); + i2c_new_client_device(&dev->i2c_adap, &info); } static int saa7134_raw_decode_irq(struct saa7134_dev *dev) diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 011b766f0bff..4dd98f94a91e 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,12 +1,12 @@ # 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 STA2X11 || COMPILE_TEST select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG - depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS - depends on VIDEO_V4L2_SUBDEV_API - depends on I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help Say Y for support for STA2X11 VIP (Video Input Port) capture device. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index e01bbb9dd1c1..c57ee78fa99d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -5,7 +5,6 @@ menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" - depends on MEDIA_CAMERA_SUPPORT help Say Y here to enable support for platform-specific V4L drivers. @@ -15,7 +14,7 @@ source "drivers/media/platform/marvell-ccic/Kconfig" config VIDEO_VIA_CAMERA tristate "VIAFB camera controller support" - depends on FB_VIA + depends on FB_VIA && VIDEO_V4L2 select VIDEOBUF2_DMA_SG select VIDEO_OV7670 help @@ -43,7 +42,6 @@ config VIDEO_ASPEED config VIDEO_SH_VOU tristate "SuperH VOU video output driver" - depends on MEDIA_CAMERA_SUPPORT depends on VIDEO_DEV && I2C depends on ARCH_SHMOBILE || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG @@ -65,7 +63,9 @@ config VIDEO_VIU config VIDEO_MUX tristate "Video Multiplexer" select MULTIPLEXER - depends on VIDEO_V4L2 && OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && OF + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select REGMAP select V4L2_FWNODE help @@ -73,10 +73,12 @@ config VIDEO_MUX config VIDEO_OMAP3 tristate "OMAP 3 Camera support" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + 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 @@ -101,16 +103,19 @@ config VIDEO_PXA27x config VIDEO_QCOM_CAMSS tristate "Qualcomm V4L2 Camera Subsystem driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 && VIDEO_V4L2_SUBDEV_API - depends on PM + 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 @@ -121,9 +126,10 @@ config VIDEO_S3C_CAMIF config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" - depends on VIDEO_V4L2 && OF && MEDIA_CONTROLLER + 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) @@ -150,7 +156,9 @@ source "drivers/media/platform/sunxi/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" - depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + 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 @@ -165,7 +173,6 @@ endif # V4L_PLATFORM_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" depends on VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help Say Y here to enable selecting drivers for V4L devices that use system memory for both source and destination buffers, as opposed @@ -180,6 +187,7 @@ config VIDEO_CODA select SRAM select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC + select V4L2_JPEG_HELPER select V4L2_MEM2MEM_DEV select GENERIC_ALLOCATOR help @@ -385,15 +393,6 @@ config VIDEO_STI_DELTA_DRIVER endif # VIDEO_STI_DELTA -config VIDEO_SH_VEU - tristate "SuperH VEU mem2mem video processing driver" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - Support for the Video Engine Unit (VEU) on SuperH and - SH-Mobile SoCs. - config VIDEO_RENESAS_FDP1 tristate "Renesas Fine Display Processor" depends on VIDEO_DEV && VIDEO_V4L2 @@ -435,9 +434,11 @@ config VIDEO_RENESAS_FCP config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 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 @@ -532,29 +533,6 @@ config VIDEO_TI_SC config VIDEO_TI_CSC tristate -menuconfig V4L_TEST_DRIVERS - bool "Media test drivers" - depends on MEDIA_CAMERA_SUPPORT - -if V4L_TEST_DRIVERS - -source "drivers/media/platform/vimc/Kconfig" - -source "drivers/media/platform/vivid/Kconfig" - -config VIDEO_VIM2M - tristate "Virtual Memory-to-Memory Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - select VIDEOBUF2_VMALLOC - select V4L2_MEM2MEM_DEV - help - This is a virtual test device for the memory-to-memory driver - framework. - -source "drivers/media/platform/vicodec/Kconfig" - -endif #V4L_TEST_DRIVERS - menuconfig DVB_PLATFORM_DRIVERS bool "DVB platform devices" depends on MEDIA_DIGITAL_TV_SUPPORT @@ -565,131 +543,6 @@ if DVB_PLATFORM_DRIVERS source "drivers/media/platform/sti/c8sectpfe/Kconfig" endif #DVB_PLATFORM_DRIVERS -menuconfig CEC_PLATFORM_DRIVERS - bool "CEC platform devices" - depends on MEDIA_CEC_SUPPORT - -if CEC_PLATFORM_DRIVERS - -config VIDEO_CROS_EC_CEC - tristate "ChromeOS EC CEC driver" - depends on CROS_EC - select CEC_CORE - select CEC_NOTIFIER - select CROS_EC_PROTO - help - If you say yes here you will get support for the - ChromeOS Embedded Controller's CEC. - The CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_MESON_AO_CEC - tristate "Amlogic Meson AO CEC driver" - depends on ARCH_MESON || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for Amlogic Meson SoCs AO CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - -config VIDEO_MESON_G12A_AO_CEC - tristate "Amlogic Meson G12A AO CEC driver" - depends on ARCH_MESON || COMPILE_TEST - depends on COMMON_CLK && OF - select REGMAP - select REGMAP_MMIO - select CEC_CORE - select CEC_NOTIFIER - ---help--- - This is a driver for Amlogic Meson G12A SoCs AO CEC interface. - This driver if for the new AO-CEC module found in G12A SoCs, - usually named AO_CEC_B in documentation. - It uses the generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config CEC_GPIO - tristate "Generic GPIO-based CEC driver" - depends on PREEMPTION || COMPILE_TEST - select CEC_CORE - select CEC_PIN - select CEC_NOTIFIER - select GPIOLIB - help - This is a generic GPIO-based CEC driver. - The CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_SAMSUNG_S5P_CEC - tristate "Samsung S5P CEC driver" - depends on ARCH_EXYNOS || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for Samsung S5P HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_STI_HDMI_CEC - tristate "STMicroelectronics STiH4xx HDMI CEC driver" - depends on ARCH_STI || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for STIH4xx HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_STM32_HDMI_CEC - tristate "STMicroelectronics STM32 HDMI CEC driver" - depends on ARCH_STM32 || COMPILE_TEST - select REGMAP - select REGMAP_MMIO - select CEC_CORE - help - This is a driver for STM32 interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_TEGRA_HDMI_CEC - tristate "Tegra HDMI CEC driver" - depends on ARCH_TEGRA || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for the Tegra HDMI CEC interface. It uses the - generic CEC framework interface. - The CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_SECO_CEC - tristate "SECO Boards HDMI CEC driver" - depends on (X86 || IA64) || COMPILE_TEST - depends on PCI && DMI - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for SECO Boards integrated CEC interface. - Selecting it will enable support for this device. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. - -config VIDEO_SECO_RC - bool "SECO Boards IR RC5 support" - depends on VIDEO_SECO_CEC - depends on RC_CORE=y || RC_CORE = VIDEO_SECO_CEC - help - If you say yes here you will get support for the - SECO Boards Consumer-IR in seco-cec driver. - The embedded controller supports RC5 protocol only, default mapping - is set to rc-hauppauge. - -endif #CEC_PLATFORM_DRIVERS - menuconfig SDR_PLATFORM_DRIVERS bool "SDR platform devices" depends on MEDIA_SDR_SUPPORT diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index d13db96e3015..62b6cdc8c730 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -14,11 +14,6 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o -obj-$(CONFIG_VIDEO_VIMC) += vimc/ -obj-$(CONFIG_VIDEO_VIVID) += vivid/ -obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o -obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ - obj-y += ti-vpe/ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o @@ -26,10 +21,6 @@ obj-$(CONFIG_VIDEO_CODA) += coda/ obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o -obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o - -obj-$(CONFIG_CEC_GPIO) += cec-gpio/ - obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_MUX) += video-mux.o @@ -40,22 +31,16 @@ 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_S5P_CEC) += s5p-cec/ 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_HDMI_CEC) += sti/cec/ obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/ -obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC) += tegra-cec/ - obj-y += stm32/ -obj-$(CONFIG_VIDEO_SECO_CEC) += seco-cec/ - obj-y += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o @@ -94,8 +79,4 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ -obj-y += meson/ - -obj-y += cros-ec-cec/ - obj-y += sunxi/ diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig index d6f2e3d0cbef..9ef898f512de 100644 --- a/drivers/media/platform/am437x/Kconfig +++ b/drivers/media/platform/am437x/Kconfig @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_AM437X_VPFE tristate "TI AM437x VPFE video capture driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 depends on SOC_AM43XX || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig index 5ae3f60b81b1..1850fe7f9360 100644 --- a/drivers/media/platform/atmel/Kconfig +++ b/drivers/media/platform/atmel/Kconfig @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_ATMEL_ISC tristate "ATMEL Image Sensor Controller (ISC) support" - depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && COMMON_CLK depends on ARCH_AT91 || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO select V4L2_FWNODE diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index c154e368d701..80cf601323ce 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -13,8 +13,8 @@ if VIDEO_CADENCE config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" depends on VIDEO_V4L2 - depends on MEDIA_CONTROLLER - depends on VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help Support for the Cadence MIPI CSI2 Receiver controller. @@ -25,8 +25,8 @@ config VIDEO_CADENCE_CSI2RX config VIDEO_CADENCE_CSI2TX tristate "Cadence MIPI-CSI2 TX Controller" depends on VIDEO_V4L2 - depends on MEDIA_CONTROLLER - depends on VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help Support for the Cadence MIPI CSI2 Transceiver controller. diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 3443396ba5f3..b021604eceaa 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1215,7 +1215,8 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); } - if (ctx->params.bitrate) { + if (ctx->params.bitrate && (ctx->params.frame_rc_enable || + ctx->params.mb_rc_enable)) { ctx->params.bitrate_changed = false; ctx->params.h264_intra_qp_changed = false; @@ -1276,7 +1277,11 @@ static int coda_start_encoding(struct coda_ctx *ctx) } coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); + if (ctx->params.frame_rc_enable && !ctx->params.mb_rc_enable) + value = 1; + else + value = 0; + coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); coda_setup_iram(ctx); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index d0d093dd8f7c..6f41f74d492c 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -159,6 +159,7 @@ static const struct coda_codec coda9_codecs[] = { CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), + CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), }; struct coda_video_device { @@ -252,6 +253,22 @@ static const struct coda_video_device coda9_jpeg_encoder = { }, }; +static const struct coda_video_device coda9_jpeg_decoder = { + .name = "coda-jpeg-decoder", + .type = CODA_INST_DECODER, + .ops = &coda9_jpeg_decode_ops, + .direct = true, + .src_formats = { + V4L2_PIX_FMT_JPEG, + }, + .dst_formats = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUV422P, + }, +}; + static const struct coda_video_device *codadx6_video_devices[] = { &coda_bit_encoder, }; @@ -270,6 +287,7 @@ static const struct coda_video_device *coda7_video_devices[] = { static const struct coda_video_device *coda9_video_devices[] = { &coda9_jpeg_encoder, + &coda9_jpeg_decoder, &coda_bit_encoder, &coda_bit_decoder, }; @@ -411,6 +429,12 @@ static int coda_querycap(struct file *file, void *priv, return 0; } +static const u32 coda_formats_420[CODA_MAX_FORMATS] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, +}; + static int coda_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -421,10 +445,33 @@ static int coda_enum_fmt(struct file *file, void *priv, if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) formats = cvd->src_formats; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + struct coda_q_data *q_data_src; + struct vb2_queue *src_vq; + formats = cvd->dst_formats; - else + + /* + * If the source format is already fixed, only allow the same + * chroma subsampling. + */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && + vb2_is_streaming(src_vq)) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + formats = coda_formats_420; + } else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + f->pixelformat = V4L2_PIX_FMT_YUV422P; + return f->index ? -EINVAL : 0; + } + } + } else { return -EINVAL; + } if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) return -EINVAL; @@ -614,12 +661,23 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, /* * If the source format is already fixed, only allow the same output - * resolution + * resolution. When decoding JPEG images, we also have to make sure to + * use the same chroma subsampling. */ src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); if (vb2_is_streaming(src_vq)) { f->fmt.pix.width = q_data_src->width; f->fmt.pix.height = q_data_src->height; + + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420 && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + else if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_422) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + } } f->fmt.pix.colorspace = ctx->colorspace; @@ -637,12 +695,18 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; - /* The h.264 decoder only returns complete 16x16 macroblocks */ - if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) { - f->fmt.pix.height = round_up(f->fmt.pix.height, 16); + /* The decoders always write complete macroblocks or MCUs */ + if (ctx->inst_type == CODA_INST_DECODER) { f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height * 3 / 2; + f->fmt.pix.height = round_up(f->fmt.pix.height, 16); + if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 2; + } else { + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + } ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); if (ret < 0) @@ -747,6 +811,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, /* else fall through */ case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUV422P: ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; break; default: @@ -1088,6 +1153,51 @@ static int coda_try_decoder_cmd(struct file *file, void *fh, return v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); } +static bool coda_mark_last_meta(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta; + + coda_dbg(1, ctx, "marking last meta\n"); + + spin_lock(&ctx->buffer_meta_lock); + if (list_empty(&ctx->buffer_meta_list)) { + spin_unlock(&ctx->buffer_meta_lock); + return false; + } + + meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, + list); + meta->last = true; + + spin_unlock(&ctx->buffer_meta_lock); + return true; +} + +static bool coda_mark_last_dst_buf(struct coda_ctx *ctx) +{ + struct vb2_v4l2_buffer *buf; + struct vb2_buffer *dst_vb; + struct vb2_queue *dst_vq; + unsigned long flags; + + coda_dbg(1, ctx, "marking last capture buffer\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + spin_lock_irqsave(&dst_vq->done_lock, flags); + if (list_empty(&dst_vq->done_list)) { + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return false; + } + + dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, + done_entry); + buf = to_vb2_v4l2_buffer(dst_vb); + buf->flags |= V4L2_BUF_FLAG_LAST; + + spin_unlock_irqrestore(&dst_vq->done_lock, flags); + return true; +} + static int coda_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { @@ -1120,6 +1230,8 @@ static int coda_decoder_cmd(struct file *file, void *fh, stream_end = false; wakeup = false; + mutex_lock(&ctx->wakeup_mutex); + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); if (buf) { coda_dbg(1, ctx, "marking last pending buffer\n"); @@ -1132,22 +1244,14 @@ static int coda_decoder_cmd(struct file *file, void *fh, stream_end = true; } } else { - coda_dbg(1, ctx, "marking last meta\n"); - - /* Mark last meta */ - spin_lock(&ctx->buffer_meta_lock); - if (!list_empty(&ctx->buffer_meta_list)) { - struct coda_buffer_meta *meta; - - meta = list_last_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, - list); - meta->last = true; - stream_end = true; - } else { - wakeup = true; - } - spin_unlock(&ctx->buffer_meta_lock); + if (ctx->use_bit) + if (coda_mark_last_meta(ctx)) + stream_end = true; + else + wakeup = true; + else + if (!coda_mark_last_dst_buf(ctx)) + wakeup = true; } if (stream_end) { @@ -1164,6 +1268,7 @@ static int coda_decoder_cmd(struct file *file, void *fh, coda_wake_up_capture_queue(ctx); } + mutex_unlock(&ctx->wakeup_mutex); break; default: return -EINVAL; @@ -1894,6 +1999,42 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) } } + /* + * Check the first input JPEG buffer to determine chroma + * subsampling. + */ + if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { + buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + ret = coda_jpeg_decode_header(ctx, &buf->vb2_buf); + if (ret < 0) { + v4l2_err(v4l2_dev, + "failed to decode JPEG header: %d\n", + ret); + goto err; + } + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + q_data_dst->width = round_up(q_data_src->width, 16); + q_data_dst->height = round_up(q_data_src->height, 16); + q_data_dst->bytesperline = q_data_dst->width; + if (ctx->params.jpeg_chroma_subsampling == + V4L2_JPEG_CHROMA_SUBSAMPLING_420) { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 3 / 2; + if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) + q_data_dst->fourcc = V4L2_PIX_FMT_NV12; + } else { + q_data_dst->sizeimage = + q_data_dst->bytesperline * + q_data_dst->height * 2; + q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; + } + q_data_dst->rect.left = 0; + q_data_dst->rect.top = 0; + q_data_dst->rect.width = q_data_src->width; + q_data_dst->rect.height = q_data_src->height; + } ctx->streamon_out = 1; } else { ctx->streamon_cap = 1; @@ -2082,6 +2223,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: ctx->params.h264_constrained_intra_pred_flag = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + ctx->params.frame_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + ctx->params.mb_rc_enable = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: ctx->params.h264_chroma_qp_index_offset = ctrl->val; break; @@ -2181,6 +2328,10 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c index 92234fd1f4fd..00d19859db50 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/coda/coda-jpeg.c @@ -15,6 +15,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-fh.h> +#include <media/v4l2-jpeg.h> #include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> @@ -23,10 +24,12 @@ #include "trace.h" #define SOI_MARKER 0xffd8 +#define APP9_MARKER 0xffe9 #define DRI_MARKER 0xffdd #define DQT_MARKER 0xffdb #define DHT_MARKER 0xffc4 #define SOF_MARKER 0xffc0 +#define SOS_MARKER 0xffda #define EOI_MARKER 0xffd9 enum { @@ -37,6 +40,18 @@ enum { CODA9_JPEG_FORMAT_400, }; +struct coda_huff_tab { + u8 luma_dc[16 + 12]; + u8 chroma_dc[16 + 12]; + u8 luma_ac[16 + 162]; + u8 chroma_ac[16 + 162]; + + /* DC Luma, DC Chroma, AC Luma, AC Chroma */ + s16 min[4 * 16]; + s16 max[4 * 16]; + s8 ptr[4 * 16]; +}; + #define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) /* @@ -247,6 +262,291 @@ bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) return false; } +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); + +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) +{ + struct coda_dev *dev = ctx->dev; + u8 *buf = vb2_plane_vaddr(vb, 0); + size_t len = vb2_get_plane_payload(vb, 0); + struct v4l2_jpeg_scan_header scan_header; + struct v4l2_jpeg_reference quantization_tables[4] = { }; + struct v4l2_jpeg_reference huffman_tables[4] = { }; + struct v4l2_jpeg_header header = { + .scan = &scan_header, + .quantization_tables = quantization_tables, + .huffman_tables = huffman_tables, + }; + struct coda_q_data *q_data_src; + struct coda_huff_tab *huff_tab; + int i, j, ret; + + ret = v4l2_jpeg_parse_header(buf, len, &header); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to parse header\n"); + return ret; + } + + ctx->params.jpeg_restart_interval = header.restart_interval; + + /* check frame header */ + if (header.frame.height > ctx->codec->max_h || + header.frame.width > ctx->codec->max_w) { + v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (header.frame.height != q_data_src->height || + header.frame.width != q_data_src->width) { + v4l2_err(&dev->v4l2_dev, + "dimensions don't match format: %dx%d\n", + header.frame.width, header.frame.height); + return -EINVAL; + } + + if (header.frame.num_components != 3) { + v4l2_err(&dev->v4l2_dev, + "unsupported number of components: %d\n", + header.frame.num_components); + return -EINVAL; + } + + /* install quantization tables */ + if (quantization_tables[3].start) { + v4l2_err(&dev->v4l2_dev, + "only 3 quantization tables supported\n"); + return -EINVAL; + } + for (i = 0; i < 3; i++) { + if (!quantization_tables[i].start) + continue; + if (quantization_tables[i].length != 64) { + v4l2_err(&dev->v4l2_dev, + "only 8-bit quantization tables supported\n"); + continue; + } + if (!ctx->params.jpeg_qmat_tab[i]) + ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + memcpy(ctx->params.jpeg_qmat_tab[i], + quantization_tables[i].start, 64); + } + + /* install Huffman tables */ + for (i = 0; i < 4; i++) { + if (!huffman_tables[i].start) { + v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); + return -EINVAL; + } + /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ + if (huffman_tables[i].length < 17 || + huffman_tables[i].length > 178 || + ((i & 2) == 0 && huffman_tables[i].length > 28)) { + v4l2_err(&dev->v4l2_dev, + "invalid Huffman table %d length: %zu\n", + i, huffman_tables[i].length); + return -EINVAL; + } + } + huff_tab = ctx->params.jpeg_huff_tab; + if (!huff_tab) { + huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); + if (!huff_tab) + return -ENOMEM; + ctx->params.jpeg_huff_tab = huff_tab; + } + + memset(huff_tab, 0, sizeof(*huff_tab)); + memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); + memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); + memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); + memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); + + /* check scan header */ + for (i = 0; i < scan_header.num_components; i++) { + struct v4l2_jpeg_scan_component_spec *scan_component; + + scan_component = &scan_header.component[i]; + for (j = 0; j < header.frame.num_components; j++) { + if (header.frame.component[j].component_identifier == + scan_component->component_selector) + break; + } + if (j == header.frame.num_components) + continue; + + ctx->params.jpeg_huff_dc_index[j] = + scan_component->dc_entropy_coding_table_selector; + ctx->params.jpeg_huff_ac_index[j] = + scan_component->ac_entropy_coding_table_selector; + } + + /* Generate Huffman table information */ + for (i = 0; i < 4; i++) + coda9_jpeg_gen_dec_huff_tab(ctx, i); + + /* start of entropy coded segment */ + ctx->jpeg_ecs_offset = header.ecs_offset; + + switch (header.frame.subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; + break; + default: + v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", + header.frame.subsampling); + return -EINVAL; + } + + return 0; +} + +static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, + int num_values) +{ + s8 *values = (s8 *)(bits + 16); + int huff_length, i; + + for (huff_length = 0, i = 0; i < 16; i++) + huff_length += bits[i]; + for (i = huff_length; i < num_values; i++) + values[i] = -1; + for (i = 0; i < num_values; i++) + coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); +} + +static int coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + struct coda_dev *dev = ctx->dev; + s16 *huff_min = huff_tab->min; + s16 *huff_max = huff_tab->max; + s8 *huff_ptr = huff_tab->ptr; + int i; + + /* MIN Tables */ + coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); + + /* MAX Tables */ + coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); + + /* PTR Tables */ + coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); + coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); + for (i = 0; i < 4 * 16; i++) + coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); + + /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ + coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); + coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); + coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); + coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); + return 0; +} + +static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, + u8 *qmat, int index) +{ + int i; + + coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); + for (i = 0; i < 64; i++) + coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); + coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); +} + +static void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int *qmat_index = ctx->params.jpeg_qmat_index; + u8 **qmat_tab = ctx->params.jpeg_qmat_tab; + + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); + coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); +} + +static void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, + struct vb2_buffer *buf, u32 ecs_offset) +{ + struct coda_dev *dev = ctx->dev; + int page_ptr, word_ptr, bit_ptr; + u32 bbc_base_addr, end_addr; + int bbc_cur_pos; + int ret, val; + + bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); + end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); + + page_ptr = ecs_offset / 256; + word_ptr = (ecs_offset % 256) / 4; + if (page_ptr & 1) + word_ptr += 64; + bit_ptr = (ecs_offset % 4) * 8; + if (word_ptr & 1) + bit_ptr += 32; + word_ptr &= ~0x1; + + coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); + coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); + + /* Leave 3 256-byte page margin to avoid a BBC interrupt */ + coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); + val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; + coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); + + bbc_cur_pos = page_ptr; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), + CODA9_REG_JPEG_BBC_EXT_ADDR); + coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); + coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); + coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); + do { + ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); + } while (ret == 1); + + bbc_cur_pos++; + coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); + coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); + + coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); + coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); + coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); + if (page_ptr & 1) { + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); + } else { + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); + coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); + } + coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); + coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); + coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); +} + static const int bus_req_num[] = { [CODA9_JPEG_FORMAT_420] = 2, [CODA9_JPEG_FORMAT_422] = 3, @@ -345,6 +645,71 @@ out: #define DC_TABLE_INDEX1 2 #define AC_TABLE_INDEX1 3 +static u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) +{ + struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; + + if (!huff_tab) + return NULL; + + switch (tab_num) { + case DC_TABLE_INDEX0: return huff_tab->luma_dc; + case AC_TABLE_INDEX0: return huff_tab->luma_ac; + case DC_TABLE_INDEX1: return huff_tab->chroma_dc; + case AC_TABLE_INDEX1: return huff_tab->chroma_ac; + } + + return NULL; +} + +static int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) +{ + int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; + u8 *huff_bits; + s16 *huff_max; + s16 *huff_min; + s8 *huff_ptr; + int ofs; + int i; + + huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); + if (!huff_bits) + return -EINVAL; + + /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ + ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); + ofs *= 16; + + huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; + huff_max = ctx->params.jpeg_huff_tab->max + ofs; + huff_min = ctx->params.jpeg_huff_tab->min + ofs; + + for (i = 0; i < 16; i++) { + if (huff_bits[i]) { + huff_ptr[i] = ptr_cnt; + ptr_cnt += huff_bits[i]; + huff_min[i] = huff_code; + huff_max[i] = huff_code + (huff_bits[i] - 1); + data_flag = 1; + zero_flag = 0; + } else { + huff_ptr[i] = -1; + huff_min[i] = -1; + huff_max[i] = -1; + zero_flag = 1; + } + + if (data_flag == 1) { + if (zero_flag == 1) + huff_code <<= 1; + else + huff_code = (huff_max[i] + 1) << 1; + } + } + + return 0; +} + static int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) { struct { @@ -880,6 +1245,13 @@ static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", dst_buf->sequence, (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each encode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); } static void coda9_jpeg_release(struct coda_ctx *ctx) @@ -893,6 +1265,7 @@ static void coda9_jpeg_release(struct coda_ctx *ctx) for (i = 0; i < 3; i++) kfree(ctx->params.jpeg_qmat_tab[i]); kfree(ctx->params.jpeg_huff_data); + kfree(ctx->params.jpeg_huff_tab); } const struct coda_context_ops coda9_jpeg_encode_ops = { @@ -903,6 +1276,210 @@ const struct coda_context_ops coda9_jpeg_encode_ops = { .release = coda9_jpeg_release, }; +/* + * Decoder context operations + */ + +static int coda9_jpeg_start_decoding(struct coda_ctx *ctx) +{ + ctx->params.jpeg_qmat_index[0] = 0; + ctx->params.jpeg_qmat_index[1] = 1; + ctx->params.jpeg_qmat_index[2] = 1; + ctx->params.jpeg_qmat_tab[0] = luma_q; + ctx->params.jpeg_qmat_tab[1] = chroma_q; + /* nothing more to do here */ + + /* TODO: we could already scan the first header to get the chroma + * format. + */ + + return 0; +} + +static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + int aligned_width, aligned_height; + int chroma_format; + int ret; + u32 val, dst_fourcc; + struct coda_q_data *q_data_src, *q_data_dst; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + int chroma_interleave; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) + vb2_set_plane_payload(&src_buf->vb2_buf, 0, + vb2_plane_size(&src_buf->vb2_buf, 0)); + + chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); + if (chroma_format < 0) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return chroma_format; + } + + ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to decode JPEG header: %d\n", + ret); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return ret; + } + + /* Round image dimensions to multiple of MCU size */ + aligned_width = round_up(q_data_src->width, width_align[chroma_format]); + aligned_height = round_up(q_data_src->height, height_align[chroma_format]); + if (aligned_width != q_data_dst->bytesperline) { + v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", + aligned_width, q_data_dst->bytesperline); + } + + coda_set_gdi_regs(ctx); + + val = ctx->params.jpeg_huff_ac_index[0] << 12 | + ctx->params.jpeg_huff_ac_index[1] << 11 | + ctx->params.jpeg_huff_ac_index[2] << 10 | + ctx->params.jpeg_huff_dc_index[0] << 9 | + ctx->params.jpeg_huff_dc_index[1] << 8 | + ctx->params.jpeg_huff_dc_index[2] << 7; + if (ctx->params.jpeg_huff_tab) + val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; + coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_REG_JPEG_PIC_SIZE); + + chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); + coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); + coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); + coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); + coda_write(dev, ctx->params.jpeg_restart_interval, + CODA9_REG_JPEG_RST_INTVAL); + + if (ctx->params.jpeg_huff_tab) { + ret = coda9_jpeg_dec_huff_setup(ctx); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to set up Huffman tables: %d\n", ret); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + return ret; + } + } + + coda9_jpeg_qmat_setup(ctx); + + coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, + ctx->jpeg_ecs_offset); + + coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); + coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); + + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); + coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); + + coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); + + coda_write(dev, 1, CODA9_GDI_CONTROL); + do { + ret = coda_read(dev, CODA9_GDI_STATUS); + } while (!ret); + + val = (chroma_format << 17) | (chroma_interleave << 16) | + q_data_dst->bytesperline; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + val |= 3 << 20; + coda_write(dev, val, CODA9_GDI_INFO_CONTROL); + + coda_write(dev, aligned_width << 16 | aligned_height, + CODA9_GDI_INFO_PIC_SIZE); + + coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); + + coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); + coda_write(dev, 0, CODA9_GDI_CONTROL); + coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); + + trace_coda_jpeg_run(ctx, src_buf); + + coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); + + return 0; +} + +static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *dst_buf, *src_buf; + struct coda_q_data *q_data_dst; + u32 err_mb; + + err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); + if (err_mb) + v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); + + coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); + + /* + * Lock to make sure that a decoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + dst_buf->sequence = ctx->osequence++; + + trace_coda_jpeg_done(ctx, dst_buf); + + dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); + + mutex_unlock(&ctx->wakeup_mutex); + + coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); + + /* + * Reset JPEG processing unit after each decode run to work + * around hangups when switching context between encoder and + * decoder. + */ + coda_hw_reset(ctx); +} + +const struct coda_context_ops coda9_jpeg_decode_ops = { + .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ + .start_streaming = coda9_jpeg_start_decoding, + .prepare_run = coda9_jpeg_prepare_decode, + .finish_run = coda9_jpeg_finish_decode, + .release = coda9_jpeg_release, +}; + irqreturn_t coda9_jpeg_irq_handler(int irq, void *data) { struct coda_dev *dev = data; diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 43bda175f517..b81f3aca9209 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -69,7 +69,7 @@ struct coda_aux_buf { struct coda_dev { struct v4l2_device v4l2_dev; - struct video_device vfd[5]; + struct video_device vfd[6]; struct device *dev; const struct coda_devtype *devtype; int firmware; @@ -123,10 +123,15 @@ struct coda_params { u8 mpeg4_inter_qp; u8 gop_size; int intra_refresh; + enum v4l2_jpeg_chroma_subsampling jpeg_chroma_subsampling; u8 jpeg_quality; u8 jpeg_restart_interval; u8 *jpeg_qmat_tab[3]; + int jpeg_qmat_index[3]; + int jpeg_huff_dc_index[3]; + int jpeg_huff_ac_index[3]; u32 *jpeg_huff_data; + struct coda_huff_tab *jpeg_huff_tab; int codec_mode; int codec_mode_aux; enum v4l2_mpeg_video_multi_slice_mode slice_mode; @@ -143,6 +148,8 @@ struct coda_params { bool h264_intra_qp_changed; bool intra_refresh_changed; bool slice_mode_changed; + bool frame_rc_enable; + bool mb_rc_enable; }; struct coda_buffer_meta { @@ -238,6 +245,7 @@ struct coda_ctx { struct v4l2_fh fh; int gopcounter; int runcounter; + int jpeg_ecs_offset; char vpu_header[3][64]; int vpu_header_size[3]; struct kfifo bitstream_fifo; @@ -362,12 +370,14 @@ void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, u8 level_idc); bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); extern const struct coda_context_ops coda_bit_encode_ops; extern const struct coda_context_ops coda_bit_decode_ops; extern const struct coda_context_ops coda9_jpeg_encode_ops; +extern const struct coda_context_ops coda9_jpeg_decode_ops; irqreturn_t coda_irq_handler(int irq, void *data); irqreturn_t coda9_jpeg_irq_handler(int irq, void *data); diff --git a/drivers/media/platform/cros-ec-cec/Makefile b/drivers/media/platform/cros-ec-cec/Makefile deleted file mode 100644 index 2615cdc6e227..000000000000 --- a/drivers/media/platform/cros-ec-cec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_CROS_EC_CEC) += cros-ec-cec.o diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index ead14c49d4f5..7d55fd45240e 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1225,7 +1225,6 @@ static int vpif_probe_complete(void) probe_out: for (k = 0; k < j; k++) { ch = vpif_obj.dev[k]; - common = &ch->common[k]; video_unregister_device(&ch->video_dev); } return err; diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index be4effcbfe7b..136d3b2a0fbb 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -2,9 +2,10 @@ config VIDEO_SAMSUNG_EXYNOS4_IS tristate "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && OF && COMMON_CLK depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - depends on OF && COMMON_CLK + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help Say Y here to enable camera host interface devices for diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 37fdcc53a1c4..9a09a10a3631 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -556,7 +556,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, clkdev_create(mcam->mclk, "xclk", "%d-%04x", i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); - if (i2c_new_device(cam->i2c_adapter, &ov7670_info)) { + if (!IS_ERR(i2c_new_client_device(cam->i2c_adapter, &ov7670_info))) { cam->registered = 1; return 0; } diff --git a/drivers/media/platform/meson/Makefile b/drivers/media/platform/meson/Makefile deleted file mode 100644 index 6bf728addbf8..000000000000 --- a/drivers/media/platform/meson/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_MESON_AO_CEC) += ao-cec.o -obj-$(CONFIG_VIDEO_MESON_G12A_AO_CEC) += ao-cec-g12a.o diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 14991685adb7..58abfbdfb82d 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -15,10 +15,10 @@ static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = { - "mdp_rdma", - "mdp_rsz", - "mdp_wdma", - "mdp_wrot", + "mdp-rdma", + "mdp-rsz", + "mdp-wdma", + "mdp-wrot", }; struct mtk_mdp_comp_match { diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 70c85a2a10f5..3c5fe737d36f 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1016,7 +1016,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, * - a videobuffer is queued on the pcdev->capture list * * Please check the "DMA hot chaining timeslice issue" in - * Documentation/media/v4l-drivers/pxa_camera.rst + * Documentation/driver-api/media/drivers/pxa_camera.rst * * Context: should only be called within the dma irq handler */ @@ -1438,7 +1438,7 @@ static void pxac_vb2_queue(struct vb2_buffer *vb) /* * Please check the DMA prepared buffer structure in : - * Documentation/media/v4l-drivers/pxa_camera.rst + * Documentation/driver-api/media/drivers/pxa_camera.rst * Please check also in pxa_camera_check_link_miss() to understand why DMA chain * modification while DMA chain is running will work anyway. */ diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 194b10b98767..203c6538044f 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -210,6 +210,8 @@ static int venus_probe(struct platform_device *pdev) if (!core->res) return -ENODEV; + mutex_init(&core->pm_lock); + core->pm_ops = venus_pm_get(core->res->hfi_version); if (!core->pm_ops) return -ENODEV; @@ -242,10 +244,6 @@ static int venus_probe(struct platform_device *pdev) if (ret) return ret; - ret = icc_set_bw(core->cpucfg_path, 0, kbps_to_icc(1000)); - if (ret) - return ret; - ret = hfi_create(core, &venus_core_ops); if (ret) return ret; @@ -320,7 +318,6 @@ static int venus_remove(struct platform_device *pdev) ret = hfi_core_deinit(core, true); WARN_ON(ret); - hfi_destroy(core); venus_shutdown(core); of_platform_depopulate(dev); @@ -332,10 +329,14 @@ static int venus_remove(struct platform_device *pdev) if (pm_ops->core_put) pm_ops->core_put(dev); + hfi_destroy(core); + icc_put(core->video_path); icc_put(core->cpucfg_path); v4l2_device_unregister(&core->v4l2_dev); + mutex_destroy(&core->pm_lock); + mutex_destroy(&core->lock); return ret; } @@ -350,6 +351,10 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) if (ret) return ret; + ret = icc_set_bw(core->cpucfg_path, 0, 0); + if (ret) + return ret; + if (pm_ops->core_power) ret = pm_ops->core_power(dev, POWER_OFF); @@ -368,6 +373,10 @@ static __maybe_unused int venus_runtime_resume(struct device *dev) return ret; } + ret = icc_set_bw(core->cpucfg_path, 0, kbps_to_icc(1000)); + if (ret) + return ret; + return hfi_core_resume(core, false); } @@ -447,7 +456,7 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; -static struct codec_freq_data sdm845_codec_freq_data[] = { +static const struct codec_freq_data sdm845_codec_freq_data[] = { { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index bd3ac6a4501f..7118612673c9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -128,6 +128,7 @@ struct venus_caps { * @error: an error returned during last HFI sync operations * @sys_error: an error flag that signal system error event * @core_ops: the core operations + * @pm_lock: a lock for PM operations * @enc_codecs: encoders supported by this core * @dec_codecs: decoders supported by this core * @max_sessions_supported: holds the maximum number of sessions @@ -168,6 +169,7 @@ struct venus_core { bool sys_error; const struct hfi_core_ops *core_ops; const struct venus_pm_ops *pm_ops; + struct mutex pm_lock; unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; @@ -259,7 +261,8 @@ enum venus_dec_state { VENUS_DEC_STATE_SEEK = 4, VENUS_DEC_STATE_DRAIN = 5, VENUS_DEC_STATE_DECODING = 6, - VENUS_DEC_STATE_DRC = 7 + VENUS_DEC_STATE_DRC = 7, + VENUS_DEC_STATE_DRC_FLUSH_DONE = 8, }; struct venus_ts_metadata { @@ -324,6 +327,7 @@ struct venus_ts_metadata { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property + * @last_buf: last capture buffer for dynamic-resoluton-change */ struct venus_inst { struct list_head list; @@ -385,6 +389,7 @@ struct venus_inst { union hfi_get_property hprop; unsigned int core_acquired: 1; unsigned int bit_depth; + struct vb2_buffer *last_buf; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index bcc603804041..0143af7822b2 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1129,15 +1129,18 @@ unlock: } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue); -void venus_helper_buffers_done(struct venus_inst *inst, +void venus_helper_buffers_done(struct venus_inst *inst, unsigned int type, enum vb2_buffer_state state) { struct vb2_v4l2_buffer *buf; - while ((buf = v4l2_m2m_src_buf_remove(inst->m2m_ctx))) - v4l2_m2m_buf_done(buf, state); - while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx))) - v4l2_m2m_buf_done(buf, state); + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + while ((buf = v4l2_m2m_src_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, state); + } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, state); + } } EXPORT_SYMBOL_GPL(venus_helper_buffers_done); @@ -1168,7 +1171,10 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) INIT_LIST_HEAD(&inst->registeredbufs); } - venus_helper_buffers_done(inst, VB2_BUF_STATE_ERROR); + venus_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + VB2_BUF_STATE_ERROR); + venus_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + VB2_BUF_STATE_ERROR); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->streamon_out = 0; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index b64875564064..8fbbda12a4fe 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -14,7 +14,7 @@ struct venus_core; bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt); struct vb2_v4l2_buffer *venus_helper_find_buf(struct venus_inst *inst, unsigned int type, u32 idx); -void venus_helper_buffers_done(struct venus_inst *inst, +void venus_helper_buffers_done(struct venus_inst *inst, unsigned int type, enum vb2_buffer_state state); int venus_helper_vb2_buf_init(struct vb2_buffer *vb); int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb); diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 3d8b1284d1f3..a211eb93e0f9 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -382,7 +382,7 @@ int hfi_session_unload_res(struct venus_inst *inst) } EXPORT_SYMBOL_GPL(hfi_session_unload_res); -int hfi_session_flush(struct venus_inst *inst, u32 type) +int hfi_session_flush(struct venus_inst *inst, u32 type, bool block) { const struct hfi_ops *ops = inst->core->ops; int ret; @@ -393,9 +393,11 @@ int hfi_session_flush(struct venus_inst *inst, u32 type) if (ret) return ret; - ret = wait_session_msg(inst); - if (ret) - return ret; + if (block) { + ret = wait_session_msg(inst); + if (ret) + return ret; + } return 0; } diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index 855822c9f39b..62c315291484 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -102,6 +102,7 @@ struct hfi_inst_ops { u32 hfi_flags, u64 timestamp_us); void (*event_notify)(struct venus_inst *inst, u32 event, struct hfi_event_data *data); + void (*flush_done)(struct venus_inst *inst); }; struct hfi_ops { @@ -161,7 +162,7 @@ int hfi_session_continue(struct venus_inst *inst); int hfi_session_abort(struct venus_inst *inst); int hfi_session_load_res(struct venus_inst *inst); int hfi_session_unload_res(struct venus_inst *inst); -int hfi_session_flush(struct venus_inst *inst, u32 type); +int hfi_session_flush(struct venus_inst *inst, u32 type, bool block); int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd); int hfi_session_unset_buffers(struct venus_inst *inst, diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index cae9d5d61c0c..83705e237f1c 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -107,7 +107,7 @@ struct hfi_session_abort_pkt { struct hfi_session_set_property_pkt { struct hfi_session_hdr_pkt shdr; u32 num_properties; - u32 data[0]; + u32 data[]; }; struct hfi_session_set_buffers_pkt { diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 04ef2286efc6..279a9d6fe737 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -439,6 +439,8 @@ static void hfi_session_flush_done(struct venus_core *core, inst->error = pkt->error_type; complete(&inst->done); + if (inst->ops->flush_done) + inst->ops->flush_done(inst); } static void hfi_session_etb_done(struct venus_core *core, diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.h b/drivers/media/platform/qcom/venus/hfi_msgs.h index 7694b1d25d9d..526d9f5b487b 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.h +++ b/drivers/media/platform/qcom/venus/hfi_msgs.h @@ -155,7 +155,7 @@ struct hfi_msg_session_empty_buffer_done_pkt { u32 input_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[0]; + u32 data[]; }; struct hfi_msg_session_fbd_compressed_pkt { @@ -175,7 +175,7 @@ struct hfi_msg_session_fbd_compressed_pkt { u32 picture_type; u32 packet_buffer; u32 extradata_buffer; - u32 data[0]; + u32 data[]; }; struct hfi_msg_session_fbd_uncompressed_plane0_pkt { @@ -202,7 +202,7 @@ struct hfi_msg_session_fbd_uncompressed_plane0_pkt { u32 picture_type; u32 packet_buffer; u32 extradata_buffer; - u32 data[0]; + u32 data[]; }; struct hfi_msg_session_fbd_uncompressed_plane1_pkt { @@ -211,7 +211,7 @@ struct hfi_msg_session_fbd_uncompressed_plane1_pkt { u32 filled_len; u32 offset; u32 packet_buffer2; - u32 data[0]; + u32 data[]; }; struct hfi_msg_session_fbd_uncompressed_plane2_pkt { @@ -220,7 +220,7 @@ struct hfi_msg_session_fbd_uncompressed_plane2_pkt { u32 filled_len; u32 offset; u32 packet_buffer3; - u32 data[0]; + u32 data[]; }; struct hfi_msg_session_parse_sequence_header_done_pkt { diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 4ed2628585a1..7c4c483d5438 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -276,6 +276,14 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) const struct venus_format *fmt; struct v4l2_format format; u32 pixfmt_out = 0, pixfmt_cap = 0; + struct vb2_queue *q; + + q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); + if (!q) + return -EINVAL; + + if (vb2_is_busy(q)) + return -EBUSY; orig_pixmp = *pixmp; @@ -545,6 +553,64 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { .vidioc_decoder_cmd = vdec_decoder_cmd, }; +static int vdec_pm_get(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret; + + mutex_lock(&core->pm_lock); + ret = pm_runtime_get_sync(dev); + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static int vdec_pm_put(struct venus_inst *inst, bool autosuspend) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret; + + mutex_lock(&core->pm_lock); + + if (autosuspend) + ret = pm_runtime_put_autosuspend(dev); + else + ret = pm_runtime_put_sync(dev); + + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static int vdec_pm_get_put(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev_dec; + int ret = 0; + + mutex_lock(&core->pm_lock); + + if (pm_runtime_suspended(dev)) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto error; + + ret = pm_runtime_put_autosuspend(dev); + } + +error: + mutex_unlock(&core->pm_lock); + + return ret < 0 ? ret : 0; +} + +static void vdec_pm_touch(struct venus_inst *inst) +{ + pm_runtime_mark_last_busy(inst->core->dev_dec); +} + static int vdec_set_properties(struct venus_inst *inst) { struct vdec_controls *ctr = &inst->controls.dec; @@ -746,12 +812,20 @@ static int vdec_queue_setup(struct vb2_queue *q, return 0; } - ret = vdec_session_init(inst); + ret = vdec_pm_get(inst); if (ret) return ret; + ret = vdec_session_init(inst); + if (ret) + goto put_power; + ret = vdec_num_buffers(inst, &in_num, &out_num); if (ret) + goto put_power; + + ret = vdec_pm_put(inst, false); + if (ret) return ret; switch (q->type) { @@ -786,6 +860,10 @@ static int vdec_queue_setup(struct vb2_queue *q, } return ret; + +put_power: + vdec_pm_put(inst, false); + return ret; } static int vdec_verify_conf(struct venus_inst *inst) @@ -836,7 +914,7 @@ static int vdec_start_capture(struct venus_inst *inst) return 0; reconfigure: - ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT); + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); if (ret) return ret; @@ -947,14 +1025,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&inst->lock); - ret = venus_pm_acquire_core(inst); - if (ret) - goto error; - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { ret = vdec_start_capture(inst); - else + } else { + ret = vdec_pm_get(inst); + if (ret) + goto error; + + ret = venus_pm_acquire_core(inst); + if (ret) + goto put_power; + + ret = vdec_pm_put(inst, true); + if (ret) + goto error; + ret = vdec_start_output(inst); + } if (ret) goto error; @@ -962,8 +1049,10 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) mutex_unlock(&inst->lock); return 0; +put_power: + vdec_pm_put(inst, false); error: - venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED); mutex_unlock(&inst->lock); return ret; } @@ -982,23 +1071,25 @@ static int vdec_stop_capture(struct venus_inst *inst) switch (inst->codec_state) { case VENUS_DEC_STATE_DECODING: - ret = hfi_session_flush(inst, HFI_FLUSH_ALL); + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); /* fallthrough */ case VENUS_DEC_STATE_DRAIN: vdec_cancel_dst_buffers(inst); inst->codec_state = VENUS_DEC_STATE_STOPPED; break; case VENUS_DEC_STATE_DRC: - ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT); - vdec_cancel_dst_buffers(inst); + WARN_ON(1); + fallthrough; + case VENUS_DEC_STATE_DRC_FLUSH_DONE: inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; - INIT_LIST_HEAD(&inst->registeredbufs); venus_helper_free_dpb_bufs(inst); break; default: - return 0; + break; } + INIT_LIST_HEAD(&inst->registeredbufs); + return ret; } @@ -1010,12 +1101,12 @@ static int vdec_stop_output(struct venus_inst *inst) case VENUS_DEC_STATE_DECODING: case VENUS_DEC_STATE_DRAIN: case VENUS_DEC_STATE_STOPPED: - ret = hfi_session_flush(inst, HFI_FLUSH_ALL); + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); inst->codec_state = VENUS_DEC_STATE_SEEK; break; case VENUS_DEC_STATE_INIT: case VENUS_DEC_STATE_CAPTURE_SETUP: - ret = hfi_session_flush(inst, HFI_FLUSH_INPUT); + ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true); break; default: break; @@ -1036,7 +1127,7 @@ static void vdec_stop_streaming(struct vb2_queue *q) else ret = vdec_stop_output(inst); - venus_helper_buffers_done(inst, VB2_BUF_STATE_ERROR); + venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); if (ret) goto unlock; @@ -1055,8 +1146,9 @@ static void vdec_session_release(struct venus_inst *inst) struct venus_core *core = inst->core; int ret, abort = 0; - mutex_lock(&inst->lock); + vdec_pm_get(inst); + mutex_lock(&inst->lock); inst->codec_state = VENUS_DEC_STATE_DEINIT; ret = hfi_session_stop(inst); @@ -1078,10 +1170,11 @@ static void vdec_session_release(struct venus_inst *inst) venus_helper_free_dpb_bufs(inst); venus_pm_load_scale(inst); - venus_pm_release_core(inst); INIT_LIST_HEAD(&inst->registeredbufs); - mutex_unlock(&inst->lock); + + venus_pm_release_core(inst); + vdec_pm_put(inst, false); } static int vdec_buf_init(struct vb2_buffer *vb) @@ -1102,6 +1195,15 @@ static void vdec_buf_cleanup(struct vb2_buffer *vb) vdec_session_release(inst); } +static void vdec_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + vdec_pm_get_put(inst); + + venus_helper_vb2_buf_queue(vb); +} + static const struct vb2_ops vdec_vb2_ops = { .queue_setup = vdec_queue_setup, .buf_init = vdec_buf_init, @@ -1109,7 +1211,7 @@ static const struct vb2_ops vdec_vb2_ops = { .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = vdec_start_streaming, .stop_streaming = vdec_stop_streaming, - .buf_queue = venus_helper_vb2_buf_queue, + .buf_queue = vdec_vb2_buf_queue, }; static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, @@ -1121,6 +1223,8 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, struct vb2_buffer *vb; unsigned int type; + vdec_pm_touch(inst); + if (buf_type == HFI_BUFFER_INPUT) type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; else @@ -1140,6 +1244,13 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; + if (inst->last_buf == vb) { + inst->last_buf = NULL; + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vb2_set_plane_payload(vb, 0, 0); + vb->timestamp = 0; + } + if (vbuf->flags & V4L2_BUF_FLAG_LAST) { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; @@ -1148,6 +1259,9 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, if (inst->codec_state == VENUS_DEC_STATE_DRAIN) inst->codec_state = VENUS_DEC_STATE_STOPPED; } + + if (!bytesused) + state = VB2_BUF_STATE_ERROR; } else { vbuf->sequence = inst->sequence_out++; } @@ -1214,6 +1328,25 @@ static void vdec_event_change(struct venus_inst *inst, } } + /* + * The assumption is that the firmware have to return the last buffer + * before this event is received in the v4l2 driver. Also the firmware + * itself doesn't mark the last decoder output buffer with HFI EOS flag. + */ + + if (!sufficient && inst->codec_state == VENUS_DEC_STATE_DRC) { + struct vb2_v4l2_buffer *last; + int ret; + + last = v4l2_m2m_last_dst_buf(inst->m2m_ctx); + if (last) + inst->last_buf = &last->vb2_buf; + + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false); + if (ret) + dev_dbg(dev, "flush output error %d\n", ret); + } + inst->reconfig = true; v4l2_event_queue_fh(&inst->fh, &ev); wake_up(&inst->reconf_wait); @@ -1227,6 +1360,8 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event, struct venus_core *core = inst->core; struct device *dev = core->dev_dec; + vdec_pm_touch(inst); + switch (event) { case EVT_SESSION_ERROR: inst->session_error = true; @@ -1252,9 +1387,16 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event, } } +static void vdec_flush_done(struct venus_inst *inst) +{ + if (inst->codec_state == VENUS_DEC_STATE_DRC) + inst->codec_state = VENUS_DEC_STATE_DRC_FLUSH_DONE; +} + static const struct hfi_inst_ops vdec_hfi_ops = { .buf_done = vdec_buf_done, .event_notify = vdec_event_notify, + .flush_done = vdec_flush_done, }; static void vdec_inst_init(struct venus_inst *inst) @@ -1347,13 +1489,9 @@ static int vdec_open(struct file *file) init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); - ret = pm_runtime_get_sync(core->dev_dec); - if (ret < 0) - goto err_free_inst; - ret = vdec_ctrl_init(inst); if (ret) - goto err_put_sync; + goto err_free; ret = hfi_session_create(inst, &vdec_hfi_ops); if (ret) @@ -1392,9 +1530,7 @@ err_session_destroy: hfi_session_destroy(inst); err_ctrl_deinit: vdec_ctrl_deinit(inst); -err_put_sync: - pm_runtime_put_sync(core->dev_dec); -err_free_inst: +err_free: kfree(inst); return ret; } @@ -1403,6 +1539,8 @@ static int vdec_close(struct file *file) { struct venus_inst *inst = to_inst(file); + vdec_pm_get(inst); + v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); vdec_ctrl_deinit(inst); @@ -1411,7 +1549,7 @@ static int vdec_close(struct file *file) v4l2_fh_del(&inst->fh); v4l2_fh_exit(&inst->fh); - pm_runtime_put_sync(inst->core->dev_dec); + vdec_pm_put(inst, false); kfree(inst); return 0; @@ -1468,6 +1606,8 @@ static int vdec_probe(struct platform_device *pdev) core->dev_dec = dev; video_set_drvdata(vdev, core); + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); return 0; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 9981a2a27c90..feed648550d1 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -357,6 +357,14 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) const struct venus_format *fmt; struct v4l2_format format; u32 pixfmt_out = 0, pixfmt_cap = 0; + struct vb2_queue *q; + + q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); + if (!q) + return -EINVAL; + + if (vb2_is_busy(q)) + return -EBUSY; orig_pixmp = *pixmp; @@ -1018,7 +1026,7 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) deinit_sess: hfi_session_deinit(inst); bufs_done: - venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->streamon_out = 0; else diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index 43c78620c9d8..5c6b00737fe7 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -8,6 +8,7 @@ */ #include <linux/device.h> +#include <linux/dma-mapping.h> #include <linux/list.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -21,6 +22,7 @@ struct rcar_fcp_device { struct list_head list; struct device *dev; + struct device_dma_parameters dma_parms; }; static LIST_HEAD(fcp_devices); @@ -136,6 +138,9 @@ static int rcar_fcp_probe(struct platform_device *pdev) fcp->dev = &pdev->dev; + fcp->dev->dma_parms = &fcp->dma_parms; + dma_set_max_seg_size(fcp->dev, DMA_BIT_MASK(32)); + pm_runtime_enable(&pdev->dev); mutex_lock(&fcp_lock); diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index 240ac3f3c941..ca0d906dce2f 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_RCAR_CSI2 tristate "R-Car MIPI CSI-2 Receiver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF + 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 help @@ -14,8 +16,10 @@ config VIDEO_RCAR_CSI2 config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && OF depends on ARCH_RENESAS || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index faa9fb23a2e9..151e6a90c5fb 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -52,8 +52,8 @@ struct rcar_csi2; /* * Channel Data Type Select - * VCDT[0-15]: Channel 1 VCDT[16-31]: Channel 2 - * VCDT2[0-15]: Channel 3 VCDT2[16-31]: Channel 4 + * VCDT[0-15]: Channel 0 VCDT[16-31]: Channel 1 + * VCDT2[0-15]: Channel 2 VCDT2[16-31]: Channel 3 */ #define VCDT_REG 0x10 #define VCDT2_REG 0x14 diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 5151a3cd8a6e..f421e2584875 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -343,6 +343,29 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, unsigned int i; int matched; + /* + * If mbus_code is set only enumerate supported pixel formats for that + * bus code. Converting from YCbCr to RGB and RGB to YCbCr is possible + * with VIN, so all supported YCbCr and RGB media bus codes can produce + * all of the related pixel formats. If mbus_code is not set enumerate + * all possible pixelformats. + * + * TODO: Once raw capture formats are added to the driver this needs + * to be extended so raw media bus codes only result in raw pixel + * formats. + */ + switch (f->mbus_code) { + case 0: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + break; + default: + return -EINVAL; + } + matched = -1; for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) { if (rvin_format_from_pixel(vin, rvin_formats[i].fourcc)) @@ -767,18 +790,6 @@ static int rvin_mc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int rvin_mc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_CAMERA; - strscpy(i->name, "Camera", sizeof(i->name)); - - return 0; -} - static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = { .vidioc_querycap = rvin_querycap, .vidioc_try_fmt_vid_cap = rvin_mc_try_fmt_vid_cap, @@ -786,10 +797,6 @@ static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = { .vidioc_s_fmt_vid_cap = rvin_mc_s_fmt_vid_cap, .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, - .vidioc_enum_input = rvin_mc_enum_input, - .vidioc_g_input = rvin_g_input, - .vidioc_s_input = rvin_s_input, - .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_querybuf = vb2_ioctl_querybuf, @@ -961,6 +968,7 @@ int rvin_v4l2_register(struct rvin_dev *vin) vin->format.colorspace = RVIN_DEFAULT_COLORSPACE; if (vin->info->use_mc) { + vdev->device_caps |= V4L2_CAP_IO_MC; vdev->ioctl_ops = &rvin_mc_ioctl_ops; } else { vdev->ioctl_ops = &rvin_ioctl_ops; diff --git a/drivers/media/platform/seco-cec/Makefile b/drivers/media/platform/seco-cec/Makefile deleted file mode 100644 index 79fde6947ff2..000000000000 --- a/drivers/media/platform/seco-cec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_SECO_CEC) += seco-cec.o diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c deleted file mode 100644 index f08b8fc192d8..000000000000 --- a/drivers/media/platform/sh_veu.c +++ /dev/null @@ -1,1203 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sh-mobile VEU mem2mem driver - * - * Copyright (C) 2012 Renesas Electronics Corporation - * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> - * Copyright (C) 2008 Magnus Damm - */ - -#include <linux/err.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/videodev2.h> - -#include <media/v4l2-dev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mem2mem.h> -#include <media/v4l2-image-sizes.h> -#include <media/videobuf2-dma-contig.h> - -#define VEU_STR 0x00 /* start register */ -#define VEU_SWR 0x10 /* src: line length */ -#define VEU_SSR 0x14 /* src: image size */ -#define VEU_SAYR 0x18 /* src: y/rgb plane address */ -#define VEU_SACR 0x1c /* src: c plane address */ -#define VEU_BSSR 0x20 /* bundle mode register */ -#define VEU_EDWR 0x30 /* dst: line length */ -#define VEU_DAYR 0x34 /* dst: y/rgb plane address */ -#define VEU_DACR 0x38 /* dst: c plane address */ -#define VEU_TRCR 0x50 /* transform control */ -#define VEU_RFCR 0x54 /* resize scale */ -#define VEU_RFSR 0x58 /* resize clip */ -#define VEU_ENHR 0x5c /* enhance */ -#define VEU_FMCR 0x70 /* filter mode */ -#define VEU_VTCR 0x74 /* lowpass vertical */ -#define VEU_HTCR 0x78 /* lowpass horizontal */ -#define VEU_APCR 0x80 /* color match */ -#define VEU_ECCR 0x84 /* color replace */ -#define VEU_AFXR 0x90 /* fixed mode */ -#define VEU_SWPR 0x94 /* swap */ -#define VEU_EIER 0xa0 /* interrupt mask */ -#define VEU_EVTR 0xa4 /* interrupt event */ -#define VEU_STAR 0xb0 /* status */ -#define VEU_BSRR 0xb4 /* reset */ - -#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */ -#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */ -#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */ -#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */ -#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */ -#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */ -#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */ -#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */ -#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */ -#define VEU_COFFR 0x224 /* color conversion offset */ -#define VEU_CBR 0x228 /* color conversion clip */ - -/* - * 4092x4092 max size is the normal case. In some cases it can be reduced to - * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188. - */ -#define MAX_W 4092 -#define MAX_H 4092 -#define MIN_W 8 -#define MIN_H 8 -#define ALIGN_W 4 - -/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */ -#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024) - -#define MEM2MEM_DEF_TRANSLEN 1 - -struct sh_veu_dev; - -struct sh_veu_file { - struct v4l2_fh fh; - struct sh_veu_dev *veu_dev; - bool cfg_needed; -}; - -struct sh_veu_format { - u32 fourcc; - unsigned int depth; - unsigned int ydepth; -}; - -/* video data format */ -struct sh_veu_vfmt { - /* Replace with v4l2_rect */ - struct v4l2_rect frame; - unsigned int bytesperline; - unsigned int offset_y; - unsigned int offset_c; - const struct sh_veu_format *fmt; -}; - -struct sh_veu_dev { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct v4l2_m2m_dev *m2m_dev; - struct device *dev; - struct v4l2_m2m_ctx *m2m_ctx; - struct sh_veu_vfmt vfmt_out; - struct sh_veu_vfmt vfmt_in; - /* Only single user per direction so far */ - struct sh_veu_file *capture; - struct sh_veu_file *output; - struct mutex fop_lock; - void __iomem *base; - spinlock_t lock; - bool is_2h; - unsigned int xaction; - bool aborting; -}; - -enum sh_veu_fmt_idx { - SH_VEU_FMT_NV12, - SH_VEU_FMT_NV16, - SH_VEU_FMT_NV24, - SH_VEU_FMT_RGB332, - SH_VEU_FMT_RGB444, - SH_VEU_FMT_RGB565, - SH_VEU_FMT_RGB666, - SH_VEU_FMT_RGB24, -}; - -#define DEFAULT_IN_WIDTH VGA_WIDTH -#define DEFAULT_IN_HEIGHT VGA_HEIGHT -#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12 -#define DEFAULT_OUT_WIDTH VGA_WIDTH -#define DEFAULT_OUT_HEIGHT VGA_HEIGHT -#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565 - -/* - * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte - * aligned for NV24. - */ -static const struct sh_veu_format sh_veu_fmt[] = { - [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .fourcc = V4L2_PIX_FMT_NV12 }, - [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .fourcc = V4L2_PIX_FMT_NV16 }, - [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .fourcc = V4L2_PIX_FMT_NV24 }, - [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .fourcc = V4L2_PIX_FMT_RGB332 }, - [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .fourcc = V4L2_PIX_FMT_RGB444 }, - [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .fourcc = V4L2_PIX_FMT_RGB565 }, - [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .fourcc = V4L2_PIX_FMT_BGR666 }, - [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .fourcc = V4L2_PIX_FMT_RGB24 }, -}; - -#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \ - .frame = { \ - .width = VGA_WIDTH, \ - .height = VGA_HEIGHT, \ - }, \ - .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \ - .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \ -} - -#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \ - .frame = { \ - .width = VGA_WIDTH, \ - .height = VGA_HEIGHT, \ - }, \ - .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \ - .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \ -} - -/* - * TODO: add support for further output formats: - * SH_VEU_FMT_NV12, - * SH_VEU_FMT_NV16, - * SH_VEU_FMT_NV24, - * SH_VEU_FMT_RGB332, - * SH_VEU_FMT_RGB444, - * SH_VEU_FMT_RGB666, - * SH_VEU_FMT_RGB24, - */ - -static const int sh_veu_fmt_out[] = { - SH_VEU_FMT_RGB565, -}; - -/* - * TODO: add support for further input formats: - * SH_VEU_FMT_NV16, - * SH_VEU_FMT_NV24, - * SH_VEU_FMT_RGB565, - * SH_VEU_FMT_RGB666, - * SH_VEU_FMT_RGB24, - */ -static const int sh_veu_fmt_in[] = { - SH_VEU_FMT_NV12, -}; - -static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) -{ - switch (fourcc) { - default: - BUG(); - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV24: - return V4L2_COLORSPACE_SMPTE170M; - case V4L2_PIX_FMT_RGB332: - case V4L2_PIX_FMT_RGB444: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_BGR666: - case V4L2_PIX_FMT_RGB24: - return V4L2_COLORSPACE_SRGB; - } -} - -static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg) -{ - return ioread32(veu->base + reg); -} - -static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg, - u32 value) -{ - iowrite32(value, veu->base + reg); -} - - /* ========== mem2mem callbacks ========== */ - -static void sh_veu_job_abort(void *priv) -{ - struct sh_veu_dev *veu = priv; - - /* Will cancel the transaction in the next interrupt handler */ - veu->aborting = true; -} - -static void sh_veu_process(struct sh_veu_dev *veu, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf) -{ - dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - - sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); - sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? - addr + veu->vfmt_out.offset_c : 0); - dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__, - (unsigned long)addr, - veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); - - addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); - sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? - addr + veu->vfmt_in.offset_c : 0); - dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__, - (unsigned long)addr, - veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); - - sh_veu_reg_write(veu, VEU_STR, 1); - - sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */ -} - -/* - * sh_veu_device_run() - prepares and starts the device - * - * This will be called by the framework when it decides to schedule a particular - * instance. - */ -static void sh_veu_device_run(void *priv) -{ - struct sh_veu_dev *veu = priv; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - - src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); - - if (src_buf && dst_buf) - sh_veu_process(veu, &src_buf->vb2_buf, &dst_buf->vb2_buf); -} - - /* ========== video ioctls ========== */ - -static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, - enum v4l2_buf_type type) -{ - return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - veu_file == veu->capture) || - (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - veu_file == veu->output); -} - -static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - -/* - * It is not unusual to have video nodes open()ed multiple times. While some - * V4L2 operations are non-intrusive, like querying formats and various - * parameters, others, like setting formats, starting and stopping streaming, - * queuing and dequeuing buffers, directly affect hardware configuration and / - * or execution. This function verifies availability of the requested interface - * and, if available, reserves it for the requesting user. - */ -static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, - enum v4l2_buf_type type) -{ - struct sh_veu_file **stream; - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - stream = &veu->capture; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - stream = &veu->output; - break; - default: - return -EINVAL; - } - - if (*stream == veu_file) - return 0; - - if (*stream) - return -EBUSY; - - *stream = veu_file; - - return 0; -} - -static int sh_veu_context_init(struct sh_veu_dev *veu) -{ - if (veu->m2m_ctx) - return 0; - - veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, - sh_veu_queue_init); - - return PTR_ERR_OR_ZERO(veu->m2m_ctx); -} - -static int sh_veu_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "sh-veu", sizeof(cap->driver)); - strscpy(cap->card, "sh-mobile VEU", sizeof(cap->card)); - strscpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info)); - return 0; -} - -static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num) -{ - if (f->index >= fmt_num) - return -EINVAL; - - f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc; - return 0; -} - -static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out)); -} - -static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in)); -} - -static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu, - enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &veu->vfmt_out; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return &veu->vfmt_in; - default: - return NULL; - } -} - -static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix = &f->fmt.pix; - struct sh_veu_dev *veu = veu_file->veu_dev; - struct sh_veu_vfmt *vfmt; - - vfmt = sh_veu_get_vfmt(veu, f->type); - - pix->width = vfmt->frame.width; - pix->height = vfmt->frame.height; - pix->field = V4L2_FIELD_NONE; - pix->pixelformat = vfmt->fmt->fourcc; - pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); - pix->bytesperline = vfmt->bytesperline; - pix->sizeimage = vfmt->bytesperline * pix->height * - vfmt->fmt->depth / vfmt->fmt->ydepth; - dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, - f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); - - return 0; -} - -static int sh_veu_g_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - return sh_veu_g_fmt(priv, f); -} - -static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - return sh_veu_g_fmt(priv, f); -} - -static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt) -{ - struct v4l2_pix_format *pix = &f->fmt.pix; - unsigned int y_bytes_used; - - /* - * V4L2 specification suggests, that the driver should correct the - * format struct if any of the dimensions is unsupported - */ - switch (pix->field) { - default: - case V4L2_FIELD_ANY: - pix->field = V4L2_FIELD_NONE; - /* fall through: continue handling V4L2_FIELD_NONE */ - case V4L2_FIELD_NONE: - break; - } - - v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W, - &pix->height, MIN_H, MAX_H, 0, 0); - - y_bytes_used = (pix->width * fmt->ydepth) >> 3; - - if (pix->bytesperline < y_bytes_used) - pix->bytesperline = y_bytes_used; - pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth; - - pix->pixelformat = fmt->fourcc; - pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); - - pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); - - return 0; -} - -static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f) -{ - const int *fmt; - int i, n, dflt; - - pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - fmt = sh_veu_fmt_out; - n = ARRAY_SIZE(sh_veu_fmt_out); - dflt = DEFAULT_OUT_FMTIDX; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - default: - fmt = sh_veu_fmt_in; - n = ARRAY_SIZE(sh_veu_fmt_in); - dflt = DEFAULT_IN_FMTIDX; - break; - } - - for (i = 0; i < n; i++) - if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat) - return &sh_veu_fmt[fmt[i]]; - - return &sh_veu_fmt[dflt]; -} - -static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct sh_veu_format *fmt; - - fmt = sh_veu_find_fmt(f); - - return sh_veu_try_fmt(f, fmt); -} - -static int sh_veu_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct sh_veu_format *fmt; - - fmt = sh_veu_find_fmt(f); - - return sh_veu_try_fmt(f, fmt); -} - -static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt) -{ - /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ - unsigned int left = vfmt->frame.left & ~0x03; - unsigned int top = vfmt->frame.top; - dma_addr_t offset = (dma_addr_t)top * veu->vfmt_out.bytesperline + - (((dma_addr_t)left * veu->vfmt_out.fmt->depth) >> 3); - unsigned int y_line; - - vfmt->offset_y = offset; - - switch (vfmt->fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV24: - y_line = ALIGN(vfmt->frame.width, 16); - vfmt->offset_c = offset + y_line * vfmt->frame.height; - break; - case V4L2_PIX_FMT_RGB332: - case V4L2_PIX_FMT_RGB444: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_BGR666: - case V4L2_PIX_FMT_RGB24: - vfmt->offset_c = 0; - break; - default: - BUG(); - } -} - -static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix = &f->fmt.pix; - struct sh_veu_dev *veu = veu_file->veu_dev; - struct sh_veu_vfmt *vfmt; - struct vb2_queue *vq; - int ret = sh_veu_context_init(veu); - if (ret < 0) - return ret; - - vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - if (vb2_is_busy(vq)) { - v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - vfmt = sh_veu_get_vfmt(veu, f->type); - /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */ - - vfmt->fmt = sh_veu_find_fmt(f); - /* vfmt->fmt != NULL following the same argument as above */ - vfmt->frame.width = pix->width; - vfmt->frame.height = pix->height; - vfmt->bytesperline = pix->bytesperline; - - sh_veu_colour_offset(veu, vfmt); - - /* - * We could also verify and require configuration only if any parameters - * actually have changed, but it is unlikely, that the user requests the - * same configuration several times without closing the device. - */ - veu_file->cfg_needed = true; - - dev_dbg(veu->dev, - "Setting format for type %d, wxh: %dx%d, fmt: %x\n", - f->type, pix->width, pix->height, vfmt->fmt->fourcc); - - return 0; -} - -static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - int ret = sh_veu_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - return sh_veu_s_fmt(priv, f); -} - -static int sh_veu_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - int ret = sh_veu_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - return sh_veu_s_fmt(priv, f); -} - -static int sh_veu_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct sh_veu_file *veu_file = priv; - struct sh_veu_dev *veu = veu_file->veu_dev; - int ret = sh_veu_context_init(veu); - if (ret < 0) - return ret; - - ret = sh_veu_stream_init(veu, veu_file, reqbufs->type); - if (ret < 0) - return ret; - - return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs); -} - -static int sh_veu_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct sh_veu_file *veu_file = priv; - - if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) - return -EBUSY; - - return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf); -} - -static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct sh_veu_file *veu_file = priv; - - dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); - if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) - return -EBUSY; - - return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf); -} - -static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct sh_veu_file *veu_file = priv; - - dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); - if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) - return -EBUSY; - - return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf); -} - -static void sh_veu_calc_scale(struct sh_veu_dev *veu, - int size_in, int size_out, int crop_out, - u32 *mant, u32 *frac, u32 *rep) -{ - u32 fixpoint; - - /* calculate FRAC and MANT */ - *rep = *mant = *frac = 0; - - if (size_in == size_out) { - if (crop_out != size_out) - *mant = 1; /* needed for cropping */ - return; - } - - /* VEU2H special upscale */ - if (veu->is_2h && size_out > size_in) { - u32 fixpoint = (4096 * size_in) / size_out; - *mant = fixpoint / 4096; - *frac = (fixpoint - (*mant * 4096)) & ~0x07; - - switch (*frac) { - case 0x800: - *rep = 1; - break; - case 0x400: - *rep = 3; - break; - case 0x200: - *rep = 7; - break; - } - if (*rep) - return; - } - - fixpoint = (4096 * (size_in - 1)) / (size_out + 1); - *mant = fixpoint / 4096; - *frac = fixpoint - (*mant * 4096); - - if (*frac & 0x07) { - /* - * FIXME: do we really have to round down twice in the - * up-scaling case? - */ - *frac &= ~0x07; - if (size_out > size_in) - *frac -= 8; /* round down if scaling up */ - else - *frac += 8; /* round up if scaling down */ - } -} - -static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu, - int size_in, int size_out, int crop_out) -{ - u32 mant, frac, value, rep; - - sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); - - /* set scale */ - value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) | - (((mant << 12) | frac) << 16); - - sh_veu_reg_write(veu, VEU_RFCR, value); - - /* set clip */ - value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) | - (((rep << 12) | crop_out) << 16); - - sh_veu_reg_write(veu, VEU_RFSR, value); - - return ALIGN((size_in * crop_out) / size_out, 4); -} - -static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu, - int size_in, int size_out, int crop_out) -{ - u32 mant, frac, value, rep; - - sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); - - /* set scale */ - value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) | - (mant << 12) | frac; - - sh_veu_reg_write(veu, VEU_RFCR, value); - - /* set clip */ - value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) | - (rep << 12) | crop_out; - - sh_veu_reg_write(veu, VEU_RFSR, value); - - return ALIGN((size_in * crop_out) / size_out, 4); -} - -static void sh_veu_configure(struct sh_veu_dev *veu) -{ - u32 src_width, src_stride, src_height; - u32 dst_width, dst_stride, dst_height; - u32 real_w, real_h; - - /* reset VEU */ - sh_veu_reg_write(veu, VEU_BSRR, 0x100); - - src_width = veu->vfmt_in.frame.width; - src_height = veu->vfmt_in.frame.height; - src_stride = ALIGN(veu->vfmt_in.frame.width, 16); - - dst_width = real_w = veu->vfmt_out.frame.width; - dst_height = real_h = veu->vfmt_out.frame.height; - /* Datasheet is unclear - whether it's always number of bytes or not */ - dst_stride = veu->vfmt_out.bytesperline; - - /* - * So far real_w == dst_width && real_h == dst_height, but it wasn't - * necessarily the case in the original vidix driver, so, it may change - * here in the future too. - */ - src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width); - src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height); - - sh_veu_reg_write(veu, VEU_SWR, src_stride); - sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16)); - sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */ - - sh_veu_reg_write(veu, VEU_EDWR, dst_stride); - sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */ - - sh_veu_reg_write(veu, VEU_SWPR, 0x67); - sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4); - - if (veu->is_2h) { - sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5); - sh_veu_reg_write(veu, VEU_MCR01, 0x0950); - sh_veu_reg_write(veu, VEU_MCR02, 0x0000); - - sh_veu_reg_write(veu, VEU_MCR10, 0x397f); - sh_veu_reg_write(veu, VEU_MCR11, 0x0950); - sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd); - - sh_veu_reg_write(veu, VEU_MCR20, 0x0000); - sh_veu_reg_write(veu, VEU_MCR21, 0x0950); - sh_veu_reg_write(veu, VEU_MCR22, 0x1023); - - sh_veu_reg_write(veu, VEU_COFFR, 0x00800010); - } -} - -static int sh_veu_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct sh_veu_file *veu_file = priv; - - if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) - return -EBUSY; - - if (veu_file->cfg_needed) { - struct sh_veu_dev *veu = veu_file->veu_dev; - veu_file->cfg_needed = false; - sh_veu_configure(veu_file->veu_dev); - veu->xaction = 0; - veu->aborting = false; - } - - return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type); -} - -static int sh_veu_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct sh_veu_file *veu_file = priv; - - if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) - return -EBUSY; - - return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type); -} - -static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { - .vidioc_querycap = sh_veu_querycap, - - .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap, - - .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out, - .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out, - - .vidioc_reqbufs = sh_veu_reqbufs, - .vidioc_querybuf = sh_veu_querybuf, - - .vidioc_qbuf = sh_veu_qbuf, - .vidioc_dqbuf = sh_veu_dqbuf, - - .vidioc_streamon = sh_veu_streamon, - .vidioc_streamoff = sh_veu_streamoff, -}; - - /* ========== Queue operations ========== */ - -static int sh_veu_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct sh_veu_dev *veu = vb2_get_drv_priv(vq); - struct sh_veu_vfmt *vfmt = sh_veu_get_vfmt(veu, vq->type); - unsigned int count = *nbuffers; - unsigned int size = vfmt->bytesperline * vfmt->frame.height * - vfmt->fmt->depth / vfmt->fmt->ydepth; - - if (count < 2) - *nbuffers = count = 2; - - if (size * count > VIDEO_MEM_LIMIT) { - count = VIDEO_MEM_LIMIT / size; - *nbuffers = count; - } - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); - - return 0; -} - -static int sh_veu_buf_prepare(struct vb2_buffer *vb) -{ - struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); - struct sh_veu_vfmt *vfmt; - unsigned int sizeimage; - - vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type); - sizeimage = vfmt->bytesperline * vfmt->frame.height * - vfmt->fmt->depth / vfmt->fmt->ydepth; - - if (vb2_plane_size(vb, 0) < sizeimage) { - dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n", - __func__, vb2_plane_size(vb, 0), sizeimage); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, sizeimage); - - return 0; -} - -static void sh_veu_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); - dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->type); - v4l2_m2m_buf_queue(veu->m2m_ctx, vbuf); -} - -static const struct vb2_ops sh_veu_qops = { - .queue_setup = sh_veu_queue_setup, - .buf_prepare = sh_veu_buf_prepare, - .buf_queue = sh_veu_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct sh_veu_dev *veu = priv; - int ret; - - memset(src_vq, 0, sizeof(*src_vq)); - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = veu; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->ops = &sh_veu_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->lock = &veu->fop_lock; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->dev = veu->v4l2_dev.dev; - - ret = vb2_queue_init(src_vq); - if (ret < 0) - return ret; - - memset(dst_vq, 0, sizeof(*dst_vq)); - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = veu; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &sh_veu_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->lock = &veu->fop_lock; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->dev = veu->v4l2_dev.dev; - - return vb2_queue_init(dst_vq); -} - - /* ========== File operations ========== */ - -static int sh_veu_open(struct file *file) -{ - struct sh_veu_dev *veu = video_drvdata(file); - struct sh_veu_file *veu_file; - - veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL); - if (!veu_file) - return -ENOMEM; - - v4l2_fh_init(&veu_file->fh, video_devdata(file)); - veu_file->veu_dev = veu; - veu_file->cfg_needed = true; - - file->private_data = veu_file; - - pm_runtime_get_sync(veu->dev); - v4l2_fh_add(&veu_file->fh); - - dev_dbg(veu->dev, "Created instance %p\n", veu_file); - - return 0; -} - -static int sh_veu_release(struct file *file) -{ - struct sh_veu_dev *veu = video_drvdata(file); - struct sh_veu_file *veu_file = file->private_data; - - dev_dbg(veu->dev, "Releasing instance %p\n", veu_file); - - if (veu_file == veu->capture) { - veu->capture = NULL; - vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)); - } - - if (veu_file == veu->output) { - veu->output = NULL; - vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT)); - } - - if (!veu->output && !veu->capture && veu->m2m_ctx) { - v4l2_m2m_ctx_release(veu->m2m_ctx); - veu->m2m_ctx = NULL; - } - - pm_runtime_put(veu->dev); - v4l2_fh_del(&veu_file->fh); - v4l2_fh_exit(&veu_file->fh); - - kfree(veu_file); - - return 0; -} - -static __poll_t sh_veu_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct sh_veu_file *veu_file = file->private_data; - - return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait); -} - -static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sh_veu_file *veu_file = file->private_data; - - return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma); -} - -static const struct v4l2_file_operations sh_veu_fops = { - .owner = THIS_MODULE, - .open = sh_veu_open, - .release = sh_veu_release, - .poll = sh_veu_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = sh_veu_mmap, -}; - -static const struct video_device sh_veu_videodev = { - .name = "sh-veu", - .fops = &sh_veu_fops, - .ioctl_ops = &sh_veu_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .vfl_dir = VFL_DIR_M2M, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, -}; - -static const struct v4l2_m2m_ops sh_veu_m2m_ops = { - .device_run = sh_veu_device_run, - .job_abort = sh_veu_job_abort, -}; - -static irqreturn_t sh_veu_bh(int irq, void *dev_id) -{ - struct sh_veu_dev *veu = dev_id; - - if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) { - v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx); - veu->xaction = 0; - } else { - sh_veu_device_run(veu); - } - - return IRQ_HANDLED; -} - -static irqreturn_t sh_veu_isr(int irq, void *dev_id) -{ - struct sh_veu_dev *veu = dev_id; - struct vb2_v4l2_buffer *dst; - struct vb2_v4l2_buffer *src; - u32 status = sh_veu_reg_read(veu, VEU_EVTR); - - /* bundle read mode not used */ - if (!(status & 1)) - return IRQ_NONE; - - /* disable interrupt in VEU */ - sh_veu_reg_write(veu, VEU_EIER, 0); - /* halt operation */ - sh_veu_reg_write(veu, VEU_STR, 0); - /* ack int, write 0 to clear bits */ - sh_veu_reg_write(veu, VEU_EVTR, status & ~1); - - /* conversion completed */ - dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx); - src = v4l2_m2m_src_buf_remove(veu->m2m_ctx); - if (!src || !dst) - return IRQ_NONE; - - dst->vb2_buf.timestamp = src->vb2_buf.timestamp; - dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst->flags |= - src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst->timecode = src->timecode; - - spin_lock(&veu->lock); - v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); - spin_unlock(&veu->lock); - - veu->xaction++; - - return IRQ_WAKE_THREAD; -} - -static int sh_veu_probe(struct platform_device *pdev) -{ - struct sh_veu_dev *veu; - struct resource *reg_res; - struct video_device *vdev; - int irq, ret; - - reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - - if (!reg_res || irq <= 0) { - dev_err(&pdev->dev, "Insufficient VEU platform information.\n"); - return -ENODEV; - } - - veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL); - if (!veu) - return -ENOMEM; - - veu->is_2h = resource_size(reg_res) == 0x22c; - - veu->base = devm_ioremap_resource(&pdev->dev, reg_res); - if (IS_ERR(veu->base)) - return PTR_ERR(veu->base); - - ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, - 0, "veu", veu); - if (ret < 0) - return ret; - - ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev); - if (ret < 0) { - dev_err(&pdev->dev, "Error registering v4l2 device\n"); - return ret; - } - - vdev = &veu->vdev; - - *vdev = sh_veu_videodev; - vdev->v4l2_dev = &veu->v4l2_dev; - spin_lock_init(&veu->lock); - mutex_init(&veu->fop_lock); - vdev->lock = &veu->fop_lock; - - video_set_drvdata(vdev, veu); - - veu->dev = &pdev->dev; - veu->vfmt_out = DEFAULT_OUT_VFMT; - veu->vfmt_in = DEFAULT_IN_VFMT; - - veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops); - if (IS_ERR(veu->m2m_dev)) { - ret = PTR_ERR(veu->m2m_dev); - v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret); - goto em2minit; - } - - pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - pm_runtime_suspend(&pdev->dev); - if (ret < 0) - goto evidreg; - - return ret; - -evidreg: - pm_runtime_disable(&pdev->dev); - v4l2_m2m_release(veu->m2m_dev); -em2minit: - v4l2_device_unregister(&veu->v4l2_dev); - return ret; -} - -static int sh_veu_remove(struct platform_device *pdev) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); - struct sh_veu_dev *veu = container_of(v4l2_dev, - struct sh_veu_dev, v4l2_dev); - - video_unregister_device(&veu->vdev); - pm_runtime_disable(&pdev->dev); - v4l2_m2m_release(veu->m2m_dev); - v4l2_device_unregister(&veu->v4l2_dev); - - return 0; -} - -static struct platform_driver __refdata sh_veu_pdrv = { - .remove = sh_veu_remove, - .driver = { - .name = "sh_veu", - }, -}; - -module_platform_driver_probe(sh_veu_pdrv, sh_veu_probe); - -MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); -MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/sti/cec/Makefile b/drivers/media/platform/sti/cec/Makefile deleted file mode 100644 index d0c6b4ae94d6..000000000000 --- a/drivers/media/platform/sti/cec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += stih-cec.o diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile index 5ed73599ca44..48b36db2c2e2 100644 --- a/drivers/media/platform/stm32/Makefile +++ b/drivers/media/platform/stm32/Makefile @@ -1,3 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o -obj-$(CONFIG_VIDEO_STM32_HDMI_CEC) += stm32-cec.o diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig index 71808e93ac2e..7151cc249afa 100644 --- a/drivers/media/platform/sunxi/Kconfig +++ b/drivers/media/platform/sunxi/Kconfig @@ -1,2 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + source "drivers/media/platform/sunxi/sun4i-csi/Kconfig" source "drivers/media/platform/sunxi/sun6i-csi/Kconfig" diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index ff0993f70dc3..fc537c9f5ca9 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + obj-y += sun4i-csi/ obj-y += sun6i-csi/ obj-y += sun8i-di/ diff --git a/drivers/media/platform/sunxi/sun4i-csi/Kconfig b/drivers/media/platform/sunxi/sun4i-csi/Kconfig index e86e29b6a603..903c6152f6e8 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/Kconfig +++ b/drivers/media/platform/sunxi/sun4i-csi/Kconfig @@ -1,7 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + config VIDEO_SUN4I_CSI tristate "Allwinner A10 CMOS Sensor Interface Support" - depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA depends on ARCH_SUNXI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/sunxi/sun4i-csi/Makefile b/drivers/media/platform/sunxi/sun4i-csi/Makefile index 7c790a57f5ee..5062b006d63e 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/Makefile +++ b/drivers/media/platform/sunxi/sun4i-csi/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + sun4i-csi-y += sun4i_csi.o sun4i-csi-y += sun4i_dma.o sun4i-csi-y += sun4i_v4l2.o diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig index 269b3ebf4f52..586e3fb3a80d 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/Kconfig +++ b/drivers/media/platform/sunxi/sun6i-csi/Kconfig @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_SUN6I_CSI tristate "Allwinner V3s Camera Sensor Interface driver" - depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA depends on ARCH_SUNXI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO select V4L2_FWNODE diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index d78f6593ddd1..ba5d07886607 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -941,7 +941,7 @@ static int deinterlace_runtime_resume(struct device *device) if (ret) { dev_err(dev->dev, "Failed to enable bus clock\n"); - goto err_exlusive_rate; + goto err_exclusive_rate; } ret = clk_prepare_enable(dev->mod_clk); @@ -969,14 +969,14 @@ static int deinterlace_runtime_resume(struct device *device) return 0; -err_exlusive_rate: - clk_rate_exclusive_put(dev->mod_clk); err_ram_clk: clk_disable_unprepare(dev->ram_clk); err_mod_clk: clk_disable_unprepare(dev->mod_clk); err_bus_clk: clk_disable_unprepare(dev->bus_clk); +err_exclusive_rate: + clk_rate_exclusive_put(dev->mod_clk); return ret; } diff --git a/drivers/media/platform/tegra-cec/Makefile b/drivers/media/platform/tegra-cec/Makefile deleted file mode 100644 index 97e57c7493c0..000000000000 --- a/drivers/media/platform/tegra-cec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC) += tegra_cec.o diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 6c8f3702eac0..9b18db7af6c3 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -6,6 +6,7 @@ * Benoit Parrot, <bparrot@ti.com> */ +#include <linux/clk.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/ioctl.h> @@ -340,6 +341,7 @@ static const struct cal_data am654_cal_data = { * all instances. */ struct cal_dev { + struct clk *fclk; int irq; void __iomem *base; struct resource *res; @@ -412,6 +414,8 @@ struct cal_ctx { struct cal_buffer *cur_frm; /* Pointer pointing to next v4l2_buffer */ struct cal_buffer *next_frm; + + bool dma_act; }; static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, @@ -643,36 +647,12 @@ static void i913_errata(struct cal_dev *dev, unsigned int port) { u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); - set_field(®10, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); } -static int cal_runtime_get(struct cal_dev *dev) -{ - int r; - - r = pm_runtime_get_sync(&dev->pdev->dev); - - if (dev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { - /* - * Apply errata on both port eveytime we (re-)enable - * the clock - */ - i913_errata(dev, 0); - i913_errata(dev, 1); - } - - return r; -} - -static inline void cal_runtime_put(struct cal_dev *dev) -{ - pm_runtime_put_sync(&dev->pdev->dev); -} - static void cal_quickdump_regs(struct cal_dev *dev) { cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); @@ -704,16 +684,31 @@ static void cal_quickdump_regs(struct cal_dev *dev) */ static void enable_irqs(struct cal_ctx *ctx) { + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), + CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + cio_err_mask); + + /* Always enable OCPO error */ + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); + /* Enable IRQ_WDMA_END 0/1 */ - reg_write_field(ctx->dev, - CAL_HL_IRQENABLE_SET(2), - CAL_HL_IRQ_ENABLE, - CAL_HL_IRQ_MASK(ctx->csi2_port)); + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); /* Enable IRQ_WDMA_START 0/1 */ - reg_write_field(ctx->dev, - CAL_HL_IRQENABLE_SET(3), - CAL_HL_IRQ_ENABLE, - CAL_HL_IRQ_MASK(ctx->csi2_port)); + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); } @@ -722,24 +717,59 @@ static void disable_irqs(struct cal_ctx *ctx) { u32 val; + /* Disable CIO error irqs */ + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), + CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + 0); + /* Disable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, CAL_HL_IRQ_CLEAR, CAL_HL_IRQ_MASK(ctx->csi2_port)); + set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, CAL_HL_IRQ_CLEAR, CAL_HL_IRQ_MASK(ctx->csi2_port)); + set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); } +static void csi2_cio_power(struct cal_ctx *ctx, bool enable) +{ + u32 target_state; + unsigned int i; + + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; + + reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + + for (i = 0; i < 10; i++) { + u32 current_state; + + current_state = reg_read_field(ctx->dev, + CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); + + if (current_state == target_state) + break; + + usleep_range(1000, 1100); + } + + if (i == 10) + ctx_err(ctx, "Failed to power %s complexio\n", + enable ? "up" : "down"); +} + static void csi2_phy_config(struct cal_ctx *ctx); static void csi2_phy_init(struct cal_ctx *ctx) { - int i; u32 val; + u32 sscounter; /* Steps * 1. Configure D-PHY mode and enable required lanes @@ -762,66 +792,90 @@ static void csi2_phy_init(struct cal_ctx *ctx) camerarx_phy_enable(ctx); /* 2. Reset complex IO - Do not wait for reset completion */ - val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", ctx->csi2_port, reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); - /* Dummy read to allow SCP to complete */ - val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + /* Dummy read to allow SCP reset to complete */ + reg_read(ctx->cc, CAL_CSI2_PHY_REG0); /* 3.A. Program Phy Timing Parameters */ csi2_phy_config(ctx); /* 3.B. Program Stop States */ + /* + * The stop-state-counter is based on fclk cycles, and we always use + * the x16 and x4 settings, so stop-state-timeout = + * fclk-cycle * 16 * 4 * counter. + * + * Stop-state-timeout must be more than 100us as per CSI2 spec, so we + * calculate a timeout that's 100us (rounding up). + */ + sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); + val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); - set_field(&val, CAL_GEN_ENABLE, - CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - set_field(&val, CAL_GEN_DISABLE, - CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - set_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", ctx->csi2_port, reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); /* 4. Force FORCERXMODE */ - val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); - set_field(&val, CAL_GEN_ENABLE, - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); + reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", ctx->csi2_port, reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); /* E. Power up the PHY using the complex IO */ - val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON, - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + csi2_cio_power(ctx, true); +} - /* F. Wait for power up completion */ - for (i = 0; i < 10; i++) { +static void csi2_wait_complexio_reset(struct cal_ctx *ctx) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) == - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON) + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) break; - usleep_range(1000, 1100); + usleep_range(500, 5000); } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered UP %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), - (i >= 10) ? "(timeout)" : ""); + + if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); } -static void csi2_wait_for_phy(struct cal_ctx *ctx) +static void csi2_wait_stop_state(struct cal_ctx *ctx) { - int i; + unsigned long timeout; + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (reg_read_field(ctx->dev, + CAL_CSI2_TIMING(ctx->csi2_port), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } + + if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + ctx_err(ctx, "Timeout waiting for stop state\n"); +} + +static void csi2_wait_for_phy(struct cal_ctx *ctx) +{ /* Steps * 2. Wait for completion of reset * Note if the external sensor is not sending byte clock, @@ -832,32 +886,10 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) */ /* 2. Wait for reset completion */ - for (i = 0; i < 250; i++) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(1000, 1100); - } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO Reset Done (%d) %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, - (i >= 250) ? "(timeout)" : ""); + csi2_wait_complexio_reset(ctx); /* 4. G. Wait for all enabled lane to reach stop state */ - for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, - CAL_CSI2_TIMING(ctx->csi2_port), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == - CAL_GEN_DISABLE) - break; - usleep_range(1000, 1100); - } - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop State Reached %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)), - (i >= 10) ? "(timeout)" : ""); + csi2_wait_stop_state(ctx); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); @@ -866,33 +898,13 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) static void csi2_phy_deinit(struct cal_ctx *ctx) { int i; - u32 val; - /* Power down the PHY using the complex IO */ - val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF, - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) == - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF) - break; - usleep_range(1000, 1100); - } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered Down %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), - (i >= 10) ? "(timeout)" : ""); + csi2_cio_power(ctx, false); /* Assert Comple IO Reset */ - val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { @@ -942,14 +954,15 @@ static void csi2_lane_config(struct cal_ctx *ctx) static void csi2_ppi_enable(struct cal_ctx *ctx) { + reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - CAL_GEN_ENABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK); + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } static void csi2_ppi_disable(struct cal_ctx *ctx) { reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - CAL_GEN_DISABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK); + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } static void csi2_ctx_config(struct cal_ctx *ctx) @@ -969,8 +982,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); /* Virtual Channel from the CSI2 sensor usually 0! */ set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); - /* NUM_LINES_PER_FRAME => 0 means auto detect */ - set_field(&val, 0, CAL_CSI2_CTX_LINES_MASK); + set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); @@ -1024,7 +1036,7 @@ static void pix_proc_config(struct cal_ctx *ctx) set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); - set_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK); + set_field(&val, 1, CAL_PIX_PROC_EN_MASK); reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); @@ -1044,7 +1056,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, CAL_WR_DMA_CTRL_MODE_MASK); set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); - set_field(&val, CAL_GEN_ENABLE, CAL_WR_DMA_CTRL_STALL_RD_MASK); + set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); @@ -1192,57 +1204,74 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dev *dev = (struct cal_dev *)data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst2, irqst3; + u32 irqst1, irqst2, irqst3; + + irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); + if (irqst1) { + int i; + + reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + + if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) + dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); + + for (i = 1; i <= 2; ++i) { + if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { + u32 cio_stat = reg_read(dev, + CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); + + dev_err_ratelimited(&dev->pdev->dev, + "CIO%d error: %#08x\n", i, cio_stat); + + reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cio_stat); + } + } + } /* Check which DMA just finished */ irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); if (irqst2) { + int i; + /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); - /* Need to check both port */ - if (isportirqset(irqst2, 1)) { - ctx = dev->ctx[0]; + for (i = 1; i <= 2; ++i) { + if (isportirqset(irqst2, i)) { + ctx = dev->ctx[i - 1]; - if (ctx->cur_frm != ctx->next_frm) - cal_process_buffer_complete(ctx); - } + spin_lock(&ctx->slock); + ctx->dma_act = false; - if (isportirqset(irqst2, 2)) { - ctx = dev->ctx[1]; + if (ctx->cur_frm != ctx->next_frm) + cal_process_buffer_complete(ctx); - if (ctx->cur_frm != ctx->next_frm) - cal_process_buffer_complete(ctx); + spin_unlock(&ctx->slock); + } } } /* Check which DMA just started */ irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); if (irqst3) { + int i; + /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); - /* Need to check both port */ - if (isportirqset(irqst3, 1)) { - ctx = dev->ctx[0]; - dma_q = &ctx->vidq; - - spin_lock(&ctx->slock); - if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) - cal_schedule_next_buffer(ctx); - spin_unlock(&ctx->slock); - } - - if (isportirqset(irqst3, 2)) { - ctx = dev->ctx[1]; - dma_q = &ctx->vidq; - - spin_lock(&ctx->slock); - if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) - cal_schedule_next_buffer(ctx); - spin_unlock(&ctx->slock); + for (i = 1; i <= 2; ++i) { + if (isportirqset(irqst3, i)) { + ctx = dev->ctx[i - 1]; + dma_q = &ctx->vidq; + + spin_lock(&ctx->slock); + ctx->dma_act = true; + if (!list_empty(&dma_q->active) && + ctx->cur_frm == ctx->next_frm) + cal_schedule_next_buffer(ctx); + spin_unlock(&ctx->slock); + } } } @@ -1665,7 +1694,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - cal_runtime_get(ctx->dev); + pm_runtime_get_sync(&ctx->dev->pdev->dev); csi2_ctx_config(ctx); pix_proc_config(ctx); @@ -1680,7 +1709,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { v4l2_subdev_call(ctx->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); - cal_runtime_put(ctx->dev); + pm_runtime_put_sync(&ctx->dev->pdev->dev); goto err; } @@ -1711,10 +1740,27 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; + unsigned long timeout; unsigned long flags; int ret; + bool dma_act; csi2_ppi_disable(ctx); + + /* wait for stream and dma to finish */ + dma_act = true; + timeout = jiffies + msecs_to_jiffies(500); + while (dma_act && time_before(jiffies, timeout)) { + msleep(50); + + spin_lock_irqsave(&ctx->slock, flags); + dma_act = ctx->dma_act; + spin_unlock_irqrestore(&ctx->slock, flags); + } + + if (dma_act) + ctx_err(ctx, "failed to disable dma cleanly\n"); + disable_irqs(ctx); csi2_phy_deinit(ctx); @@ -1743,7 +1789,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx->next_frm = NULL; spin_unlock_irqrestore(&ctx->slock, flags); - cal_runtime_put(ctx->dev); + pm_runtime_put_sync(&ctx->dev->pdev->dev); } static const struct vb2_ops cal_video_qops = { @@ -2191,7 +2237,26 @@ err_exit: return NULL; } -static const struct of_device_id cal_of_match[]; +static const struct of_device_id cal_of_match[] = { + { + .compatible = "ti,dra72-cal", + .data = (void *)&dra72x_cal_data, + }, + { + .compatible = "ti,dra72-pre-es2-cal", + .data = (void *)&dra72x_es1_cal_data, + }, + { + .compatible = "ti,dra76-cal", + .data = (void *)&dra76x_cal_data, + }, + { + .compatible = "ti,am654-cal", + .data = (void *)&am654_cal_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cal_of_match); static int cal_probe(struct platform_device *pdev) { @@ -2223,6 +2288,12 @@ static int cal_probe(struct platform_device *pdev) /* save pdev pointer */ dev->pdev = pdev; + dev->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(dev->fclk)) { + dev_err(&pdev->dev, "cannot get CAL fclk\n"); + return PTR_ERR(dev->fclk); + } + syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, "ti,camerrx-control"); ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, @@ -2296,20 +2367,24 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + pm_runtime_enable(&pdev->dev); - ret = cal_runtime_get(dev); + ret = pm_runtime_get_sync(&pdev->dev); if (ret) goto runtime_disable; /* Just check we can actually access the module */ cal_get_hwinfo(dev); - cal_runtime_put(dev); + pm_runtime_put_sync(&pdev->dev); return 0; runtime_disable: + vb2_dma_contig_clear_max_seg_size(&pdev->dev); + pm_runtime_disable(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { ctx = dev->ctx[i]; @@ -2333,7 +2408,7 @@ static int cal_remove(struct platform_device *pdev) cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); - cal_runtime_get(dev); + pm_runtime_get_sync(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { ctx = dev->ctx[i]; @@ -2349,41 +2424,41 @@ static int cal_remove(struct platform_device *pdev) } } - cal_runtime_put(dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); + return 0; } -#if defined(CONFIG_OF) -static const struct of_device_id cal_of_match[] = { - { - .compatible = "ti,dra72-cal", - .data = (void *)&dra72x_cal_data, - }, - { - .compatible = "ti,dra72-pre-es2-cal", - .data = (void *)&dra72x_es1_cal_data, - }, - { - .compatible = "ti,dra76-cal", - .data = (void *)&dra76x_cal_data, - }, - { - .compatible = "ti,am654-cal", - .data = (void *)&am654_cal_data, - }, - {}, +static int cal_runtime_resume(struct device *dev) +{ + struct cal_dev *caldev = dev_get_drvdata(dev); + + if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + /* + * Apply errata on both port everytime we (re-)enable + * the clock + */ + i913_errata(caldev, 0); + i913_errata(caldev, 1); + } + + return 0; +} + +static const struct dev_pm_ops cal_pm_ops = { + .runtime_resume = cal_runtime_resume, }; -MODULE_DEVICE_TABLE(of, cal_of_match); -#endif static struct platform_driver cal_pdrv = { .probe = cal_probe, .remove = cal_remove, .driver = { .name = CAL_MODULE_NAME, - .of_match_table = of_match_ptr(cal_of_match), + .pm = &cal_pm_ops, + .of_match_table = cal_of_match, }, }; diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index 0b76d1186074..ac54a2fe7bb6 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -101,15 +101,6 @@ #define CM_CTRL_CORE_CAMERRX_CONTROL 0x000 /********************************************************************* -* Generic value used in various field below -*********************************************************************/ - -#define CAL_GEN_DISABLE 0 -#define CAL_GEN_ENABLE 1 -#define CAL_GEN_FALSE 0 -#define CAL_GEN_TRUE 1 - -/********************************************************************* * Field Definition Macros *********************************************************************/ @@ -151,12 +142,11 @@ #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 #define CAL_HL_IRQ_MASK(m) BIT((m) - 1) -#define CAL_HL_IRQ_NOACTION 0x0 -#define CAL_HL_IRQ_ENABLE 0x1 -#define CAL_HL_IRQ_CLEAR 0x1 -#define CAL_HL_IRQ_DISABLED 0x0 -#define CAL_HL_IRQ_ENABLED 0x1 -#define CAL_HL_IRQ_PENDING 0x1 + +#define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) + +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + ((i) - 1) * 8) +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + ((i) - 1) * 8) #define CAL_PIX_PROC_EN_MASK BIT(0) #define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) @@ -414,6 +404,7 @@ #define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT(17) #define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT(18) #define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT(19) +#define CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK GENMASK(19, 0) #define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT(20) #define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT(21) #define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT(22) diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index ddd0e338f9e4..53570250a25d 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -17,10 +17,12 @@ #include <media/v4l2-async.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> #include <media/v4l2-subdev.h> struct video_mux { struct v4l2_subdev subdev; + struct v4l2_async_notifier notifier; struct media_pad *pads; struct v4l2_mbus_framefmt *format_mbus; struct mux_control *mux; @@ -35,6 +37,12 @@ static const struct v4l2_mbus_framefmt video_mux_format_mbus_default = { .field = V4L2_FIELD_NONE, }; +static inline struct video_mux * +notifier_to_video_mux(struct v4l2_async_notifier *n) +{ + return container_of(n, struct video_mux, notifier); +} + static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd) { return container_of(sd, struct video_mux, subdev); @@ -96,6 +104,7 @@ out: static const struct media_entity_operations video_mux_ops = { .link_setup = video_mux_link_setup, .link_validate = v4l2_subdev_link_validate, + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, }; static int video_mux_s_stream(struct v4l2_subdev *sd, int enable) @@ -330,36 +339,64 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { .video = &video_mux_subdev_video_ops, }; -static int video_mux_parse_endpoint(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { - /* - * it's not an error if remote is missing on a video-mux - * input port, return -ENOTCONN to skip this endpoint with - * no error. - */ - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; + struct video_mux *vmux = notifier_to_video_mux(notifier); + + return v4l2_create_fwnode_links(sd, &vmux->subdev); } +static const struct v4l2_async_notifier_operations video_mux_notify_ops = { + .bound = video_mux_notify_bound, +}; + static int video_mux_async_register(struct video_mux *vmux, unsigned int num_input_pads) { - unsigned int i, *ports; + unsigned int i; int ret; - ports = kcalloc(num_input_pads, sizeof(*ports), GFP_KERNEL); - if (!ports) - return -ENOMEM; - for (i = 0; i < num_input_pads; i++) - ports[i] = i; + v4l2_async_notifier_init(&vmux->notifier); - ret = v4l2_async_register_fwnode_subdev( - &vmux->subdev, sizeof(struct v4l2_async_subdev), - ports, num_input_pads, video_mux_parse_endpoint); + for (i = 0; i < num_input_pads; i++) { + struct v4l2_async_subdev *asd; + struct fwnode_handle *ep; - kfree(ports); - return ret; + ep = fwnode_graph_get_endpoint_by_id( + dev_fwnode(vmux->subdev.dev), i, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + continue; + + asd = kzalloc(sizeof(*asd), GFP_KERNEL); + if (!asd) { + fwnode_handle_put(ep); + return -ENOMEM; + } + + ret = v4l2_async_notifier_add_fwnode_remote_subdev( + &vmux->notifier, ep, asd); + + fwnode_handle_put(ep); + + if (ret) { + kfree(asd); + /* OK if asd already exists */ + if (ret != -EEXIST) + return ret; + } + } + + vmux->notifier.ops = &video_mux_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&vmux->subdev, + &vmux->notifier); + if (ret) + return ret; + + return v4l2_async_register_subdev(&vmux->subdev); } static int video_mux_probe(struct platform_device *pdev) @@ -434,7 +471,13 @@ static int video_mux_probe(struct platform_device *pdev) vmux->subdev.entity.ops = &video_mux_ops; - return video_mux_async_register(vmux, num_pads - 1); + ret = video_mux_async_register(vmux, num_pads - 1); + if (ret) { + v4l2_async_notifier_unregister(&vmux->notifier); + v4l2_async_notifier_cleanup(&vmux->notifier); + } + + return ret; } static int video_mux_remove(struct platform_device *pdev) @@ -442,6 +485,8 @@ static int video_mux_remove(struct platform_device *pdev) struct video_mux *vmux = platform_get_drvdata(pdev); struct v4l2_subdev *sd = &vmux->subdev; + v4l2_async_notifier_unregister(&vmux->notifier); + v4l2_async_notifier_cleanup(&vmux->notifier); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index a2773ad7c185..01c96fb66414 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -2,7 +2,9 @@ config VIDEO_XILINX tristate "Xilinx Video IP (EXPERIMENTAL)" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on VIDEO_V4L2 && OF && HAS_DMA + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index eb79d99787bd..d29e29645e04 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -36,7 +36,7 @@ config RADIO_SI476X 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/media/media_uapi.rst>. + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the module will be called radio-si476x. @@ -76,7 +76,7 @@ config RADIO_MAXIRADIO In order to control your radio card, you will need to use programs that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found at - <file:Documentation/media/media_uapi.rst>. + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the module will be called radio-maxiradio. @@ -94,7 +94,7 @@ config RADIO_SHARK In order to control your radio card, you will need to use programs that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found at - <file:Documentation/media/media_uapi.rst>. + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the module will be called radio-shark. @@ -111,7 +111,7 @@ config RADIO_SHARK2 In order to control your radio card, you will need to use programs that are compatible with the Video For Linux API. Information on this API and pointers to "v4l" programs may be found at - <file:Documentation/media/media_uapi.rst>. + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the module will be called radio-shark2. @@ -218,7 +218,7 @@ config RADIO_WL1273 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/media/media_uapi.rst>. + <file:Documentation/userspace-api/media/index.rst>. To compile this driver as a module, choose M here: the module will be called radio-wl1273. @@ -272,7 +272,7 @@ config RADIO_RTRACK been reported to be used by these cards. More information is contained in the file - <file:Documentation/media/v4l-drivers/radiotrack.rst>. + <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. diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig index a1ba8bc54b62..7161bd6cd13c 100644 --- a/drivers/media/radio/si470x/Kconfig +++ b/drivers/media/radio/si470x/Kconfig @@ -30,7 +30,7 @@ config USB_SI470X Please have a look at the documentation, especially on how to redirect the audio stream from the radio to your sound device: - Documentation/media/v4l-drivers/si470x.rst + Documentation/admin-guide/media/si470x.rst Say Y here if you want to connect this type of radio to your computer's USB port. diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig index 1dee7277004b..d5ae3388d3db 100644 --- a/drivers/media/radio/wl128x/Kconfig +++ b/drivers/media/radio/wl128x/Kconfig @@ -2,7 +2,6 @@ # # TI's wl128x FM driver based on TI's ST driver. # -menu "Texas Instruments WL128x FM driver (ST based)" config RADIO_WL128X tristate "Texas Instruments WL128x FM Radio" depends on VIDEO_V4L2 && RFKILL && TTY && TI_ST @@ -13,6 +12,4 @@ config RADIO_WL128X 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/media/media_uapi.rst>. - -endmenu + <file:Documentation/userspace-api/media/index.rst>. diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c index 18ca12d78314..f33b443bfa47 100644 --- a/drivers/media/rc/gpio-ir-tx.c +++ b/drivers/media/rc/gpio-ir-tx.c @@ -42,7 +42,7 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) { struct gpio_ir *gpio_ir = dev->priv; - if (!carrier) + if (carrier > 500000) return -EINVAL; gpio_ir->carrier = carrier; @@ -50,10 +50,35 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) return 0; } -static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, - unsigned int count) +static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf, + uint count) +{ + unsigned long flags; + ktime_t edge; + s32 delta; + int i; + + spin_lock_irqsave(&gpio_ir->lock, flags); + + edge = ktime_get(); + + for (i = 0; i < count; i++) { + 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); + } + + gpiod_set_value(gpio_ir->gpio, 0); + + spin_unlock_irqrestore(&gpio_ir->lock, flags); +} + +static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, + uint count) { - struct gpio_ir *gpio_ir = dev->priv; unsigned long flags; ktime_t edge; /* @@ -79,13 +104,8 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, // space edge = ktime_add_us(edge, txbuf[i]); delta = ktime_us_delta(edge, ktime_get()); - if (delta > 10) { - spin_unlock_irqrestore(&gpio_ir->lock, flags); - usleep_range(delta, delta + 10); - spin_lock_irqsave(&gpio_ir->lock, flags); - } else if (delta > 0) { + if (delta > 0) udelay(delta); - } } else { // pulse ktime_t last = ktime_add_us(edge, txbuf[i]); @@ -110,6 +130,17 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, } spin_unlock_irqrestore(&gpio_ir->lock, flags); +} + +static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, + unsigned int count) +{ + struct gpio_ir *gpio_ir = dev->priv; + + if (gpio_ir->carrier) + gpio_ir_tx_modulated(gpio_ir, txbuf, count); + else + gpio_ir_tx_unmodulated(gpio_ir, txbuf, count); return count; } diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 3c8bd13d029a..566c2816d5be 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -14,7 +14,6 @@ #include <linux/completion.h> #include <media/rc-core.h> -#define DRIVER_NAME "iguanair" #define BUF_SIZE 152 struct iguanair { @@ -27,8 +26,6 @@ struct iguanair { uint8_t bufsize; uint8_t cycle_overhead; - struct mutex lock; - /* receiver support */ bool receiver_on; dma_addr_t dma_in, dma_out; @@ -284,8 +281,6 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier) if (carrier < 25000 || carrier > 150000) return -EINVAL; - mutex_lock(&ir->lock); - if (carrier != ir->carrier) { uint32_t cycles, fours, sevens; @@ -314,8 +309,6 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier) ir->packet->busy4 = 110 - fours; } - mutex_unlock(&ir->lock); - return 0; } @@ -326,9 +319,7 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask) if (mask > 15) return 4; - mutex_lock(&ir->lock); ir->packet->channels = mask << 4; - mutex_unlock(&ir->lock); return 0; } @@ -339,8 +330,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count) unsigned int i, size, p, periods; int rc; - mutex_lock(&ir->lock); - /* convert from us to carrier periods */ for (i = size = 0; i < count; i++) { periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000); @@ -368,8 +357,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count) rc = -EOVERFLOW; out: - mutex_unlock(&ir->lock); - return rc ? rc : count; } @@ -378,14 +365,10 @@ static int iguanair_open(struct rc_dev *rdev) struct iguanair *ir = rdev->priv; int rc; - mutex_lock(&ir->lock); - rc = iguanair_receiver(ir, true); if (rc == 0) ir->receiver_on = true; - mutex_unlock(&ir->lock); - return rc; } @@ -394,14 +377,10 @@ static void iguanair_close(struct rc_dev *rdev) struct iguanair *ir = rdev->priv; int rc; - mutex_lock(&ir->lock); - rc = iguanair_receiver(ir, false); ir->receiver_on = false; if (rc && rc != -ENODEV) dev_warn(ir->dev, "failed to disable receiver: %d\n", rc); - - mutex_unlock(&ir->lock); } static int iguanair_probe(struct usb_interface *intf, @@ -441,7 +420,6 @@ static int iguanair_probe(struct usb_interface *intf, ir->rc = rc; ir->dev = &intf->dev; ir->udev = udev; - mutex_init(&ir->lock); init_completion(&ir->completion); pipeout = usb_sndintpipe(udev, @@ -483,7 +461,7 @@ static int iguanair_probe(struct usb_interface *intf, rc->s_tx_mask = iguanair_set_tx_mask; rc->s_tx_carrier = iguanair_set_tx_carrier; rc->tx_ir = iguanair_tx; - rc->driver_name = DRIVER_NAME; + rc->driver_name = KBUILD_MODNAME; rc->map_name = RC_MAP_RC6_MCE; rc->min_timeout = 1; rc->timeout = IR_DEFAULT_TIMEOUT; @@ -538,8 +516,6 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message) struct iguanair *ir = usb_get_intfdata(intf); int rc = 0; - mutex_lock(&ir->lock); - if (ir->receiver_on) { rc = iguanair_receiver(ir, false); if (rc) @@ -549,17 +525,13 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message) usb_kill_urb(ir->urb_in); usb_kill_urb(ir->urb_out); - mutex_unlock(&ir->lock); - return rc; } static int iguanair_resume(struct usb_interface *intf) { struct iguanair *ir = usb_get_intfdata(intf); - int rc = 0; - - mutex_lock(&ir->lock); + int rc; rc = usb_submit_urb(ir->urb_in, GFP_KERNEL); if (rc) @@ -571,8 +543,6 @@ static int iguanair_resume(struct usb_interface *intf) dev_warn(ir->dev, "failed to enable receiver after resume\n"); } - mutex_unlock(&ir->lock); - return rc; } @@ -582,7 +552,7 @@ static const struct usb_device_id iguanair_table[] = { }; static struct usb_driver iguanair_driver = { - .name = DRIVER_NAME, + .name = KBUILD_MODNAME, .probe = iguanair_probe, .disconnect = iguanair_disconnect, .suspend = iguanair_suspend, diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 8574eda45102..a0d9c02a7588 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -241,7 +241,7 @@ static int ir_rx51_probe(struct platform_device *dev) } /* Use default, in case userspace does not set the carrier */ - ir_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); + ir_rx51.freq = DIV_ROUND_CLOSEST_ULL(pwm_get_period(pwm), NSEC_PER_SEC); pwm_put(pwm); hrtimer_init(&ir_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 5f36244cc34f..1eeab277a08e 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -64,6 +64,7 @@ struct ir_raw_event_ctrl { u32 bpf_sample; struct bpf_prog_array __rcu *progs; #endif +#if IS_ENABLED(CONFIG_IR_NEC_DECODER) struct nec_dec { int state; unsigned count; @@ -71,12 +72,16 @@ struct ir_raw_event_ctrl { bool is_nec_x; bool necx_repeat; } nec; +#endif +#if IS_ENABLED(CONFIG_IR_RC5_DECODER) struct rc5_dec { int state; u32 bits; unsigned count; bool is_rc5x; } rc5; +#endif +#if IS_ENABLED(CONFIG_IR_RC6_DECODER) struct rc6_dec { int state; u8 header; @@ -85,11 +90,15 @@ struct ir_raw_event_ctrl { unsigned count; unsigned wanted_bits; } rc6; +#endif +#if IS_ENABLED(CONFIG_IR_SONY_DECODER) struct sony_dec { int state; u32 bits; unsigned count; } sony; +#endif +#if IS_ENABLED(CONFIG_IR_JVC_DECODER) struct jvc_dec { int state; u16 bits; @@ -98,17 +107,23 @@ struct ir_raw_event_ctrl { bool first; bool toggle; } jvc; +#endif +#if IS_ENABLED(CONFIG_IR_SANYO_DECODER) struct sanyo_dec { int state; unsigned count; u64 bits; } sanyo; +#endif +#if IS_ENABLED(CONFIG_IR_SHARP_DECODER) struct sharp_dec { int state; unsigned count; u32 bits; unsigned int pulse_len; } sharp; +#endif +#if IS_ENABLED(CONFIG_IR_MCE_KBD_DECODER) struct mce_kbd_dec { /* locks key up timer */ spinlock_t keylock; @@ -119,11 +134,15 @@ struct ir_raw_event_ctrl { unsigned count; unsigned wanted_bits; } mce_kbd; +#endif +#if IS_ENABLED(CONFIG_IR_XMP_DECODER) struct xmp_dec { int state; unsigned count; u32 durations[16]; } xmp; +#endif +#if IS_ENABLED(CONFIG_IR_IMON_DECODER) struct imon_dec { int state; int count; @@ -131,11 +150,14 @@ struct ir_raw_event_ctrl { unsigned int bits; bool stick_keyboard; } imon; +#endif +#if IS_ENABLED(CONFIG_IR_RCMM_DECODER) struct rcmm_dec { int state; unsigned int count; u32 bits; } rcmm; +#endif }; /* Mutex for locking raw IR processing and handler change */ diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index bcc49cb47de6..857ef4ace6e9 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -1,15 +1,17 @@ # SPDX-License-Identifier: GPL-2.0-only if VIDEO_V4L2 -comment "SPI drivers hidden by 'Autoselect ancillary drivers'" - depends on MEDIA_HIDE_ANCILLARY_SUBDRV +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 && VIDEO_V4L2_SUBDEV_API + depends on SPI && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help Enable the GS1662 driver which serializes video streams. diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig new file mode 100644 index 000000000000..188381c85593 --- /dev/null +++ b/drivers/media/test-drivers/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig V4L_TEST_DRIVERS + bool "V4L test drivers" + depends on VIDEO_DEV + +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 + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API + help + This is a virtual test device for the memory-to-memory driver + framework. + +source "drivers/media/test-drivers/vicodec/Kconfig" + +endif #V4L_TEST_DRIVERS diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile new file mode 100644 index 000000000000..74410d3a9f2d --- /dev/null +++ b/drivers/media/test-drivers/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# 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/ diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/test-drivers/vicodec/Kconfig index 89456665cb16..d77c67810c73 100644 --- a/drivers/media/platform/vicodec/Kconfig +++ b/drivers/media/test-drivers/vicodec/Kconfig @@ -4,6 +4,8 @@ config VIDEO_VICODEC depends on VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API help Driver for a Virtual Codec diff --git a/drivers/media/platform/vicodec/Makefile b/drivers/media/test-drivers/vicodec/Makefile index 01bf7e9308a6..01bf7e9308a6 100644 --- a/drivers/media/platform/vicodec/Makefile +++ b/drivers/media/test-drivers/vicodec/Makefile diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/test-drivers/vicodec/codec-fwht.c index 31faf319e508..31faf319e508 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/test-drivers/vicodec/codec-fwht.c diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/test-drivers/vicodec/codec-fwht.h index b6fec2b1cbca..b6fec2b1cbca 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/test-drivers/vicodec/codec-fwht.h diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c index b6e39fbd8ad5..b6e39fbd8ad5 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.h index 1a0d2a9f931a..1a0d2a9f931a 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.h diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 30ced1c21387..e879290727ef 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -2114,16 +2114,19 @@ static int vicodec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); - if (register_instance(dev, &dev->stateful_enc, - "stateful-encoder", true)) + ret = register_instance(dev, &dev->stateful_enc, "stateful-encoder", + true); + if (ret) goto unreg_dev; - if (register_instance(dev, &dev->stateful_dec, - "stateful-decoder", false)) + ret = register_instance(dev, &dev->stateful_dec, "stateful-decoder", + false); + if (ret) goto unreg_sf_enc; - if (register_instance(dev, &dev->stateless_dec, - "stateless-decoder", false)) + ret = register_instance(dev, &dev->stateless_dec, "stateless-decoder", + false); + if (ret) goto unreg_sf_dec; #ifdef CONFIG_MEDIA_CONTROLLER diff --git a/drivers/media/platform/vim2m.c b/drivers/media/test-drivers/vim2m.c index ac6717fbb764..a776bb8e0e09 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -216,7 +216,6 @@ struct vim2m_ctx { struct mutex vb_mutex; struct delayed_work work_run; - spinlock_t irqlock; /* Abort requested by m2m */ int aborting; @@ -622,7 +621,6 @@ static void device_work(struct work_struct *w) struct vim2m_ctx *curr_ctx; struct vim2m_dev *vim2m_dev; struct vb2_v4l2_buffer *src_vb, *dst_vb; - unsigned long flags; curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); @@ -638,10 +636,8 @@ static void device_work(struct work_struct *w) curr_ctx->num_processed++; - spin_lock_irqsave(&curr_ctx->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); - spin_unlock_irqrestore(&curr_ctx->irqlock, flags); if (curr_ctx->num_processed == curr_ctx->translen || curr_ctx->aborting) { @@ -1084,7 +1080,6 @@ static void vim2m_stop_streaming(struct vb2_queue *q) { struct vim2m_ctx *ctx = vb2_get_drv_priv(q); struct vb2_v4l2_buffer *vbuf; - unsigned long flags; cancel_delayed_work_sync(&ctx->work_run); @@ -1097,9 +1092,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q) return; v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl); - spin_lock_irqsave(&ctx->irqlock, flags); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - spin_unlock_irqrestore(&ctx->irqlock, flags); } } @@ -1226,7 +1219,6 @@ static int vim2m_open(struct file *file) ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); mutex_init(&ctx->vb_mutex); - spin_lock_init(&ctx->irqlock); INIT_DELAYED_WORK(&ctx->work_run, device_work); if (IS_ERR(ctx->fh.m2m_ctx)) { diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/test-drivers/vimc/Kconfig index bd221d3e1a4a..4068a67585f9 100644 --- a/drivers/media/platform/vimc/Kconfig +++ b/drivers/media/test-drivers/vimc/Kconfig @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_VIMC tristate "Virtual Media Controller Driver (VIMC)" - depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_DEV && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC select VIDEO_V4L2_TPG help diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/test-drivers/vimc/Makefile index a53b2b532e9f..a53b2b532e9f 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/test-drivers/vimc/Makefile diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index 23e740c1c5c0..c63496b17b9a 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -37,7 +37,7 @@ static const struct v4l2_pix_format fmt_default = { .height = 480, .pixelformat = V4L2_PIX_FMT_RGB24, .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, + .colorspace = V4L2_COLORSPACE_SRGB, }; struct vimc_cap_buffer { @@ -107,6 +107,9 @@ static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv, vimc_colorimetry_clamp(format); + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = fmt_default.colorspace; + return 0; } @@ -146,7 +149,16 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(f->index); + const struct vimc_pix_map *vpix; + + if (f->mbus_code) { + if (f->index > 0) + return -EINVAL; + + vpix = vimc_pix_map_by_code(f->mbus_code); + } else { + vpix = vimc_pix_map_by_index(f->index); + } if (!vpix) return -EINVAL; @@ -325,7 +337,7 @@ static const struct media_entity_operations vimc_cap_mops = { .link_validate = vimc_vdev_link_validate, }; -void vimc_cap_release(struct vimc_ent_device *ved) +static void vimc_cap_release(struct vimc_ent_device *ved) { struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); @@ -334,7 +346,7 @@ void vimc_cap_release(struct vimc_ent_device *ved) kfree(vcap); } -void vimc_cap_unregister(struct vimc_ent_device *ved) +static void vimc_cap_unregister(struct vimc_ent_device *ved) { struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); @@ -382,8 +394,8 @@ static void *vimc_cap_process_frame(struct vimc_ent_device *ved, return NULL; } -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; const struct vimc_pix_map *vpix; @@ -395,7 +407,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, /* Allocate the vimc_cap_device struct */ vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); if (!vcap) - return NULL; + return ERR_PTR(-ENOMEM); /* Initialize the media entity */ vcap->vdev.entity.name = vcfg_name; @@ -447,7 +459,8 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, /* Initialize the video_device struct */ vdev = &vcap->vdev; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING + | V4L2_CAP_IO_MC; vdev->entity.ops = &vimc_cap_mops; vdev->release = video_device_release_empty; vdev->fops = &vimc_cap_fops; @@ -476,5 +489,11 @@ err_clean_m_ent: err_free_vcap: kfree(vcap); - return NULL; + return ERR_PTR(ret); } + +struct vimc_ent_type vimc_cap_type = { + .add = vimc_cap_add, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release +}; diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c index c95c17c048f2..7b27153c0728 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/test-drivers/vimc/vimc-common.c @@ -19,19 +19,31 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { /* RGB formats */ { - .code = MEDIA_BUS_FMT_BGR888_1X24, + .code = { + MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_BGR888_3X8 + }, .pixelformat = V4L2_PIX_FMT_BGR24, .bpp = 3, .bayer = false, }, { - .code = MEDIA_BUS_FMT_RGB888_1X24, + .code = { + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_RGB888_2X12_BE, + MEDIA_BUS_FMT_RGB888_2X12_LE, + MEDIA_BUS_FMT_RGB888_3X8, + MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, + MEDIA_BUS_FMT_RGB888_1X32_PADHI, + MEDIA_BUS_FMT_GBR888_1X24 + }, .pixelformat = V4L2_PIX_FMT_RGB24, .bpp = 3, .bayer = false, }, { - .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .code = { MEDIA_BUS_FMT_ARGB8888_1X32 }, .pixelformat = V4L2_PIX_FMT_ARGB32, .bpp = 4, .bayer = false, @@ -39,49 +51,49 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { /* Bayer formats */ { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .code = { MEDIA_BUS_FMT_SBGGR8_1X8 }, .pixelformat = V4L2_PIX_FMT_SBGGR8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .code = { MEDIA_BUS_FMT_SGBRG8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGBRG8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .code = { MEDIA_BUS_FMT_SGRBG8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGRBG8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .code = { MEDIA_BUS_FMT_SRGGB8_1X8 }, .pixelformat = V4L2_PIX_FMT_SRGGB8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .code = { MEDIA_BUS_FMT_SBGGR10_1X10 }, .pixelformat = V4L2_PIX_FMT_SBGGR10, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .code = { MEDIA_BUS_FMT_SGBRG10_1X10 }, .pixelformat = V4L2_PIX_FMT_SGBRG10, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .code = { MEDIA_BUS_FMT_SGRBG10_1X10 }, .pixelformat = V4L2_PIX_FMT_SGRBG10, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .code = { MEDIA_BUS_FMT_SRGGB10_1X10 }, .pixelformat = V4L2_PIX_FMT_SRGGB10, .bpp = 2, .bayer = true, @@ -89,25 +101,25 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { /* 10bit raw bayer a-law compressed to 8 bits */ { - .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, + .code = { MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8 }, .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, + .code = { MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, + .code = { MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, + .code = { MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8 }, .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8, .bpp = 1, .bayer = true, @@ -115,49 +127,49 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { /* 10bit raw bayer DPCM compressed to 8 bits */ { - .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, + .code = { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 }, .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, + .code = { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .code = { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 }, .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, + .code = { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 }, .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8, .bpp = 1, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .code = { MEDIA_BUS_FMT_SBGGR12_1X12 }, .pixelformat = V4L2_PIX_FMT_SBGGR12, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .code = { MEDIA_BUS_FMT_SGBRG12_1X12 }, .pixelformat = V4L2_PIX_FMT_SGBRG12, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .code = { MEDIA_BUS_FMT_SGRBG12_1X12 }, .pixelformat = V4L2_PIX_FMT_SGRBG12, .bpp = 2, .bayer = true, }, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .code = { MEDIA_BUS_FMT_SRGGB12_1X12 }, .pixelformat = V4L2_PIX_FMT_SRGGB12, .bpp = 2, .bayer = true, @@ -182,13 +194,32 @@ const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) return &vimc_pix_map_list[i]; } +u32 vimc_mbus_code_by_index(unsigned int index) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { + for (j = 0; j < ARRAY_SIZE(vimc_pix_map_list[i].code); j++) { + if (!vimc_pix_map_list[i].code[j]) + break; + + if (!index) + return vimc_pix_map_list[i].code[j]; + index--; + } + } + return 0; +} + const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) { - unsigned int i; + unsigned int i, j; for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { - if (vimc_pix_map_list[i].code == code) - return &vimc_pix_map_list[i]; + for (j = 0; j < ARRAY_SIZE(vimc_pix_map_list[i].code); j++) { + if (vimc_pix_map_list[i].code[j] == code) + return &vimc_pix_map_list[i]; + } } return NULL; } diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 616d5a6b0754..ae163dec2459 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -32,8 +32,10 @@ #define VIMC_IS_SRC(pad) (pad) #define VIMC_IS_SINK(pad) (!(pad)) +#define VIMC_PIX_FMT_MAX_CODES 8 + /** - * struct vimc_colorimetry_clamp - Adjust colorimetry parameters + * vimc_colorimetry_clamp - Adjust colorimetry parameters * * @fmt: the pointer to struct v4l2_pix_format or * struct v4l2_mbus_framefmt @@ -62,14 +64,15 @@ do { \ * struct vimc_pix_map - maps media bus code with v4l2 pixel format * * @code: media bus format code defined by MEDIA_BUS_FMT_* macros - * @bbp: number of bytes each pixel occupies - * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros + * @bpp: number of bytes each pixel occupies + * @pixelformat: pixel format defined by V4L2_PIX_FMT_* macros + * @bayer: true if this is a bayer format * * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp) */ struct vimc_pix_map { - unsigned int code; + unsigned int code[VIMC_PIX_FMT_MAX_CODES]; unsigned int bpp; u32 pixelformat; bool bayer; @@ -90,7 +93,7 @@ struct vimc_pix_map { * the node it will be of an instance of v4l2_subdev or video_device struct * where both contains a struct media_entity. * Those structures should embedded the vimc_ent_device struct through - * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the + * v4l2_set_subdevdata() and video_set_drvdata() respectively, allowing the * vimc_ent_device struct to be retrieved from the corresponding struct * media_entity */ @@ -106,10 +109,10 @@ struct vimc_ent_device { /** * struct vimc_device - main device for vimc driver * - * @pipe_cfg pointer to the vimc pipeline configuration structure - * @ent_devs array of vimc_ent_device pointers - * @mdev the associated media_device parent - * @v4l2_dev Internal v4l2 parent device + * @pipe_cfg: pointer to the vimc pipeline configuration structure + * @ent_devs: array of vimc_ent_device pointers + * @mdev: the associated media_device parent + * @v4l2_dev: Internal v4l2 parent device */ struct vimc_device { const struct vimc_pipeline_config *pipe_cfg; @@ -119,20 +122,16 @@ struct vimc_device { }; /** - * struct vimc_ent_config Structure which describes individual - * configuration for each entity + * struct vimc_ent_type Structure for the callbacks of the entity types * - * @name entity name - * @ved pointer to vimc_ent_device (a node in the - * topology) - * @add initializes and registers - * vim entity - called from vimc-core - * @unregister unregisters vimc entity - called from vimc-core - * @release releases vimc entity - called from the v4l2_dev - * release callback + * + * @add: initializes and registers + * vimc entity - called from vimc-core + * @unregister: unregisters vimc entity - called from vimc-core + * @release: releases vimc entity - called from the v4l2_dev + * release callback */ -struct vimc_ent_config { - const char *name; +struct vimc_ent_type { struct vimc_ent_device *(*add)(struct vimc_device *vimc, const char *vcfg_name); void (*unregister)(struct vimc_ent_device *ved); @@ -140,6 +139,19 @@ struct vimc_ent_config { }; /** + * struct vimc_ent_config Structure which describes individual + * configuration for each entity + * + * @name: entity name + * @type: contain the callbacks of this entity type + * + */ +struct vimc_ent_config { + const char *name; + struct vimc_ent_type *type; +}; + +/** * vimc_is_source - returns true if the entity has only source pads * * @ent: pointer to &struct media_entity @@ -147,23 +159,10 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -/* prototypes for vimc_ent_config hooks */ -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_cap_unregister(struct vimc_ent_device *ved); -void vimc_cap_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_deb_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sca_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sen_release(struct vimc_ent_device *ved); +extern struct vimc_ent_type vimc_sen_type; +extern struct vimc_ent_type vimc_deb_type; +extern struct vimc_ent_type vimc_sca_type; +extern struct vimc_ent_type vimc_cap_type; /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index @@ -173,6 +172,15 @@ void vimc_sen_release(struct vimc_ent_device *ved); const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i); /** + * vimc_mbus_code_by_index - get mbus code by its index + * + * @index: index of the mbus code in vimc_pix_map_list + * + * Returns 0 if no mbus code is found for the given index. + */ +u32 vimc_mbus_code_by_index(unsigned int index); + +/** * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code * * @code: media bus format code defined by MEDIA_BUS_FMT_* macros @@ -182,7 +190,7 @@ const struct vimc_pix_map *vimc_pix_map_by_code(u32 code); /** * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format * - * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros + * @pixelformat: pixel format defined by V4L2_PIX_FMT_* macros */ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); @@ -197,7 +205,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @function: media entity function defined by MEDIA_ENT_F_* macros * @num_pads: number of pads to initialize * @pads: the array of pads of the entity, the caller should set the - flags of the pads + * flags of the pads * @sd_ops: pointer to &struct v4l2_subdev_ops. * * Helper function initialize and register the struct vimc_ent_device and struct diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 339126e565dc..11210aaa2551 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -47,52 +47,40 @@ struct vimc_pipeline_config { static struct vimc_ent_config ent_config[] = { { .name = "Sensor A", - .add = vimc_sen_add, - .release = vimc_sen_release, + .type = &vimc_sen_type }, { .name = "Sensor B", - .add = vimc_sen_add, - .release = vimc_sen_release, + .type = &vimc_sen_type }, { .name = "Debayer A", - .add = vimc_deb_add, - .release = vimc_deb_release, + .type = &vimc_deb_type }, { .name = "Debayer B", - .add = vimc_deb_add, - .release = vimc_deb_release, + .type = &vimc_deb_type }, { .name = "Raw Capture 0", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, + .type = &vimc_cap_type }, { .name = "Raw Capture 1", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, + .type = &vimc_cap_type }, { /* TODO: change this to vimc-input when it is implemented */ .name = "RGB/YUV Input", - .add = vimc_sen_add, - .release = vimc_sen_release, + .type = &vimc_sen_type }, { .name = "Scaler", - .add = vimc_sca_add, - .release = vimc_sca_release, + .type = &vimc_sca_type }, { .name = "RGB/YUV Capture", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, + .type = &vimc_cap_type }, }; @@ -160,40 +148,45 @@ err_rm_links: return ret; } -static int vimc_add_subdevs(struct vimc_device *vimc) +static void vimc_release_subdevs(struct vimc_device *vimc) { unsigned int i; - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - dev_dbg(vimc->mdev.dev, "new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, - vimc->pipe_cfg->ents[i].name); - if (!vimc->ent_devs[i]) { - dev_err(vimc->mdev.dev, "add new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - return -EINVAL; - } - } - return 0; + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + if (vimc->ent_devs[i]) + vimc->pipe_cfg->ents[i].type->release(vimc->ent_devs[i]); } -static void vimc_release_subdevs(struct vimc_device *vimc) +static void vimc_unregister_subdevs(struct vimc_device *vimc) { unsigned int i; for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i]) - vimc->pipe_cfg->ents[i].release(vimc->ent_devs[i]); + if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].type->unregister) + vimc->pipe_cfg->ents[i].type->unregister(vimc->ent_devs[i]); } -static void vimc_unregister_subdevs(struct vimc_device *vimc) +static int vimc_add_subdevs(struct vimc_device *vimc) { unsigned int i; - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].unregister) - vimc->pipe_cfg->ents[i].unregister(vimc->ent_devs[i]); + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { + dev_dbg(vimc->mdev.dev, "new entity for %s\n", + vimc->pipe_cfg->ents[i].name); + vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].type->add(vimc, + vimc->pipe_cfg->ents[i].name); + if (IS_ERR(vimc->ent_devs[i])) { + int err = PTR_ERR(vimc->ent_devs[i]); + + dev_err(vimc->mdev.dev, "adding entity %s failed (%d)\n", + vimc->pipe_cfg->ents[i].name, err); + vimc->ent_devs[i] = NULL; + vimc_unregister_subdevs(vimc); + vimc_release_subdevs(vimc); + return err; + } + } + return 0; } static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) @@ -229,8 +222,7 @@ static int vimc_register_devices(struct vimc_device *vimc) /* Invoke entity config hooks to initialize and register subdevs */ ret = vimc_add_subdevs(vimc); if (ret) - /* remove sundevs that got added */ - goto err_rm_subdevs; + goto err_free_ent_devs; /* Initialize links */ ret = vimc_create_links(vimc); @@ -261,6 +253,7 @@ err_mdev_unregister: err_rm_subdevs: vimc_unregister_subdevs(vimc); vimc_release_subdevs(vimc); +err_free_ent_devs: kfree(vimc->ent_devs); err_v4l2_unregister: v4l2_device_unregister(&vimc->v4l2_dev); @@ -268,13 +261,6 @@ err_v4l2_unregister: return ret; } -static void vimc_unregister(struct vimc_device *vimc) -{ - vimc_unregister_subdevs(vimc); - media_device_unregister(&vimc->mdev); - v4l2_device_unregister(&vimc->v4l2_dev); -} - static int vimc_probe(struct platform_device *pdev) { struct vimc_device *vimc; @@ -321,7 +307,9 @@ static int vimc_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "remove"); - vimc_unregister(vimc); + vimc_unregister_subdevs(vimc); + media_device_unregister(&vimc->mdev); + v4l2_device_unregister(&vimc->v4l2_dev); v4l2_device_put(&vimc->v4l2_dev); return 0; diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index baf6bf9f65b5..c3f6fef34f68 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -48,7 +48,20 @@ static const struct v4l2_mbus_framefmt sink_fmt_default = { .height = 480, .code = MEDIA_BUS_FMT_SRGGB8_1X8, .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static const u32 vimc_deb_src_mbus_codes[] = { + MEDIA_BUS_FMT_GBR888_1X24, + MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_BGR888_3X8, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_RGB888_2X12_BE, + MEDIA_BUS_FMT_RGB888_2X12_LE, + MEDIA_BUS_FMT_RGB888_3X8, + MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, + MEDIA_BUS_FMT_RGB888_1X32_PADHI, }; static const struct vimc_deb_pix_map vimc_deb_pix_map_list[] = { @@ -125,6 +138,17 @@ static const struct vimc_deb_pix_map *vimc_deb_pix_map_by_code(u32 code) return NULL; } +static bool vimc_deb_src_code_is_valid(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vimc_deb_src_mbus_codes); i++) + if (vimc_deb_src_mbus_codes[i] == code) + return true; + + return false; +} + static int vimc_deb_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { @@ -148,14 +172,11 @@ static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - /* We only support one format for source pads */ if (VIMC_IS_SRC(code->pad)) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - - if (code->index) + if (code->index >= ARRAY_SIZE(vimc_deb_src_mbus_codes)) return -EINVAL; - code->code = vdeb->src_code; + code->code = vimc_deb_src_mbus_codes[code->index]; } else { if (code->index >= ARRAY_SIZE(vimc_deb_pix_map_list)) return -EINVAL; @@ -170,8 +191,6 @@ static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - if (fse->index) return -EINVAL; @@ -181,7 +200,7 @@ static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, if (!vpix) return -EINVAL; - } else if (fse->code != vdeb->src_code) { + } else if (!vimc_deb_src_code_is_valid(fse->code)) { return -EINVAL; } @@ -237,6 +256,7 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; + u32 *src_code; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ @@ -244,8 +264,10 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, return -EBUSY; sink_fmt = &vdeb->sink_fmt; + src_code = &vdeb->src_code; } else { sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + src_code = &v4l2_subdev_get_try_format(sd, cfg, 1)->code; } /* @@ -253,9 +275,14 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, * it is propagated from the sink */ if (VIMC_IS_SRC(fmt->pad)) { + u32 code = fmt->format.code; + fmt->format = *sink_fmt; - /* TODO: Add support for other formats */ - fmt->format.code = vdeb->src_code; + + if (vimc_deb_src_code_is_valid(code)) + *src_code = code; + + fmt->format.code = *src_code; } else { /* Set the new format in the sink pad */ vimc_deb_adjust_sink_fmt(&fmt->format); @@ -286,16 +313,26 @@ static const struct v4l2_subdev_pad_ops vimc_deb_pad_ops = { .set_fmt = vimc_deb_set_fmt, }; -static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb, - unsigned int lin, - unsigned int col, - unsigned int rgb[3]) +static void vimc_deb_process_rgb_frame(struct vimc_deb_device *vdeb, + unsigned int lin, + unsigned int col, + unsigned int rgb[3]) { + const struct vimc_pix_map *vpix; unsigned int i, index; + vpix = vimc_pix_map_by_code(vdeb->src_code); index = VIMC_FRAME_INDEX(lin, col, vdeb->sink_fmt.width, 3); - for (i = 0; i < 3; i++) - vdeb->src_frame[index + i] = rgb[i]; + for (i = 0; i < 3; i++) { + switch (vpix->pixelformat) { + case V4L2_PIX_FMT_RGB24: + vdeb->src_frame[index + i] = rgb[i]; + break; + case V4L2_PIX_FMT_BGR24: + vdeb->src_frame[index + i] = rgb[2 - i]; + break; + } + } } static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) @@ -494,7 +531,7 @@ static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { .s_ctrl = vimc_deb_s_ctrl, }; -void vimc_deb_release(struct vimc_ent_device *ved) +static void vimc_deb_release(struct vimc_ent_device *ved) { struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, ved); @@ -522,8 +559,8 @@ static const struct v4l2_ctrl_config vimc_deb_ctrl_mean_win_size = { .def = 3, }; -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_deb_device *vdeb; @@ -532,7 +569,7 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, /* Allocate the vdeb struct */ vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); if (!vdeb) - return NULL; + return ERR_PTR(-ENOMEM); /* Create controls: */ v4l2_ctrl_handler_init(&vdeb->hdl, 2); @@ -568,7 +605,7 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, * for the code */ vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24; - vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24; + vdeb->set_rgb_src = vimc_deb_process_rgb_frame; return &vdeb->ved; @@ -577,5 +614,10 @@ err_free_hdl: err_free_vdeb: kfree(vdeb); - return NULL; + return ERR_PTR(ret); } + +struct vimc_ent_type vimc_deb_type = { + .add = vimc_deb_add, + .release = vimc_deb_release +}; diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index 7521439747c5..121fa7d62a2e 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -42,7 +42,7 @@ static const struct v4l2_mbus_framefmt sink_fmt_default = { .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, .code = MEDIA_BUS_FMT_RGB888_1X24, .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, + .colorspace = V4L2_COLORSPACE_SRGB, }; static const struct v4l2_rect crop_rect_default = { @@ -110,13 +110,19 @@ static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); + u32 mbus_code = vimc_mbus_code_by_index(code->index); + const struct vimc_pix_map *vpix; + + if (!mbus_code) + return -EINVAL; + + vpix = vimc_pix_map_by_code(mbus_code); /* We don't support bayer format */ if (!vpix || vpix->bayer) return -EINVAL; - code->code = vpix->code; + code->code = mbus_code; return 0; } @@ -464,7 +470,7 @@ static void *vimc_sca_process_frame(struct vimc_ent_device *ved, return vsca->src_frame; }; -void vimc_sca_release(struct vimc_ent_device *ved) +static void vimc_sca_release(struct vimc_ent_device *ved) { struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, ved); @@ -473,8 +479,8 @@ void vimc_sca_release(struct vimc_ent_device *ved) kfree(vsca); } -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_sca_device *vsca; @@ -483,7 +489,7 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, /* Allocate the vsca struct */ vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); if (!vsca) - return NULL; + return ERR_PTR(-ENOMEM); /* Initialize ved and sd */ vsca->pads[0].flags = MEDIA_PAD_FL_SINK; @@ -495,7 +501,7 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, vsca->pads, &vimc_sca_ops); if (ret) { kfree(vsca); - return NULL; + return ERR_PTR(ret); } vsca->ved.process_frame = vimc_sca_process_frame; @@ -509,3 +515,8 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, return &vsca->ved; } + +struct vimc_ent_type vimc_sca_type = { + .add = vimc_sca_add, + .release = vimc_sca_release +}; diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 92daee58209e..a2f09ac9a360 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -30,7 +30,7 @@ static const struct v4l2_mbus_framefmt fmt_default = { .height = 480, .code = MEDIA_BUS_FMT_RGB888_1X24, .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, + .colorspace = V4L2_COLORSPACE_SRGB, }; static int vimc_sen_init_cfg(struct v4l2_subdev *sd, @@ -52,12 +52,12 @@ static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); + u32 mbus_code = vimc_mbus_code_by_index(code->index); - if (!vpix) + if (!mbus_code) return -EINVAL; - code->code = vpix->code; + code->code = mbus_code; return 0; } @@ -279,7 +279,7 @@ static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = { .s_ctrl = vimc_sen_s_ctrl, }; -void vimc_sen_release(struct vimc_ent_device *ved) +static void vimc_sen_release(struct vimc_ent_device *ved) { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); @@ -307,8 +307,8 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { .qmenu = tpg_pattern_strings, }; -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_sen_device *vsen; @@ -317,7 +317,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, /* Allocate the vsen struct */ vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); if (!vsen) - return NULL; + return ERR_PTR(-ENOMEM); v4l2_ctrl_handler_init(&vsen->hdl, 4); @@ -372,5 +372,10 @@ err_free_hdl: err_free_vsen: kfree(vsen); - return NULL; + return ERR_PTR(ret); } + +struct vimc_ent_type vimc_sen_type = { + .add = vimc_sen_add, + .release = vimc_sen_release +}; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c index 65feb3c596db..65feb3c596db 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/test-drivers/vimc/vimc-streamer.c diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/test-drivers/vimc/vimc-streamer.h index fe3c51f15fad..3bb6731b8d4d 100644 --- a/drivers/media/platform/vimc/vimc-streamer.h +++ b/drivers/media/test-drivers/vimc/vimc-streamer.h @@ -20,9 +20,10 @@ * * @pipe: the media pipeline object associated with this stream * @ved_pipeline: array containing all the entities participating in the - * stream. The order is from a video device (usually a capture device) where - * stream_on was called, to the entity generating the first base image to be - * processed in the pipeline. + * stream. The order is from a video device (usually a + * capture device) where stream_on was called, to the + * entity generating the first base image to be + * processed in the pipeline. * @pipe_size: size of @ved_pipeline * @kthread: thread that generates the frames of the stream. * diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/test-drivers/vivid/Kconfig index e2ff06edfa93..c3abde2986b2 100644 --- a/drivers/media/platform/vivid/Kconfig +++ b/drivers/media/test-drivers/vivid/Kconfig @@ -11,6 +11,8 @@ config VIDEO_VIVID select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEO_V4L2_TPG + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API help Enables a virtual video driver. This driver emulates a webcam, TV, S-Video and HDMI capture hardware, including VBI support for diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/test-drivers/vivid/Makefile index b12ad0152a3e..b12ad0152a3e 100644 --- a/drivers/media/platform/vivid/Makefile +++ b/drivers/media/test-drivers/vivid/Makefile diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 4d2413e87730..4d2413e87730 100644 --- a/drivers/media/platform/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/test-drivers/vivid/vivid-cec.h index 7524ed48a914..7524ed48a914 100644 --- a/drivers/media/platform/vivid/vivid-cec.h +++ b/drivers/media/test-drivers/vivid/vivid-cec.h diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 6c740e3e6999..6c740e3e6999 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index 99e69b8f770f..99e69b8f770f 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 334130568dcb..334130568dcb 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/test-drivers/vivid/vivid-ctrls.h index 6fad5f5d0054..6fad5f5d0054 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.h +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.h diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 01a9d671b947..01a9d671b947 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/test-drivers/vivid/vivid-kthread-cap.h index 0f43015306d6..0f43015306d6 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.h diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/test-drivers/vivid/vivid-kthread-out.c index 6780687978f9..6780687978f9 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-out.c diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/test-drivers/vivid/vivid-kthread-out.h index d5bcf44bbaca..d5bcf44bbaca 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.h +++ b/drivers/media/test-drivers/vivid/vivid-kthread-out.h diff --git a/drivers/media/platform/vivid/vivid-kthread-touch.c b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c index 674507b5ccb5..674507b5ccb5 100644 --- a/drivers/media/platform/vivid/vivid-kthread-touch.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c diff --git a/drivers/media/platform/vivid/vivid-kthread-touch.h b/drivers/media/test-drivers/vivid/vivid-kthread-touch.h index ecf79b46209e..ecf79b46209e 100644 --- a/drivers/media/platform/vivid/vivid-kthread-touch.h +++ b/drivers/media/test-drivers/vivid/vivid-kthread-touch.h diff --git a/drivers/media/platform/vivid/vivid-meta-cap.c b/drivers/media/test-drivers/vivid/vivid-meta-cap.c index 780f96860a6d..780f96860a6d 100644 --- a/drivers/media/platform/vivid/vivid-meta-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-cap.c diff --git a/drivers/media/platform/vivid/vivid-meta-cap.h b/drivers/media/test-drivers/vivid/vivid-meta-cap.h index 4670d00d1576..4670d00d1576 100644 --- a/drivers/media/platform/vivid/vivid-meta-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-meta-cap.h diff --git a/drivers/media/platform/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c index ff8a039aba72..ff8a039aba72 100644 --- a/drivers/media/platform/vivid/vivid-meta-out.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c diff --git a/drivers/media/platform/vivid/vivid-meta-out.h b/drivers/media/test-drivers/vivid/vivid-meta-out.h index 0c639b7c2842..0c639b7c2842 100644 --- a/drivers/media/platform/vivid/vivid-meta-out.h +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.h diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/test-drivers/vivid/vivid-osd.c index fbaec8acc161..fbaec8acc161 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/test-drivers/vivid/vivid-osd.c diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/test-drivers/vivid/vivid-osd.h index f9ac1af25dd3..f9ac1af25dd3 100644 --- a/drivers/media/platform/vivid/vivid-osd.h +++ b/drivers/media/test-drivers/vivid/vivid-osd.h diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/test-drivers/vivid/vivid-radio-common.c index 138c7bce68b1..138c7bce68b1 100644 --- a/drivers/media/platform/vivid/vivid-radio-common.c +++ b/drivers/media/test-drivers/vivid/vivid-radio-common.c diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/test-drivers/vivid/vivid-radio-common.h index 30a9900e5b2b..30a9900e5b2b 100644 --- a/drivers/media/platform/vivid/vivid-radio-common.h +++ b/drivers/media/test-drivers/vivid/vivid-radio-common.h diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/test-drivers/vivid/vivid-radio-rx.c index 232cab508f48..232cab508f48 100644 --- a/drivers/media/platform/vivid/vivid-radio-rx.c +++ b/drivers/media/test-drivers/vivid/vivid-radio-rx.c diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/test-drivers/vivid/vivid-radio-rx.h index c9c7849f6f99..c9c7849f6f99 100644 --- a/drivers/media/platform/vivid/vivid-radio-rx.h +++ b/drivers/media/test-drivers/vivid/vivid-radio-rx.h diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/test-drivers/vivid/vivid-radio-tx.c index 049d40b948bb..049d40b948bb 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.c +++ b/drivers/media/test-drivers/vivid/vivid-radio-tx.c diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/test-drivers/vivid/vivid-radio-tx.h index c2bf1e7e634a..c2bf1e7e634a 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.h +++ b/drivers/media/test-drivers/vivid/vivid-radio-tx.h diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/test-drivers/vivid/vivid-rds-gen.c index b5b104ee64c9..b5b104ee64c9 100644 --- a/drivers/media/platform/vivid/vivid-rds-gen.c +++ b/drivers/media/test-drivers/vivid/vivid-rds-gen.c diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/test-drivers/vivid/vivid-rds-gen.h index 35ac5742302b..35ac5742302b 100644 --- a/drivers/media/platform/vivid/vivid-rds-gen.h +++ b/drivers/media/test-drivers/vivid/vivid-rds-gen.h diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index 2b7522e16efc..2b7522e16efc 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/test-drivers/vivid/vivid-sdr-cap.h index 813c9248e5a7..813c9248e5a7 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.h diff --git a/drivers/media/platform/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index ebb00b128030..ebb00b128030 100644 --- a/drivers/media/platform/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c diff --git a/drivers/media/platform/vivid/vivid-touch-cap.h b/drivers/media/test-drivers/vivid/vivid-touch-cap.h index 07e514046ae8..07e514046ae8 100644 --- a/drivers/media/platform/vivid/vivid-touch-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.h diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index 1a9348eea781..1a9348eea781 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/test-drivers/vivid/vivid-vbi-cap.h index 91d2de01381c..91d2de01381c 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.h diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c index acc98445a1fa..acc98445a1fa 100644 --- a/drivers/media/platform/vivid/vivid-vbi-gen.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/test-drivers/vivid/vivid-vbi-gen.h index 2657a7f5571c..2657a7f5571c 100644 --- a/drivers/media/platform/vivid/vivid-vbi-gen.h +++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.h diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c index cd56476902a2..cd56476902a2 100644 --- a/drivers/media/platform/vivid/vivid-vbi-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/test-drivers/vivid/vivid-vbi-out.h index 76584940cdaf..76584940cdaf 100644 --- a/drivers/media/platform/vivid/vivid-vbi-out.h +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.h diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index e94beef008c8..e94beef008c8 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 1e422a59eeab..1e422a59eeab 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 76b0be670ebb..76b0be670ebb 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/test-drivers/vivid/vivid-vid-common.h index d908d9725283..d908d9725283 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.h diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index ee3446e3217c..ee3446e3217c 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/test-drivers/vivid/vivid-vid-out.h index 8d56314f4ea1..8d56314f4ea1 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.h diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index e104bb7766e1..4605bb377574 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -15,7 +15,7 @@ config MEDIA_TUNER select MEDIA_TUNER_TDA9887 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT -comment "Tuner drivers hidden by 'Autoselect ancillary drivers'" +comment "Tuner drivers auto-selected by 'Autoselect ancillary drivers'" depends on MEDIA_HIDE_ANCILLARY_SUBDRV depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT @@ -223,7 +223,7 @@ config MEDIA_TUNER_TDA18212 config MEDIA_TUNER_E4000 tristate "Elonics E4000 silicon tuner" - depends on MEDIA_SUPPORT && I2C + depends on MEDIA_SUPPORT && I2C && VIDEO_V4L2 select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -231,7 +231,7 @@ config MEDIA_TUNER_E4000 config MEDIA_TUNER_FC2580 tristate "FCI FC2580 silicon tuner" - depends on MEDIA_SUPPORT && I2C + depends on MEDIA_SUPPORT && I2C && VIDEO_V4L2 select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 898e0f9f8b70..fefb2625f655 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -9,6 +9,10 @@ static const struct dvb_tuner_ops si2157_ops; +static int tuner_lock_debug; +module_param(tuner_lock_debug, int, 0644); +MODULE_PARM_DESC(tuner_lock_debug, "if set, signal lock is briefly waited on after setting params"); + /* execute firmware command */ static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd) { @@ -47,14 +51,20 @@ static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd) break; } - dev_dbg(&client->dev, "cmd execution took %d ms\n", - jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - TIMEOUT)); + dev_dbg(&client->dev, "cmd execution took %d ms, status=%x\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT), + cmd->args[0]); if (!((cmd->args[0] >> 7) & 0x01)) { ret = -ETIMEDOUT; goto err_mutex_unlock; } + /* check error status bit */ + if (cmd->args[0] & 0x40) { + ret = -EAGAIN; + goto err_mutex_unlock; + } } mutex_unlock(&dev->i2c_mutex); @@ -75,24 +85,23 @@ static int si2157_init(struct dvb_frontend *fe) struct si2157_cmd cmd; const struct firmware *fw; const char *fw_name; - unsigned int uitmp, chip_id; + unsigned int chip_id, xtal_trim; dev_dbg(&client->dev, "\n"); - /* Returned IF frequency is garbage when firmware is not running */ - memcpy(cmd.args, "\x15\x00\x06\x07", 4); + /* Try to get Xtal trim property, to verify tuner still running */ + memcpy(cmd.args, "\x15\x00\x04\x02", 4); cmd.wlen = 4; cmd.rlen = 4; ret = si2157_cmd_execute(client, &cmd); - if (ret) - goto err; - uitmp = cmd.args[2] << 0 | cmd.args[3] << 8; - dev_dbg(&client->dev, "if_frequency kHz=%u\n", uitmp); + xtal_trim = cmd.args[2] | (cmd.args[3] << 8); - if (uitmp == dev->if_frequency / 1000) + if (ret == 0 && xtal_trim < 16) goto warm; + dev->if_frequency = 0; /* we no longer know current tuner state */ + /* power up */ if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); @@ -106,7 +115,7 @@ static int si2157_init(struct dvb_frontend *fe) } cmd.rlen = 1; ret = si2157_cmd_execute(client, &cmd); - if (ret) + if (ret && (dev->chiptype != SI2157_CHIPTYPE_SI2141 || ret != -EAGAIN)) goto err; /* Si2141 needs a second command before it answers the revision query */ @@ -230,6 +239,28 @@ skip_fw_download: dev_info(&client->dev, "firmware version: %c.%c.%d\n", cmd.args[6], cmd.args[7], cmd.args[8]); + + /* enable tuner status flags */ + memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x06\x01\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x07\x01\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; warm: /* init statistics in order signal app which are supported */ c->strength.len = 1; @@ -274,6 +305,93 @@ err: return ret; } +static int si2157_tune_wait(struct i2c_client *client, u8 is_digital) +{ +#define TUN_TIMEOUT 40 +#define DIG_TIMEOUT 30 +#define ANALOG_TIMEOUT 150 + struct si2157_dev *dev = i2c_get_clientdata(client); + int ret; + unsigned long timeout; + unsigned long start_time; + u8 wait_status; + u8 tune_lock_mask; + + if (is_digital) + tune_lock_mask = 0x04; + else + tune_lock_mask = 0x02; + + mutex_lock(&dev->i2c_mutex); + + /* wait tuner command complete */ + start_time = jiffies; + timeout = start_time + msecs_to_jiffies(TUN_TIMEOUT); + while (1) { + ret = i2c_master_recv(client, &wait_status, + sizeof(wait_status)); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != sizeof(wait_status)) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + if (time_after(jiffies, timeout)) + break; + + /* tuner done? */ + if ((wait_status & 0x81) == 0x81) + break; + usleep_range(5000, 10000); + } + + dev_dbg(&client->dev, "tuning took %d ms, status=0x%x\n", + jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), + wait_status); + + /* if we tuned ok, wait a bit for tuner lock */ + if (tuner_lock_debug && (wait_status & 0x81) == 0x81) { + if (is_digital) + timeout = jiffies + msecs_to_jiffies(DIG_TIMEOUT); + else + timeout = jiffies + msecs_to_jiffies(ANALOG_TIMEOUT); + + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(client, &wait_status, + sizeof(wait_status)); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != sizeof(wait_status)) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* tuner locked? */ + if (wait_status & tune_lock_mask) + break; + usleep_range(5000, 10000); + } + + dev_dbg(&client->dev, "tuning+lock took %d ms, status=0x%x\n", + jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), + wait_status); + } + + if ((wait_status & 0xc0) != 0x80) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } + + mutex_unlock(&dev->i2c_mutex); + return 0; + +err_mutex_unlock: + mutex_unlock(&dev->i2c_mutex); + dev_err(&client->dev, "failed=%d\n", ret); + return ret; +} + static int si2157_set_params(struct dvb_frontend *fe) { struct i2c_client *client = fe->tuner_priv; @@ -344,7 +462,7 @@ static int si2157_set_params(struct dvb_frontend *fe) if (ret) goto err; - /* set if frequency if needed */ + /* set digital if frequency if needed */ if (if_frequency != dev->if_frequency) { memcpy(cmd.args, "\x14\x00\x06\x07", 4); cmd.args[4] = (if_frequency / 1000) & 0xff; @@ -358,7 +476,7 @@ static int si2157_set_params(struct dvb_frontend *fe) dev->if_frequency = if_frequency; } - /* set frequency */ + /* set digital frequency */ memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); cmd.args[4] = (c->frequency >> 0) & 0xff; cmd.args[5] = (c->frequency >> 8) & 0xff; @@ -370,21 +488,283 @@ static int si2157_set_params(struct dvb_frontend *fe) if (ret) goto err; + dev->bandwidth = bandwidth; + dev->frequency = c->frequency; + + si2157_tune_wait(client, 1); /* wait to complete, ignore any errors */ + return 0; err: + dev->bandwidth = 0; + dev->frequency = 0; + dev->if_frequency = 0; dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } +static int si2157_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); + char *std; /* for debugging */ + int ret; + struct si2157_cmd cmd; + u32 bandwidth = 0; + u32 if_frequency = 0; + u32 freq = 0; + u64 tmp_lval = 0; + u8 system = 0; + u8 color = 0; /* 0=NTSC/PAL, 0x10=SECAM */ + u8 invert_analog = 1; /* analog tuner spectrum; 0=normal, 1=inverted */ + + if (dev->chiptype != SI2157_CHIPTYPE_SI2157) { + dev_info(&client->dev, "Analog tuning not supported for chiptype=%u\n", + dev->chiptype); + ret = -EINVAL; + goto err; + } + + if (!dev->active) + si2157_init(fe); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + if (params->mode == V4L2_TUNER_RADIO) { + /* + * std = "fm"; + * bandwidth = 1700000; //best can do for FM, AGC will be a mess though + * if_frequency = 1250000; //HVR-225x(saa7164), HVR-12xx(cx23885) + * if_frequency = 6600000; //HVR-9xx(cx231xx) + * if_frequency = 5500000; //HVR-19xx(pvrusb2) + */ + dev_err(&client->dev, "si2157 does not currently support FM radio\n"); + ret = -EINVAL; + goto err; + } + tmp_lval = params->frequency * 625LL; + do_div(tmp_lval, 10); /* convert to HZ */ + freq = (u32)tmp_lval; + + if (freq < 1000000) /* is freq in KHz */ + freq = freq * 1000; + dev->frequency = freq; + + /* if_frequency values based on tda187271C2 */ + if (params->std & (V4L2_STD_B | V4L2_STD_GH)) { + if (freq >= 470000000) { + std = "palGH"; + bandwidth = 8000000; + if_frequency = 6000000; + system = 1; + if (params->std & + (V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) { + std = "secamGH"; + color = 0x10; + } + } else { + std = "palB"; + bandwidth = 7000000; + if_frequency = 6000000; + system = 0; + if (params->std & V4L2_STD_SECAM_B) { + std = "secamB"; + color = 0x10; + } + } + } else if (params->std & V4L2_STD_MN) { + std = "MN"; + bandwidth = 6000000; + if_frequency = 5400000; + system = 2; + } else if (params->std & V4L2_STD_PAL_I) { + std = "palI"; + bandwidth = 8000000; + if_frequency = 7250000; /* TODO: does not work yet */ + system = 4; + } else if (params->std & V4L2_STD_DK) { + std = "palDK"; + bandwidth = 8000000; + if_frequency = 6900000; /* TODO: does not work yet */ + system = 5; + if (params->std & V4L2_STD_SECAM_DK) { + std = "secamDK"; + color = 0x10; + } + } else if (params->std & V4L2_STD_SECAM_L) { + std = "secamL"; + bandwidth = 8000000; + if_frequency = 6750000; /* TODO: untested */ + system = 6; + color = 0x10; + } else if (params->std & V4L2_STD_SECAM_LC) { + std = "secamL'"; + bandwidth = 7000000; + if_frequency = 1250000; /* TODO: untested */ + system = 7; + color = 0x10; + } else { + std = "unknown"; + } + /* calc channel center freq */ + freq = freq - 1250000 + (bandwidth / 2); + + dev_dbg(&client->dev, + "mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u\n", + params->mode, system, std, params->frequency, + freq, if_frequency, bandwidth); + + /* set analog IF port */ + memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6); + /* in using dev->if_port, we assume analog and digital IF's */ + /* are always on different ports */ + /* assumes if_port definition is 0 or 1 for digital out */ + cmd.args[4] = (dev->if_port == 1) ? 8 : 10; + /* Analog AGC assumed external */ + cmd.args[5] = (dev->if_port == 1) ? 2 : 1; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set analog IF output config */ + memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* make this distinct from a digital IF */ + dev->if_frequency = if_frequency | 1; + + /* calc and set tuner analog if center frequency */ + if_frequency = if_frequency + 1250000 - (bandwidth / 2); + dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency); + + memcpy(cmd.args, "\x14\x00\x0C\x06", 4); + cmd.args[4] = (if_frequency / 1000) & 0xff; + cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set analog AGC config */ + memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set analog video mode */ + memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); + cmd.args[4] = system | color; + /* can use dev->inversion if assumed applies to both digital/analog */ + if (invert_analog) + cmd.args[5] |= 0x02; + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + /* set analog frequency */ + memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8); + cmd.args[4] = (freq >> 0) & 0xff; + cmd.args[5] = (freq >> 8) & 0xff; + cmd.args[6] = (freq >> 16) & 0xff; + cmd.args[7] = (freq >> 24) & 0xff; + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + dev->bandwidth = bandwidth; + + si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */ + + return 0; +err: + dev->bandwidth = 0; + dev->frequency = 0; + dev->if_frequency = 0; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int si2157_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); + + *frequency = dev->frequency; + dev_dbg(&client->dev, "freq=%u\n", dev->frequency); + return 0; +} + +static int si2157_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); + + *bandwidth = dev->bandwidth; + dev_dbg(&client->dev, "bandwidth=%u\n", dev->bandwidth); + return 0; +} + static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { struct i2c_client *client = fe->tuner_priv; struct si2157_dev *dev = i2c_get_clientdata(client); - *frequency = dev->if_frequency; + *frequency = dev->if_frequency & ~1; /* strip analog IF indicator bit */ + dev_dbg(&client->dev, "if_frequency=%u\n", *frequency); return 0; } +static int si2157_get_rf_strength(struct dvb_frontend *fe, u16 *rssi) +{ + struct i2c_client *client = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct si2157_cmd cmd; + int ret; + int strength; + + dev_dbg(&client->dev, "\n"); + + memcpy(cmd.args, "\x42\x00", 2); + cmd.wlen = 2; + cmd.rlen = 12; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = (s8)cmd.args[3] * 1000; + + /* normalize values based on Silicon Labs reference + * add 100, then anything > 80 is 100% signal + */ + strength = (s8)cmd.args[3] + 100; + strength = clamp_val(strength, 0, 80); + *rssi = (u16)(strength * 0xffff / 80); + + dev_dbg(&client->dev, "strength=%d rssi=%u\n", + (s8)cmd.args[3], *rssi); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static const struct dvb_tuner_ops si2157_ops = { .info = { .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", @@ -395,7 +775,12 @@ static const struct dvb_tuner_ops si2157_ops = { .init = si2157_init, .sleep = si2157_sleep, .set_params = si2157_set_params, - .get_if_frequency = si2157_get_if_frequency, + .set_analog_params = si2157_set_analog_params, + .get_frequency = si2157_get_frequency, + .get_bandwidth = si2157_get_bandwidth, + .get_if_frequency = si2157_get_if_frequency, + + .get_rf_strength = si2157_get_rf_strength, }; static void si2157_stat_work(struct work_struct *work) @@ -456,7 +841,7 @@ static int si2157_probe(struct i2c_client *client, cmd.wlen = 0; cmd.rlen = 1; ret = si2157_cmd_execute(client, &cmd); - if (ret) + if (ret && ret != -EAGAIN) goto err_kfree; memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 778f81b39996..ef4796098931 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -29,6 +29,8 @@ struct si2157_dev { u8 chiptype; u8 if_port; u32 if_frequency; + u32 bandwidth; + u32 frequency; struct delayed_work stat_work; #if defined(CONFIG_MEDIA_CONTROLLER) diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index e678d3d11467..00feadb217d8 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -1,4 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only + +# This Kconfig option is also used by the legacy av7110 driver +config TTPCI_EEPROM + tristate + depends on I2C + if USB && MEDIA_SUPPORT menuconfig MEDIA_USB_SUPPORT @@ -60,11 +66,5 @@ source "drivers/media/usb/hackrf/Kconfig" source "drivers/media/usb/msi2500/Kconfig" endif -if MEDIA_CEC_SUPPORT - comment "USB HDMI CEC adapters" -source "drivers/media/usb/pulse8-cec/Kconfig" -source "drivers/media/usb/rainshadow-cec/Kconfig" -endif - endif #MEDIA_USB_SUPPORT endif #USB diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 169aa07c97bd..3eaff3149ef4 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -24,5 +24,3 @@ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_DVB_AS102) += as102/ -obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec/ -obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow-cec/ diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 0974965e848f..3d3c881c8e58 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -587,14 +587,27 @@ int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) return status; } } - if (dev->tuner_type == TUNER_NXP_TDA18271) + switch (dev->model) { /* i2c device tuners */ + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: status = cx231xx_set_decoder_video_input(dev, CX231XX_VMUX_TELEVISION, INPUT(input)->vmux); - else - status = cx231xx_set_decoder_video_input(dev, + break; + default: + if (dev->tuner_type == TUNER_NXP_TDA18271) + status = cx231xx_set_decoder_video_input(dev, + CX231XX_VMUX_TELEVISION, + INPUT(input)->vmux); + else + status = cx231xx_set_decoder_video_input(dev, CX231XX_VMUX_COMPOSITE1, INPUT(input)->vmux); + break; + } break; default: @@ -1193,12 +1206,22 @@ int cx231xx_set_audio_decoder_input(struct cx231xx *dev, cx231xx_set_field(FLD_SIF_EN, 0)); break; default: + switch (dev->model) { /* i2c device tuners */ + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: + /* TODO: Normal mode: SIF passthrough at 14.32 MHz?? */ + break; + default: /* This is just a casual suggestion to people adding new boards in case they use a tuner type we don't currently know about */ - dev_info(dev->dev, - "Unknown tuner type configuring SIF"); - break; + dev_info(dev->dev, + "Unknown tuner type configuring SIF"); + break; + } } break; diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index 9f88c640ec2b..8149702bcf89 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -88,7 +88,7 @@ int cx231xx_ir_init(struct cx231xx *dev) ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master; dev_dbg(dev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n", ir_i2c_bus, info.addr); - dev->ir_i2c_client = i2c_new_device( + dev->ir_i2c_client = i2c_new_client_device( cx231xx_get_i2c_adap(dev, ir_i2c_bus), &info); return 0; @@ -96,7 +96,6 @@ int cx231xx_ir_init(struct cx231xx *dev) void cx231xx_ir_exit(struct cx231xx *dev) { - if (dev->ir_i2c_client) - i2c_unregister_device(dev->ir_i2c_client); + i2c_unregister_device(dev->ir_i2c_client); dev->ir_i2c_client = NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 8bff7d8a0310..d9f953f2d088 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1129,7 +1129,7 @@ int cx231xx_s_frequency(struct file *file, void *priv, { struct cx231xx *dev = video_drvdata(file); struct v4l2_frequency new_freq = *f; - int rc; + int rc, need_if_freq = 0; u32 if_frequency = 5400000; dev_dbg(dev->dev, @@ -1142,14 +1142,30 @@ int cx231xx_s_frequency(struct file *file, void *priv, /* set pre channel change settings in DIF first */ rc = cx231xx_tuner_pre_channel_change(dev); - call_all(dev, tuner, s_frequency, f); - call_all(dev, tuner, g_frequency, &new_freq); - dev->ctl_freq = new_freq.frequency; + switch (dev->model) { /* i2c device tuners */ + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: + if (dev->cx231xx_set_analog_freq) + dev->cx231xx_set_analog_freq(dev, f->frequency); + dev->ctl_freq = f->frequency; + need_if_freq = 1; + break; + default: + call_all(dev, tuner, s_frequency, f); + call_all(dev, tuner, g_frequency, &new_freq); + dev->ctl_freq = new_freq.frequency; + break; + } + + pr_debug("%s() %u : %u\n", __func__, f->frequency, dev->ctl_freq); /* set post channel change settings in DIF first */ rc = cx231xx_tuner_post_channel_change(dev); - if (dev->tuner_type == TUNER_NXP_TDA18271) { + if (need_if_freq || dev->tuner_type == TUNER_NXP_TDA18271) { if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443)) if_frequency = 5400000; /*5.4MHz */ else if (dev->norm & V4L2_STD_B) @@ -1362,9 +1378,20 @@ int cx231xx_querycap(struct file *file, void *priv, V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; if (video_is_registered(&dev->radio_dev)) cap->capabilities |= V4L2_CAP_RADIO; - if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + switch (dev->model) { + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: + cap->capabilities |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + break; + } return 0; } @@ -1708,10 +1735,20 @@ static void cx231xx_vdev_init(struct cx231xx *dev, video_set_drvdata(vfd, dev); if (dev->tuner_type == TUNER_ABSENT) { - v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY); - v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY); - v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); - v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); + switch (dev->model) { + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: + break; + default: + v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); + v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); + break; + } } } @@ -1781,8 +1818,20 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->vdev.queue = q; dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + + switch (dev->model) { /* i2c device tuners */ + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: dev->vdev.device_caps |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + dev->vdev.device_caps |= V4L2_CAP_TUNER; + break; + } /* register v4l2 video video_device */ ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, @@ -1829,8 +1878,18 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->vbi_dev.queue = q; dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + switch (dev->model) { /* i2c device tuners */ + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + case CX231XX_BOARD_HAUPPAUGE_935C: + case CX231XX_BOARD_HAUPPAUGE_955Q: + case CX231XX_BOARD_HAUPPAUGE_975: + case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD: dev->vbi_dev.device_caps |= V4L2_CAP_TUNER; + break; + default: + if (dev->tuner_type != TUNER_ABSENT) + dev->vbi_dev.device_caps |= V4L2_CAP_TUNER; + } /* register v4l2 vbi video_device */ ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI, diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index b21a4d413872..5c75303fba9d 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -7,7 +7,7 @@ config DVB_USB_V2 USB1.1 and USB2.0 DVB devices. Almost every USB device needs a firmware, please look into - <file:Documentation/media/dvb-drivers/dvb-usb.rst>. + <file:Documentation/driver-api/media/drivers/dvb-usb.rst>. For a complete list of supported USB devices see the LinuxTV DVB Wiki: <https://linuxtv.org/wiki/index.php/DVB_USB> @@ -38,7 +38,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 + select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) select MEDIA_TUNER_IT913X if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Afatech AF9035 based DVB USB receiver. @@ -137,12 +137,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) + select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT && VIDEO_V4L2) select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_E4000 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_FC2580 if (MEDIA_SUBDRV_AUTOSELECT && VIDEO_V4L2) select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index e30305876840..7ed0ab9e429b 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -332,22 +332,17 @@ static const struct dvb_usb_device_properties ec168_props = { }, }; -static const struct dvb_usb_driver_info ec168_driver_info = { - .name = "E3C EC168 reference design", - .props = &ec168_props, -}; - static const struct usb_device_id ec168_id[] = { - { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168), - .driver_info = (kernel_ulong_t) &ec168_driver_info }, - { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2), - .driver_info = (kernel_ulong_t) &ec168_driver_info }, - { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3), - .driver_info = (kernel_ulong_t) &ec168_driver_info }, - { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4), - .driver_info = (kernel_ulong_t) &ec168_driver_info }, - { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5), - .driver_info = (kernel_ulong_t) &ec168_driver_info }, + { DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168, + &ec168_props, "E3C EC168 reference design", NULL)}, + { DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2, + &ec168_props, "E3C EC168 reference design", NULL)}, + { DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3, + &ec168_props, "E3C EC168 reference design", NULL)}, + { DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4, + &ec168_props, "E3C EC168 reference design", NULL)}, + { DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5, + &ec168_props, "E3C EC168 reference design", NULL)}, {} }; MODULE_DEVICE_TABLE(usb, ec168_id); diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index 19217dcf20f1..b7ca236174f3 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* DVB USB compliant linux driver for GL861 USB2.0 devices. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include <linux/string.h> @@ -550,7 +550,7 @@ static struct dvb_usb_device_properties friio_props = { static const struct usb_device_id gl861_id_table[] = { { DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801, &gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) }, - { DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU, + { DVB_USB_DEVICE(USB_VID_ALINK, USB_PID_ALINK_DTU, &gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE, &friio_props, "774 Friio White ISDB-T USB2.0", NULL) }, diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index fd8b42bb9a84..8a3c0eeed959 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -22,7 +22,7 @@ * * LME2510C + M88RS2000 * - * For firmware see Documentation/media/dvb-drivers/lmedm04.rst + * For firmware see Documentation/admin-guide/media/lmedm04.rst * * I2C addresses: * 0xd0 - STV0288 - Demodulator @@ -39,7 +39,7 @@ * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information * * Known Issues : * LME2510: Non Intel USB chipsets fail to maintain High Speed on diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.h b/drivers/media/usb/dvb-usb-v2/lmedm04.h index 766a8348624d..4335b6ebcc1c 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.h +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.h @@ -14,7 +14,7 @@ * MVB0001F (LME2510C+LGTDQT-P001F) * * * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_LME2510_H_ #define _DVB_USB_LME2510_H_ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 55b4ae7037a4..7865fa0a8295 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include <linux/vmalloc.h> diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index 70bd2a2a8ec1..e57e5d2353b4 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2010-2014 Michael Krufky (mkrufky@linuxtv.org) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_MXL111SF_H_ diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 1a3e5f965ae4..15d29c91662f 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -2,12 +2,13 @@ config DVB_USB tristate "Support for various USB DVB devices" depends on DVB_CORE && USB && I2C && RC_CORE + select CYPRESS_FIRMWARE help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. Almost every USB device needs a firmware, please look into - <file:Documentation/media/dvb-drivers/dvb-usb.rst>. + <file:Documentation/driver-api/media/drivers/dvb-usb.rst>. For a complete list of supported USB devices see the LinuxTV DVB Wiki: <https://linuxtv.org/wiki/index.php/DVB_USB> diff --git a/drivers/media/usb/dvb-usb/a800.c b/drivers/media/usb/dvb-usb/a800.c index 666213f5d5d8..36b5b6227412 100644 --- a/drivers/media/usb/dvb-usb/a800.c +++ b/drivers/media/usb/dvb-usb/a800.c @@ -8,7 +8,7 @@ * - AVerMedia who kindly provided information and * - Glen Harris who suffered from my mistakes during development. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" @@ -27,8 +27,10 @@ static int a800_power_ctrl(struct dvb_usb_device *d, int onoff) } /* assure to put cold to 0 for iManufacturer == 1 */ -static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, int *cold) +static int a800_identify_state(struct usb_device *udev, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold) { *cold = udev->descriptor.iManufacturer != 1; return 0; diff --git a/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c index 6c960f723457..9d6fa0556d7b 100644 --- a/drivers/media/usb/dvb-usb/af9005-fe.c +++ b/drivers/media/usb/dvb-usb/af9005-fe.c @@ -6,7 +6,7 @@ * * Thanks to Afatech who kindly provided information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "af9005.h" #include "af9005-script.h" diff --git a/drivers/media/usb/dvb-usb/af9005-remote.c b/drivers/media/usb/dvb-usb/af9005-remote.c index c664353f3911..41d48b3c8d05 100644 --- a/drivers/media/usb/dvb-usb/af9005-remote.c +++ b/drivers/media/usb/dvb-usb/af9005-remote.c @@ -8,7 +8,7 @@ * * Thanks to Afatech who kindly provided information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "af9005.h" /* debug */ diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index 89b4b5d84cdf..b6a2436d16e9 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -6,7 +6,7 @@ * * Thanks to Afatech who kindly provided information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "af9005.h" @@ -955,8 +955,8 @@ static int af9005_pid_filter(struct dvb_usb_adapter *adap, int index, } static int af9005_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { int ret; diff --git a/drivers/media/usb/dvb-usb/af9005.h b/drivers/media/usb/dvb-usb/af9005.h index 3179a7c71e8f..11d74dc26d83 100644 --- a/drivers/media/usb/dvb-usb/af9005.h +++ b/drivers/media/usb/dvb-usb/af9005.h @@ -6,7 +6,7 @@ * * Thanks to Afatech who kindly provided information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_AF9005_H_ #define _DVB_USB_AF9005_H_ diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c index 8de18da0c4bd..1c39b61cde29 100644 --- a/drivers/media/usb/dvb-usb/az6027.c +++ b/drivers/media/usb/dvb-usb/az6027.c @@ -4,7 +4,7 @@ * * Copyright (C) 2009 Adams.Xu <adams.xu@azwave.com.cn> * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "az6027.h" @@ -1051,8 +1051,8 @@ static struct i2c_algorithm az6027_i2c_algo = { }; static int az6027_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { u8 *b; diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index c421b603be44..761992ad05e2 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -18,7 +18,7 @@ * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au) * Copyright (C) 2011, 2017 Maciej S. Szmigiero (mail@maciej.szmigiero.name) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include <media/tuner.h> #include <linux/delay.h> @@ -1358,8 +1358,8 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) * not, and forget a match if it turns out we selected the wrong device. */ static int bluebird_fx2_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { int wascold = *cold; diff --git a/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h index ca4d3d2da969..2defbd8b6fc1 100644 --- a/drivers/media/usb/dvb-usb/dib0700.h +++ b/drivers/media/usb/dvb-usb/dib0700.h @@ -52,20 +52,25 @@ struct dib0700_state { struct i2c_client *i2c_client_tuner; }; -extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, - u32 *romversion, u32 *ramversion, u32 *fwtype); -extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); -extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); -extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); -extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); -extern int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf); -extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); -extern struct i2c_algorithm dib0700_i2c_algo; -extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, int *cold); -extern int dib0700_change_protocol(struct rc_dev *dev, u64 *rc_proto); -extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz); +int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, + u32 *romversion, u32 *ramversion, u32 *fwtype); +int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, + u8 gpio_dir, u8 gpio_val); +int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); +int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, + u8 rxlen); +int dib0700_download_firmware(struct usb_device *d, + const struct firmware *fw); +int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf); +int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); +int dib0700_identify_state(struct usb_device *d, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold); +int dib0700_change_protocol(struct rc_dev *dev, u64 *rc_proto); +int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz); +extern struct i2c_algorithm dib0700_i2c_algo; extern int dib0700_device_count; extern int dvb_usb_dib0700_ir_proto; extern struct dvb_usb_device_properties dib0700_devices[]; diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index ef62dd6c5ae4..70219b3e8566 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -372,8 +372,10 @@ struct i2c_algorithm dib0700_i2c_algo = { .functionality = dib0700_i2c_func, }; -int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, int *cold) +int dib0700_identify_state(struct usb_device *udev, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold) { s16 ret; u8 *b; diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index 59ce2dec11e9..02b51d1a1b67 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -3,7 +3,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" diff --git a/drivers/media/usb/dvb-usb/dibusb-mb.c b/drivers/media/usb/dvb-usb/dibusb-mb.c index d4ea72bf09c5..e9dc27f73970 100644 --- a/drivers/media/usb/dvb-usb/dibusb-mb.c +++ b/drivers/media/usb/dvb-usb/dibusb-mb.c @@ -7,7 +7,7 @@ * based on GPL code from DiBcom, which has * Copyright (C) 2004 Amaury Demol for DiBcom * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" @@ -81,7 +81,7 @@ static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap) if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) { err("tuner i2c write failed."); - ret = -EREMOTEIO; + return -EREMOTEIO; } if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c index 967027e29c17..b8cde4cded33 100644 --- a/drivers/media/usb/dvb-usb/dibusb-mc-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c @@ -3,7 +3,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" diff --git a/drivers/media/usb/dvb-usb/dibusb-mc.c b/drivers/media/usb/dvb-usb/dibusb-mc.c index ada3bee296c2..e2689977c8c8 100644 --- a/drivers/media/usb/dvb-usb/dibusb-mc.c +++ b/drivers/media/usb/dvb-usb/dibusb-mc.c @@ -7,7 +7,7 @@ * based on GPL code from DiBcom, which has * Copyright (C) 2004 Amaury Demol for DiBcom * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" diff --git a/drivers/media/usb/dvb-usb/dibusb.h b/drivers/media/usb/dvb-usb/dibusb.h index a83326c36ca7..f61de0744821 100644 --- a/drivers/media/usb/dvb-usb/dibusb.h +++ b/drivers/media/usb/dvb-usb/dibusb.h @@ -3,7 +3,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_DIBUSB_H_ #define _DVB_USB_DIBUSB_H_ diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c index 99a39339d45d..4e3b3c064bcf 100644 --- a/drivers/media/usb/dvb-usb/digitv.c +++ b/drivers/media/usb/dvb-usb/digitv.c @@ -6,7 +6,7 @@ * * partly based on the SDK published by Nebula Electronics * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "digitv.h" @@ -90,9 +90,10 @@ static struct i2c_algorithm digitv_i2c_algo = { }; /* Callbacks for DVB USB */ -static int digitv_identify_state (struct usb_device *udev, struct - dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, - int *cold) +static int digitv_identify_state(struct usb_device *udev, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold) { *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; return 0; @@ -230,14 +231,15 @@ static struct rc_map_table rc_map_digitv_table[] = { static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { + struct rc_map_table *entry; int ret, i; - u8 key[5]; + u8 key[4]; u8 b[4] = { 0 }; *event = 0; *state = REMOTE_NO_KEY_PRESSED; - ret = digitv_ctrl_msg(d, USB_READ_REMOTE, 0, NULL, 0, &key[1], 4); + ret = digitv_ctrl_msg(d, USB_READ_REMOTE, 0, NULL, 0, key, 4); if (ret) return ret; @@ -248,20 +250,21 @@ static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return ret; /* if something is inside the buffer, simulate key press */ - if (key[1] != 0) - { - for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { - if (rc5_custom(&d->props.rc.legacy.rc_map_table[i]) == key[1] && - rc5_data(&d->props.rc.legacy.rc_map_table[i]) == key[2]) { - *event = d->props.rc.legacy.rc_map_table[i].keycode; + if (key[0] != 0) { + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { + entry = &d->props.rc.legacy.rc_map_table[i]; + + if (rc5_custom(entry) == key[0] && + rc5_data(entry) == key[1]) { + *event = entry->keycode; *state = REMOTE_KEY_PRESSED; return 0; } } + + deb_rc("key: %*ph\n", 4, key); } - if (key[0] != 0) - deb_rc("key: %*ph\n", 5, key); return 0; } diff --git a/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c index 00ce723c7bf0..9f83560ba63d 100644 --- a/drivers/media/usb/dvb-usb/dtt200u-fe.c +++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c @@ -4,7 +4,7 @@ * * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de> * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dtt200u.h" diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c index 1e7296b2e5b2..24efa023d827 100644 --- a/drivers/media/usb/dvb-usb/dtt200u.c +++ b/drivers/media/usb/dvb-usb/dtt200u.c @@ -6,7 +6,7 @@ * * Thanks to Steve Chang from WideView for providing support for the WT-220U. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dtt200u.h" diff --git a/drivers/media/usb/dvb-usb/dtt200u.h b/drivers/media/usb/dvb-usb/dtt200u.h index 832f355114e4..696c2c1f3af3 100644 --- a/drivers/media/usb/dvb-usb/dtt200u.h +++ b/drivers/media/usb/dvb-usb/dtt200u.h @@ -4,7 +4,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_DTT200U_H_ #define _DVB_USB_DTT200U_H_ diff --git a/drivers/media/usb/dvb-usb/dvb-usb-common.h b/drivers/media/usb/dvb-usb/dvb-usb-common.h index 8c51ac4493dd..70f4eedd7c48 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-common.h +++ b/drivers/media/usb/dvb-usb/dvb-usb-common.h @@ -26,7 +26,8 @@ extern int dvb_usb_disable_rc_polling; #define deb_uxfer(args...) dprintk(dvb_usb_debug,0x100,args) /* commonly used methods */ -extern int dvb_usb_download_firmware(struct usb_device *, struct dvb_usb_device_properties *); +int dvb_usb_download_firmware(struct usb_device *udev, + const struct dvb_usb_device_properties *props); extern int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c index 42c207aacbb1..0fb3fa6100e4 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c @@ -84,7 +84,8 @@ int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw } EXPORT_SYMBOL(usb_cypress_load_firmware); -int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_properties *props) +int dvb_usb_download_firmware(struct usb_device *udev, + const struct dvb_usb_device_properties *props) { int ret; const struct firmware *fw = NULL; diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 16a0b4a359ea..c1a7634e27b4 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -6,7 +6,7 @@ * * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dvb-usb-common.h" @@ -184,10 +184,10 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) } /* determine the name and the state of the just found USB device */ -static struct dvb_usb_device_description *dvb_usb_find_device(struct usb_device *udev, struct dvb_usb_device_properties *props, int *cold) +static const struct dvb_usb_device_description *dvb_usb_find_device(struct usb_device *udev, const struct dvb_usb_device_properties *props, int *cold) { int i, j; - struct dvb_usb_device_description *desc = NULL; + const struct dvb_usb_device_description *desc = NULL; *cold = -1; @@ -242,13 +242,13 @@ int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff) * USB */ int dvb_usb_device_init(struct usb_interface *intf, - struct dvb_usb_device_properties *props, + const struct dvb_usb_device_properties *props, struct module *owner, struct dvb_usb_device **du, short *adapter_nums) { struct usb_device *udev = interface_to_usbdev(intf); struct dvb_usb_device *d = NULL; - struct dvb_usb_device_description *desc = NULL; + const struct dvb_usb_device_description *desc = NULL; int ret = -ENOMEM, cold = 0; diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h index 2eb0e24e8943..741be0e69447 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb.h +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -291,8 +291,10 @@ struct dvb_usb_device_properties { int (*power_ctrl) (struct dvb_usb_device *, int); int (*read_mac_address) (struct dvb_usb_device *, u8 []); - int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *, - struct dvb_usb_device_description **, int *); + int (*identify_state)(struct usb_device *udev, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold); struct { enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */ @@ -436,7 +438,7 @@ struct dvb_usb_adapter { */ struct dvb_usb_device { struct dvb_usb_device_properties props; - struct dvb_usb_device_description *desc; + const struct dvb_usb_device_description *desc; struct usb_device *udev; @@ -473,7 +475,7 @@ struct dvb_usb_device { }; extern int dvb_usb_device_init(struct usb_interface *, - struct dvb_usb_device_properties *, + const struct dvb_usb_device_properties *, struct module *, struct dvb_usb_device **, short *adapter_nums); extern void dvb_usb_device_exit(struct usb_interface *); diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 1007366a295b..f96626fe2c0b 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -8,7 +8,7 @@ * Terratec Cinergy S2 cards * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include <media/dvb-usb-ids.h> #include "dw2102.h" @@ -955,8 +955,8 @@ static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) } static int su3000_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { info("%s", __func__); @@ -1779,6 +1779,8 @@ enum dw2102_table_entry { TERRATEC_CINERGY_S2_R2, TERRATEC_CINERGY_S2_R3, TERRATEC_CINERGY_S2_R4, + TERRATEC_CINERGY_S2_1, + TERRATEC_CINERGY_S2_2, GOTVIEW_SAT_HD, GENIATECH_T220, TECHNOTREND_S2_4600, @@ -1806,9 +1808,16 @@ static struct usb_device_id dw2102_table[] = { [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)}, [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)}, - [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R2)}, - [TERRATEC_CINERGY_S2_R3] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R3)}, - [TERRATEC_CINERGY_S2_R4] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R4)}, + [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_S2_R2)}, + [TERRATEC_CINERGY_S2_R3] = {USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_S2_R3)}, + [TERRATEC_CINERGY_S2_R4] = {USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_CINERGY_S2_R4)}, + [TERRATEC_CINERGY_S2_1] = {USB_DEVICE(USB_VID_TERRATEC_2, + USB_PID_TERRATEC_CINERGY_S2_1)}, + [TERRATEC_CINERGY_S2_2] = {USB_DEVICE(USB_VID_TERRATEC_2, + USB_PID_TERRATEC_CINERGY_S2_2)}, [GOTVIEW_SAT_HD] = {USB_DEVICE(0x1FE1, USB_PID_GOTVIEW_SAT_HD)}, [GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)}, [TECHNOTREND_S2_4600] = {USB_DEVICE(USB_VID_TECHNOTREND, @@ -2221,7 +2230,7 @@ static struct dvb_usb_device_properties su3000_properties = { }}, } }, - .num_device_descs = 6, + .num_device_descs = 8, .devices = { { "SU3000HD DVB-S USB2.0", { &dw2102_table[GENIATECH_SU3000], NULL }, @@ -2243,6 +2252,14 @@ static struct dvb_usb_device_properties su3000_properties = { { &dw2102_table[TERRATEC_CINERGY_S2_R3], NULL }, { NULL }, }, + { "Terratec Cinergy S2 PCIe Dual Port 1", + { &dw2102_table[TERRATEC_CINERGY_S2_1], NULL }, + { NULL }, + }, + { "Terratec Cinergy S2 PCIe Dual Port 2", + { &dw2102_table[TERRATEC_CINERGY_S2_2], NULL }, + { NULL }, + }, { "GOTVIEW Satellite HD", { &dw2102_table[GOTVIEW_SAT_HD], NULL }, { NULL }, diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index 1282f701f185..c07f46f5176e 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -9,7 +9,7 @@ * * This module is based off the vp7045 and vp702x modules * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "gp8psk.h" #include "gp8psk-fe.h" diff --git a/drivers/media/usb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h index 2f4c1368eabe..5293dfdd2609 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.h +++ b/drivers/media/usb/dvb-usb/gp8psk.h @@ -9,7 +9,7 @@ * * This module is based off the vp7045 and vp702x modules * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_GP8PSK_H_ #define _DVB_USB_GP8PSK_H_ diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index d866a1990a7d..4bb5b82599a7 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "m920x.h" @@ -459,8 +459,8 @@ static int m920x_firmware_download(struct usb_device *udev, const struct firmwar /* Callbacks for DVB USB */ static int m920x_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { struct usb_host_interface *alt; diff --git a/drivers/media/usb/dvb-usb/nova-t-usb2.c b/drivers/media/usb/dvb-usb/nova-t-usb2.c index e368935a5089..e7b290552b66 100644 --- a/drivers/media/usb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/usb/dvb-usb/nova-t-usb2.c @@ -4,7 +4,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c index 823b33ae828d..e8d784b9d119 100644 --- a/drivers/media/usb/dvb-usb/opera1.c +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -4,7 +4,7 @@ * Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) * Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) * -* see Documentation/media/dvb-drivers/dvb-usb.rst for more information +* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #define DVB_USB_LOG_PREFIX "opera" diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index 676d233d46d5..f172120db2aa 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -330,8 +330,8 @@ schedule: /* method to find out whether the firmware has to be downloaded or not */ static int technisat_usb2_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, int *cold) + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { int ret; u8 *version; diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index e12a5466b677..294274fd8f55 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -17,7 +17,7 @@ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net> * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org> * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #define DVB_USB_LOG_PREFIX "ttusb2" #include "dvb-usb.h" @@ -467,9 +467,10 @@ static int tt3650_rc_query(struct dvb_usb_device *d) /* Callbacks for DVB USB */ -static int ttusb2_identify_state (struct usb_device *udev, struct - dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, - int *cold) +static int ttusb2_identify_state(struct usb_device *udev, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, + int *cold) { *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; return 0; diff --git a/drivers/media/usb/dvb-usb/ttusb2.h b/drivers/media/usb/dvb-usb/ttusb2.h index 8a3853cd6a26..b34c469d83f9 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.h +++ b/drivers/media/usb/dvb-usb/ttusb2.h @@ -6,7 +6,7 @@ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net> * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.de> * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_TTUSB2_H_ #define _DVB_USB_TTUSB2_H_ diff --git a/drivers/media/usb/dvb-usb/umt-010.c b/drivers/media/usb/dvb-usb/umt-010.c index a2101bd43349..2181993771ae 100644 --- a/drivers/media/usb/dvb-usb/umt-010.c +++ b/drivers/media/usb/dvb-usb/umt-010.c @@ -4,7 +4,7 @@ * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "dibusb.h" diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c index 1c75a9c9dfca..c1e7931900ee 100644 --- a/drivers/media/usb/dvb-usb/vp702x-fe.c +++ b/drivers/media/usb/dvb-usb/vp702x-fe.c @@ -12,7 +12,7 @@ * This file can be removed soon, after the DST-driver is rewritten to provice * the frontend-controlling separately. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "vp702x.h" diff --git a/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c index 381b5c898a07..bf54747e2e01 100644 --- a/drivers/media/usb/dvb-usb/vp702x.c +++ b/drivers/media/usb/dvb-usb/vp702x.c @@ -9,7 +9,7 @@ * * Thanks to Twinhan who kindly provided hardware and information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "vp702x.h" #include <linux/mutex.h> diff --git a/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c index d253307a35f8..e99740ec2650 100644 --- a/drivers/media/usb/dvb-usb/vp7045-fe.c +++ b/drivers/media/usb/dvb-usb/vp7045-fe.c @@ -6,7 +6,7 @@ * * Thanks to Twinhan who kindly provided hardware and information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "vp7045.h" diff --git a/drivers/media/usb/dvb-usb/vp7045.c b/drivers/media/usb/dvb-usb/vp7045.c index 2baf57216d19..23e3a90af1f4 100644 --- a/drivers/media/usb/dvb-usb/vp7045.c +++ b/drivers/media/usb/dvb-usb/vp7045.c @@ -7,7 +7,7 @@ * * Thanks to Twinhan who kindly provided hardware and information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #include "vp7045.h" diff --git a/drivers/media/usb/dvb-usb/vp7045.h b/drivers/media/usb/dvb-usb/vp7045.h index 818366746c41..1c8438f22b97 100644 --- a/drivers/media/usb/dvb-usb/vp7045.h +++ b/drivers/media/usb/dvb-usb/vp7045.h @@ -6,7 +6,7 @@ * * Thanks to Twinhan who kindly provided hardware and information. * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information */ #ifndef _DVB_USB_VP7045_H_ #define _DVB_USB_VP7045_H_ diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index 77a360958239..0283e3b908e4 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -9,7 +9,7 @@ menuconfig USB_GSPCA Say Y here if you want to enable selecting webcams based on the GSPCA framework. - See <file:Documentation/media/v4l-drivers/gspca-cardlist.rst> for more info. + See <file:Documentation/admin-guide/media/gspca-cardlist.rst> for more info. This driver uses the Video For Linux API. You must say Y or M to "Video For Linux" to use this driver. diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c index 502fc2eaffe0..464aa61cd914 100644 --- a/drivers/media/usb/gspca/mr97310a.c +++ b/drivers/media/usb/gspca/mr97310a.c @@ -287,7 +287,6 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return err_code; err_code = cam_get_response16(gspca_dev, 0x21, 0); - status = data[0]; tries++; if (err_code < 0) return err_code; diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index b75c18a012a7..52e05a69c46e 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -363,9 +363,9 @@ static int hdpvr_probe(struct usb_interface *interface, } client = hdpvr_register_ir_i2c(dev); - if (!client) { + if (IS_ERR(client)) { v4l2_err(&dev->v4l2_dev, "i2c IR device register failed\n"); - retval = -ENODEV; + retval = PTR_ERR(client); goto reg_fail; } #endif diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 785c8508a46e..070559b01b01 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -44,7 +44,7 @@ struct i2c_client *hdpvr_register_ir_i2c(struct hdpvr_device *dev) init_data->polling_interval = 405; /* ms, duplicated from Windows */ info.platform_data = init_data; - return i2c_new_device(&dev->i2c_adapter, &info); + return i2c_new_client_device(&dev->i2c_adapter, &info); } static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 275394bafe7d..63db04fe12d3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -564,7 +564,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) strscpy(info.type, "ir_video", I2C_NAME_SIZE); pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", info.type, info.addr); - i2c_new_device(&hdw->i2c_adap, &info); + i2c_new_client_device(&hdw->i2c_adap, &info); break; case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */ case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ @@ -579,7 +579,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) strscpy(info.type, "ir_z8f0811_haup", I2C_NAME_SIZE); pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", info.type, info.addr); - i2c_new_device(&hdw->i2c_adap, &info); + i2c_new_client_device(&hdw->i2c_adap, &info); break; default: /* The device either doesn't support I2C-based IR or we diff --git a/drivers/media/usb/pwc/pwc-ctrl.c b/drivers/media/usb/pwc/pwc-ctrl.c index 315c55927f5c..cff64d872058 100644 --- a/drivers/media/usb/pwc/pwc-ctrl.c +++ b/drivers/media/usb/pwc/pwc-ctrl.c @@ -523,7 +523,7 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) #ifdef CONFIG_USB_PWC_DEBUG int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) { - int ret = -1, request; + int ret, request; if (pdev->type < 675) request = SENSOR_TYPE_FORMATTER1; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index d57b8b786506..61869636ec61 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -71,37 +71,45 @@ /* hotplug device table support */ static const struct usb_device_id pwc_device_table [] = { - { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ - { USB_DEVICE(0x0471, 0x0303) }, - { USB_DEVICE(0x0471, 0x0304) }, - { USB_DEVICE(0x0471, 0x0307) }, - { USB_DEVICE(0x0471, 0x0308) }, - { USB_DEVICE(0x0471, 0x030C) }, - { USB_DEVICE(0x0471, 0x0310) }, - { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ - { USB_DEVICE(0x0471, 0x0312) }, - { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ - { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ - { USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC PC Camera */ - { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ - { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam 3000 Pro */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ - { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam 4000 Pro */ { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ - { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech/Cisco VT Camera */ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */ - { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech QuickCam */ + + { USB_DEVICE(0x0471, 0x0302) }, /* Philips PCA645VC */ + { USB_DEVICE(0x0471, 0x0303) }, /* Philips PCA646VC */ + { USB_DEVICE(0x0471, 0x0304) }, /* Askey VC010 type 2 */ + { USB_DEVICE(0x0471, 0x0307) }, /* Philips PCVC675K (Vesta) */ + { USB_DEVICE(0x0471, 0x0308) }, /* Philips PCVC680K (Vesta Pro) */ + { USB_DEVICE(0x0471, 0x030C) }, /* Philips PCVC690K (Vesta Pro Scan) */ + { USB_DEVICE(0x0471, 0x0310) }, /* Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) */ + { USB_DEVICE(0x0471, 0x0311) }, /* Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) */ + { USB_DEVICE(0x0471, 0x0312) }, /* Philips PCVC750K (ToUCam Pro Scan) */ + { USB_DEVICE(0x0471, 0x0313) }, /* Philips PCVC720K/40 (ToUCam XS) */ + { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC webcam */ + { USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC webcam */ + + { USB_DEVICE(0x04CC, 0x8116) }, /* Sotec Afina Eye */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ - { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ - { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ - { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ - { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ - { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ - { USB_DEVICE(0x0d81, 0x1900) }, + + { USB_DEVICE(0x069A, 0x0001) }, /* Askey VC010 type 1 */ + + { USB_DEVICE(0x06BE, 0x8116) }, /* AME Co. Afina Eye */ + + { USB_DEVICE(0x0d81, 0x1900) }, /* Visionite VCS-UC300 */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite VCS-UM100 */ + { } }; MODULE_DEVICE_TABLE(usb, pwc_device_table); diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 3198f9624b7c..b8d39b2f777f 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -250,6 +250,7 @@ static void ttusb_dec_handle_irq( struct urb *urb) struct ttusb_dec *dec = urb->context; char *buffer = dec->irq_buffer; int retval; + int index = buffer[4]; switch(urb->status) { case 0: /*success*/ @@ -281,11 +282,11 @@ static void ttusb_dec_handle_irq( struct urb *urb) * this should/could be added later ... * for now lets report each signal as a key down and up */ - if (buffer[4] - 1 < ARRAY_SIZE(rc_keys)) { - dprintk("%s:rc signal:%d\n", __func__, buffer[4]); - input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); + if (index - 1 < ARRAY_SIZE(rc_keys)) { + dprintk("%s:rc signal:%d\n", __func__, index); + input_report_key(dec->rc_input_dev, rc_keys[index - 1], 1); input_sync(dec->rc_input_dev); - input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); + input_report_key(dec->rc_input_dev, rc_keys[index - 1], 0); input_sync(dec->rc_input_dev); } } diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig index 55b06c833667..49b4257487bb 100644 --- a/drivers/media/usb/zr364xx/Kconfig +++ b/drivers/media/usb/zr364xx/Kconfig @@ -7,7 +7,7 @@ config USB_ZR364XX help Say Y here if you want to connect this type of camera to your computer's USB port. - See <file:Documentation/media/v4l-drivers/zr364xx.rst> for more info + See <file:Documentation/admin-guide/media/zr364xx.rst> for more info and list of supported cameras. To compile this driver as a module, choose M here: the diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 39e3fb30ba0b..bf49f83cb86f 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -16,6 +16,15 @@ config VIDEO_V4L2_I2C depends on I2C && VIDEO_V4L2 default y +config VIDEO_V4L2_SUBDEV_API + bool "V4L2 sub-device userspace API" + depends on VIDEO_DEV && MEDIA_CONTROLLER + help + Enables the V4L2 sub-device pad-level userspace API used to configure + video format, size and frame rate between hardware blocks. + + This API is mostly used by camera interfaces in embedded platforms. + config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality on V4L2 drivers" help @@ -31,20 +40,18 @@ config VIDEO_FIXED_MINOR_RANGES When in doubt, say N. -config VIDEO_PCI_SKELETON - tristate "Skeleton PCI V4L2 driver" - depends on PCI - depends on SAMPLES - depends on VIDEO_V4L2 && VIDEOBUF2_CORE - depends on VIDEOBUF2_MEMOPS && VIDEOBUF2_DMA_CONTIG - help - Enable build of the skeleton PCI driver, used as a reference - when developing new drivers. - # Used by drivers that need tuner.ko config VIDEO_TUNER tristate +# Used by drivers that need v4l2-jpeg.ko +config V4L2_JPEG_HELPER + tristate + +# Used by drivers that need v4l2-h264.ko +config V4L2_H264 + tristate + # Used by drivers that need v4l2-mem2mem.ko config V4L2_MEM2MEM_DEV tristate diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 786bd1ec4d1b..2ef0c7c958a2 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -21,9 +21,12 @@ 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_FLASH_LED_CLASS) += v4l2-flash-led-class.o +obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.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 diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index d0e5ebc736f9..9e8eb45a5b03 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -250,9 +250,9 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, @@ -274,6 +274,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, /* YUV planar formats, non contiguous variant */ { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 93d33d1db4e8..b188577db40f 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -9,14 +9,15 @@ #define pr_fmt(fmt) "v4l2-ctrls: " fmt #include <linux/ctype.h> +#include <linux/export.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/export.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> #include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> #define dprintk(vdev, fmt, arg...) do { \ if (!WARN_ON(!(vdev)) && ((vdev)->dev_debug & V4L2_DEV_DEBUG_CTRL)) \ @@ -336,6 +337,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "4.2", "5", "5.1", + "5.2", + "6.0", + "6.1", + "6.2", NULL, }; static const char * const h264_loop_filter[] = { @@ -362,6 +367,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Scalable High Intra", "Stereo High", "Multiview High", + "Constrained High", NULL, }; static const char * const vui_sar_idc[] = { @@ -578,6 +584,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Annex B Start Code", NULL, }; + static const char * const camera_orientation[] = { + "Front", + "Back", + "External", + NULL, + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -703,6 +715,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return hevc_decode_mode; case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return hevc_start_code; + case V4L2_CID_CAMERA_ORIENTATION: + return camera_orientation; default: return NULL; } @@ -1015,6 +1029,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_PAN_SPEED: return "Pan, Speed"; case V4L2_CID_TILT_SPEED: return "Tilt, Speed"; case V4L2_CID_UNIT_CELL_SIZE: return "Unit Cell Size"; + case V4L2_CID_CAMERA_ORIENTATION: return "Camera Orientation"; + case V4L2_CID_CAMERA_SENSOR_ROTATION: return "Camera Sensor Rotation"; /* FM Radio Modulator controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1288,6 +1304,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + case V4L2_CID_CAMERA_ORIENTATION: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -1477,6 +1494,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: case V4L2_CID_RDS_RX_MUSIC_SPEECH: + case V4L2_CID_CAMERA_ORIENTATION: + case V4L2_CID_CAMERA_SENSOR_ROTATION: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; case V4L2_CID_RF_TUNER_PLL_LOCK: @@ -3794,7 +3813,8 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!ctrl->is_int); + if (WARN_ON(!ctrl->is_int)) + return 0; c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -3806,7 +3826,8 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + if (WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64)) + return 0; c.value64 = 0; get_ctrl(ctrl, &c); return c.value64; @@ -4215,7 +4236,8 @@ int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ - WARN_ON(!ctrl->is_int); + if (WARN_ON(!ctrl->is_int)) + return -EINVAL; ctrl->val = val; return set_ctrl(NULL, ctrl, 0); } @@ -4226,7 +4248,8 @@ int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ - WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + if (WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64)) + return -EINVAL; *ctrl->p_new.p_s64 = val; return set_ctrl(NULL, ctrl, 0); } @@ -4237,23 +4260,25 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING); + if (WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING)) + return -EINVAL; strscpy(ctrl->p_new.p_char, s, ctrl->maximum + 1); return set_ctrl(NULL, ctrl, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); -int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl, - const struct v4l2_area *area) +int __v4l2_ctrl_s_ctrl_compound(struct v4l2_ctrl *ctrl, + enum v4l2_ctrl_type type, const void *p) { lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_AREA); - *ctrl->p_new.p_area = *area; + if (WARN_ON(ctrl->type != type)) + return -EINVAL; + memcpy(ctrl->p_new.p, p, ctrl->elems * ctrl->elem_size); return set_ctrl(NULL, ctrl, 0); } -EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_area); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_compound); void v4l2_ctrl_request_complete(struct media_request *req, struct v4l2_ctrl_handler *main_hdl) @@ -4597,3 +4622,42 @@ __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) return 0; } EXPORT_SYMBOL(v4l2_ctrl_poll); + +int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ctrl_ops, + const struct v4l2_fwnode_device_properties *p) +{ + if (p->orientation != V4L2_FWNODE_PROPERTY_UNSET) { + u32 orientation_ctrl; + + switch (p->orientation) { + case V4L2_FWNODE_ORIENTATION_FRONT: + orientation_ctrl = V4L2_CAMERA_ORIENTATION_FRONT; + break; + case V4L2_FWNODE_ORIENTATION_BACK: + orientation_ctrl = V4L2_CAMERA_ORIENTATION_BACK; + break; + case V4L2_FWNODE_ORIENTATION_EXTERNAL: + orientation_ctrl = V4L2_CAMERA_ORIENTATION_EXTERNAL; + break; + default: + return -EINVAL; + } + if (!v4l2_ctrl_new_std_menu(hdl, ctrl_ops, + V4L2_CID_CAMERA_ORIENTATION, + V4L2_CAMERA_ORIENTATION_EXTERNAL, 0, + orientation_ctrl)) + return hdl->error; + } + + if (p->rotation != V4L2_FWNODE_PROPERTY_UNSET) { + if (!v4l2_ctrl_new_std(hdl, ctrl_ops, + V4L2_CID_CAMERA_SENSOR_ROTATION, + p->rotation, p->rotation, 1, + p->rotation)) + return hdl->error; + } + + return hdl->error; +} +EXPORT_SYMBOL(v4l2_ctrl_new_fwnode_properties); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 97b6a3af1361..a593ea0598b5 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -552,6 +552,7 @@ static void determine_valid_ioctls(struct video_device *vdev) (vdev->device_caps & meta_caps); bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; + bool is_io_mc = vdev->device_caps & V4L2_CAP_IO_MC; bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); @@ -725,9 +726,15 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std); if (is_rx) { SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); - SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); - SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); - SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + 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); + } else { + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + } SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); @@ -735,9 +742,15 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid); } if (is_tx) { - SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); - SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); - SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + 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); + } else { + SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); + SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); + SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + } SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout); SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index c69941214bb2..de4287251a89 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -186,7 +186,8 @@ static void v4l2_device_release_subdev_node(struct video_device *vdev) kfree(vdev); } -int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) +int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, + bool read_only) { struct video_device *vdev; struct v4l2_subdev *sd; @@ -215,6 +216,8 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) vdev->fops = &v4l2_subdev_fops; vdev->release = v4l2_device_release_subdev_node; vdev->ctrl_handler = sd->ctrl_handler; + if (read_only) + set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, sd->owner); if (err < 0) { @@ -252,7 +255,7 @@ clean_up: return err; } -EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); +EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes); void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) { diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 8a1e1b95b379..a4c3c77c1894 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -756,6 +756,48 @@ err: } EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link); +int v4l2_fwnode_device_parse(struct device *dev, + struct v4l2_fwnode_device_properties *props) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + u32 val; + int ret; + + memset(props, 0, sizeof(*props)); + + props->orientation = V4L2_FWNODE_PROPERTY_UNSET; + ret = fwnode_property_read_u32(fwnode, "orientation", &val); + if (!ret) { + switch (val) { + case V4L2_FWNODE_ORIENTATION_FRONT: + case V4L2_FWNODE_ORIENTATION_BACK: + case V4L2_FWNODE_ORIENTATION_EXTERNAL: + break; + default: + dev_warn(dev, "Unsupported device orientation: %u\n", val); + return -EINVAL; + } + + props->orientation = val; + dev_dbg(dev, "device orientation: %u\n", val); + } + + props->rotation = V4L2_FWNODE_PROPERTY_UNSET; + ret = fwnode_property_read_u32(fwnode, "rotation", &val); + if (!ret) { + if (val >= 360) { + dev_warn(dev, "Unsupported device rotation: %u\n", val); + return -EINVAL; + } + + props->rotation = val; + dev_dbg(dev, "device rotation: %u\n", val); + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse); + static int v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev, struct v4l2_async_notifier *notifier, @@ -1323,68 +1365,6 @@ out_cleanup: } EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common); -int v4l2_async_register_fwnode_subdev(struct v4l2_subdev *sd, - size_t asd_struct_size, - unsigned int *ports, - unsigned int num_ports, - parse_endpoint_func parse_endpoint) -{ - struct v4l2_async_notifier *notifier; - struct device *dev = sd->dev; - struct fwnode_handle *fwnode; - int ret; - - if (WARN_ON(!dev)) - return -ENODEV; - - fwnode = dev_fwnode(dev); - if (!fwnode_device_is_available(fwnode)) - return -ENODEV; - - notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); - if (!notifier) - return -ENOMEM; - - v4l2_async_notifier_init(notifier); - - if (!ports) { - ret = v4l2_async_notifier_parse_fwnode_endpoints(dev, notifier, - asd_struct_size, - parse_endpoint); - if (ret < 0) - goto out_cleanup; - } else { - unsigned int i; - - for (i = 0; i < num_ports; i++) { - ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(dev, notifier, asd_struct_size, ports[i], parse_endpoint); - if (ret < 0) - goto out_cleanup; - } - } - - ret = v4l2_async_subdev_notifier_register(sd, notifier); - if (ret < 0) - goto out_cleanup; - - ret = v4l2_async_register_subdev(sd); - if (ret < 0) - goto out_unregister; - - sd->subdev_notifier = notifier; - - return 0; - -out_unregister: - v4l2_async_notifier_unregister(notifier); -out_cleanup: - v4l2_async_notifier_cleanup(notifier); - kfree(notifier); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_async_register_fwnode_subdev); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c new file mode 100644 index 000000000000..edf6225f0522 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-h264.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 H264 helpers. + * + * Copyright (C) 2019 Collabora, Ltd. + * + * Author: Boris Brezillon <boris.brezillon@collabora.com> + */ + +#include <linux/module.h> +#include <linux/sort.h> + +#include <media/v4l2-h264.h> + +/** + * v4l2_h264_init_reflist_builder() - Initialize a P/B0/B1 reference list + * builder + * + * @b: the builder context to initialize + * @dec_params: decode parameters control + * @slice_params: first slice parameters control + * @sps: SPS control + * @dpb: DPB to use when creating the reference list + */ +void +v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b, + const struct v4l2_ctrl_h264_decode_params *dec_params, + const struct v4l2_ctrl_h264_slice_params *slice_params, + const struct v4l2_ctrl_h264_sps *sps, + const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]) +{ + int cur_frame_num, max_frame_num; + unsigned int i; + + max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4); + cur_frame_num = slice_params->frame_num; + + memset(b, 0, sizeof(*b)); + if (!(slice_params->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC)) + b->cur_pic_order_count = min(dec_params->bottom_field_order_cnt, + dec_params->top_field_order_cnt); + else if (slice_params->flags & V4L2_H264_SLICE_FLAG_BOTTOM_FIELD) + b->cur_pic_order_count = dec_params->bottom_field_order_cnt; + else + b->cur_pic_order_count = dec_params->top_field_order_cnt; + + for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + u32 pic_order_count; + + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + b->refs[i].pic_num = dpb[i].pic_num; + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + b->refs[i].longterm = true; + + /* + * Handle frame_num wraparound as described in section + * '8.2.4.1 Decoding process for picture numbers' of the spec. + * TODO: This logic will have to be adjusted when we start + * supporting interlaced content. + */ + if (dpb[i].frame_num > cur_frame_num) + b->refs[i].frame_num = (int)dpb[i].frame_num - + max_frame_num; + else + b->refs[i].frame_num = dpb[i].frame_num; + + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) + pic_order_count = min(dpb[i].top_field_order_cnt, + dpb[i].bottom_field_order_cnt); + else if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD) + pic_order_count = dpb[i].bottom_field_order_cnt; + else + pic_order_count = dpb[i].top_field_order_cnt; + + b->refs[i].pic_order_count = pic_order_count; + b->unordered_reflist[b->num_valid] = i; + b->num_valid++; + } + + for (i = b->num_valid; i < ARRAY_SIZE(b->unordered_reflist); i++) + b->unordered_reflist[i] = i; +} +EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder); + +static int v4l2_h264_p_ref_list_cmp(const void *ptra, const void *ptrb, + const void *data) +{ + const struct v4l2_h264_reflist_builder *builder = data; + u8 idxa, idxb; + + idxa = *((u8 *)ptra); + idxb = *((u8 *)ptrb); + + if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES || + idxb >= V4L2_H264_NUM_DPB_ENTRIES)) + return 1; + + if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) { + /* Short term pics first. */ + if (!builder->refs[idxa].longterm) + return -1; + else + return 1; + } + + /* + * Short term pics in descending pic num order, long term ones in + * ascending order. + */ + if (!builder->refs[idxa].longterm) + return builder->refs[idxb].frame_num < + builder->refs[idxa].frame_num ? + -1 : 1; + + return builder->refs[idxa].pic_num < builder->refs[idxb].pic_num ? + -1 : 1; +} + +static int v4l2_h264_b0_ref_list_cmp(const void *ptra, const void *ptrb, + const void *data) +{ + const struct v4l2_h264_reflist_builder *builder = data; + s32 poca, pocb; + u8 idxa, idxb; + + idxa = *((u8 *)ptra); + idxb = *((u8 *)ptrb); + + if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES || + idxb >= V4L2_H264_NUM_DPB_ENTRIES)) + return 1; + + if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) { + /* Short term pics first. */ + if (!builder->refs[idxa].longterm) + return -1; + else + return 1; + } + + /* Long term pics in ascending pic num order. */ + if (builder->refs[idxa].longterm) + return builder->refs[idxa].pic_num < + builder->refs[idxb].pic_num ? + -1 : 1; + + poca = builder->refs[idxa].pic_order_count; + pocb = builder->refs[idxb].pic_order_count; + + /* + * Short term pics with POC < cur POC first in POC descending order + * followed by short term pics with POC > cur POC in POC ascending + * order. + */ + if ((poca < builder->cur_pic_order_count) != + (pocb < builder->cur_pic_order_count)) + return poca < pocb ? -1 : 1; + else if (poca < builder->cur_pic_order_count) + return pocb < poca ? -1 : 1; + + return poca < pocb ? -1 : 1; +} + +static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb, + const void *data) +{ + const struct v4l2_h264_reflist_builder *builder = data; + s32 poca, pocb; + u8 idxa, idxb; + + idxa = *((u8 *)ptra); + idxb = *((u8 *)ptrb); + + if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES || + idxb >= V4L2_H264_NUM_DPB_ENTRIES)) + return 1; + + if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) { + /* Short term pics first. */ + if (!builder->refs[idxa].longterm) + return -1; + else + return 1; + } + + /* Long term pics in ascending pic num order. */ + if (builder->refs[idxa].longterm) + return builder->refs[idxa].pic_num < + builder->refs[idxb].pic_num ? + -1 : 1; + + poca = builder->refs[idxa].pic_order_count; + pocb = builder->refs[idxb].pic_order_count; + + /* + * Short term pics with POC > cur POC first in POC ascending order + * followed by short term pics with POC < cur POC in POC descending + * order. + */ + if ((poca < builder->cur_pic_order_count) != + (pocb < builder->cur_pic_order_count)) + return pocb < poca ? -1 : 1; + else if (poca < builder->cur_pic_order_count) + return pocb < poca ? -1 : 1; + + return poca < pocb ? -1 : 1; +} + +/** + * v4l2_h264_build_p_ref_list() - Build the P reference list + * + * @builder: reference list builder context + * @reflist: 16-bytes array used to store the P reference list. Each entry + * is an index in the DPB + * + * This functions builds the P reference lists. This procedure is describe in + * section '8.2.4 Decoding process for reference picture lists construction' + * of the H264 spec. This function can be used by H264 decoder drivers that + * need to pass a P reference list to the hardware. + */ +void +v4l2_h264_build_p_ref_list(const struct v4l2_h264_reflist_builder *builder, + u8 *reflist) +{ + memcpy(reflist, builder->unordered_reflist, + sizeof(builder->unordered_reflist[0]) * builder->num_valid); + sort_r(reflist, builder->num_valid, sizeof(*reflist), + v4l2_h264_p_ref_list_cmp, NULL, builder); +} +EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list); + +/** + * v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists + * + * @builder: reference list builder context + * @b0_reflist: 16-bytes array used to store the B0 reference list. Each entry + * is an index in the DPB + * @b1_reflist: 16-bytes array used to store the B1 reference list. Each entry + * is an index in the DPB + * + * This functions builds the B0/B1 reference lists. This procedure is described + * in section '8.2.4 Decoding process for reference picture lists construction' + * of the H264 spec. This function can be used by H264 decoder drivers that + * need to pass B0/B1 reference lists to the hardware. + */ +void +v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder, + u8 *b0_reflist, u8 *b1_reflist) +{ + memcpy(b0_reflist, builder->unordered_reflist, + sizeof(builder->unordered_reflist[0]) * builder->num_valid); + sort_r(b0_reflist, builder->num_valid, sizeof(*b0_reflist), + v4l2_h264_b0_ref_list_cmp, NULL, builder); + + memcpy(b1_reflist, builder->unordered_reflist, + sizeof(builder->unordered_reflist[0]) * builder->num_valid); + sort_r(b1_reflist, builder->num_valid, sizeof(*b1_reflist), + v4l2_h264_b1_ref_list_cmp, NULL, builder); + + if (builder->num_valid > 1 && + !memcmp(b1_reflist, b0_reflist, builder->num_valid)) + swap(b1_reflist[0], b1_reflist[1]); +} +EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("V4L2 H264 Helpers"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>"); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index b2ef8e60ea7d..2322f08a98be 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -264,12 +264,13 @@ static void v4l_print_fmtdesc(const void *arg, bool write_only) { const struct v4l2_fmtdesc *p = arg; - pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%.*s'\n", + pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, mbus_code=0x%04x, description='%.*s'\n", p->index, prt_names(p->type, v4l2_type_names), p->flags, (p->pixelformat & 0xff), (p->pixelformat >> 8) & 0xff, (p->pixelformat >> 16) & 0xff, (p->pixelformat >> 24) & 0xff, + p->mbus_code, (int)sizeof(p->description), p->description); } @@ -1085,6 +1086,32 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, return ret; } +static int v4l_g_input(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + + if (vfd->device_caps & V4L2_CAP_IO_MC) { + *(int *)arg = 0; + return 0; + } + + return ops->vidioc_g_input(file, fh, arg); +} + +static int v4l_g_output(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + + if (vfd->device_caps & V4L2_CAP_IO_MC) { + *(int *)arg = 0; + return 0; + } + + return ops->vidioc_g_output(file, fh, arg); +} + static int v4l_s_input(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1094,12 +1121,21 @@ static int v4l_s_input(const struct v4l2_ioctl_ops *ops, ret = v4l_enable_media_source(vfd); if (ret) return ret; + + if (vfd->device_caps & V4L2_CAP_IO_MC) + return *(int *)arg ? -EINVAL : 0; + return ops->vidioc_s_input(file, fh, *(unsigned int *)arg); } static int v4l_s_output(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vfd = video_devdata(file); + + if (vfd->device_caps & V4L2_CAP_IO_MC) + return *(int *)arg ? -EINVAL : 0; + return ops->vidioc_s_output(file, fh, *(unsigned int *)arg); } @@ -1143,6 +1179,14 @@ static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, if (is_valid_ioctl(vfd, VIDIOC_S_STD)) p->capabilities |= V4L2_IN_CAP_STD; + if (vfd->device_caps & V4L2_CAP_IO_MC) { + if (p->index) + return -EINVAL; + strscpy(p->name, vfd->name, sizeof(p->name)); + p->type = V4L2_INPUT_TYPE_CAMERA; + return 0; + } + return ops->vidioc_enum_input(file, fh, p); } @@ -1161,6 +1205,14 @@ static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, if (is_valid_ioctl(vfd, VIDIOC_S_STD)) p->capabilities |= V4L2_OUT_CAP_STD; + if (vfd->device_caps & V4L2_CAP_IO_MC) { + if (p->index) + return -EINVAL; + strscpy(p->name, vfd->name, sizeof(p->name)); + p->type = V4L2_OUTPUT_TYPE_ANALOG; + return 0; + } + return ops->vidioc_enum_output(file, fh, p); } @@ -1421,12 +1473,20 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, struct video_device *vdev = video_devdata(file); struct v4l2_fmtdesc *p = arg; int ret = check_fmt(file, p->type); + u32 mbus_code; u32 cap_mask; if (ret) return ret; ret = -EINVAL; + if (!(vdev->device_caps & V4L2_CAP_IO_MC)) + p->mbus_code = 0; + + mbus_code = p->mbus_code; + CLEAR_AFTER_FIELD(p, type); + p->mbus_code = mbus_code; + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -2683,10 +2743,8 @@ DEFINE_V4L_STUB_FUNC(expbuf) DEFINE_V4L_STUB_FUNC(g_std) DEFINE_V4L_STUB_FUNC(g_audio) DEFINE_V4L_STUB_FUNC(s_audio) -DEFINE_V4L_STUB_FUNC(g_input) DEFINE_V4L_STUB_FUNC(g_edid) DEFINE_V4L_STUB_FUNC(s_edid) -DEFINE_V4L_STUB_FUNC(g_output) DEFINE_V4L_STUB_FUNC(g_audout) DEFINE_V4L_STUB_FUNC(s_audout) DEFINE_V4L_STUB_FUNC(g_jpegcomp) @@ -2708,7 +2766,7 @@ DEFINE_V4L_STUB_FUNC(dv_timings_cap) static const struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), - IOCTL_INFO(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), + IOCTL_INFO(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, 0), IOCTL_INFO(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0), IOCTL_INFO(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), @@ -2735,11 +2793,11 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO(VIDIOC_S_AUDIO, v4l_stub_s_audio, v4l_print_audio, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), IOCTL_INFO(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), - IOCTL_INFO(VIDIOC_G_INPUT, v4l_stub_g_input, v4l_print_u32, 0), + IOCTL_INFO(VIDIOC_G_INPUT, v4l_g_input, v4l_print_u32, 0), IOCTL_INFO(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_G_EDID, v4l_stub_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY), IOCTL_INFO(VIDIOC_S_EDID, v4l_stub_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY), - IOCTL_INFO(VIDIOC_G_OUTPUT, v4l_stub_g_output, v4l_print_u32, 0), + IOCTL_INFO(VIDIOC_G_OUTPUT, v4l_g_output, v4l_print_u32, 0), IOCTL_INFO(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), IOCTL_INFO(VIDIOC_G_AUDOUT, v4l_stub_g_audout, v4l_print_audioout, 0), @@ -2805,13 +2863,11 @@ static struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return vdev->lock; -#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) if (vfh && vfh->m2m_ctx && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) { if (vfh->m2m_ctx->q_lock) return vfh->m2m_ctx->q_lock; } -#endif if (vdev->queue && vdev->queue->lock && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) return vdev->queue->lock; diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c new file mode 100644 index 000000000000..8947fd95c6f1 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * V4L2 JPEG header parser helpers. + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de> + * + * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] + * + * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ + +#include <asm/unaligned.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <media/v4l2-jpeg.h> + +MODULE_DESCRIPTION("V4L2 JPEG header parser helpers"); +MODULE_AUTHOR("Philipp Zabel <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); + +/* Table B.1 - Marker code assignments */ +#define SOF0 0xffc0 /* start of frame */ +#define SOF1 0xffc1 +#define SOF2 0xffc2 +#define SOF3 0xffc3 +#define SOF5 0xffc5 +#define SOF7 0xffc7 +#define JPG 0xffc8 /* extensions */ +#define SOF9 0xffc9 +#define SOF11 0xffcb +#define SOF13 0xffcd +#define SOF15 0xffcf +#define DHT 0xffc4 /* huffman table */ +#define DAC 0xffcc /* arithmetic coding conditioning */ +#define RST0 0xffd0 /* restart */ +#define RST7 0xffd7 +#define SOI 0xffd8 /* start of image */ +#define EOI 0xffd9 /* end of image */ +#define SOS 0xffda /* start of stream */ +#define DQT 0xffdb /* quantization table */ +#define DNL 0xffdc /* number of lines */ +#define DRI 0xffdd /* restart interval */ +#define DHP 0xffde /* hierarchical progression */ +#define EXP 0xffdf /* expand reference */ +#define APP0 0xffe0 /* application data */ +#define APP15 0xffef +#define JPG0 0xfff0 /* extensions */ +#define JPG13 0xfffd +#define COM 0xfffe /* comment */ +#define TEM 0xff01 /* temporary */ + +/** + * struct jpeg_stream - JPEG byte stream + * @curr: current position in stream + * @end: end position, after last byte + */ +struct jpeg_stream { + u8 *curr; + u8 *end; +}; + +/* returns a value that fits into u8, or negative error */ +static int jpeg_get_byte(struct jpeg_stream *stream) +{ + if (stream->curr >= stream->end) + return -EINVAL; + + return *stream->curr++; +} + +/* returns a value that fits into u16, or negative error */ +static int jpeg_get_word_be(struct jpeg_stream *stream) +{ + u16 word; + + if (stream->curr + sizeof(__be16) > stream->end) + return -EINVAL; + + word = get_unaligned_be16(stream->curr); + stream->curr += sizeof(__be16); + + return word; +} + +static int jpeg_skip(struct jpeg_stream *stream, size_t len) +{ + if (stream->curr + len > stream->end) + return -EINVAL; + + stream->curr += len; + + return 0; +} + +static int jpeg_next_marker(struct jpeg_stream *stream) +{ + int byte; + u16 marker = 0; + + while ((byte = jpeg_get_byte(stream)) >= 0) { + marker = (marker << 8) | byte; + /* skip stuffing bytes and REServed markers */ + if (marker == TEM || (marker > 0xffbf && marker < 0xffff)) + return marker; + } + + return byte; +} + +/* this does not advance the current position in the stream */ +static int jpeg_reference_segment(struct jpeg_stream *stream, + struct v4l2_jpeg_reference *segment) +{ + u16 len; + + if (stream->curr + sizeof(__be16) > stream->end) + return -EINVAL; + + len = get_unaligned_be16(stream->curr); + if (stream->curr + len > stream->end) + return -EINVAL; + + segment->start = stream->curr; + segment->length = len; + + return 0; +} + +static int v4l2_jpeg_decode_subsampling(u8 nf, u8 h_v) +{ + if (nf == 1) + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + + /* no chroma subsampling for 4-component images */ + if (nf == 4 && h_v != 0x11) + return -EINVAL; + + switch (h_v) { + case 0x11: + return V4L2_JPEG_CHROMA_SUBSAMPLING_444; + case 0x21: + return V4L2_JPEG_CHROMA_SUBSAMPLING_422; + case 0x22: + return V4L2_JPEG_CHROMA_SUBSAMPLING_420; + case 0x41: + return V4L2_JPEG_CHROMA_SUBSAMPLING_411; + default: + return -EINVAL; + } +} + +static int jpeg_parse_frame_header(struct jpeg_stream *stream, u16 sof_marker, + struct v4l2_jpeg_frame_header *frame_header) +{ + int len = jpeg_get_word_be(stream); + + if (len < 0) + return len; + /* Lf = 8 + 3 * Nf, Nf >= 1 */ + if (len < 8 + 3) + return -EINVAL; + + if (frame_header) { + /* Table B.2 - Frame header parameter sizes and values */ + int p, y, x, nf; + int i; + + p = jpeg_get_byte(stream); + if (p < 0) + return p; + /* + * Baseline DCT only supports 8-bit precision. + * Extended sequential DCT also supports 12-bit precision. + */ + if (p != 8 && (p != 12 || sof_marker != SOF1)) + return -EINVAL; + + y = jpeg_get_word_be(stream); + if (y < 0) + return y; + if (y == 0) + return -EINVAL; + + x = jpeg_get_word_be(stream); + if (x < 0) + return x; + if (x == 0) + return -EINVAL; + + nf = jpeg_get_byte(stream); + if (nf < 0) + return nf; + /* + * The spec allows 1 <= Nf <= 255, but we only support up to 4 + * components. + */ + if (nf < 1 || nf > V4L2_JPEG_MAX_COMPONENTS) + return -EINVAL; + if (len != 8 + 3 * nf) + return -EINVAL; + + frame_header->precision = p; + frame_header->height = y; + frame_header->width = x; + frame_header->num_components = nf; + + for (i = 0; i < nf; i++) { + struct v4l2_jpeg_frame_component_spec *component; + int c, h_v, tq; + + c = jpeg_get_byte(stream); + if (c < 0) + return c; + + h_v = jpeg_get_byte(stream); + if (h_v < 0) + return h_v; + if (i == 0) { + int subs; + + subs = v4l2_jpeg_decode_subsampling(nf, h_v); + if (subs < 0) + return subs; + frame_header->subsampling = subs; + } else if (h_v != 0x11) { + /* all chroma sampling factors must be 1 */ + return -EINVAL; + } + + tq = jpeg_get_byte(stream); + if (tq < 0) + return tq; + + component = &frame_header->component[i]; + component->component_identifier = c; + component->horizontal_sampling_factor = + (h_v >> 4) & 0xf; + component->vertical_sampling_factor = h_v & 0xf; + component->quantization_table_selector = tq; + } + } else { + return jpeg_skip(stream, len - 2); + } + + return 0; +} + +static int jpeg_parse_scan_header(struct jpeg_stream *stream, + struct v4l2_jpeg_scan_header *scan_header) +{ + size_t skip; + int len = jpeg_get_word_be(stream); + + if (len < 0) + return len; + /* Ls = 8 + 3 * Ns, Ns >= 1 */ + if (len < 6 + 2) + return -EINVAL; + + if (scan_header) { + int ns; + int i; + + ns = jpeg_get_byte(stream); + if (ns < 0) + return ns; + if (ns < 1 || ns > 4 || len != 6 + 2 * ns) + return -EINVAL; + + scan_header->num_components = ns; + + for (i = 0; i < ns; i++) { + struct v4l2_jpeg_scan_component_spec *component; + int cs, td_ta; + + cs = jpeg_get_byte(stream); + if (cs < 0) + return cs; + + td_ta = jpeg_get_byte(stream); + if (td_ta < 0) + return td_ta; + + component = &scan_header->component[i]; + component->component_selector = cs; + component->dc_entropy_coding_table_selector = + (td_ta >> 4) & 0xf; + component->ac_entropy_coding_table_selector = + td_ta & 0xf; + } + + skip = 3; /* skip Ss, Se, Ah, and Al */ + } else { + skip = len - 2; + } + + return jpeg_skip(stream, skip); +} + +/* B.2.4.1 Quantization table-specification syntax */ +static int jpeg_parse_quantization_tables(struct jpeg_stream *stream, + u8 precision, + struct v4l2_jpeg_reference *tables) +{ + int len = jpeg_get_word_be(stream); + + if (len < 0) + return len; + /* Lq = 2 + n * 65 (for baseline DCT), n >= 1 */ + if (len < 2 + 65) + return -EINVAL; + + len -= 2; + while (len >= 65) { + u8 pq, tq, *qk; + int ret; + int pq_tq = jpeg_get_byte(stream); + + if (pq_tq < 0) + return pq_tq; + + /* quantization table element precision */ + pq = (pq_tq >> 4) & 0xf; + /* + * Only 8-bit Qk values for 8-bit sample precision. Extended + * sequential DCT with 12-bit sample precision also supports + * 16-bit Qk values. + */ + if (pq != 0 && (pq != 1 || precision != 12)) + return -EINVAL; + + /* quantization table destination identifier */ + tq = pq_tq & 0xf; + if (tq > 3) + return -EINVAL; + + /* quantization table element */ + qk = stream->curr; + ret = jpeg_skip(stream, pq ? 128 : 64); + if (ret < 0) + return -EINVAL; + + if (tables) { + tables[tq].start = qk; + tables[tq].length = pq ? 128 : 64; + } + + len -= pq ? 129 : 65; + } + + return 0; +} + +/* B.2.4.2 Huffman table-specification syntax */ +static int jpeg_parse_huffman_tables(struct jpeg_stream *stream, + struct v4l2_jpeg_reference *tables) +{ + int mt; + int len = jpeg_get_word_be(stream); + + if (len < 0) + return len; + /* Table B.5 - Huffman table specification parameter sizes and values */ + if (len < 2 + 17) + return -EINVAL; + + for (len -= 2; len >= 17; len -= 17 + mt) { + u8 tc, th, *table; + int tc_th = jpeg_get_byte(stream); + int i, ret; + + if (tc_th < 0) + return tc_th; + + /* table class - 0 = DC, 1 = AC */ + tc = (tc_th >> 4) & 0xf; + if (tc > 1) + return -EINVAL; + + /* huffman table destination identifier */ + th = tc_th & 0xf; + /* only two Huffman tables for baseline DCT */ + if (th > 1) + return -EINVAL; + + /* BITS - number of Huffman codes with length i */ + table = stream->curr; + mt = 0; + for (i = 0; i < 16; i++) { + int li; + + li = jpeg_get_byte(stream); + if (li < 0) + return li; + + mt += li; + } + /* HUFFVAL - values associated with each Huffman code */ + ret = jpeg_skip(stream, mt); + if (ret < 0) + return ret; + + if (tables) { + tables[(tc << 1) | th].start = table; + tables[(tc << 1) | th].length = stream->curr - table; + } + } + + return jpeg_skip(stream, len - 2); +} + +/* B.2.4.4 Restart interval definition syntax */ +static int jpeg_parse_restart_interval(struct jpeg_stream *stream, + u16 *restart_interval) +{ + int len = jpeg_get_word_be(stream); + int ri; + + if (len < 0) + return len; + if (len != 4) + return -EINVAL; + + ri = jpeg_get_word_be(stream); + if (ri < 0) + return ri; + + *restart_interval = ri; + + return 0; +} + +static int jpeg_skip_segment(struct jpeg_stream *stream) +{ + int len = jpeg_get_word_be(stream); + + if (len < 0) + return len; + if (len < 2) + return -EINVAL; + + return jpeg_skip(stream, len - 2); +} + +/** + * jpeg_parse_header - locate marker segments and optionally parse headers + * @buf: address of the JPEG buffer, should start with a SOI marker + * @len: length of the JPEG buffer + * @out: returns marker segment positions and optionally parsed headers + * + * The out->scan_header pointer must be initialized to NULL or point to a valid + * v4l2_jpeg_scan_header structure. The out->huffman_tables and + * out->quantization_tables pointers must be initialized to NULL or point to a + * valid array of 4 v4l2_jpeg_reference structures each. + * + * Returns 0 or negative error if parsing failed. + */ +int v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out) +{ + struct jpeg_stream stream; + int marker; + int ret = 0; + + stream.curr = buf; + stream.end = stream.curr + len; + + out->num_dht = 0; + out->num_dqt = 0; + + /* the first marker must be SOI */ + marker = jpeg_next_marker(&stream); + if (marker < 0) + return marker; + if (marker != SOI) + return -EINVAL; + + /* loop through marker segments */ + while ((marker = jpeg_next_marker(&stream)) >= 0) { + switch (marker) { + /* baseline DCT, extended sequential DCT */ + case SOF0 ... SOF1: + ret = jpeg_reference_segment(&stream, &out->sof); + if (ret < 0) + return ret; + ret = jpeg_parse_frame_header(&stream, marker, + &out->frame); + break; + /* progressive, lossless */ + case SOF2 ... SOF3: + /* differential coding */ + case SOF5 ... SOF7: + /* arithmetic coding */ + case SOF9 ... SOF11: + case SOF13 ... SOF15: + case DAC: + case TEM: + return -EINVAL; + + case DHT: + ret = jpeg_reference_segment(&stream, + &out->dht[out->num_dht++ % 4]); + if (ret < 0) + return ret; + ret = jpeg_parse_huffman_tables(&stream, + out->huffman_tables); + break; + case DQT: + ret = jpeg_reference_segment(&stream, + &out->dqt[out->num_dqt++ % 4]); + if (ret < 0) + return ret; + ret = jpeg_parse_quantization_tables(&stream, + out->frame.precision, + out->quantization_tables); + break; + case DRI: + ret = jpeg_parse_restart_interval(&stream, + &out->restart_interval); + break; + + case SOS: + ret = jpeg_reference_segment(&stream, &out->sos); + if (ret < 0) + return ret; + ret = jpeg_parse_scan_header(&stream, out->scan); + /* + * stop parsing, the scan header marks the beginning of + * the entropy coded segment + */ + out->ecs_offset = stream.curr - (u8 *)buf; + return ret; + + /* markers without parameters */ + case RST0 ... RST7: /* restart */ + case SOI: /* start of image */ + case EOI: /* end of image */ + break; + + /* skip unknown or unsupported marker segments */ + default: + ret = jpeg_skip_segment(&stream); + break; + } + if (ret < 0) + return ret; + } + + return marker; +} +EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header); + +/** + * v4l2_jpeg_parse_frame_header - parse frame header + * @buf: address of the frame header, after the SOF0 marker + * @len: length of the frame header + * @frame_header: returns the parsed frame header + * + * Returns 0 or negative error if parsing failed. + */ +int v4l2_jpeg_parse_frame_header(void *buf, size_t len, + struct v4l2_jpeg_frame_header *frame_header) +{ + struct jpeg_stream stream; + + stream.curr = buf; + stream.end = stream.curr + len; + return jpeg_parse_frame_header(&stream, SOF0, frame_header); +} +EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header); + +/** + * v4l2_jpeg_parse_scan_header - parse scan header + * @buf: address of the scan header, after the SOS marker + * @len: length of the scan header + * @scan_header: returns the parsed scan header + * + * Returns 0 or negative error if parsing failed. + */ +int v4l2_jpeg_parse_scan_header(void *buf, size_t len, + struct v4l2_jpeg_scan_header *scan_header) +{ + struct jpeg_stream stream; + + stream.curr = buf; + stream.end = stream.curr + len; + return jpeg_parse_scan_header(&stream, scan_header); +} +EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header); + +/** + * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment + * @buf: address of the quantization table segment, after the DQT marker + * @len: length of the quantization table segment + * @precision: sample precision (P) in bits per component + * @q_tables: returns four references into the buffer for the + * four possible quantization table destinations + * + * Returns 0 or negative error if parsing failed. + */ +int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision, + struct v4l2_jpeg_reference *q_tables) +{ + struct jpeg_stream stream; + + stream.curr = buf; + stream.end = stream.curr + len; + return jpeg_parse_quantization_tables(&stream, precision, q_tables); +} +EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables); + +/** + * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment + * @buf: address of the Huffman table segment, after the DHT marker + * @len: length of the Huffman table segment + * @huffman_tables: returns four references into the buffer for the + * four possible Huffman table destinations, in + * the order DC0, DC1, AC0, AC1 + * + * Returns 0 or negative error if parsing failed. + */ +int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len, + struct v4l2_jpeg_reference *huffman_tables) +{ + struct jpeg_stream stream; + + stream.curr = buf; + stream.end = stream.curr + len; + return jpeg_parse_huffman_tables(&stream, huffman_tables); +} +EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables); diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 0fffdd3ce6a4..ba2f2b8dcc8c 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -309,6 +309,101 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); +int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, + struct media_pad *sink) +{ + struct fwnode_handle *endpoint; + struct v4l2_subdev *sink_sd; + + if (!(sink->flags & MEDIA_PAD_FL_SINK) || + !is_media_entity_v4l2_subdev(sink->entity)) + return -EINVAL; + + sink_sd = media_entity_to_v4l2_subdev(sink->entity); + + fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) { + struct fwnode_handle *remote_ep; + int src_idx, sink_idx, ret; + struct media_pad *src; + + src_idx = media_entity_get_fwnode_pad(&src_sd->entity, + endpoint, + MEDIA_PAD_FL_SOURCE); + if (src_idx < 0) + continue; + + remote_ep = fwnode_graph_get_remote_endpoint(endpoint); + if (!remote_ep) + continue; + + /* + * ask the sink to verify it owns the remote endpoint, + * and translate to a sink pad. + */ + sink_idx = media_entity_get_fwnode_pad(&sink_sd->entity, + remote_ep, + MEDIA_PAD_FL_SINK); + fwnode_handle_put(remote_ep); + + if (sink_idx < 0 || sink_idx != sink->index) + continue; + + /* + * the source endpoint corresponds to one of its source pads, + * the source endpoint connects to an endpoint at the sink + * entity, and the sink endpoint corresponds to the sink + * pad requested, so we have found an endpoint connection + * that works, create the media link for it. + */ + + src = &src_sd->entity.pads[src_idx]; + + /* skip if link already exists */ + if (media_entity_find_link(src, sink)) + continue; + + dev_dbg(sink_sd->dev, "creating link %s:%d -> %s:%d\n", + src_sd->entity.name, src_idx, + sink_sd->entity.name, sink_idx); + + ret = media_create_pad_link(&src_sd->entity, src_idx, + &sink_sd->entity, sink_idx, 0); + if (ret) { + dev_err(sink_sd->dev, + "link %s:%d -> %s:%d failed with %d\n", + src_sd->entity.name, src_idx, + sink_sd->entity.name, sink_idx, ret); + + fwnode_handle_put(endpoint); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad); + +int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd, + struct v4l2_subdev *sink_sd) +{ + unsigned int i; + + for (i = 0; i < sink_sd->entity.num_pads; i++) { + struct media_pad *pad = &sink_sd->entity.pads[i]; + int ret; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + continue; + + ret = v4l2_create_fwnode_links_to_pad(src_sd, pad); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links); + /* ----------------------------------------------------------------------------- * Pipeline power management * diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 8986c31176e9..62ac9424c92a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -504,12 +504,21 @@ void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev, if (WARN_ON(!src_buf || !dst_buf)) goto unlock; - v4l2_m2m_buf_done(src_buf, state); dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; if (!dst_buf->is_held) { v4l2_m2m_dst_buf_remove(m2m_ctx); v4l2_m2m_buf_done(dst_buf, state); } + /* + * If the request API is being used, returning the OUTPUT + * (src) buffer will wake-up any process waiting on the + * request file descriptor. + * + * Therefore, return the CAPTURE (dst) buffer first, + * to avoid signalling the request file descriptor + * before the CAPTURE buffer is done. + */ + v4l2_m2m_buf_done(src_buf, state); schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); unlock: spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index a376b351135f..6b989fe5a0a9 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -15,6 +15,7 @@ #include <linux/types.h> #include <linux/videodev2.h> #include <linux/export.h> +#include <linux/version.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -22,24 +23,22 @@ #include <media/v4l2-fh.h> #include <media/v4l2-event.h> +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) if (sd->entity.num_pads) { fh->pad = v4l2_subdev_alloc_pad_config(sd); if (fh->pad == NULL) return -ENOMEM; } -#endif + return 0; } static void subdev_fh_free(struct v4l2_subdev_fh *fh) { -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) v4l2_subdev_free_pad_config(fh->pad); fh->pad = NULL; -#endif } static int subdev_open(struct file *file) @@ -111,6 +110,17 @@ static int subdev_close(struct file *file) return 0; } +#else /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static int subdev_open(struct file *file) +{ + return -ENODEV; +} + +static int subdev_close(struct file *file) +{ + return -ENODEV; +} +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ static inline int check_which(u32 which) { @@ -324,17 +334,27 @@ const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = { }; EXPORT_SYMBOL(v4l2_subdev_call_wrappers); +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_fh *vfh = file->private_data; -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); -#endif + bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); int rval; switch (cmd) { + case VIDIOC_SUBDEV_QUERYCAP: { + struct v4l2_subdev_capability *cap = arg; + + memset(cap->reserved, 0, sizeof(cap->reserved)); + cap->version = LINUX_VERSION_CODE; + cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0; + + return 0; + } + case VIDIOC_QUERYCTRL: /* * TODO: this really should be folded into v4l2_queryctrl (this @@ -465,7 +485,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return ret; } -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) case VIDIOC_SUBDEV_G_FMT: { struct v4l2_subdev_format *format = arg; @@ -477,6 +496,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_FMT: { struct v4l2_subdev_format *format = arg; + if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format); @@ -504,6 +526,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + memset(crop->reserved, 0, sizeof(crop->reserved)); memset(&sel, 0, sizeof(sel)); sel.which = crop->which; @@ -545,6 +570,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval *fi = arg; + if (ro_subdev) + return -EPERM; + memset(fi->reserved, 0, sizeof(fi->reserved)); return v4l2_subdev_call(sd, video, s_frame_interval, arg); } @@ -568,6 +596,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_SELECTION: { struct v4l2_subdev_selection *sel = arg; + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( sd, pad, set_selection, subdev_fh->pad, sel); @@ -604,6 +635,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, video, g_dv_timings, arg); case VIDIOC_SUBDEV_S_DV_TIMINGS: + if (ro_subdev) + return -EPERM; + return v4l2_subdev_call(sd, video, s_dv_timings, arg); case VIDIOC_SUBDEV_G_STD: @@ -612,6 +646,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_STD: { v4l2_std_id *std = arg; + if (ro_subdev) + return -EPERM; + return v4l2_subdev_call(sd, video, s_std, *std); } @@ -627,7 +664,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); -#endif + default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } @@ -667,6 +704,22 @@ static long subdev_compat_ioctl32(struct file *file, unsigned int cmd, } #endif +#else /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static long subdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -ENODEV; +} + +#ifdef CONFIG_COMPAT +static long subdev_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -ENODEV; +} +#endif +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ + static __poll_t subdev_poll(struct file *file, poll_table *wait) { struct video_device *vdev = video_devdata(file); @@ -696,6 +749,28 @@ const struct v4l2_file_operations v4l2_subdev_fops = { }; #ifdef CONFIG_MEDIA_CONTROLLER + +int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity, + struct fwnode_endpoint *endpoint) +{ + struct fwnode_handle *fwnode; + struct v4l2_subdev *sd; + + if (!is_media_entity_v4l2_subdev(entity)) + return -EINVAL; + + sd = media_entity_to_v4l2_subdev(entity); + + fwnode = fwnode_graph_get_port_parent(endpoint->local_fwnode); + fwnode_handle_put(fwnode); + + if (dev_fwnode(sd->dev) == fwnode) + return endpoint->port; + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_get_fwnode_pad_1_to_1); + int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct media_link *link, struct v4l2_subdev_format *source_fmt, |